雙校驗 單例類
阿新 • • 發佈:2020-10-18
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)