彻底玩转单例模式

Posted 偶像java练习生

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了彻底玩转单例模式相关的知识,希望对你有一定的参考价值。

单例模式

饿汉式,DCL 懒汉式,深究!

饿汉式

package com.sigle;

//饿汉式单例: 缺点浪费内存
public class Hungry {

    //可能会浪费空间
    private byte [] date1 = new byte[1024*1024];
    private byte [] date2 = new byte[1024*1024];
    private byte [] date3 = new byte[1024*1024];
    private byte [] date4 = new byte[1024*1024];

    //构造器私有
   private Hungry(){

   }

   //饿汉式一上来就加载了初始化的对象
   private final static Hungry HUNGRY = new Hungry();


   public static Hungry getInstance(){
       return HUNGRY;
   }

}

类初始化的时候就创建对象

DCL 懒汉式

package com.sigle;

//懒汉式单例模式
//道高一尺魔高一丈
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

/**
 * 并发情况下,是有问题的,会创建多个对象
 */

public class LazyMan {

    private static boolean qingjiang =false;


     //三重检测
     private LazyMan(){
         synchronized (LazyMan.class){
//             if(qingjiang ==false){
//                 qingjiang =true;
//             }else{
//                     throw  new RuntimeException("不要试图使用反射破坏异常");
//             }
             if(lazyMan !=null){
                 throw  new RuntimeException("不要试图使用反射破坏异常");
             }
         }


         System.out.println(Thread.currentThread().getName()+"ok");
     }


     //增加volatile 关键字,防止指令重排,即代码只会初始化一个lazyman 对象
     private volatile static  LazyMan lazyMan;

    //双重检测锁模式的懒汉式单例 ,DCL 懒汉式
     public static LazyMan getInstance(){
         if(lazyMan == null){
             synchronized (LazyMan.class){
                 if(lazyMan == null){
                     lazyMan = new LazyMan();//不是一个原子性操作
                     /**
                      * 1. 分配内存空间
                      * 2. 执行构造方法,初始化对象
                      * 3. 把这个对象指向这个空间
                      * 期望按照 123 走
                      * 可能指令重排按132走 ,先占用空间,再把内存对象放进去 A
                      * 此时一个线程 B 来了,他看到这个空间已经被占用了,认为lazyman 已经初始化了,他可能直接return 了
                      */
                 }
             }
         }
         return lazyMan;// 此时lazyMan 还没有完成构造
     }
     // 多线程并发
    // 反射! 只要有反射,任何代码都不安全,反射可以破坏单例,如下面代码操作
     public static void main(String[] args) throws Exception {
//         for (int i = 0; i < 10; i++) {
//             new Thread(() ->{
//                 LazyMan.getInstance();
//             }).start();
//         }

         Field qingjiang = LazyMan.class.getDeclaredField("qingjiang");
         qingjiang.setAccessible(true);

        //  LazyMan instance = LazyMan.getInstance();
         Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
         //无视私有构造器
         declaredConstructor.setAccessible(true);


         LazyMan instance2 = declaredConstructor.newInstance();
         qingjiang.set(instance2,false);
         LazyMan instance3 = declaredConstructor.newInstance();
       //  qingjiang.set(instance,false);
       //  LazyMan instance2 = declaredConstructor.newInstance();



         //System.out.println(instance);
         System.out.println(instance3);
         System.out.println(instance2);
     }


}

静态类不类

package com.sigle;

//静态内部类
public class Holder {

    //构造器私有
    private Holder(){

    }


    public static  Holder getInstance(){
      return InnerClass.HOLDER;
    }

    public static  class InnerClass{

     private static  final  Holder HOLDER = new Holder();

    }
}

单例不安全,反射
则用枚举

package com.sigle;


import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

// enum 是一个什么,本身也是一个Class 类
//反射不能破坏枚举
public enum  EnumSingle {


    Instance;

    public EnumSingle getInstance(){
        return  Instance;
    }

}

class  Test{

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        EnumSingle enumSingle1 = EnumSingle.Instance;
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
        //私有权限破除
        declaredConstructor.setAccessible(true);
        //Exception in thread "main" java.lang.NoSuchMethodException: com.sigle.EnumSingle.<init>() ,枚举中是有参构造器
        //Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects// 反射不能破解枚举的单例
        EnumSingle enumSingle2 = declaredConstructor.newInstance();
        System.out.println(enumSingle1);
        System.out.println(enumSingle2);


//        EnumSingle enumSingle2 = EnumSingle.Instance;
//        System.out.println(enumSingle1);
//        System.out.println(enumSingle2);

    }



}

javap -EnumSingle 文件,反编译后的文件发现还真有个无参构造器
在这里插入图片描述

但是报错还是没有无参的构造器,然后通过专业的jda 反编译工具编译后是这样的:
在这里插入图片描述

以上是关于彻底玩转单例模式的主要内容,如果未能解决你的问题,请参考以下文章

彻底玩转单例模式

15彻底玩转单例模式

15彻底玩转单例模式

JUC并发编程(13)--- 彻底玩转单例模式

波吉学设计模式——玩转单例模式

单例模式_反射破坏单例模式_枚举类_枚举类实现单例_枚举类解决单例模式破坏