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


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

    单例的好处:

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

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

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

    1. public class Singleton{
    2.     private static volatile Singleton mInstance;//声明私有属性的对象
    3.     private Singleton(){}//构造方法私有化
    4.     public static Singleton getInstance(){//创建一个外部可以访问的公开的方法返回当前对象的实例
    5.         if(mInstance == null){
    6.             synchronized (Singleton.class) {
    7.                 if(mInstance == null){
    8.                     mInstance = new Singleton();
    9.                 }
    10.             }
    11.         }
    12.         return mInstance;
    13.     }
    14. }

        调试:

    1. public static void getReflect() {
    2.         Singleton singleton = Singleton.getInstance();
    3.         try {
    4.             Constructor constructor = Singleton.class.getDeclaredConstructor();
    5.             constructor.setAccessible(true);//允许访问私有构造器
    6.             Singleton reflectSingleton = constructor.newInstance();
    7.             System.out.println(reflectSingleton == singleton);//判断当前两个实例是否是同一个对象
    8.         } catch (Exception e) {
    9.             e.printStackTrace();
    10.         }
    11.     }

     输出:false

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

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

    1. public class Singleton{
    2.     private static volatile Singleton mInstance;
    3.     private static volatile boolean mIsInstantiated = false;
    4.     private Singleton(){
    5.         if (mIsInstantiated){
    6.             throw new RuntimeException("休想反射破坏我的单例");
    7.         }
    8.         mIsInstantiated = true;
    9.     }
    10.     public static Singleton getInstance(){
    11.         if(mInstance == null){
    12.             synchronized (Singleton.class) {
    13.                 if(mInstance == null){
    14.                     mInstance = new Singleton();
    15.                 }
    16.             }
    17.         }
    18.         return mInstance;
    19.     }
    20. }

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

    1. public class Singleton implements Cloneable {
    2.     private static volatile Singleton mInstance;
    3.     private Singleton() {
    4.     }
    5.     public static Singleton getInstance() {
    6.         if (mInstance == null) {
    7.             synchronized (Singleton.class) {
    8.                 if (mInstance == null) {
    9.                     mInstance = new Singleton();
    10.                 }
    11.             }
    12.         }
    13.         return mInstance;
    14.     }
    15.     @NonNull
    16.     @Override
    17.     protected Object clone() throws CloneNotSupportedException {
    18.         return super.clone();
    19.     }
    20. }

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

    1.   protected Object clone() throws CloneNotSupportedException {
    2.         if (!(this instanceof Cloneable)) {
    3.             throw new CloneNotSupportedException("Class " + getClass().getName() +
    4.                                                  " doesn't implement Cloneable");
    5.         }
    6.         return internalClone();
    7.     }

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

    调试:

    1.   public static void getClone() {
    2.         try {
    3.             Singleton singleton = Singleton.getInstance();
    4.             Singleton cloneSingleton;
    5.             cloneSingleton = (Singleton) Singleton.getInstance().clone();
    6.             System.out.println(cloneSingleton == singleton);
    7.         } catch (CloneNotSupportedException e) {
    8.             e.printStackTrace();
    9.         }
    10.     }
    11.    

    输出:false

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

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

    1. public class Singleton implements Cloneable {
    2.     private static volatile Singleton mInstance;
    3.     private Singleton() {
    4.     }
    5.     public static Singleton getInstance() {
    6.         if (mInstance == null) {
    7.             synchronized (Singleton.class) {
    8.                 if (mInstance == null) {
    9.                     mInstance = new Singleton();
    10.                 }
    11.             }
    12.         }
    13.         return mInstance;
    14.     }
    15.     @NonNull
    16.     @Override
    17.     protected Object clone() throws CloneNotSupportedException {
    18.       //  return super.clone();
    19.         return mInstance;//调clone()时直接返回已经实例的对象
    20.     }
    21. }

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

    1. public class Singleton implements Serializable {
    2.     private static volatile Singleton mInstance;
    3.     private Singleton(){
    4.     }
    5.     public static Singleton getInstance(){
    6.         if(mInstance == null){
    7.             synchronized (Singleton.class) {
    8.                 if(mInstance == null){
    9.                     mInstance = new Singleton();
    10.                 }
    11.             }
    12.         }
    13.         return mInstance;
    14.     }
    15.  
    16. }

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

    调试: 

    1. public  static  void getSerializable(){
    2.         try {
    3.             Singleton singleton = Singleton.getInstance();
    4.             FileOutputStream fos = new FileOutputStream("singleton.txt");
    5.             ObjectOutputStream oos = new ObjectOutputStream(fos);
    6.             oos.writeObject(singleton);
    7.             oos.close();
    8.             fos.close();
    9.             FileInputStream fis = new FileInputStream("singleton.txt");
    10.             ObjectInputStream ois = new ObjectInputStream(fis);
    11.             Singleton serializedSingleton = (Singleton) ois.readObject();
    12.             fis.close();
    13.             ois.close();
    14.             System.out.println(serializedSingleton==singleton);
    15.         } catch (Exception e) {
    16.             e.printStackTrace();
    17.         }
    18.     }
    19. }

    输出:false

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

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

    1. public class Singleton implements Serializable {
    2.     private static volatile Singleton mInstance;
    3.     private Singleton() {
    4.     }
    5.     public static Singleton getInstance() {
    6.         if (mInstance == null) {
    7.             synchronized (Singleton.class) {
    8.                 if (mInstance == null) {
    9.                     mInstance = new Singleton();
    10.                 }
    11.             }
    12.         }
    13.         return mInstance;
    14.     }
    15.     protected Object readResolve() throws ObjectStreamException {
    16.         return mInstance;
    17.     }
    18. }
  • 相关阅读:
    14 【接口规范和业务分层】
    matplotlib 设置手动设置图例的位置大小
    modernC++手撸任意层神经网络22前向传播反向传播&梯度下降等23代码补全的例子0901b
    短视频矩阵源码开发部署---技术解析
    华为ENSP网络设备配置实战2(较为复杂的ospf)
    Codeforces-1696 D: Permutation Graph【构造、分治、数据结构】
    C++ 封装之析构函数、this指针、static、const 关键字
    android Binder安全性、Unix Socket安全性研究
    【spring】初识spring基础
    后端实现大文件分片上传
  • 原文地址:https://blog.csdn.net/csj731742019/article/details/126395327