検索
ホームページJava&#&チュートリアルJava でのシングルトンの実装の難しさ

シングルトン パターンを実装する簡単で効率的な方法はありますが、あらゆる状況下でシングルトンの整合性を保証する方法はありません。

シングルトン パターンは、クラスが 1 回だけインスタンス化され、グローバルまたはシステム全体のコンポーネントを表すために使用されることを意味します。シングルトン パターンは、ロギング、ファクトリ、ウィンドウ マネージャー、プラットフォーム コンポーネント管理などによく使用されます。シングルトンパターンは一度実装すると変更やオーバーロードが難しく、テストケースの書き方の難しさやコード構造の悪さなどの問題が発生するため、できるだけ避けるべきだと思います。また、次の記事のシングルトン パターンは安全ではありません。

シングルトン パターンをより適切に実装する方法の研究に多くのエネルギーが費やされていますが、シンプルで効率的な実装方法があります。ただし、あらゆる状況下でシングルトンの整合性を保証する唯一の方法はありません。以下をお読みになり、同意するかどうかを確認してください。

最終フィールド

このメソッドはコンストラクターをプライベート化し、パブリック静的最終オブジェクトを提供します:

public class FooSingleton {    
    public final static FooSingleton INSTANCE = new FooSingleton();   
    private FooSingleton() { }    
    public void bar() { }}

クラスがロードされると、静的オブジェクトが初期化され、この時点でプライベート コンストラクターが最初と最後の転送に使用されます。クラスが初期化される前に複数のスレッドがこのクラスを呼び出した場合でも、JVM はスレッドが実行を継続するときにクラスが完全に初期化されていることを保証できます。ただし、リフレクションと setAccessible(true) メソッドを使用すると、他の新しいインスタンスを作成することができます:

Constructor[] constructors = FooSingleton.class.getDeclaredConstructors();
Constructor constructor = constructors[0];
constructor.setAccessible(true);
FooSingleton spuriousFoo = (FooSingleton) constructor.newInstance(new Object[0]);

コンストラクターを複数回呼び出す必要がないように変更する必要があります。たとえば、呼び出されたときに例外をスローします。また。 FooSingleton コンストラクターを次のように変更すると、そのような攻撃を防ぐことができます:

public class FooSingleton2 {    
private static boolean INSTANCE_CREATED;    
public final static FooSingleton2 INSTANCE = new FooSingleton2();    
private FooSingleton2() {        
    if (INSTANCE_CREATED) {            
        throw new IllegalStateException("You must only create one instance of this class");        
     } else {            
         INSTANCE_CREATED = true;        
     }    
  }    
      public void bar() { }
}

これはより安全に見えますが、実際には、新しいインスタンスを作成するのと同じくらい簡単です。 INSTANCE_CREATED フィールドを変更して、同じトリックをもう一度実行するだけです:

Field f = FooSingleton2.class.getDeclaredField("INSTANCE_CREATED");
f.setAccessible(true);
f.set(null, false);
Constructor[] constructors = FooSingleton2.class.getDeclaredConstructors();
Constructor constructor = constructors[0];
constructor.setAccessible(true);
FooSingleton2 spuriousFoo = (FooSingleton2) constructor.newInstance(new Object[0]);

私たちが講じた予防措置はバイパスされる可能性があるため、この解決策は実現できません。

静的ファクトリー

このメソッドを使用すると、パブリック メンバーは静的ファクトリーと同様になります:

public class FooSingleton3 {    
  public final static FooSingleton3 INSTANCE = new FooSingleton3();    
  private FooSingleton3() { }    
  public static FooSingleton3 getInstance() { return INSTANCE; }    
  public void bar() { }
}

getInstance() メソッドは常に同じオブジェクト参照を返します。この解決策では反射を防ぐことはできませんが、それでもいくつかの利点があります。たとえば、API を変更せずにシングルトンの実装を変更できます。 getInstance() は、ほぼすべてのシングルトン実装に出現し、これが実際にはシングルトン パターンであることを示します。

遅延読み込みシングルトン モード

(翻訳者注: ソフトウェア工学では、Initialization-on-demand というイディオムは遅延読み込みシングルトン モードを指します。Wikipedia を参照してください)

できるだけ遅延させたい場合は、シングルトンを作成するには(遅延読み込み)、getInstance() メソッドが初めて呼び出されたときに、遅延初期化メソッドを使用してシングルトンをスレッドセーフに作成できます。クラスが初めて参照されるときにシングルトンを作成する以前のソリューション (ハングリー スタイルのロード) と比較すると、これは改善です。次のとおりです:

public class FooSingleton4 {    
private FooSingleton4() {    
}    
public static FooSingleton4 getInstance() {       
 return FooSingleton4Holder.INSTANCE;    
 }    
private static class FooSingleton4Holder {       
 private static final FooSingleton4 INSTANCE = new FooSingleton4();    
 }
}

シリアル化には注意してください

シングルトンがシリアル化を実装すると、別の脅威に直面します。したがって、すべてのフィールドを一時的として宣言し (シリアル化されないように)、一意のインスタンス INSTANCE への参照を返すカスタム readResolve() メソッドを提供する必要があります。

列挙

ここでは、単一インスタンス INSTANCE のコンテナとして列挙を使用します:

public enum FooEnumSingleton {
    INSTANCE;    
    public static FooEnumSingleton getInstance() { 
    return INSTANCE; 
    }    
    public void bar() { }
}

Java 言語仕様 8.9 によると、「Enum の最終的な複製メソッドは列挙が決して複製できないことを保証し、その特別なシリアル化メカニズムは列挙が複製できないことを保証します」同時に、列挙型をインスタンス化するためにリフレクションを使用することも禁止されています。これにより、列挙型定数の外に他の同様の列挙型インスタンスが存在しないことが保証されます。そして反射攻撃。この一節を初めて見たとき、私はすぐにそれが間違いであることを証明したいと思いました。次のコードに示すように、これらの保護を回避するのは簡単です:

Constructor con = FooEnumSingleton.class.getDeclaredConstructors()[0];
 Method[] methods = con.getClass().getDeclaredMethods();
 for (Method method : methods) {
     if (method.getName().equals("acquireConstructorAccessor")) {
         method.setAccessible(true);
         method.invoke(con, new Object[0]);
     }
  }
  Field[] fields = con.getClass().getDeclaredFields();
  Object ca = null;  for (Field field : fields) {
      if (field.getName().equals("constructorAccessor")) {
          field.setAccessible(true);
          ca = field.get(con);
      }
  }  Method method = ca.getClass().getMethod("newInstance", new Class[]{Object[].class});
  method.setAccessible(true);
  FooEnumSingleton spuriousEnum = (FooEnumSingleton) method.invoke(ca, new Object[]{new Object[]{"SPURIOUS_INSTANCE", 1}});
  printInfo(FooEnumSingleton.INSTANCE);
  printInfo(spuriousEnum);
}private static void printInfo(FooEnumSingleton e) {
    System.out.println(e.getClass() + ":" + e.name() + ":" + e.ordinal());
}

このコードを実行すると、次の結果が得られます:

class com.blogspot.minborgsjavapot.singleton.FooEnumSingleton:INSTANCE:0
class com.blogspot.minborgsjavapot.singleton.FooEnumSingleton:SPURIOUS_INSTANCE:1

列挙型の欠点は、すでに継承されているため、別の基本クラスから継承できないことです。 java.lang .Enum.この種の継承をシミュレートしたい場合は、私の他の記事で紹介したミックスイン パターンを参照してください。

列挙型の利点の 1 つは、後で「デュアルトン」または「トリングルトン」が必要になった場合に、新しい列挙型インスタンスを追加するだけで済むことです。たとえば、シングルトン キャッシュを作成した後、そのキャッシュに複数のレイヤーを導入することもできます。

結論

シングルトンに対するこれらの保護をバイパスするのは簡単ではありませんが、確実な解決策は実際にはありません。より良い解決策があれば、お気軽にお知らせください。

列挙は、シングルトン パターンを実装する簡単かつ効率的な方法です。継承または遅延ロードが必要な場合は、遅延初期化が良い選択です。

シングルトンの幸運を祈ります!

声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
高度なJavaプロジェクト管理、自動化の構築、依存関係の解像度にMavenまたはGradleを使用するにはどうすればよいですか?高度なJavaプロジェクト管理、自動化の構築、依存関係の解像度にMavenまたはGradleを使用するにはどうすればよいですか?Mar 17, 2025 pm 05:46 PM

この記事では、Javaプロジェクト管理、自動化の構築、依存関係の解像度にMavenとGradleを使用して、アプローチと最適化戦略を比較して説明します。

適切なバージョン化と依存関係管理を備えたカスタムJavaライブラリ(JARファイル)を作成および使用するにはどうすればよいですか?適切なバージョン化と依存関係管理を備えたカスタムJavaライブラリ(JARファイル)を作成および使用するにはどうすればよいですか?Mar 17, 2025 pm 05:45 PM

この記事では、MavenやGradleなどのツールを使用して、適切なバージョン化と依存関係管理を使用して、カスタムJavaライブラリ(JARファイル)の作成と使用について説明します。

カフェインやグアバキャッシュなどのライブラリを使用して、Javaアプリケーションにマルチレベルキャッシュを実装するにはどうすればよいですか?カフェインやグアバキャッシュなどのライブラリを使用して、Javaアプリケーションにマルチレベルキャッシュを実装するにはどうすればよいですか?Mar 17, 2025 pm 05:44 PM

この記事では、カフェインとグアバキャッシュを使用してJavaでマルチレベルキャッシュを実装してアプリケーションのパフォーマンスを向上させています。セットアップ、統合、パフォーマンスの利点をカバーし、構成と立ち退きポリシー管理Best Pra

キャッシュや怠zyなロードなどの高度な機能を備えたオブジェクトリレーショナルマッピングにJPA(Java Persistence API)を使用するにはどうすればよいですか?キャッシュや怠zyなロードなどの高度な機能を備えたオブジェクトリレーショナルマッピングにJPA(Java Persistence API)を使用するにはどうすればよいですか?Mar 17, 2025 pm 05:43 PM

この記事では、キャッシュや怠zyなロードなどの高度な機能を備えたオブジェクトリレーショナルマッピングにJPAを使用することについて説明します。潜在的な落とし穴を強調しながら、パフォーマンスを最適化するためのセットアップ、エンティティマッピング、およびベストプラクティスをカバーしています。[159文字]

Javaのクラスロードメカニズムは、さまざまなクラスローダーやその委任モデルを含むどのように機能しますか?Javaのクラスロードメカニズムは、さまざまなクラスローダーやその委任モデルを含むどのように機能しますか?Mar 17, 2025 pm 05:35 PM

Javaのクラスロードには、ブートストラップ、拡張機能、およびアプリケーションクラスローダーを備えた階層システムを使用して、クラスの読み込み、リンク、および初期化が含まれます。親の委任モデルは、コアクラスが最初にロードされ、カスタムクラスのLOAに影響を与えることを保証します

See all articles

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

DVWA

DVWA

Damn Vulnerable Web App (DVWA) は、非常に脆弱な PHP/MySQL Web アプリケーションです。その主な目的は、セキュリティ専門家が法的環境でスキルとツールをテストするのに役立ち、Web 開発者が Web アプリケーションを保護するプロセスをより深く理解できるようにし、教師/生徒が教室環境で Web アプリケーションを教え/学習できるようにすることです。安全。 DVWA の目標は、シンプルでわかりやすいインターフェイスを通じて、さまざまな難易度で最も一般的な Web 脆弱性のいくつかを実践することです。このソフトウェアは、

WebStorm Mac版

WebStorm Mac版

便利なJavaScript開発ツール

VSCode Windows 64 ビットのダウンロード

VSCode Windows 64 ビットのダウンロード

Microsoft によって発売された無料で強力な IDE エディター

mPDF

mPDF

mPDF は、UTF-8 でエンコードされた HTML から PDF ファイルを生成できる PHP ライブラリです。オリジナルの作者である Ian Back は、Web サイトから「オンザフライ」で PDF ファイルを出力し、さまざまな言語を処理するために mPDF を作成しました。 HTML2FPDF などのオリジナルのスクリプトよりも遅く、Unicode フォントを使用すると生成されるファイルが大きくなりますが、CSS スタイルなどをサポートし、多くの機能強化が施されています。 RTL (アラビア語とヘブライ語) や CJK (中国語、日本語、韓国語) を含むほぼすべての言語をサポートします。ネストされたブロックレベル要素 (P、DIV など) をサポートします。

PhpStorm Mac バージョン

PhpStorm Mac バージョン

最新(2018.2.1)のプロフェッショナル向けPHP統合開発ツール