单例模式详解——参考B站课程

课程地址

单例模式是为了确保只有一个实例的存在,从而防止多个实例存在会对程序造成干扰或攻击。

1.懒汉模式

public class LazySingleTon {
    private static LazySingleTon singleTon;
    private LazySingleTon(){}

    public synchronized static LazySingleTon getInstance(){
        if(singleTon == null)
            singleTon = new LazySingleTon();
        return singleTon;
    }
}

缺点:同步机制导致效率低下,并且无法防控反射攻击,如下

public class LazySingleTon {
    private static LazySingleTon singleTon;
    private LazySingleTon(){}

    public synchronized static LazySingleTon getInstance(){
        if(singleTon == null)
            singleTon = new LazySingleTon();
        else throw  new RuntimeException("多个实例不允许");
        return singleTon;
    }
}
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Test {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Constructor<LazySingleTon>  singleTonConstructor = LazySingleTon.class.getDeclaredConstructor();
        singleTonConstructor.setAccessible(true);
        LazySingleTon singleTon = singleTonConstructor.newInstance();
        LazySingleTon singleTon1 = LazySingleTon.getInstance();
        System.out.println(singleTon == singleTon1);
   


    }
}

2.DCL机制 Double Check Locking

public class DCLSingleTon {
    private volatile static DCLSingleTon singleTon;
    private DCLSingleTon(){};
    public static DCLSingleTon getInstance(){
        if(singleTon == null){
            synchronized (DCLSingleTon.class){
                if(singleTon == null)
                    singleTon = new DCLSingleTon();
            }
                    
        }
        return singleTon;
    }
}

DCL机制满足一般的并发情况,提高了效率,用volatile修饰singleTon是为了禁止指令重排序和优化,如singleTon = new DCLSingleTon();这一行不是由一步完成的,而是三个步骤完成的,包括堆分配空间、初始化,以及栈内引用指向堆空间地址三个步骤,而voliate保证了指令按照顺序进行,而不会被编译器优化,防止了空指针情况的出现。当然,DCL模式也无法防御反射攻击。

3.饿汉模式

public class SingTon {
    public static SingTon singTon = new SingTon();
    private SingTon(){
        if(singTon != null) throw  new RuntimeException("多个实例不允许");
    }

    public static SingTon getInstance(){
        return singTon;
    }
}

这种模式缺点是单例在最一开始就有了,而不是在需要它的时候才出现。好处是基于JVM类加载机制保证线程安全,并可以防护反射攻击

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Test {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

        Constructor<SingTon> constructor = SingTon.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        SingTon singTon = constructor.newInstance();

        SingTon singTon1 = SingTon.getInstance();
        System.out.println(singTon == singTon1);

    }
}

4.静态内部类

public class InnerSingleTon {
    public static class InnerClassHoler{
        private static InnerSingleTon singleTon = new InnerSingleTon();
    }

    private InnerSingleTon(){
        if(InnerClassHoler.singleTon != null) throw  new RuntimeException("多个实例不允许");
    }

    public static InnerSingleTon getInstance(){
        return InnerClassHoler.singleTon;
    }
}

优点可以防护反射攻击、同时只会被加载一次 

 

 改进:实现序列化

实现Serializable接口并定义long型序列号,写readResolve()方法

import java.io.Serializable;

public class InnerSingleTon implements Serializable {
    static final long serialVersionUID = 42L;
    public static class InnerClassHoler{
        private static InnerSingleTon singleTon = new InnerSingleTon();
    }

    private InnerSingleTon(){
        if(InnerClassHoler.singleTon != null) throw  new RuntimeException("多个实例不允许");
    }

    public static InnerSingleTon getInstance(){
        return InnerClassHoler.singleTon;
    }

    Object readResolve() {
        return InnerClassHoler.singleTon;
    }

    final int a = 1;

}
import java.io.*;


public class Test {
    public static void main(String[] args) throws IOException,  ClassNotFoundException {


        InnerSingleTon singTon1 = InnerSingleTon.getInstance();
       

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("inner"));
        oos.writeObject(singTon1);

        oos.close();
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("inner"));
        InnerSingleTon obj = (InnerSingleTon) ois.readObject();
        System.out.println(singTon1 == obj);
    }
}

5.枚举

public enum EnumSingleTon {
    INSTANCE;
}
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;


public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

        EnumSingleTon singleTon = EnumSingleTon.INSTANCE;
        EnumSingleTon singleTon1 = EnumSingleTon.INSTANCE;
        System.out.println(singleTon == singleTon1);

        Constructor<EnumSingleTon> constructor = EnumSingleTon.class.getDeclaredConstructor(String.class,int.class);
        constructor.setAccessible(true);
        EnumSingleTon singleTon2 = constructor.newInstance("INSTANCE",0);
        System.out.println(singleTon == singleTon2);

    }
}

实验结果表明枚举不能通过反射的方式创建实例,枚举类继承抽象类java.lang.Enum,所以在构造器的时候需要传参数

 

反射安全,反序列化机制安全 线程安全

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值