反射破坏单例模式及解决深入

涉及反射与枚举,先跳转:
反射与枚举
首先写一个饿汉式的单例,利用反射创建对象,得到哈希code不一样的两个对象,说明,反射破坏了单例,步骤看注释:

import java.lang.reflect.Constructor;

//反射,
public class Reflex {
    private Reflex(){
        }

        System.out.println(Thread.currentThread().getName()+"ok");
    }
    private volatile static Reflex Reflex;

    public static Reflex getInstance(){
        if (Reflex==null) {
            synchronized (Reflex.class){
                if (Reflex==null) {
                    Reflex=new Reflex();//不是一个原子性操作
                }
            }
        }
        return Reflex;
    }

    public static void main(String[] args) throws Exception {//异常抛到最大
        Reflex instance=Reflex.getInstance();//正常创建
        Constructor<Reflex> declaredConstructor = Reflex.class.getDeclaredConstructor(null);//反射破坏单例,获取反射对象,拿到构造器,空参所以null
        declaredConstructor.setAccessible(true);//无视了私有的构造器
        Reflex instance2=declaredConstructor.newInstance();//通过反射来new一个对象

        //如果被破坏,就是不一样的hashcode
        System.out.println(instance);
        System.out.println(instance2);

    }

}

解决方法,在构造器中加入锁:

import java.lang.reflect.Constructor;

//反射,
public class Reflex {
    private Reflex(){

        synchronized (Reflex.class){
            if (Reflex!=null) {
                throw new RuntimeException("不要试图用反射破坏异常");
            }
        }

        System.out.println(Thread.currentThread().getName()+"ok");
    }
    private volatile static Reflex Reflex;

    public static Reflex getInstance(){
        if (Reflex==null) {
            synchronized (Reflex.class){
                if (Reflex==null) {
                    Reflex=new Reflex();//不是一个原子性操作
                }
            }
        }
        return Reflex;
    }

    public static void main(String[] args) throws Exception {//异常抛到最大
        Reflex instance=Reflex.getInstance();//正常创建
        Constructor<Reflex> declaredConstructor = Reflex.class.getDeclaredConstructor(null);//反射破坏单例,获取反射对象,拿到构造器,空参所以null
        declaredConstructor.setAccessible(true);//无视了私有的构造器
        Reflex instance2=declaredConstructor.newInstance();//通过反射来new一个对象

        //如果被破坏,就是不一样的hashcode
        System.out.println(instance);
        System.out.println(instance2);

    }

}

可是如果在创建对象中,两个都是用反射来创建对象,那么得到的还是不一样的hashcode,所以:

//反射,
public class Reflex {

    private static boolean bianliang=false;//红绿灯法,标识位,见多线程详解

    private Reflex() {

        synchronized (Reflex.class) {
            if (bianliang == false) {//不通过反编译,是找不到这个关键字的
                bianliang = true;
            } else {
                throw new RuntimeException("不要试图用反射破坏异常");
            }
        }
        System.out.println(Thread.currentThread().getName()+"ok");
    }
    private volatile static Reflex Reflex;

    public static Reflex getInstance(){
        if (Reflex==null) {
            synchronized (Reflex.class){
                if (Reflex==null) {
                    Reflex=new Reflex();//不是一个原子性操作
                }
            }
        }
        return Reflex;
    }

    public static void main(String[] args) throws Exception {//异常抛到最大

        Constructor<Reflex> declaredConstructor = Reflex.class.getDeclaredConstructor(null);//反射破坏单例,获取反射对象,拿到构造器,空参所以null
        declaredConstructor.setAccessible(true);//无视了私有的构造器
        Reflex instance2=declaredConstructor.newInstance();//通过反射来new一个对象
        Reflex instance=declaredConstructor.newInstance();//通过反射来new一个对象

        //如果被破坏,就是不一样的hashcode
        System.out.println(instance);
        System.out.println(instance2);

    }

}

在构造器加入一个标识位,通过非当前的对象,谁都不知道,但假设找到了这个关键字但还是可以破坏的:

//反射,
public class Reflex {
    private static boolean bianliang=false;//红绿灯法,标识位,见多线程详解
    private Reflex() {
        synchronized (Reflex.class) {
            if (bianliang == false) {//不通过反编译,是找不到这个关键字的
                bianliang = true;
            } else {
                throw new RuntimeException("不要试图用反射破坏异常");
            }
        }
        System.out.println(Thread.currentThread().getName()+"ok");
    }
    private volatile static Reflex Reflex;

    public static Reflex getInstance(){
        if (Reflex==null) {
            synchronized (Reflex.class){
                if (Reflex==null) {
                    Reflex=new Reflex();//不是一个原子性操作
                }
            }
        }
        return Reflex;
    }

    public static void main(String[] args) throws Exception {//异常抛到最大
        Field bianliang = Reflex.class.getDeclaredField("bianliang");//获取字段
        bianliang.setAccessible(true);//破坏
        

        Constructor<Reflex> declaredConstructor = Reflex.class.getDeclaredConstructor(null);//反射破坏单例,获取反射对象,拿到构造器,空参所以null
        declaredConstructor.setAccessible(true);//无视了私有的构造器
        Reflex instance2=declaredConstructor.newInstance();//通过反射来new一个对象
        bianliang.set(instance2,false);//值改回来

        Reflex instance=declaredConstructor.newInstance();//通过反射来new一个对象

        //如果被破坏,就是不一样的hashcode
        System.out.println(instance);
        System.out.println(instance2);

    }
}

在这里插入图片描述
单例又被破坏了,所以魔高一尺道高一丈,分析源码
在这里插入图片描述
这里告诉我们如果这个类型是一个枚举类型,那么告诉我们不能使用反射破坏枚举,枚举是jdk1.5出来的,自带单例模式
写个枚举:
测试,对象是否唯一

//枚举本身也是是一个class类
public enum EnumSingle {
    INSTANCE;
    public EnumSingle getInstance(){
        return INSTANCE;//测试,对象是否唯一
    }
}

class Test{
    public static void main(String[] args) {
        EnumSingle instance = EnumSingle.INSTANCE;
        EnumSingle instance2 = EnumSingle.INSTANCE;

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

在这里插入图片描述
target文件说,是个无参构造函数,我们测试一下是不是假的,尝试破坏枚举的单例:

import java.lang.reflect.Constructor;

//枚举本身也是是一个class类
public enum EnumSingle {
    INSTANCE;
    public EnumSingle getInstance(){
        return INSTANCE;//测试,对象是否唯一
    }
}

class Test{
    public static void main(String[] args) throws Exception {
        EnumSingle instance = EnumSingle.INSTANCE;
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        EnumSingle enumSingle = declaredConstructor.newInstance();
        System.out.println(instance);
        System.out.println(enumSingle);


    }
}

在这里插入图片描述
注意报错,报的是这个枚举类里,没有一个空参的构造方法
正常的应该是告诉我们:
throw new IllegalArgumentException(“Cannot reflectively create enum objects”);
通过反编译:反编译calss文件,:javap -p EnumSingle .class(cmd)中,查看结果,没用,使用jad反编译
(使用jad -s java EnumSingle .class)
在这里插入图片描述
用了一个有参构造器,所以我们在代码中改成有参的参数


import java.lang.reflect.Constructor;

//枚举本身也是是一个class类
public enum EnumSingle {
    INSTANCE;
    public EnumSingle getInstance(){
        return INSTANCE;//测试,对象是否唯一
    }
}

class Test{
    //Exception in thread "main" java.lang.NoSuchMethodException:
    public static void main(String[] args) throws Exception {
        EnumSingle instance = EnumSingle.INSTANCE;
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
        declaredConstructor.setAccessible(true);
        EnumSingle enumSingle = declaredConstructor.newInstance();
        System.out.println(instance);
        System.out.println(enumSingle);
    }
}

得到结果(正确结果):
在这里插入图片描述
现在我们才知道,反射确实不能破坏枚举的单例

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值