Singleton

By | September 29, 2020
Share the joy
  •  
  •  
  •  
  •  
  •  
  •  

Singleton with public final field

Problem of this is that this can create another instance by reflection. The fix is that in private method, add a INSTANCE check.

public class SingletonWithPublicFinalField {

    public static final SingletonWithPublicFinalField INSTANCE = new SingletonWithPublicFinalField();

    private SingletonWithPublicFinalField() {
//        if (INSTANCE != null) {
//            throw new RuntimeException("Singleton instance already existed");
//        }
    }

    public static void main(String[] args) throws Exception{
        SingletonWithPublicFinalField instance1 = SingletonWithPublicFinalField.INSTANCE;
        System.out.println(instance1);

        Constructor.setAccessible(SingletonWithPublicFinalField.class.getConstructors(), true);
        Object instance2 = Class.forName(SingletonWithPublicFinalField.class.getName()).newInstance();
        System.out.println(instance2);
    }

}

Singleton with public static factory method. Good of it is that it is very like a class. It is easy to convert it into a normal non-singleton class. Just rewrite getInstance() method.

public class SingletonWithPublicStaticFactoryMethod {

    public static final SingletonWithPublicStaticFactoryMethod INSTANCE = new SingletonWithPublicStaticFactoryMethod();

    private SingletonWithPublicStaticFactoryMethod() {}

    public static SingletonWithPublicStaticFactoryMethod getInstance() {
        return INSTANCE;
    }

    public static void main(String[] args) throws Exception{
        SingletonWithPublicStaticFactoryMethod instance = SingletonWithPublicStaticFactoryMethod.getInstance();
        System.out.println(instance);
    }

}

Singleton serializable issue. When deserializing, it creates a different instance. The fix is to override readResolve() method and return the INSTANCE.

public class SingletonWithSerializable implements Serializable {

    public static final SingletonWithSerializable INSTANCE = new SingletonWithSerializable();

    private SingletonWithSerializable() {}

    public static SingletonWithSerializable getInstance() {
        return INSTANCE;
    }

    // When deserializing, it returns a different instance. The fix is to add below
//    private Object readRsolve() {
//        return INSTANCE;
//    }

    public static void main(String[] args) throws Exception{
        SingletonWithSerializable instance = SingletonWithSerializable.getInstance();
        System.out.println(instance);

        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("file1.ser"))) {
            out.writeObject(instance);
        }

        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("file1.ser"))) {
            SingletonWithSerializable readObject = (SingletonWithSerializable) in.readObject();
            System.out.println(readObject);
        }
    }

}

The best option is to use enum singleton. It avoids reflection hacking or serializing issue very easily.

public enum  SingletonWithEnum {
    INSTANCE;

    public void doSomeMethod() {
        System.out.println("some method");
    }

    public static void main(String[] args) throws Exception {
        SingletonWithEnum singletonWithEnum = SingletonWithEnum.INSTANCE;
        SingletonWithEnum instance = SingletonWithEnum.INSTANCE;
        System.out.println(instance);

        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("file1.ser"))) {
            out.writeObject(instance);
        }

        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("file1.ser"))) {
            SingletonWithEnum readObject = (SingletonWithEnum) in.readObject();
            System.out.println(readObject);
        }

        instance.doSomeMethod();
    }
    
}