如何破坏单例

首先提供一个双重加锁的单例模式

public class CheckSingleton{
     
   private CheckSingleton(){};

   private volatile static CheckSingleton checksingleton;

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

一、【反射破坏单例】

import java.lang.reflect.Constructor;
public class SingletonTest {

    public static void main(String[] args) {
        CheckSingleton  singleton = CheckSingleton.getSingleton();
        try {
            Class<CheckSingleton> singleClass = (Class<Singleton>)Class.forName("com.dev.interview.CheckSingleton");

            Constructor<CheckSingleton> constructor = singleClass.getDeclaredConstructor(null);

            constructor.setAccessible(true);

            CheckSingleton singletonByReflect = constructor.newInstance();

            System.out.println("singleton : " + singleton);
            System.out.println("singletonByReflect : " + singletonByReflect);
            System.out.println("singleton == singletonByReflect : " + (singleton == singletonByReflect));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

发现发射生成的一个新的对象 ,此时单例模式被破坏

防止方案(设置为如果通过反射创建则抛出异常):

private Singleton() {
    if (null!= CheckSingleton) {
        throw new RuntimeException("Singleton constructor is called... ");
    }
}

二、反序列化生成新的实例

public class SingletonTest {

    public static void main(String[] args) {
        CheckSingleton singleton = CheckSingleton.getSingleton();

        //Write Obj to file
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(new FileOutputStream("tempFile"));
            oos.writeObject(singleton);
            //Read Obj from file
            File file = new File("tempFile");

            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
            Singleton singletonBySerialize = (CheckSingleton)ois.readObject();
            //判断是否是同一个对象

            System.out.println("singleton : " + singleton);
            System.out.println("singletonBySerialize : " + singletonBySerialize);
            System.out.println("singleton == singletonBySerialize : " + (singleton == singletonBySerialize));

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

通过先序列化再反序列化的方式,可获取到一个新的单例对象,这就破坏了单例。

避免方案(在单例中加入readResolve方法,因为在反序列化执行过程中会执行到ObjectInputStream#readOrdinaryObject方法,这个方法会判断对象是否包含readResolve方法,如果包含的话会直接调用这个方法获得对象实例。)

private Object readResolve() {
    return getSingleton();
}

如果没有该方法,会通过反序列化中特殊的反射方式得到对象,和上边第一种破坏方式中的反射不一样。

三、建议使用枚举做单例模式,天生安全。

public enum Singleton{

   INSTANCE;
   
   public void say(){
       System.out.print("hello ,enum")
   }

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值