怎么防止反射、序列化问题,实现一个完美的单例?

本文介绍了如何通过静态内部类和java关键字实现单例,但指出这两种方法都可能被反射和序列化破坏。针对反射破坏,可以在构造函数中检查实例是否已存在并抛出异常。对于序列化问题,单例类需实现Serializable接口,并自定义反序列化方法以避免创建新实例。最后,提供了一种防反射和序列化破坏的完美单例实现方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、编码实现的单例
  • 静态内部类方式(JVM类加载实现)
public class Singleton {

    private Singleton() {}

    private static class SingleTonHolder {
        static final Singleton INSTANCE = new Singleton();
    }
	// 调用 getInstance 方法,才获取加载 SingleTonHolder,JVM保证只会有一个线程对类进行加载
    public static Singleton getInstance() {
        return SingleTonHolder.INSTANCE;
    }
}
  • java关键字实现(JMM实现)
public class SingleTon {

	private Singleton() {}
	
	// 利用 volatile 的语义:禁止指令重排序
    private volatile static SingleTon INSTANCE;

    public static SingleTon get() {
    	// Double-Check:即保证并发量,又保证线程安全。
        if (INSTANCE == null) {
            synchronized (SingleTon.class) {
                if (INSTANCE == null) {
                    INSTANCE = new SingleTon ();
                }
            }
        }
        return INSTANCE ;
    }

}

这两个手写编码的单例看起来很完美,但是利用反射、序列化,可以获得新对象,破坏单例。

2、反射问题
  • 反射破坏
public class SingletonReflect {

    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Singleton origin = Singleton.getInstance();
        System.out.println("origin: " + System.identityHashCode(origin));
        Class<Singleton> singletonClass = Singleton.class;
        Constructor<Singleton> constructor = singletonClass.getDeclaredConstructor();
        constructor.setAccessible(true);
        Singleton reflect = constructor.newInstance();
        System.out.println("reflect: " + System.identityHashCode(reflect));
    }

}

执行结果:

origin: 59559151
reflect: 1450821318

两个对象的地址值并不相同。反射破坏成功。

  • 解决方法思考
    //

  • 解决方法

在构造函数中进行判断,实例是否不为 null,如果不为null,抛出异常。

public class Singleton {

    private Singleton() {
    	// 增加判断
        if (SingleTonHolder.INSTANCE != null) {
            throw new IllegalStateException("can't reflect Singleton");
        }
    }

    private static class SingleTonHolder {
        static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingleTonHolder.INSTANCE;
    }

}

这里判断的依据涉及到的知识点:
1、初始化一个类,包括执行这个类的静态初始化和初始化在这个类中声明的静态字段。因此静态成员变量会随着类加载而存在。
2、Java规范中一个类或接口类型T被初始化的情况包括:
2.1:T是一个类,而且一个T类型的实例被创建;
2.2:T是一个类,且T中声明的一个静态方法被调用;
2.3:T中声明的一个静态字段被赋值;
2.4:T中声明的一个静态字段被使用,而且这个字段不是一个常量字段;常量字段:使用关键字 final 修饰的字段。
2.5:T是一个顶级类(top level class,见java语言规范的§7.6),而且一个断言语句嵌套在T内部被执行。

运行结果:
在这里插入图片描述

3、序列化问题

实现序列化,需要单例类实现Serializable 接口,定义serialVersionUID常量。

  • 序列化破坏
public class SingletonSerialize {

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

        Singleton origin = Singleton.getInstance();
        System.out.println("origin: " + System.identityHashCode(origin));
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\serialize\\instance.txt"));
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\serialize\\instance.txt"))) {
            oos.writeObject(origin);
            Singleton serialize = (Singleton) ois.readObject();
            System.out.println("serialize: " + System.identityHashCode(serialize));
        }
    }

}

执行结果:

origin: 1476011703
serialize: 1911728085
  • 解决方法思考
    //
  • 解决方法
    在单例类中增加两个方法,自定义反序列化方法。当反序列化时,抛出异常。
    private void readObject(ObjectInputStream in) throws IOException,
            ClassNotFoundException {
        throw new InvalidObjectException("can't deserialize singleton");
    }

    private void readObjectNoData() throws ObjectStreamException {
        throw new InvalidObjectException("can't deserialize singleton");
    }

运行结果:
在这里插入图片描述

4、总结

一个编码实现的完美单例写法:

public class Singleton implements Serializable {

    private static final long serialVersionUID = -3782723772939643172L;

    private static class SingleTonHolder {
        static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingleTonHolder.INSTANCE;
    }

    private Singleton() {
        if (SingleTonHolder.INSTANCE != null) {
            throw new IllegalStateException("can't reflect singleton");
        }
    }
    
    private void readObject(ObjectInputStream in) throws IOException,
            ClassNotFoundException {
        throw new InvalidObjectException("can't deserialize singleton");
    }

    private void readObjectNoData() throws ObjectStreamException {
        throw new InvalidObjectException("can't deserialize singleton");
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值