package com.example.demo; import java.io.ObjectStreamException; import java.io.Serializable; public class Person implements Serializable { private static final long serialVersionUID = 1L; private volatile static Person singletion; private Person() { if (null == Person.singletion) { return; } else { throw new RuntimeException("单例类不允许二次调用构造函数"); } } public static Person getSingletion() { if (null == Person.singletion) { synchronized (Person.class) { if (null == Person.singletion) { Person.singletion = new Person(); } } } return Person.singletion; } private Object readResolve() throws ObjectStreamException { return Person.singletion; } protected Object clone() throws CloneNotSupportedException { return Person.singletion; } }
volatile 禁止重排序,强制让所有线程缓存失效, 避免了线程获取未构造完全的实例,也避免了线程缓存造成的判 null 问题。构造方法、readResolve、clone 三个方法可以避免反射,反序列化,克隆造成的单例失效。测试代码如下
public static void main(String[] arg) throws Exception { Person person = Person.getSingletion(); ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream("F:\work\IDEA_SPACE\bigdata\demo\src\main\resources\a.txt", true)); out.writeObject(person); ObjectInputStream in = new ObjectInputStream( new FileInputStream("F:\work\IDEA_SPACE\bigdata\demo\src\main\resources\a.txt")); Person person1 = (Person)in.readObject(); log.debug("序列化相等: {}", person == person1); Person person3 = (Person) person.clone(); log.debug("克隆相等: {}", person == person3); Constructor<Person> constructor = Person.class.getDeclaredConstructor(); constructor.setAccessible(true); Person person2 = constructor.newInstance(); log.debug("反射相等: {}", person == person2); }
运行结果如下:
225 [main] DEBUG c.Main - 序列化相等: true 228 [main] DEBUG c.Main - 克隆相等: true Exception in thread "main" java.lang.reflect.InvocationTargetException at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:423) at com.example.demo.Main.main(Main.java:79) Caused by: java.lang.RuntimeException: 单例类不允许二次调用构造函数 at com.example.demo.Person.<init>(Person.java:16)