# 1.饿汉式

# 代码示例1

public class SingletonHungry {

    private static SingletonHungry instance = new SingletonHungry();

    private SingletonHungry(){
    }

    private static SingletonHungry getInstance(){
        return instance;
    }
}

# 代码示例2

public class SingletonHungry {

    private static SingletonHungry instance;

    static {
        instance = new SingletonHungry();
    }

    private SingletonHungry(){
    }

    private static SingletonHungry getInstance(){
        return instance;
    }
}

# 原理

# 2.懒汉式

# 代码示例

public class SingletonLazy {

    private static volatile SingletonLazy instance;

    private SingletonLazy(){
    }

    public static synchronized SingletonLazy getInstance(){
        if(instance == null){
            instance = new SingletonLazy();
        }
        return instance;
    }
}

# 原理

# 3.双重检查锁

# 代码示例

public class DoubleCheckLock {

    private static volatile DoubleCheckLock instance;

    private DoubleCheckLock(){
    }

    public static DoubleCheckLock getInstance(){
        if(instance == null){
            synchronized (DoubleCheckLock.class){
                if(instance == null){
                    instance = new DoubleCheckLock();
                }
            }
        }
        return instance;
    }
}

# 原理

# 4.静态内部类

# 代码示例

public class SingletonStaticInner {
	//私有构造器
    //无法由外部创建此对象
    private SingletonStaticInner(){
    }

    private static class Holder{
        //创建外部类的实例对象, 如果实例对象构造复杂, 可以选择静态代码块构造
        private static SingletonStaticInner instance = new SingletonStaticInner();
    }

    public static SingletonStaticInner getInstance(){
        //调用静态内部类时, 构造外部类的实例对象, 并返回
        //实现懒加载, 调用时才会构造静态内部类
        return Holder.instance;
    }
}

# 原理

# 静态内部类构造单例对象

public class SingletonStaticInner {
    //单例对象的域引用
    private SingletonClass singletonClass;

    private SingletonStaticInner(){
        //静态内部类在构造外部类时, 用私有构造器构造单例对象
        singletonClass = new SingletonClass();
    }

    private static class Holder{
        //静态内部类被调用时会去构造外部类的实例对象
        //保证外部类一定是单例对象
        private static SingletonStaticInner singletonStaticInner 
            	= new SingletonStaticInner();
        
        //或者使用静态代码块来构造外部类的单例对象
        static {
            //当外部类构造较为复杂时使用, 也可以在外部类的私有构造器中进行构造
        }
    }

    //外部类调用此静态方法获取自己的单例对象
    public static SingletonStaticInner getInstance(){
        return Holder.singletonStaticInner;
    }

    //获取外部类中真实需要构造的实例对象
    public SingletonClass get() {
        return singletonClass;
    }
}

# 5.枚举实现单例

枚举实现单例模式有其他模式不具备的好处:

  1. 能正常序列化和反序列化(避免序列化对单例的破坏)
  2. 反射无法创建对象
public enum SingletonEnum {
    instance;
    public void whateverMethod() {
        //do something
    }
}

# 使用枚举构造单例对象

public enum SingletonEnum {
    //一个枚举实例,一定是一个唯一存在的对象
    singleton;
    //枚举实例中的域, 指向对应单例对象
    private SingletonClass instance;
    
    SingletonEnum(){
        //初始化枚举类时, 创建或构造单例对象
        this.instance = new SingletonClass();
    }
    
    public SingletonClass get() {
        //提供获取单例对象的方法
        return instance;
    }
}

# 反射对单例的破坏

# 反序列化对单例的破坏

序列化与反序列化同样会对单例造成破坏, Java默认的反序列化时, 正是使用反射调用无参构造方法创建一个新的对象.

解决方案: 在Singleton类中定义readResolve方法, 直接return单例对象就可以了.

# 为什么反序列化不会破坏枚举实现的单例

原因如下: 在序列化的时候Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过java.lang.Enum的valueOf方法来根据名字查找枚举对象。同时,编译器是不允许任何对这种序列化机制的定制的,因此禁用了writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法。

修改于: 8/11/2022, 3:17:56 PM