1. 程式人生 > 實用技巧 >雙校驗 單例類

雙校驗 單例類

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)