单例模式

单例模式就是指在某个系统中只存在一个实例。单例模式的要求:

  • 私有构造方法
  • 提供访问唯一对象的方法

单例模式分为两类:饿汉式、懒汉式

1、饿汉式

饿汉式:类加载就会导致该单实例对象被创建

缺点:如果该对象足够大的话,而一直没有使用就会造成内存的浪费

/**
 * 饿汉式一:使用静态变量
 */
public class Singleton {
    //私有构造方法
    private Singleton() {}

    //使用static修饰或者static代码块,在类加载后就创建了唯一的实例对象。
    private static Singleton instance = new Singleton();

    //对外提供静态方法获取该对象
    public static Singleton getInstance() {
        return instance;
    }
}

/**
 * 饿汉式二:使用静态代码块
 */
public class Singleton {

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

    //在成员位置创建该类的对象
    private static Singleton instance;

    static {
        instance = new Singleton();
    }

    //对外提供静态方法获取该对象
    public static Singleton getInstance() {
        return instance;
    }
}
饿汉式

2、懒汉式

懒汉式:类加载不会创建该单实例对象,而是首次使用该对象时才会创建

/**
 * 懒汉式(线程安全)
 */
public class Singleton { 

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

    //volatile对静态变量的修饰能保证变量值在各线程访问时的同步性、唯一性
    private volatile static Singleton instance;

   //对外提供静态方法获取该对象
    public static Singleton getInstance() {
        //第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实例
        if(instance == null) {
            synchronized (Singleton.class) {
                //抢到锁之后再次判断是否为null(双检锁)
                if(instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
懒汉式

3、破坏单例模式

方式一: 序列化

这种方式需要单例类实现序列化(Serializable)接口

//定义单例类,实现序列化接口
import java.io.Serializable;

public class Singleton implements Serializable {

    private Singleton(){}

    private static volatile Singleton singleton;

    public static Singleton getInstance() {

        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}
定义单例类,并实现序列化

破坏单例:

  • 通过单例类获取实例对象,然后写入到文件中
  • 读取该文件,生成实例类
//序列化的方式破坏单例
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Test {
    /**
     * 将单例的实例对象写入文件
     */
    public static void writeObject2File() throws Exception {
        //获取Singleton类的对象
        Singleton instance = Singleton.getInstance();
        //创建对象输出流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:/a.txt"));
        //将instance对象写出到文件中
        oos.writeObject(instance);
    }

    /**
     * 读取文件,生成单例的实例对象
     */
    private static Singleton readObjectFromFile() throws Exception {
        //创建对象输入流对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/a.txt"));
        //第一个读取Singleton对象
        Singleton instance = (Singleton) ois.readObject();

        return instance;
    }


    public static void main(String[] args) throws Exception {
        //往文件中写对象
        //writeObject2File();

        //从文件中读取对象
        Singleton s1 = readObjectFromFile();
        Singleton s2 = readObjectFromFile();

        //判断两个反序列化后的对象是否是同一个对象
        System.out.println(s1 == s2);//false,生成了多个Singleton对象
    }
}
序列化方式创建多个单例对象

如何阻止破坏:

在Singleton类中添加`readResolve()`方法,在反序列化时被反射调用,如果定义了这个方法,就返回这个方法的值,如果没有定义,则返回新new出来的对象。

//序列话破坏单例的解决方法:添加readResolve方法
import java.io.Serializable;

public class Singleton implements Serializable {

    private Singleton(){}

    private static volatile Singleton singleton;

    public static Singleton getInstance() {

        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }

    /**
     * 下面是为了解决序列化反序列化破解单例模式
     */
    private Object readResolve() {
     //返回单例对象,如果没有写这个方法就会返回一个新new的
        return Singleton.getInstance();
    }
}
解决序列化创建多个单例

 源码解析:ObjectInputStream

public final Object readObject() throws IOException, ClassNotFoundException{
    ...
    // if nested read, passHandle contains handle of enclosing object
    int outerHandle = passHandle;
    try {
        Object obj = readObject0(false);//重点查看readObject0方法
    .....
}
    
private Object readObject0(boolean unshared) throws IOException {
    ...
    try {
        switch (tc) {
            ...
            case TC_OBJECT:
                return checkResolve(readOrdinaryObject(unshared));//重点查看readOrdinaryObject方法
            ...
        }
    } finally {
        depth--;
        bin.setBlockDataMode(oldMode);
    }    
}
    
private Object readOrdinaryObject(boolean unshared) throws IOException {
    ...
    //isInstantiable 返回true,执行 desc.newInstance(),通过反射创建新的单例类,
    obj = desc.isInstantiable() ? desc.newInstance() : null; 
    ...
    // 在Singleton类中添加 readResolve 方法后 desc.hasReadResolveMethod() 方法执行结果为true
    if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()) {
        // 通过反射调用 Singleton 类中的 readResolve 方法,将返回值赋值给rep变量
        // 这样多次调用ObjectInputStream类中的readObject方法,继而就会调用我们定义的readResolve方法,所以返回的是同一个对象。
        Object rep = desc.invokeReadResolve(obj);
         ...
    }
    return obj;
}
ObjectInputStream源码分析

 

 可以看出读取序列化文件时调用了readResolve方法

方式二:反射

 利用反射来创建单例类的实例对象

import java.lang.reflect.Constructor;

public class Test2 {

    public static void reflect() throws Exception {
        //获取Singleton类的字节码对象
        Class clazz = Singleton.class;
        //获取Singleton类的私有无参构造方法对象
        Constructor constructor = clazz.getDeclaredConstructor();
        //取消访问检查
        constructor.setAccessible(true);

        //创建Singleton类的对象s1
        Singleton s1 = (Singleton) constructor.newInstance();

        //创建Singleton类的对象s2
        Singleton s2 = (Singleton) constructor.newInstance();

        //判断通过反射创建的两个Singleton对象是否是同一个对象
        System.out.println(s1 == s2);//false,说明生成了不同的实例对象
    }

    public static void main(String[] args) throws Exception {
        reflect();
    }
}
反射创建多个单例对象

 如何阻止破坏 :

import java.io.Serializable;

public class Singleton implements Serializable {

    //私有构造器
    private Singleton(){
        /**
         * 阻止反射破解单例模式:
         *  如果实例对象已经存在了就抛异常(构造方法返回不了实例对象,只能抛异常)
         */
        if(singleton != null) {
            throw new RuntimeException();
        }
    }

    private static volatile Singleton singleton;

    public static Singleton getInstance() {

        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}
解决反射创建多个单例

4、完整的单例模式

import java.io.Serializable;

public class Singleton implements Serializable {

    /**
     * 私有构造
     */
    private Singleton(){
        /**
         * 阻止反射破解单例模式:
         *  如果实例对象已经存在了就抛异常(构造方法返回不了实例对象,只能抛异常)
         */
        if(instance != null) {
            throw new RuntimeException();
        }
    }

    private static volatile Singleton instance;

    /**
     * 获取单例对象
     * @return
     */
    public static Singleton getInstance() {

        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }

    /**
     * 解决序列化反序列化破解单例模式
     */
    private Object readResolve() {
        return Singleton.getInstance();
    }

}
完整的单例模式

 5、单例的应用

  •  Runtime类 

 

posted @ 2022-12-05 16:53  weslie  阅读(55)  评论(0)    收藏  举报