Java破坏单例模式的方式及原理

单例的目的:仅允许程序中存在有且仅有一个实例

单例的好处:

1、对于频繁使用的对象,可以省略创建对象所需的时间。

2、减少了new对象的操作次数,降低了系统内存的使用频率,减轻了GC的压力,缩短了DC的停顿时间

一、反射对单例模式的破坏

public class Singleton{

    private static volatile Singleton mInstance;//声明私有属性的对象

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

    public static Singleton getInstance(){//创建一个外部可以访问的公开的方法返回当前对象的实例

        if(mInstance == null){

            synchronized (Singleton.class) {

                if(mInstance == null){

                    mInstance = new Singleton();

                }

            }

        }

        return mInstance;

    }

}

    调试:

 public static void getReflect() {

        Singleton singleton = Singleton.getInstance();

        try {

            Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();

            constructor.setAccessible(true);//允许访问私有构造器

            Singleton reflectSingleton = constructor.newInstance();

            System.out.println(reflectSingleton == singleton);//判断当前两个实例是否是同一个对象

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

 输出:false

    原理解析:通过反射获得私有的构造器,通过把这个构造器的setAccessible属性设置为true,这样直接无视了构造器的私有性,我们先通过正常的getInstance()方法创建一个实例,再通过反射得到的构造器创建一个实例,

    解决方案: 其思想就是采用一个全局变量,来标记是否已经实例化过了,如果已经实例化过了,第二次实例化的时候,抛出异常。

public class Singleton{

    private static volatile Singleton mInstance;

    private static volatile boolean mIsInstantiated = false;

    private Singleton(){

        if (mIsInstantiated){

            throw new RuntimeException("休想反射破坏我的单例");
        }

        mIsInstantiated = true;

    }

    public static Singleton getInstance(){

        if(mInstance == null){

            synchronized (Singleton.class) {

                if(mInstance == null){

                    mInstance = new Singleton();

                }

            }

        }

        return mInstance;

    }

}

二、 clone()对单例模式的破坏

public class Singleton implements Cloneable {

    private static volatile Singleton mInstance;

    private Singleton() {

    }

    public static Singleton getInstance() {

        if (mInstance == null) {

            synchronized (Singleton.class) {

                if (mInstance == null) {

                    mInstance = new Singleton();

                }

            }

        }

        return mInstance;

    }

    @NonNull

    @Override

    protected Object clone() throws CloneNotSupportedException {

        return super.clone();

    }

}

注意点:若要具有克隆能力,实现Cloneable接口的类必须重写从Object继承来的clone方法,并调用Object的clone方法。

  protected Object clone() throws CloneNotSupportedException {

        if (!(this instanceof Cloneable)) {

            throw new CloneNotSupportedException("Class " + getClass().getName() +

                                                 " doesn't implement Cloneable");

        }



        return internalClone();

    }

clone方法首先会判对象是否实现了Cloneable接口,若无则抛出CloneNotSupportedException, 最后会调用internalClone. intervalClone是一个native方法。

调试:

  public static void getClone() {


        try {

            Singleton singleton = Singleton.getInstance();

            Singleton cloneSingleton;

            cloneSingleton = (Singleton) Singleton.getInstance().clone();

            System.out.println(cloneSingleton == singleton);

        } catch (CloneNotSupportedException e) {

            e.printStackTrace();

        }

    }

   

输出:false

    原理解析:java.lang.Obeject#clone() 方法不会调用构造方法,而是直接从内存中拷贝内存区域。

    解决方案:重写clone()方法,调clone()时直接返回已经实例的对象

 public class Singleton implements Cloneable {

    private static volatile Singleton mInstance;

    private Singleton() {

    }

    public static Singleton getInstance() {

        if (mInstance == null) {

            synchronized (Singleton.class) {

                if (mInstance == null) {

                    mInstance = new Singleton();

                }

            }

        }

        return mInstance;

    }


    @NonNull

    @Override

    protected Object clone() throws CloneNotSupportedException {

      //  return super.clone();

        return mInstance;//调clone()时直接返回已经实例的对象

    }

}

三、 序列化对单例模式的破坏

public class Singleton implements Serializable {

    private static volatile Singleton mInstance;

    private Singleton(){

    }

    public static Singleton getInstance(){

        if(mInstance == null){

            synchronized (Singleton.class) {

                if(mInstance == null){

                    mInstance = new Singleton();

                }

            }

        }

        return mInstance;

    }
 

}

很简单 实现Serializable接口就行了。

调试: 

 public  static  void getSerializable(){

        try {

            Singleton singleton = Singleton.getInstance();

            FileOutputStream fos = new FileOutputStream("singleton.txt");

            ObjectOutputStream oos = new ObjectOutputStream(fos);

            oos.writeObject(singleton);

            oos.close();

            fos.close();

            FileInputStream fis = new FileInputStream("singleton.txt");

            ObjectInputStream ois = new ObjectInputStream(fis);

            Singleton serializedSingleton = (Singleton) ois.readObject();

            fis.close();

            ois.close();

            System.out.println(serializedSingleton==singleton);

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

}

输出:false

原理解析:在反序列化时,ObjectInputStream 因为利用反射机制调用了 readObject --> readObject0 --> readOrdinary --> CheckResolve。在readOrdinady中调用了invokeReadResolve(),该方法使用反射机制创建新的对象,从而破坏了单例唯一性。

解决方案:在反序列化时的回调方法 readResolve()中返回单例对象

public class Singleton implements Serializable {

    private static volatile Singleton mInstance;



    private Singleton() {

    }



    public static Singleton getInstance() {

        if (mInstance == null) {

            synchronized (Singleton.class) {

                if (mInstance == null) {

                    mInstance = new Singleton();

                }

            }

        }

        return mInstance;

    }



    protected Object readResolve() throws ObjectStreamException {

        return mInstance;

    }

}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Chen_ShengJie

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值