単一モードと静的クラス

4510 ワード

単例モードか静的クラスかという古い話題ですが、Javaに触れたばかりの頃からこのような議論を見ていました.ここでまとめて、少し新しいものも追加します.
まず、「静的クラス」と「すべてのメソッドが静的メソッドのクラス」という概念を明らかにし、区別します.
厳密に言えば、Javaの静的クラスとは、「static class」のように修飾されたクラス定義を指し、文法上の要求により、このようなクラスは必ず内部クラスであり、言い換えれば、「静的内部クラス」はその完全な定義である.静的内部クラスの最大の利点は、自分を隠すこと(外層のクラスでのみ使用される)と、外層のクラスのプロパティにアクセスできることです.「静的でない」内部クラスと比較して、静的クラスではなく静的メンバー変数とメソッド定義を配置できます.また、静的内部クラスは、外部クラスの非静的属性にアクセスできません.
しかし、通常、我々が言う「静的クラス」は、以下に説明する「静的クラス」でもあり、すべてのメソッド、属性が静的クラスであることを意味するとともに、それらを使用する際に、それらの静的メソッドを直接呼び出し、その中の静的属性にアクセスし、実際の例化を必要としない.このようないわゆる「静的クラス」は往々にしてfinal修飾を使用し、サブクラスがないことが多いという2つの特徴を備えている.2つ目は、コンストラクタがプライベート化されており、インスタンスを構築することは許可されていません.
1、単例モードはmockに便利で、測定性が良い.静的方法でもmock(例えば特殊な注釈を使う必要がある)は可能だが、相対的に面倒で、それほど柔軟ではない.
2、単例モードではlazy loadができると言われていますが、静的クラスはできません.これは間違いなく薄く、静的クラスも初めて使用したときに再ロードすることができます.しかし、その中で特筆すべきは、単例のDouble Check Lockであり、ここでは簡単に紹介する.次のコードを見てください.
JAvaコード:
public Singleton {  
    private static Singleton instance;  
    private Number n = new Number();  
    public Number get() {  
        return this.n; //(1)  
    }  
    public static Singleton getInstance() {  
        if(instance == null) {  
            synchronized(Singleton.class) {  
                if(instance == null)  
                    instance = new Singleton();  
            } //(2)  
        }  
        return instance;  
    }  
}  

これは一般的な書き方ですが、コンパイラの最適化により、プライマリメモリとスレッドワークメモリのデータが一致しない問題が発生することができます.これが「DCL失効」の問題です.上記のコードは、JMM仕様に従って、プライマリ・ストレージ・データとワークメモリのデータが一致しないことを許可する典型的な表現です.JDK 1.2以降、割り当てスペース、オブジェクトの初期化などの操作はワークメモリに格納されます.synchronizedキーワードの関係で、文(2)に実行されると、同期ブロックを出ると、JVMはホストメモリとワークメモリのinstance参照のオブジェクトを一致にリフレッシュします.すなわち、instanceは「表示」です.しかし、問題は上記の(1)にあり、synchronizedもvolatileもfinalもなく、getメソッドを呼び出すときに得られるnが正しい値であることを保証する人はいない.すなわち、このnは必ずしも「見える」とは限らない.nがinstanceよりもプライマリ・ストレージに同期するのが遅い場合、このギャップ内で取得されたinstanceは不健康なinstanceであり、this.nは正しいNumberオブジェクトを取得できません.
JSR 133では、JMMを修正し、synchronized、volatile、finalキーワードを導入または強化し、それらの運用により、上記の問題を解決することができる.また、静的な内部クラスを使用する方法もあります.
Javaコード
private static class InnerInstance {  
   public static Instance instance = new Instance();  
}  
public static Instance getInstance() {  
   return InnerInstance.instance;  
}  

3、単一の例を継承することができて、これは1つのとても大きい利益で、これはユーザーoverwriteの中のある方法を便利にして、もちろん、単一の例を継承するシーンは比較的に珍しいです;静的クラスは一般的に継承されません.単例の継承の詳細については、ここでは議論しないが、いくつかの方法があり、興味のある学生はJDKのCalendar類を自分で読むことができる.
4、単一の例はあるインタフェースから実現することができ、あるクラスから継承することができる.静的クラスはクラスから継承することもできますが、親クラスのprotectメンバーは使用できません.普及すると,この点と前の点はいずれもオブジェクト向けの利点と見なすことができる:パッケージ,継承,多態,静的クラスはその中の後2点をうまく備えていない.
5、単一の例は比較的便利に有限な例に拡張することができる.必要に応じて、静的クラスでは難しい2つの内部状態の異なる単一のオブジェクトを工場で生産することができます.Springは一連の大きな工場と見なすことができるが、その中のbeanもsingletonとprototypeの2種類しかなく、staticの新しいタイプを生産できない.ツールがオブジェクトになると、拡張性が向上します.もう一つの興味深い例はJDKのCalendar.getInstance()メソッドで、メソッドから見ると単一のインスタンスを得るように見えますが、実はそうではありません.毎回新しいCalendarオブジェクトを作成します.同時にabstractを使用して自身を修飾し、newインスタンス化が使用できないことを保証し、getInstanceというインタフェースを開いてデフォルトの実装を取得し、取得したデフォルトの実装は、Calendarのサブクラスでもある.この形式は単例の変体と見なすことができる.
6、使用中に申請したリソースは、メモリをタイムリーに解放して回収することができますが、静的クラスはできません.これも無理です.静的クラスもステータスを格納できることを忘れないでください.リソースを使用しないことを確定したら、リソースの参照をnullに設定すればいいです.
7、クラスのロード時に複雑な操作を行う場合、静的クラスではstaticブロックを導入してデータを初期化する必要があり、期間中に異常が投げ出された場合、「ClassDefNotFoundError」という奇妙なエラーが発生する可能性があり、これは問題の位置づけに不利である.
========================================================================
作者:四火のBLOGhttp://raychase.iteye.com/blog/1471015