Java 单例设计模式

1、懒汉模式

class LazySingleton{

    //volatile防止指令重排
    private static volatile LazySingleton SINGLETON ;

    //构造方法私有化
    private LazySingleton(){}

    /*
      双重检查锁DCL(Double Check Lock)
      检查两次是否为null,再加一把锁
     */
     public static LazySingleton getInstance(){
        if (SINGLETON == null){
            synchronized (LazySingleton.class){
                if (SINGLETON == null){
                    SINGLETON = new LazySingleton();
                }
            }
        }

        return SINGLETON;
    }

}

为什么加锁?

保证线程安全,防止多线程情况下多个线程同时执行新建对象方法。

为什么不使用synchronized直接锁getInstance()方法?

同一时间只能有一个线程访问方法,效率低下。

为什么加volatile关键字?

防止执行new MySingleton()时发生指令重排。

2、饿汉模式

/**
 * 饿汉式--单例模式
 */
class HungrySingleton{
    
    //类加载的是否就初始化了,不用担心线程安全
    private static HungrySingleton SINGLETON  = new HungrySingleton();

    //构造方法私有化
    private HungrySingleton(){}

    public static HungrySingleton getInstance(){
        return SINGLETON;
    }   
}

缺点:
实例在类初始化一开始就被创建了,哪怕后来根本没有使用它。

3、静态内部类

/**
 * 静态内部类(懒汉式)--单例模式
 */
class InnerClassSingleton{

    //静态内部类创建对象
    private static class InnerClass{
        private static InnerClassSingleton singleton = new InnerClassSingleton();
    }

    private InnerClassSingleton(){}

    public static InnerClassSingleton getInstance(){
        return InnerClass.singleton;
    }
}

通过JVM类加载来解决线程安全。
缺点:可以通过反射创建对象

4、反射攻击

我们通过静态内部类创建单例对象。虽然我们的构造方法是private的,当时仍然可以通过狗仔方法创建对象。

public class TestDemo3 {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //单例创建的对象
        System.out.println(InnerClassSingleton.getInstance());
        //通过反射创建对象
        Constructor<InnerClassSingleton> constructor = InnerClassSingleton.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        InnerClassSingleton innerClassSingleton = constructor.newInstance();
        System.out.println(innerClassSingleton);
    }
}
com.zjw.InnerClassSingleton@1540e19d
com.zjw.InnerClassSingleton@677327b6

发现两个对象不一致。。

解决办法:在构造方法处检查对象是否创建

/**
 * 静态内部类(懒汉式)--单例模式
 * 解决反射创建问题
 */
class InnerClassSingleton{

    //静态内部类创建对象
    private static class InnerClass{
        private static InnerClassSingleton singleton = new InnerClassSingleton();
    }

    //在构造方法检查对象是否创建
    private InnerClassSingleton(){
        if (InnerClass.singleton!=null){
            throw new RuntimeException("单例已经创建,不能再新建了");
        }
    }

    public static InnerClassSingleton getInstance(){
        return InnerClass.singleton;
    }
}

5、枚举类型

/**
 * 枚举类型--单例模式
 */
public enum EnumSingleton {

    SINGLETON;

    public void print(){
        System.out.println(this.hashCode());
    }
}

public class TestDemo4 {
    public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
        EnumSingleton singleton1 = EnumSingleton.SINGLETON;
        EnumSingleton singleton2 = EnumSingleton.SINGLETON;
        System.out.println(singleton1);
        System.out.println(singleton2);
        singleton1.print();
        singleton2.print();

        Constructor<EnumSingleton> constructor = EnumSingleton.class.getDeclaredConstructor(String.class, int.class);
        constructor.setAccessible(true);
        EnumSingleton singleton = constructor.newInstance("SINGLETON", 0);
    }
}

结果:

SINGLETON
SINGLETON
356573597
356573597
Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
	at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
	at com.zjw.TestDemo4.main(TestDemo4.java:17)


发现通过枚举类型创建的单例是不能通过反射创建的。

6、序列化

/**
 * 反序列化--单例模式
 */
class HungrySingleton implements Serializable {

    private static final long serialVersionUID = 23423454L;

    private static HungrySingleton SINGLETON  = new HungrySingleton();

    //构造方法私有化
    private HungrySingleton(){}

    public static HungrySingleton getInstance(){
        return SINGLETON;
    }

    Object readResolve() throws ObjectStreamException{
        return SINGLETON;
    }

}

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

        HungrySingleton instance = HungrySingleton.getInstance();

        //序列化
        ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("instance"));
        outputStream.writeObject(instance);
        outputStream.close();
        //反序列化
        ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("instance"));
        HungrySingleton readSingleton = (HungrySingleton)inputStream.readObject();
        inputStream.close();
        //比较是否为同一个对象
        System.out.println(instance==readSingleton);
    }
}

不太懂,之后再研究~~~

posted @ 2020-09-08 17:38  雨中遐想  阅读(109)  评论(0编辑  收藏  举报