双校验 单例类

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)

  

原文地址:https://www.cnblogs.com/wudeyun/p/13834692.html