单例模式

打印 上一主题 下一主题

主题 873|帖子 873|积分 2619

单例模式(反射破坏-枚举)

饿汉式单例
  1. package com.jan.single;
  2. //饿汉式单例
  3. public class Hungry {
  4.     //一上来就会加载好,可能会浪费空间
  5.     private byte[] data11=new byte[1024*1024];
  6.     private byte[] data12=new byte[1024*1024];
  7.     private byte[] data13=new byte[1024*1024];
  8.     private byte[] data14=new byte[1024*1024];
  9.     private Hungry(){
  10.     }
  11.     private final static Hungry HUNGRY = new Hungry();//保证是唯一的了
  12.     public static Hungry getInstance(){ //一上来就加载了
  13.         return HUNGRY;
  14.     }
  15. }
复制代码
懒汉式单例
  1. package com.jan.single;
  2. //懒汉式单例
  3. public class LazyMan {
  4.     private LazyMan(){ ///构造器私有
  5.         System.out.println(Thread.currentThread().getName()+"ok");
  6.     }
  7.     private  static  LazyMan lazyMan;//对象
  8.     public static LazyMan getInstance(){
  9.         if(lazyMan==null){
  10.             lazyMan = new LazyMan();
  11.         }
  12.         return lazyMan;
  13.     }
  14.     //多线程并发
  15.     public static void main(String[] args){
  16.         for (int i = 0; i < 10; i++) {
  17.             new Thread(()->{
  18.                 LazyMan.getInstance();
  19.             }).start();
  20.         }
  21.     }
  22. }
复制代码
此时运行结果:单例有问题,偶尔会有多线程

静态内部类:
  1. package com.jan.single;
  2. //静态内部类
  3. public class Holder {
  4.     private Holder(){ //构造器私有
  5.     }
  6.     public static Holder getInstance(){
  7.         return InnerClass.HOLDER;
  8.     }
  9.     public static class InnerClass{
  10.         private static final Holder HOLDER = new Holder();
  11.     }
  12. }
复制代码
进阶-->加锁
  1. package com.jan.single;
  2. //懒汉式单例
  3. public class LazyMan {
  4.     private LazyMan(){ ///构造器私有
  5.         System.out.println(Thread.currentThread().getName()+"ok");
  6.     }
  7.     //为了安全,避免 lazyMan 指令重排,必须加volatile
  8.     private volatile static  LazyMan lazyMan;//对象
  9.     public static LazyMan getInstance(){  //这代码安全吗?
  10.         //双重检测锁模式的 懒汉式单例 DCL模式
  11.         if (lazyMan==null){
  12.             synchronized (LazyMan.class){  //保证这个类 LazyMan.class 只有一个
  13.                 if(lazyMan==null){
  14.                     lazyMan = new LazyMan(); //不是原子性操作
  15.                     /*
  16.                     * 1. 分配内存空间
  17.                     * 2. 执行构造方法,初始化对象
  18.                     * 3. 把这个对象指向这个空间
  19.                     *
  20.                     * 期望执行 123
  21.                     *          132 A(先占内存空间)
  22.                     * 若此时有 B线程  基于A,系统会认为 lazyMan 不为Null   直接return
  23.                     *        //此时lazyMan没有完成构造
  24.                     * */
  25.                 }
  26.             }
  27.         }
  28.         return lazyMan;
  29.     }
  30.     //多线程并发
  31.     public static void main(String[] args){
  32.         for (int i = 0; i < 10; i++) {
  33.             new Thread(()->{
  34.                 LazyMan.getInstance();
  35.             }).start();
  36.         }
  37.     }
  38. }
复制代码
单例不安全,反射

枚举

炫技时刻 :反射破坏单例
  1. package com.jan.single;
  2. import java.lang.reflect.Constructor;
  3. //懒汉式单例
  4. public class LazyMan {
  5.     private LazyMan(){ ///构造器私有
  6.         System.out.println(Thread.currentThread().getName()+"ok");
  7.     }
  8.     //为了安全,避免 lazyMan 指令重排,必须加volatile
  9.     private volatile static  LazyMan lazyMan;//对象
  10.     public static LazyMan getInstance(){  //这代码安全吗?
  11.         //双重检测锁模式的 懒汉式单例 DCL模式
  12.         if (lazyMan==null){
  13.             synchronized (LazyMan.class){  //保证这个类 LazyMan.class 只有一个
  14.                 if(lazyMan==null){
  15.                     lazyMan = new LazyMan(); //不是原子性操作
  16.                  
  17.                 }
  18.             }
  19.         }
  20.         return lazyMan;
  21.     }
  22.     //反射
  23.     public static void main(String[] args) throws Exception {
  24.         LazyMan instance1 = LazyMan.getInstance();
  25.         Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器
  26.         declaredConstructor.setAccessible(true);//破坏私有权限,无视私有的构造器   通过反射来创建对象
  27.         LazyMan instance2= declaredConstructor.newInstance();
  28.         System.out.println(instance1);
  29.         System.out.println(instance2);
  30.     }
  31. }
复制代码
结果:

继续改进:不被反射破坏(三重检测)
  1. package com.jan.single;
  2. import java.lang.reflect.Constructor;
  3. //懒汉式单例
  4. public class LazyMan {
  5.     private LazyMan(){ ///构造器私有
  6.         synchronized (LazyMan.class){ //这里加一把锁,第二次用就会报错
  7.             if (lazyMan!=null){
  8.              throw new RuntimeException("不要试图使用反射破坏异常");
  9.             }
  10.         }
  11.     }
  12.     //为了安全,避免 lazyMan 指令重排,必须加volatile
  13.     private volatile static  LazyMan lazyMan;//对象
  14.     public static LazyMan getInstance(){  //这代码安全吗?
  15.         //双重检测锁模式的 懒汉式单例 DCL模式
  16.         if (lazyMan==null){
  17.             synchronized (LazyMan.class){  //保证这个类 LazyMan.class 只有一个
  18.                 if(lazyMan==null){
  19.                     lazyMan = new LazyMan(); //不是原子性操作
  20.                     /*
  21.                     * 1. 分配内存空间
  22.                     * 2. 执行构造方法,初始化对象
  23.                     * 3. 把这个对象指向这个空间
  24.                     *
  25.                     * 期望执行 123
  26.                     *          132 A(先占内存空间)
  27.                     * 若此时有 B线程  基于A,系统会认为 lazyMan 不为Null   直接return
  28.                     *        //此时lazyMan没有完成构造
  29.                     * */
  30.                 }
  31.             }
  32.         }
  33.         return lazyMan;
  34.     }
  35.    
  36.     //反射
  37.     public static void main(String[] args) throws Exception {
  38.         LazyMan instance1 = LazyMan.getInstance();
  39.         Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器
  40.         declaredConstructor.setAccessible(true);//破坏私有权限,无视私有的构造器   通过反射来创建对象
  41.         LazyMan instance2= declaredConstructor.newInstance();
  42.         System.out.println(instance1);
  43.         System.out.println(instance2);
  44.     }
  45. }
复制代码
结果:

继续破坏:

如果两个对象都是LazyMan instance2= declaredConstructor.newInstance();这样获得,单例又被破坏
解决:加密(jan)
  1. package com.jan.single;
  2. import java.lang.reflect.Constructor;
  3. //懒汉式单例
  4. public class LazyMan {
  5.     private static boolean jan = false;//定义一个变量
  6.     private LazyMan(){ ///构造器私有
  7.         synchronized (LazyMan.class){ //这里加一把锁,第二次用就会报错
  8.             if (jan==false){
  9.                 jan = true; //这个肯定会变
  10.             }else {
  11.                 throw new RuntimeException("不要试图使用反射破坏异常");
  12.             }
  13.         }
  14.     }
  15.     //为了安全,避免 lazyMan 指令重排,必须加volatile
  16.     private volatile static  LazyMan lazyMan;//对象
  17.     public static LazyMan getInstance(){  //这代码安全吗?
  18.         //双重检测锁模式的 懒汉式单例 DCL模式
  19.         if (lazyMan==null){
  20.             synchronized (LazyMan.class){  //保证这个类 LazyMan.class 只有一个
  21.                 if(lazyMan==null){
  22.                     lazyMan = new LazyMan(); //不是原子性操作
  23.                 }
  24.             }
  25.         }
  26.         return lazyMan;
  27.     }
  28.    
  29.     //反射
  30.     public static void main(String[] args) throws Exception {
  31.         //LazyMan instance1 = LazyMan.getInstance();
  32.         Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器
  33.         declaredConstructor.setAccessible(true);//破坏私有权限,无视私有的构造器  通过反射来创建对象
  34.         LazyMan instance2= declaredConstructor.newInstance();
  35.         LazyMan instance1= declaredConstructor.newInstance();
  36.         System.out.println(instance1);
  37.         System.out.println(instance2);
  38.     }
  39. }
复制代码
再破坏:

通过获取加密字段,从而破坏私有权限,再设置加密字段.instance = false
  1. package com.jan.single;
  2. import java.lang.reflect.Constructor;
  3. import java.lang.reflect.Field;
  4. //懒汉式单例
  5. //道高一尺魔高一丈
  6. public class LazyMan {
  7.     private static boolean jan = false;//定义一个变量
  8.     private LazyMan(){ ///构造器私有
  9.         synchronized (LazyMan.class){ //这里加一把锁,第二次用就会报错
  10.             if (jan==false){
  11.                 jan = true; //这个肯定会变
  12.             }else {
  13.                 throw new RuntimeException("不要试图使用反射破坏异常");
  14.             }
  15.         }
  16.     }
  17.     //为了安全,避免 lazyMan 指令重排,必须加volatile
  18.     private volatile static  LazyMan lazyMan;//对象
  19.     public static LazyMan getInstance(){  //这代码安全吗?
  20.         //双重检测锁模式的 懒汉式单例 DCL模式
  21.         if (lazyMan==null){
  22.             synchronized (LazyMan.class){  //保证这个类 LazyMan.class 只有一个
  23.                 if(lazyMan==null){
  24.                     lazyMan = new LazyMan(); //不是原子性操作
  25.                 }
  26.             }
  27.         }
  28.         return lazyMan;
  29.     }
  30.    
  31.     //反射
  32.     public static void main(String[] args) throws Exception {
  33.         //LazyMan instance1 =
  34.         Field jan = LazyMan.class.getDeclaredField("jan");//获取字段
  35.         jan.setAccessible(true);//破坏私有权限
  36.         Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器
  37.         declaredConstructor.setAccessible(true);//破坏私有权限,无视私有的构造器   通过反射来创建对象
  38.         LazyMan instance2= declaredConstructor.newInstance();
  39.         jan.set(instance2,false);
  40.         LazyMan instance1= declaredConstructor.newInstance();
  41.         System.out.println(instance1);
  42.         System.out.println(instance2);
  43.     }
  44. }
复制代码
结果:

万恶的反射怎么解决:枚举

反编译jad:https://varaneckas.com/jad/
  1. package com.jan.single;
  2. //枚举 是一个什么? 本身也是class 类
  3. public enum EnumSingle {
  4.    
  5.     INSTANCE;//不可能被拿走
  6.    
  7.     public EnumSingle getInstance(){
  8.         return INSTANCE;
  9.     }
  10.    
  11. }
  12. class Test{  //先保证对象是唯一的,测试一下
  13.     public static void main(String[] args) {
  14.         EnumSingle instance1= EnumSingle.INSTANCE;
  15.         EnumSingle instance2= EnumSingle.INSTANCE;
  16.         System.out.println(instance1);
  17.         System.out.println(instance2);
  18.     }
  19. }
复制代码
结果:

看源码,有一个无参构造


在测试也使用无参构造
  1. package com.jan.single;
  2. import java.lang.reflect.Constructor;
  3. //枚举 是一个什么? 本身也是class 类
  4. public enum EnumSingle {
  5.     INSTANCE;//不可能被拿走
  6.     public EnumSingle getInstance(){
  7.         return INSTANCE;
  8.     }
  9. }
  10. class Test{  //试一试能不能破坏,看源代码  有一个无参构造
  11.     public static void main(String[] args) throws Exception {
  12.         EnumSingle instance1= EnumSingle.INSTANCE; //有一个无参构造,试一试,看看是不是真的
  13.         Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
  14.         declaredConstructor.setAccessible(true);
  15.         EnumSingle instance2 = declaredConstructor.newInstance();
  16.         //com.jan.single.EnumSingle.<init>()
  17.         //(newInstance 源码)正常报错是:throw new IllegalArgumentException("Cannot reflectively create enum objects");
  18.       
  19.         System.out.println(instance1);
  20.         System.out.println(instance2);
  21.     }
  22. }
复制代码
结果:被欺骗,这源码不是真的

因为newinstance的源码:

两个输出的结果不同!!!
反编译

在IDEA的targer中找到 EnumSingle.class,打开文件的文件的位置,再从文件的卫视输入 cmd
也有空参,也是骗我们的

更专业的反编译

jad

这里是有参构造

修改代码,这里只展示不同的
  1. Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
复制代码
结果:这个反射的确不能破坏枚举的单例

枚举最终反编译源码
  1. // Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
  2. // Jad home page: http://www.kpdus.com/jad.html
  3. // Decompiler options: packimports(3)
  4. // Source File Name:   EnumSingle.java
  5. package com.jan.single;
  6. public final class EnumSingle extends Enum
  7. {
  8.     public static EnumSingle[] values()
  9.     {
  10.         return (EnumSingle[])$VALUES.clone();
  11.     }
  12.     public static EnumSingle valueOf(String name)
  13.     {
  14.         return (EnumSingle)Enum.valueOf(com/jan/single/EnumSingle, name);
  15.     }
  16.     private EnumSingle(String s, int i)
  17.     {
  18.         super(s, i);
  19.     }
  20.     public EnumSingle getInstance()
  21.     {
  22.         return INSTANCE;
  23.     }
  24.     public static final EnumSingle INSTANCE;
  25.     private static final EnumSingle $VALUES[];
  26.     static
  27.     {
  28.         INSTANCE = new EnumSingle("INSTANCE", 0);
  29.         $VALUES = (new EnumSingle[] {
  30.             INSTANCE
  31.         });
  32.     }
  33. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

九天猎人

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表