設計モード(三)——JDKの中のこれらの一例


本文は転載しますhttp://www.hollischuang.com/archives/1383
java.lang.RuntimeRuntimeクラスはJava動作時の環境をカプセル化しています.各javaプログラムは実際にはJVMプロセスを起動しています.JVMプロセスはそれぞれこのRuntimeの例に対応しています.この例はJVMによって実際に例化されています.各Javaアプリケーションは、Runtimeクラスのインスタンスを持っており、アプリケーションを実行環境に接続することができます.
Javaは単一プロセスであるため、Runtimeの例は一つしかないはずです.だから、一例を使って実現するべきです.
public class Runtime {
    private static Runtime currentRuntime = new Runtime();

    public static Runtime getRuntime() {
        return currentRuntime;
    }

    private Runtime() {}
}
以上のコードはJDKのRuntime類の部分実装であり、これは実は餓漢式の一例モードであることがわかる.このクラスが初めてclassloaderによってローディングされた時に、この実例が作成されました.
一般的にはRuntimeオブジェクトを実装することはできません.アプリケーションも独自のRuntimeクラスのインスタンスを作成することはできません.  getRuntime 方法は、現在Runtimeが動作しているときのオブジェクトの参照を取得します.
GUIの一例Runtimeが典型的な一例である以外は.JDKの中にはいくつかの種類があります.彼らは全部GUIの種類です.これらの単例のクラスとRuntimeの最大の違いは、彼らが餓漢モードではなく、つまり怠け者初期化の怠け者の一例であることにある.その原因を分析すれば簡単です.それは彼らが事前に作成する必要がないからです.初めて使う時に作成すればいいです.多くの場合、私たちはJavaのGUIとその中の対象ではないからです.餓漢単例を使うとJVMの起動速度に影響します.
Javaの強みはGUIではないので、これらはあまり使われていません.筆者も使ったことがない.コードをここに貼り付けて、一例の実現の角度から簡単に分析します.
java.awt.Toolkit铅get Default Toolkit()
public abstract class Toolkit {
    /**
     * The default toolkit.
     */
    private static Toolkit toolkit;

     public static synchronized Toolkit getDefaultToolkit() {
            if (toolkit == null) {
                java.security.AccessController.doPrivileged(
                        new java.security.PrivilegedAction() {
                    public Void run() {
                        Class> cls = null;
                        String nm = System.getProperty("awt.toolkit");
                        try {
                            cls = Class.forName(nm);
                        } catch (ClassNotFoundException e) {
                            ClassLoader cl = ClassLoader.getSystemClassLoader();
                            if (cl != null) {
                                try {
                                    cls = cl.loadClass(nm);
                                } catch (final ClassNotFoundException ignored) {
                                    throw new AWTError("Toolkit not found: " + nm);
                                }
                            }
                        }
                        try {
                            if (cls != null) {
                                toolkit = (Toolkit)cls.newInstance();
                                if (GraphicsEnvironment.isHeadless()) {
                                    toolkit = new HeadlessToolkit(toolkit);
                                }
                            }
                        } catch (final InstantiationException ignored) {
                            throw new AWTError("Could not instantiate Toolkit: " + nm);
                        } catch (final IllegalAccessException ignored) {
                            throw new AWTError("Could not access Toolkit: " + nm);
                        }
                        return null;
                    }
                });
                loadAssistiveTechnologies();
            }
            return toolkit;
        }
    }
上記のコードはToolkit類の単例で実現されます.このクラスのローディング時にはプライベートtoolkitだけが静的に宣言されています.Toolkitのインスタンスオブジェクトは作成されていません.遅延ローディングはJVM起動速度を加速します.
一例モードは作成モードとして機能し、ここでは負荷に依存するときに別の作成対象を適用し、newの新しいオブジェクトではない.
java.awt.Graphics Environment璢get LocalGrocs Evironment()
public abstract class GraphicsEnvironment {
    private static GraphicsEnvironment localEnv;
    public static synchronized GraphicsEnvironment getLocalGraphicsEnvironment() {
        if (localEnv == null) {
            localEnv = createGE();
        }

        return localEnv;
    }
}
ここでのクラスローディングはプライベートToolkitだけがインスタンスオブジェクトを作成していないことを静的に宣言している.localEnv類は、最初に起動されたときに作成されます.ここに貼り付けられていないGraphicsEnvironment方法も、反射によってオブジェクトを作成するものである.
java.awt.Desktop铅get Desktop()
public class Desktop {

    public static synchronized Desktop getDesktop(){
        if (GraphicsEnvironment.isHeadless()) throw new HeadlessException();
        if (!Desktop.isDesktopSupported()) {
            throw new UnsupportedOperationException("Desktop API is not " +
                                                    "supported on the current platform");
        }

        sun.awt.AppContext context = sun.awt.AppContext.getAppContext();
        Desktop desktop = (Desktop)context.get(Desktop.class);

        if (desktop == null) {
            desktop = new Desktop();
            context.put(Desktop.class, desktop);
        }

        return desktop;
    }
}
上のコードは一例とは違って見えます.しかし、実際にスレッドの安全な怠け者タイプの一例です.オブジェクトを取得するときは、まず環境容器に行って、存在するかどうかを調べます.インスタンスがない場合は、インスタンスを作成します.
以上の3つのクラスの取得例の方法は、同期方法によりスレッドセキュリティを保証している.
Runtime類は、静的初期化によりスレッドの安全を保証しています.
締め括りをつける
本稿では四つの例を紹介しました.一つは餓漢式の一例で、三つは怠け者式の一例です.JDKにおける実際の応用によって、以下の結論が得られます.
一つのクラスのオブジェクトが必要か、あるいは一つしかない場合は、一例モードを考慮しなければなりません.
クラスの例がJVM初期化時に作成されるべきである場合は、餓漢の単一の例を使用することが考慮されるべきである.
クラスのインスタンスが事前に作成される必要がない場合、このクラスのインスタンスは必ずしも使用できるとは限らないかもしれません.このクラスのインスタンス作成プロセスは時間がかかります.怠け者タイプの一例を考えるべきです.
怠け者タイプの一例を使うときは、スレッドの安全性を考慮するべきです.