24

今日のインタビューで、あるインタビュアーが Singleton クラスを書くように頼んだ。そして、私は私の答えを次のように与えました

public class Singleton {

    private static Singleton ref;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (ref == null) {
            ref = new Singleton();
        }
        return ref;
    }
}

突然彼は、これは古いクラスの書き方だと私に言いました。なぜ彼がそのように言ったのか、誰か助けてくれませんか。

4

13 に答える 13

42

シングルトンを作成するときに最初に頭に浮かぶのはenum. 通常、enum を使用してシングルトンを実装します。

enum Singleton {
    INSTANCE;
}

enum を使用して得られる利点の 1 つは、シリアル化です。

シングルトン クラスでは、シリアライゼーションとデシリアライゼーションがメソッドを実装することによって新しいインスタンスを作成しないことを確認する必要がありますが、readResolve()これは enum には当てはまりません。

クラスを使用して、次のようにシングルトンを作成する必要があります。

public final class Singleton implements Serializable {
    // For lazy-laoding (if only you want)
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    private Singleton() {
        if (SingletonHolder.INSTANCE != null) {
            // throw Some Exception
        }
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }

    // To avoid deserialization create new instance
    @SuppressWarnings("unused")
    private Singleton readResolve() {
        return SingletonHolder.INSTANCE;
    }
}
于 2013-09-17T16:47:02.093 に答える
10

できるよ

public enum Singleton {
    INSTANCE;
}

インスタンスを持たないユーティリティクラスの場合

public enum Utility {
     ;

     public static void method();
}
于 2013-09-17T16:47:08.817 に答える
8

他の人がすでに指摘しているように、enum パターンは現在、古い学校の方法よりもシングルトンの方が優れていると広く考えられていますが、欠点を指摘したかっただけです。

次の形式のシングルトンがありました。

public enum Foo {
    INSTANCE;
}

それはしばらくの間存在していましたが、問題なく動作していました。その後、コード レビュー中に、次のことがわかりました。

public enum Foo {
    INSTANCE,
    ANOTHER;
}

私たちが濡れたサバで彼の顔を殴った後、問題のコーダーは彼のやり方の誤りに気づき、少量よりも多くのコードを取り消すか、書き直す必要がありました。はい、生産に入る前にそれを見つけましたが、それを消去するために作業を行う必要がありました.

これは、このタイプのシングルトン (小さくておそらくまれではありますが) と旧式の方法の弱点だと思います。はい、間違って実装することで任意のパターンを破ることができますが、コーダーにとって、整形式の古い学校のシングルトンよりも列挙型シンゲルトンを破る方がはるかに簡単に思えます。

編集:

完全を期すために、追加の値が後で追加されるのを防ぐ列挙シングルトンを次に示します。

public enum Foo
{
  INSTANCE;
  // adding another type here will cause a runtime

  static
  {
    if (Foo.values().length != 1)
    {
      throw new IllegalStateException("Not a Singleton.");
    }
  }
}
于 2013-09-17T17:27:27.407 に答える
3

私が書くシングルトンは次のようになります。

@Service
class PersonService {
    // implementation here
}

しかし、列挙型のアイデアも気に入っています。実際には、上記のようなもの以外のシングルトンを作成することはありません (必要もありません)。

于 2013-09-17T16:51:16.820 に答える
3

なぜあなたはただできないのですか

public class SingletonSandBox {

    private static SingletonSandBox instance = new SingletonSandBox();

    private SingletonSandBox(){
    }

    public static SingletonSandBox getInstance(){
        return instance;
    }

}

そしてテスト

public static void main(String[] args) {

        SingletonSandBox sss1 = SingletonSandBox.getInstance();

        SingletonSandBox sss2 = SingletonSandBox.getInstance();

        System.out.println(sss1 == sss2);

}

私が知っているように、これはスレッドセーフであり、静的ブロックを使用するよりも短いです。ここでも、静的フィールド宣言は、ランタイムによる静的ブロックと比較して先に読み込まれます。

于 2013-11-19T00:33:22.060 に答える
2

からJavaでシングルトンパターンを実装する効率的な方法は何ですか?

列挙型を使用します。

 public enum Foo 
 {
   INSTANCE;
 }

Joshua Bloch は、彼の著書「Effective Java」でこのアプローチを説明しています。

最近のより良いJavaシングルトンパターンもチェックしてください。

于 2013-09-17T16:54:49.227 に答える
2

OP の最初のアプローチのスレッド セーフ バージョンに加えて、誰も同期ステートメントをあえて提案しませんでした。

final class Singleton
{
    private static Object lock = new Object();
    private static volatile Singleton instance = null;
    private Singleton() { }
    public static Singleton getInstance()
    {
        if(instance == null)
        {
            synchronized(lock)
            {
                if(instance == null)
                {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
于 2013-11-19T18:47:55.227 に答える
1

「ダブルチェックロック」を使用していないため(他の人が言ったように)、またはリフレクションを使用してプライベートコンストラクターを呼び出すことが明らかに可能であるため(セキュリティポリシーで許可されている場合)にある可能性があります。

パラメーターなしでコンストラクターを呼び出すには、空の配列を渡します。

package org.example;

public class Singleton {

    private static final Object LOCK = new Object();
    private static final Singleton SINGLETON = new Singleton();
    private static volatile boolean init = false; // 'volatile' to prevent threads from caching state locally (prevent optimizing) 

    private Singleton() {
        synchronized (LOCK) {
            if( init == true) {
                throw new RuntimeException("This is a singleton class!");
            }
            init=true;
        }
    }

    public static Singleton obtainClassInstance() {
        return SINGLETON;
    }

}


package org.example;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class SimpleSingletonTester {

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

        Class[] parameterTypes = {};
        Object[] initargs = {};
        Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor(parameterTypes);
        System.out.println( constructor.isAccessible() );
        constructor.setAccessible(true);
        System.out.println( constructor.isAccessible() );
        System.out.println( constructor.newInstance(initargs) );
        System.out.println( constructor.newInstance(initargs) );

    }

}
于 2013-11-19T19:24:44.797 に答える