Strengthen Singleton with private constructor or enumeration type

Refer to Effective Java version 3, Joshua J. Bloch

The guy who participated in JDK writing saw his name when he read the source code of Collection last time, and then turned over the book. He wrote it!

1. Common:

public class Singleton {
private static final Singleton INSTANCE=new Singleton();
private Singleton(){ //If there is no judgment, you can use the constructor to create the object through reflection, and then it is not a single case if (INSTANCE!=null){ //throw Exception } }
public static Singleton getInstance(){ return INSTANCE; }
public void doSomething(){ //... } }

Reflection: you can see that two instances of singleton are not the same.

class Main {
public static void main(String[] args) { testSingleton(); }

   private static void testSingleton() {
Singleton s1
= Singleton.getInstance(); Class<Singleton> clazz = Singleton.class; try { Constructor<Singleton> constructor = clazz.getDeclaredConstructor(new Class[]{}); constructor.setAccessible(true); Singleton s2 = constructor.newInstance(new Class[]{}); System.out.println(s1 == s2); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } }

2. Enumeration: recommended method

Advantages: referring to Effective Java, it is simple and free to provide a serialization mechanism, which absolutely prevents multiple instantiations, even in the face of complex serialization or reflection attacks. Enumeration classes of single elements are often the best way to implement Singleton. This method is not appropriate if Singleton must extend a superclass instead of Enum.

public enum EnumSingleton {
INSTANCE;
public void doSomething(){ //... } }

According to the first test, an error will be reported.

3. serialization

One problem with serialization is that when deserializing, a new instance will be created and a single instance will be destroyed. Next, let the original class implement the Serializable interface.

public class Singleton implements Serializable {

    private static final Singleton INSTANCE=new Singleton();

    private Singleton(){
        if (INSTANCE!=null){
            try {
                throw new Exception("INSTANCE Already exist!");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static Singleton getInstance(){
        return INSTANCE;
    }

    public void doSomething(){
        //...
    }
}

Test it out: Article 9 of Effective Java uses try with resources better than try finally to close resources.

private static void testSerializableSingleton() {
File file
=new File("singleton.out"); try(ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream(file)); ObjectInputStream in=new ObjectInputStream(new FileInputStream(file))){ out.writeObject(Singleton.getInstance()); Singleton singleton= (Singleton) in.readObject(); System.out.println(singleton == Singleton.getInstance()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } }

The printed result is false, indicating that serialization breaks the singleton, because deserialization is reflection calling the parameterless constructor.

Solution: add this method to the class. See Article 89 of Effective Java for details

private Object readResolve() {
        return INSTANCE;
}

Then it turns out to be true.

Keywords: Java JDK

Added by mattd123 on Thu, 14 Nov 2019 21:16:16 +0200