javaの単一の例のモードの実現方法

13492 ワード

単例(Singleton)とは、一度だけ実装されるクラスのことです。
 
   一例をどうやって実現しますか?
   単例類でよく使われる実現方法は以下のいくつかあります。
 
     1、餓漢式:クラスロード時に一例を作成する
     1.1、開示の静的定数の一例オブジェクト         
/**
 * *
 * 

* * *

StaticFinalSingleton , StaticFinalSingleton * * ( , )
* * @Copyright 2011 * * */ public class StaticFinalSingleton { /** * */ public static final StaticFinalSingleton INSTANCE = new StaticFinalSingleton(); /** * */ private StaticFinalSingleton() { } }
  
 
   1.2、静的工場法による私有静的定数の一例オブジェクトの取得
/**
 * *
 * 

* * *

StaticFinalSingleton , StaticFinalSingleton * * ( , )
* * @Copyright 2011 * * */ public class StaticFinalSingleton { /** * */ private static final StaticFinalSingleton INSTANCE = new StaticFinalSingleton(); /** * */ private StaticFinalSingleton() { } /** * StaticFinalSingleton */ public static StaticFinalSingleton getInstanceFactory() { return INSTANCE; } }
 
           2、怠け者式:遅延荷重作成の一例      2.1、静的同期ロックに基づくlazy initialization単一の例類
     この方法はあまり使われていません。実際には合併度に大きく影響し、性能が劣るからです。
     
 /**
 * 

*

lazy initialization :
* , ,
* * @Copyright 2011 * */ public class SynLockSingleton { /** * */ private static SynLockSingleton INSTANCE = null; /** * */ private SynLockSingleton() { } /** * */ public static synchronized SynLockSingleton getInstanceFactory() { if (INSTANCE == null) { INSTANCE = new SynLockSingleton(); } return INSTANCE; } }
  
     2.2、ダブルチェックロック機構に基づくlazy initialization単例類
   ダブルチェックロックはjdk 1.5および以上のバージョンだけが一例の効果を達成することができますので、一般的ではありません。
   
 /**
 * 

*

lazy initialization :
* jdk 1.5 ,
* * @Copyright 2011 * */ public class DCLSingleton { private static DCLSingleton INSTANCE = null; /** * */ private DCLSingleton() { } /** * */ public static DCLSingleton getInstance() { if (INSTANCE == null) { synchronized (DCLSingleton.class) { if (INSTANCE == null) { INSTANCE = new DCLSingleton(); } } } return INSTANCE; } }
   2.3、静的な内部類に基づくlazy initialization単例類
/**
 * 

*

lazy initialization
*
* * @Copyright 2011 * */ public class StaticInnerSingleton { /** * */ private StaticInnerSingleton() { } private static final class InnerBuildSingleton { private static final StaticInnerSingleton INSTANCE = new StaticInnerSingleton(); } /** * */ public static StaticInnerSingleton getInstanceFactory() { return InnerBuildSingleton.INSTANCE; } }
 
   
   3、列挙に基づいて一例を実現する。
 
    エニュメレート・インプリメンテーションの例を使用します。エニュメレーションの中に一つの要素しかないなら、一例の設計モードを使用して、java 1.5以上の機能を提供します。
 
    このような方法は、最も効果的で安全な単一の実施形態であるべきであり、JVMによって単例の初期化と管理が制御され、反射によって再び単例が生成される可能性が回避される。
 /**
 * 

*

: ,
* * @Copyright 2011 * */ public enum EnumSingleton { /** * INSTANCE , EnumSingleton.INSTANCE */ INSTANCE; // , void doSomething() { } private EnumSingleton() { } public static void main(String[] args) { // EnumSingleton.INSTANCE.doSomething(); } }
 
 
 二、Sington類のその他の注意事項  
1、反射によるSington類の追加例  
 
   エニュメレート・インプリメンテーションではなく、いずれも一つの問題に直面しており、javaの反射機構を通じて、この単一の例を生成することができる追加の例である。
  
    1、例を挙げる    
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class StaticFinalSingleton {
	/** *            */
	public static final StaticFinalSingleton INSTANCE = new StaticFinalSingleton();

	/** *        */
	private StaticFinalSingleton() {
	}

	/** *                * * @param String * className * @return Object */
	@SuppressWarnings("unchecked")
	public static Object getReflectInstance(String className)
			throws ClassNotFoundException, IllegalArgumentException,
			InstantiationException, IllegalAccessException,
			InvocationTargetException {
		Class c1 = Class.forName(className);
		Constructor[] cons = c1.getDeclaredConstructors();
		Constructor cc1 = cons[0];
		cc1.setAccessible(true);
		return cc1.newInstance(null);
	}

	public static void main(String[] args) {
		try {
			//         
			StaticFinalSingleton s1 = StaticFinalSingleton.INSTANCE;
			//     ,          
			StaticFinalSingleton s2 = (StaticFinalSingleton) getReflectInstance("StaticFinalSingleton");
			System.out.println("object instance --s1=" + s1 + ",s2=" + s2);
			System.out.println("object instance (s1==s2)=" + (s1 == s2));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
     
 
     出力は以下の通りです
     object instance--s 1=StaticFinalSingleton@de6ceds 2=StaticFinalSingleton@c17164     object instance(s 1=s 2)=false
   
    1.2解決方法     
     可能な解決方法の一つは、プライベートのコンストラクターの中でチェックして、すでに一例クラスが実用化されているかどうかを判断します。もし繰り返し実用化されたら、RuntimeExceptionは異常です。
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class StaticFinalSingleton {
 private static boolean isFirstFlag = false;
 /** *            */
 public static final StaticFinalSingleton INSTANCE = new StaticFinalSingleton();

 /** *        */
 private StaticFinalSingleton() {
  if (isFirstFlag) {
   throw new java.lang.RuntimeException(
     "can not initialization class=StaticFinalSingleton");
  } else {
   isFirstFlag = true; // do initialization work
  }
 }

 /** *                * * @param String * className * @return Object */
 @SuppressWarnings("unchecked")
 public static Object getReflectInstance(String className)
   throws ClassNotFoundException, IllegalArgumentException,
   InstantiationException, IllegalAccessException,
   InvocationTargetException {
  Class c1 = Class.forName(className);
  Constructor[] cons = c1.getDeclaredConstructors();
  Constructor cc1 = cons[0];
  cc1.setAccessible(true);
  return cc1.newInstance(null);
 }
 public static void main(String[] args) {
  try {
   //         
   StaticFinalSingleton s1 = StaticFinalSingleton.INSTANCE;
   //     ,          
   StaticFinalSingleton s2 = (StaticFinalSingleton) getReflectInstance("StaticFinalSingleton");
   System.out.println("object instance --s1=" + s1 + ",s2=" + s2);
   System.out.println("object instance (s1==s2)=" + (s1 == s2));
  } catch (Exception e) {
   e.printStackTrace();

  }
 }
}

 
     この場合、反射により追加の一例を作成することができません。
      出力は以下の通りです。
    
     java.lang.reflect.Invocation Target Exception at sun.reflect.NativeConstruct Access orImpl.newInstance 0(Native Method) at sun.reflect.NativeConstructor Access orImpl.newInstance(NativeConstructor Access orImpl.java:39) at sun.reflegating.Delegating ControuctoAccess orImpl.newInstance(Delegating ControuctoAccess orImpl.java:27) at java.lang.reflect.cn struct.newInstance(Controuctor.java:513) at Static FinalSingleton.getReflectInstance(Static FinalSingleton.java:47) at Static Final Singleton.main(Static Final Singleton.java:55)Caused by:java.lang.RuntimeException:can not initiazation class=Static FinalSingleton at Static FinalSingleton.(Static FinalSingleton.java:23) ... 6 more
  
  2、複数のクラスのローディングは単例類のローディングを実現する。
      
      多くの場合、複数の種類のロード器を使用するのは一般的です。servlet容器を含めています。ですから、あなたの単一の例のクラスを実現する時、どんなに多くの場合があります。 注意してください。最終的には複数の単例類のインスタンスが得られます。あなたの単例クラスが同じ種類のマウント器にしか入れられないことを確認するには、このタイプのロード器を自分で指定しなければなりません。
 
 public class StaticFinalSingleton {
 public static final StaticFinalSingleton INSTANCE = new StaticFinalSingleton();

 /**
  *       
  */
 private StaticFinalSingleton() {
 }

 /**
  *   getClass              
  * 
  * @param String
  *            className
  * @return Class
  */
 @SuppressWarnings("unused")
 private static Class getClass(String classname)
   throws ClassNotFoundException {
  ClassLoader classLoader = Thread.currentThread()
    .getContextClassLoader();
  if (classLoader == null)
   classLoader = StaticFinalSingleton.class.getClassLoader();
  return (classLoader.loadClass(classname));
 }

}

 
    方法は現在のスレッドをその種類のローダーに関連づけてみます。classloaderがnullであれば、この方法は単例クラスのベースクラスに入るものと同じ種類のローダーを使います。
 
3、単例類の序列化         Singleton類を序列化できるようにするためには、Serializableインターフェースを実現するだけでは不十分です。Singletonの単一の例を維持するためには、SingletonクラスにreadResolive方法を提供しなければなりません。そうでなければ、プロローグの実例は、毎回反プログレッシブ化されるたびに新しいインスタンスが発生します。Singletonも例外ではない。
   3.1例
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class StaticFinalSingleton implements java.io.Serializable {
 private static final long serialVersionUID = 2474672098172796959L;
 public static final StaticFinalSingleton INSTANCE = new StaticFinalSingleton();

 /** *        */
 private StaticFinalSingleton() {

 }

 public static void main(String[] args) {

  ObjectOutputStream objectOutputStream = null;
  ObjectInputStream objectInputStream = null;
  try {
   //    
   objectOutputStream = new ObjectOutputStream(new FileOutputStream(
     "C:\\StaticFinalSingleton.obj"));
   StaticFinalSingleton singleton1 = StaticFinalSingleton.INSTANCE;
   objectOutputStream.writeObject(singleton1);
   objectOutputStream.close();
   //     
   objectInputStream = new ObjectInputStream(new FileInputStream(
     "C:\\StaticFinalSingleton.obj"));
   StaticFinalSingleton singleton2 = (StaticFinalSingleton) objectInputStream
     .readObject();
   objectInputStream.close();
   //          
   System.out.println("object instance (singleton1==singleton2)="
     + (singleton1 == singleton2));
  } catch (Exception e) {

  } finally {
   if (objectOutputStream != null) {
    try {
     objectOutputStream.close();
    } catch (IOException e) {
    }
   }
   if (objectInputStream != null) {
    try {
     objectInputStream.close();
    } catch (IOException e) {
    }
   }
  }

 }
}

        出力は以下の通りです  object instance(singleton 1=singleton 2)=false
 
3.2解決方法
 
      Serializableインターフェースには確かにこのような二つの特殊な方法があります。
     
      Object writeReplace()throws Object Stream Exception;
このwriteReplace方法は、プログレッシブによって呼び出され、この方法が存在するならば、プログレッシブオブジェクトのクラスで定義された方法でアクセスできることが前提である。したがって、この方法は、プライベート(prvate)、保護された(protected)、およびプライベート(package-prvate)アクセスを持つことができる。この方法のサブクラスのアクセスは、javaアクセス規則に従う。
 
      この特別な方法は、ストリームからクラスの一例を読み取る際に、代替クラスが使用すべき正確な署名を指定する必要がある。
      Object readResolive()throws Object Stream Exception;
      このreadResove方法は、writeReplaceと同じコールルールとアクセスルールに従います。
 
      上記の2つの方法は出現する限り、以下の2つの方法(この2つの方法の本質的な意味はシーケンスと逆シーケンスのオブジェクトを置き換えることです)を履修し、それらを実行しますが、最後に得られた結果は、writeReplace、readResoveの2つの方法で書き込みまたは読み出しの対象となります。
       
      prvate void writeObject(java.io.Object Output Stream out)throws IOException
      prvate void readObject(java.io.Object Input Stream in)throws IOException、Class NotFoundException;
    
      また、writeObjectとreadObjectはペアで実現する必要がありますが、writeReplaceとreadResoliveはペアで出現する必要がなく、単独で使うのが一般的です。この四つの方法が同時に現れたら、最後に書き込みと読み出しの結果はwriteReplaceとreadResolive方法の結果に準じる。
したがって、私たちはSingleton類のためにreadResoliveを追加します。
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class StaticFinalSingleton implements java.io.Serializable {
 private static final long serialVersionUID = 2474672098172796959L;
 public static final StaticFinalSingleton INSTANCE = new StaticFinalSingleton();

 /** *        */
 private StaticFinalSingleton() {

 }

 /**
  *          INSTANCE       ,        
  * 
  * @param String
  *            className
  * @return Class
  */
 private Object readResolve() {
  return INSTANCE;
 }

 public static void main(String[] args) {

  ObjectOutputStream objectOutputStream = null;
  ObjectInputStream objectInputStream = null;
  try {
   //    
   objectOutputStream = new ObjectOutputStream(new FileOutputStream(
     "C:\\StaticFinalSingleton.obj"));
   StaticFinalSingleton singleton1 = StaticFinalSingleton.INSTANCE;
   objectOutputStream.writeObject(singleton1);
   objectOutputStream.close();
   //     
   objectInputStream = new ObjectInputStream(new FileInputStream(
     "C:\\StaticFinalSingleton.obj"));
   StaticFinalSingleton singleton2 = (StaticFinalSingleton) objectInputStream
     .readObject();
   objectInputStream.close();
   //          
   System.out.println("object instance (singleton1==singleton2)="
     + (singleton1 == singleton2));
  } catch (Exception e) {

  } finally {
   if (objectOutputStream != null) {
    try {
     objectOutputStream.close();
    } catch (IOException e) {
    }
   }
   if (objectInputStream != null) {
    try {
     objectInputStream.close();
    } catch (IOException e) {
    }
   }
  }

 }
}
  出力は以下の通りです。
object instance(singleton 1=singleton 2)=true