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)