[デザインモード]-シングルトンモード

1.シングルトンモードの概要

1.シングルトンパターンとは何ですか

シングルトンモード(シングルトン)とは、クラスにインスタンスが1つしかないことを確認し、コードを介してグローバルアクセスポイント提供することを指します実際、一般的に言えば、シングルトンパターンデザインを実装するクラスは一度だけ初期化され、すべてのユーザーがこのインスタンスを取得できるようにメソッドを提供する必要があります。

2.シングルトンモードのアプリケーションシナリオ

「デザインパターン」という本では、シングルトンパターンは次の2つのシナリオに適用できると述べられています。

  1. クラスが持つことができるインスタンスは1つだけであり、クライアントは既知のアクセスポイントからそのインスタンスにアクセスできます。
  2. この一意のインスタンスがサブクラス化によって拡張可能であり、コーダーがコードを変更せずに拡張インスタンスを使用できる必要がある場合。

簡単に言えば、エンティティとその固有のプロパティがすべてのクラスで必要であり、その固有のプロパティが変更されない場合、または変更がすべてのクラスに共通である場合、このクラスはシングルトンモードと見なすことができます。シングルトンモードの利点は、クラスにインスタンスが1つしかないことです。これにより、システムパフォーマンスのオーバーヘッドが削減され、共有リソースアクセスの問題が最適化されます。たとえば、リソースの読み込みに関連するクラスは時間がかかり、作成する必要はありません。この種のクラスは、シングルトンモードでの作成に適しています。

第二に、シングルトンパターンの実現

ここでは、いくつかのシングルトンモードの実装を紹介し、各実装の長所と短所を簡単に分析します。かわいらしさがわからない場合は、コメント領域にメッセージまたはプライベートメッセージを残すことができます。できるだけ早くあなた。

1.空腹の中国人

public class HungrySingleton {
    
    

	private static final HungrySingleton SINGLETON = new HungrySingleton();

	private HungrySingleton() {
    
    
		super();
		// TODO Auto-generated constructor stub
	}

	public static HungrySingleton getInstance() {
    
    
		return SINGLETON;
	}
}

デザインのアイデア:

  1. クラスのコンストラクターをプライベートにする
  2. シングルトンを実装する必要があるクラスは、メンバー変数として使用され、finalで装飾されます
  3. クラスのインスタンスにアクセスできるメソッドgetInstanceを提供します

**利点:**ロックがなく、実行効率が高い。
**デメリット:**クラスがロードされたときに初期化すると、スペースが消費され、メモリが浪費されます。

2.怠惰な男

public class LazySingleton {
    
    

	private static LazySingleton LAZY_SINGLETON;

	public static LazySingleton getInstance() {
    
    
		if (LAZY_SINGLETON == null) {
    
    
			LAZY_SINGLETON = new LazySingleton();
		}
		return LAZY_SINGLETON;
	}

	private LazySingleton() {
    
    
		super();
		// TODO Auto-generated constructor stub
	}

}

デザインのアイデア:

  1. クラスのコンストラクターをプライベートにする
  2. 現在のクラスのメンバー変数を提供します
  3. 現在のインスタンスを取得できるgetInstanceメソッドをメソッドに指定して、クラスがインスタンス化されているかどうかを判別します。インスタンス化されていない場合はインスタンス化し、インスタンス化されている場合は、クラスのインスタンスに直接戻ります。

利点:クラスを初めて使用するときにのみ帰宅し、メモリスペースを節約します。
短所: getInstanceメソッドはアトミックではなく、スレッドセーフの問題を引き起こします。

3. DCL

DCLのフルネームはダブルチェックロック方式と呼ばれます

public class DCLSingleton {
    
    
	private static volatile DCLSingleton dclSingleton;

	public static DCLSingleton getInstance() {
    
    
		if (dclSingleton == null) {
    
    
			synchronized (DCLSingleton.class) {
    
    
				if (dclSingleton == null) {
    
    
					dclSingleton = new DCLSingleton();
				}
			}
		}
		return dclSingleton;
	}
	private DCLSingleton() {
    
    
		super();
		// TODO Auto-generated constructor stub
	}
}

実現のアイデア:

空腹の中国スタイルと比較して、DCLシングルトンモードは最初に揮発性キーワードの変更をメンバー変数に追加して、命令の並べ替えによって引き起こされる半初期化の問題を防ぎます。
getInstanceメソッドでは、同期によって変更された同期コードが追加され、コードブロック操作のアトミック性が確保され、スレッドセーフの問題が解決されます。
スレッドセーフを確保するために、同期されたコードブロック内で別の判断を行います。

利点:遅延読み込み、メモリスペースの節約。同期コードの高速変更により、スレッドセーフが保証されます。
短所:ロックを使用すると、ユーザーエクスペリエンスに影響します。

4.静的内部クラス

public class StaticSingleton {
    
    

	private static class Singleton {
    
    
		private static final StaticSingleton SINGLETON = new StaticSingleton();
	}

	public static StaticSingleton getInstance() {
    
    
		return Singleton.SINGLETON;
	}

	private StaticSingleton() {
    
    
		super();
		// TODO Auto-generated constructor stub
	}

}

実現のアイデア:

静的内部クラスを介して、クラスがインスタンス化されるときにロードされます

利点:遅延読み込みが実装され、ロックが使用されません。
短所:明らかな短所はありません

5.列挙

public enum EnumSingleton {
    
    
	instance;
}

デザインのアイデア:

効果的なJava作成者は、シングルトン実装を推奨しています。すべての列挙は、パブリック静的最終変更として理解できます。

利点:簡単な書き込み、スレッドセーフ。
短所:空腹の中国語の読み込み

3、シングルトンモードを破壊する

よく言われるシングルトン書き込み方法はまだ完全には実現されていません。実際、いくつかの動作によってシングルトンパターン自体の特異性を破壊する可能性があります。したがって、特定の処理を行わない場合、私たちが書き込むシングルトンパターンは紳士ですが、悪役ではありません。ここでは、シングルトンパターンとソリューションを破る方法の簡単な例を示します。

1.反射破壊シングルトンモード

テストのために、上記の空腹の中国語コードにmainメソッドを追加します

	public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException,
			IllegalAccessException, IllegalArgumentException, InvocationTargetException {
    
    

		HungrySingleton instance = HungrySingleton.getInstance();
		Class<?> hun = HungrySingleton.class;
		Constructor ctr = hun.getDeclaredConstructor(null);
		ctr.setAccessible(true);
		HungrySingleton newInstance = (HungrySingleton) ctr.newInstance();
		System.out.println(instance.hashCode());
		System.out.println(newInstance.hashCode());
	}

ここで、印刷結果
ここに画像の説明を挿入
を確認できます。リフレクション後にシングルトンモードを破棄したことがわかります。また、ソリューションも非常に単純で、コンストラクターで対応する処理を実行する必要があります。

public class HungrySingleton {
    
    

	private static final HungrySingleton SINGLETON = new HungrySingleton();

	private HungrySingleton() {
    
    
		if (SINGLETON != null) {
    
    
			throw new RuntimeException("不允许创建多个实例");
		}
	}
	public static HungrySingleton getInstance() {
    
    
		return SINGLETON;
	}
}

演算結果:
ここに画像の説明を挿入

2.逆シリアル化はシングルトンモードを破壊します

オブジェクトを作成した後、オブジェクトをシリアル化してからディスクに書き込み、次にディスクからオブジェクトを読み取り、次回使用するときに逆シリアル化して、メモリオブジェクトに変換する必要がある場合があります。メモリアドレス再割り当て、つまり再作成されます。デシリアライズのターゲットがシングルトンオブジェクトの場合、シングルトンパターンの原則に違反します。例を見てみましょう。
まず、空腹の男性にシリアライズ可能なインターフェイスを実装させます。

public class HungrySingleton implements Serializable {
    
    

	private static final HungrySingleton SINGLETON = new HungrySingleton();

	private HungrySingleton() {
    
    
	}

	public static HungrySingleton getInstance() {
    
    
		return SINGLETON;
	}
}

次に、テストのメインメソッドを記述します。

	public static void main(String[] args) {
    
    

		HungrySingleton instance01 = HungrySingleton.getInstance();
		HungrySingleton instance02 = null;
		FileOutputStream fos = null;
		try {
    
    
			fos = new FileOutputStream("HungrySingleton.obj");
			ObjectOutputStream oos = new ObjectOutputStream(fos);
			oos.writeObject(instance01);
			oos.flush();
			oos.close();

			FileInputStream fis = new FileInputStream("HungrySingleton.obj");
			ObjectInputStream objectInputStream = new ObjectInputStream(fis);
			instance02 = (HungrySingleton) objectInputStream.readObject();
			objectInputStream.close();
			System.out.println(instance01.hashCode());
			System.out.println(instance02.hashCode());
		} catch (Exception e) {
    
    
			// TODO: handle exception
		}

	}

操作結果:
ここに画像の説明を挿入
解決策:
readResolve()メソッドを追加するだけです

public class HungrySingleton implements Serializable {
    
    

	private static final HungrySingleton SINGLETON = new HungrySingleton();

	private HungrySingleton() {
    
    

	}

	public static HungrySingleton getInstance() {
    
    
		return SINGLETON;
	}

	private Object readResolve() {
    
    
		return SINGLETON;
	}
}

操作結果:その
ここに画像の説明を挿入
理由は、objectInputStream.readObject()メソッドがリフレクションを使用して、逆シリアル化中に反映されるエンティティ内のreadResolveメソッドを見つけるためです。そうでない場合は、クラスコンストラクターを呼び出します。

結びの言葉

さて、シングルトンモードに関連するコンテンツが整理されました。やりがいのある友達がワンクリックで3つのリンクを取得でき、ばかではないことを願っています。

おすすめ

転載: blog.csdn.net/xiaoai1994/article/details/112217525