1. Starved Chinese style, also known as immediate loading, means that the object has been created when using the class. The common method is to instantiate the new directly.
public class SingletonObject { private static SingletonObject sObj = new SingletonObject(); private SingletonObject(){ } public static SingletonObject getInstance(){ return sObj; } }
2. Lazy, also known as delayed loading, means that the instance is created when the get() method is called.
Test code:
public class SingletonObject { //private static SingletonObject sObj = new SingletonObject(); private static SingletonObject sObj; public static SingletonObject getInstance(){ try { if (sObj == null) { synchronized(SingletonObject.class){ //Double detection is used because multiple threads may enter the first code block at the same time: if (sObj == null), //If we don't judge whether sObj is null again, after the previous thread exits the synchronization block, the later thread will get the lock and instantiate again. if(sObj == null){ //Sleep for 2 seconds, simulate time-consuming operation Thread.sleep(2000); sObj = new SingletonObject(); } } } } catch (InterruptedException e) { e.printStackTrace(); } return sObj; } }
public class SingletonObject { private SingletonObject(){ } private static class InnerObject{ private static SingletonObject sObj = new SingletonObject(); } public static SingletonObject getInstance(){ return InnerObject.sObj; } }
In most cases, this kind of writing can get the expected results and achieve thread safety.
4. When a static inner class encounters serialization and deserialization, it still gets multiple instances.Test code:
public class SingletonObject implements Serializable{ private static SingletonObject sObj; private SingletonObject(){ } private static class InnerObject{ private static SingletonObject sObj = new SingletonObject(); } public static SingletonObject getInstance(){ return InnerObject.sObj; } }
Operation result:public class RunTest { public static void main(String[] args) { try{ SingletonObject sObj = SingletonObject.getInstance(); FileOutputStream fosRef = new FileOutputStream(new File("singletonObjFile.txt")); ObjectOutputStream oosRef = new ObjectOutputStream(fosRef); oosRef.writeObject(sObj); oosRef.close(); fosRef.close(); System.out.println("write,sObj="+sObj.hashCode()); }catch(FileNotFoundException e){ e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } try{ FileInputStream fisRef = new FileInputStream(new File("singletonObjFile.txt")); ObjectInputStream iosRef = new ObjectInputStream(fisRef); SingletonObject sObj = (SingletonObject)iosRef.readObject(); iosRef.close(); fisRef.close(); System.out.println("read,sObj="+sObj.hashCode()); }catch(FileNotFoundException e){ e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
write,sObj=6585861
read,sObj=8860464
As you can see, the written value is not consistent with the read value.
The solution is to call the readResolve() method when deserializing.
Modification code:
Operation result:public class SingletonObject implements Serializable{ private SingletonObject(){ } private static class InnerObject{ private static SingletonObject sObj = new SingletonObject(); } public static SingletonObject getInstance(){ System.out.println("call getInstance method."); return InnerObject.sObj; } protected Object readResolve() throws ObjectStreamException{ System.out.println("call readResolve method."); return InnerObject.sObj; } }
call getInstance method.
write,sObj=6585861
call readResolve method.
read,sObj=6585861
5. Implement a single example with static code block.
Test code:
public class SingletonObject{ private static SingletonObject sObj; private SingletonObject(){ } static{ sObj = new SingletonObject(); } public static SingletonObject getInstance(){ return InnerObject.sObj; } }