互联网架构-精讲设计模式-011:利用反编译技术深入理解枚举单例底层实现原理

1 枚举单例源码课程介绍

课题内容:
1、为什么枚举单例能够防止反射破解?
2、为什么枚举单例无法无参构造函数初始化?
3、利用反编译技术查看枚举底层实现原理
4、序列化技术是如何实现防止破解单例的
4、精讲设计模式课后内容总结

2 Java反射技术简单回顾

什么是反射技术
动态获取当前类的信息,比如属性、方法等。forName()

反射技术使用场景
1、jdbc加载驱动 2、SpringIOC容器 3、初始化对象 4.提供扩展功能

反射技术代码演示
使用反射初始化无参对象

@Data
public class UserEntity {

    private String userName;
    private Integer age;

    private UserEntity() {
        System.out.println("无参构造函数执行..");
    }

    public UserEntity(String userName, Integer age) {
        this.userName = userName;
        this.age = age;
    }
}
public class ReflexUtils {

    public static UserEntity reflexUser() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class<?> classInfo = Class.forName("com.mayikt.singletonPattern.enumsingleton.reflect.UserEntity");
        // 1.使用java反射技术初始化对象 默认执行无参构造函数
        Constructor<?> declaredConstructor = classInfo.getDeclaredConstructor();
        declaredConstructor.setAccessible(true); // 无参构造函数为私有,拿到构造器设置权限
        UserEntity userEntity = (UserEntity) declaredConstructor.newInstance();
        return userEntity;
    }

    public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException {
        UserEntity userEntity = ReflexUtils.reflexUser();
        userEntity.setAge(11);
        userEntity.setUserName("zhangsan");
        System.out.println(userEntity.toString());

    }
}

在这里插入图片描述
如果注释掉实体类UserEntity中的无参构造函数再执行:
在这里插入图片描述
报错内容和枚举类反射报错一致,可大概推测枚举单例就是因为没有无参构造函数才不能被反射破解。

使用反射初始化有参对象

public class ReflexUtils {

    public static UserEntity reflexUser(String userName, Integer age) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class<?> classInfo = Class.forName("com.mayikt.singletonPattern.enumsingleton.reflect.UserEntity");
        // 1.使用java反射技术初始化对象 默认执行无参构造函数
        Constructor<?> declaredConstructor = classInfo.getDeclaredConstructor(String.class, Integer.class);
        declaredConstructor.setAccessible(true); // 无参构造函数为私有,拿到构造器设置权限
        UserEntity userEntity = (UserEntity) declaredConstructor.newInstance(userName, age);
        return userEntity;
    }

    public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException {
        UserEntity userEntity = ReflexUtils.reflexUser("lisi",12);
        System.out.println(userEntity.toString());
    }
}

在这里插入图片描述

3 使用反射技术破解枚举单例

public enum EnumSingleton {
    INSTANCE, MAYIKT;

    // 枚举能够绝对有效的防止实例化多次,和防止反射和序列化破解
    public void add() {
        System.out.println("add方法...");
    }

    EnumSingleton() {

    }
}
public class EnumSingletonTest {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        EnumSingleton instance1 = EnumSingleton.INSTANCE;
        EnumSingleton instance2 = EnumSingleton.INSTANCE;
        System.out.println(instance1 == instance2);
        // 单例有7种,为什么枚举是最好的?防止java的反射和序列化破解
        Class<EnumSingleton> enumSingletonClass = EnumSingleton.class;
        // 枚举没有无参构造函数..就算在类体中手动添加也会报错
        Constructor<EnumSingleton> declaredConstructor = enumSingletonClass.getDeclaredConstructor();
        EnumSingleton enumSingleton = declaredConstructor.newInstance();
        enumSingleton.add();

    }
}

枚举单例使用无参构造函数是不能破解的,枚举底层里面是有有参构造函数,那么使用java的反射技术是否可以破解有参构造函数呢?

4 使用Java反编译技术分析枚举源码

由EnumSingleton.class反编译成的EnumSingleton.java文件

package com.mayikt.singletonPattern.enumsingleton.v7;

import java.io.PrintStream;

public final class EnumSingleton extends Enum
{

    public static EnumSingleton[] values()
    {
        return (EnumSingleton[])$VALUES.clone();
    }

    public static EnumSingleton valueOf(String name)
    {
        return (EnumSingleton)Enum.valueOf(com/mayikt/singletonPattern/enumsingleton/v7/EnumSingleton, name);
    }

    public void add()
    {
        System.out.println("add\u65B9\u6CD5...");
    }

    private EnumSingleton(String s, int i)
    {
        super(s, i);
    }

    public static final EnumSingleton INSTANCE;
    public static final EnumSingleton MAYIKT;
    private static final EnumSingleton $VALUES[];

    static
    {
        INSTANCE = new EnumSingleton("INSTANCE", 0);
        MAYIKT = new EnumSingleton("MAYIKT", 1);
        $VALUES = (new EnumSingleton[] {
            INSTANCE, MAYIKT
        });
    }
}

枚举底层实现源码分析
1.通过反编译后的代码可以得知,定义的枚举类底层转换成类继承Enum
在这里插入图片描述
2.枚举中定义的对象都是在静态代码块中初始化的
在这里插入图片描述
3.枚举中转换的类,是没有无参构造函数的,默认的是一个有参数的构造函数,调用父类构造函数
在这里插入图片描述
在这里插入图片描述
s表示为对象的名称、i表示对象的序号,数组$VALUES存放当前枚举类的所有对象。
4.valueOf方法,通过名称获取枚举类对象
在这里插入图片描述

5 分析枚举单例为什么不能反射

尝试用有参构造函数反射创建枚举对象

public class EnumSingletonTest02 {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        // 枚举底层是如何实现的?
        EnumSingleton.INSTANCE.add();

        /*
         * EnumSingleton.INSTANCE是一个对象  如何初始化呢?
         * 定义的枚举最终底层肯定是一个类 枚举在底层肯定转换成类 编译器实现优化,底层转换类
         */
        // 存放枚举中的所有对象
        EnumSingleton[] values = EnumSingleton.values();

        // 1.使用java的反射技术执行枚举的有参构造函数..
        Class<EnumSingleton> enumSingletonClass = EnumSingleton.class;
        // 2.查找当前类是否有该构造函数
        Constructor<EnumSingleton> declaredConstructor = enumSingletonClass.getDeclaredConstructor(String.class, Integer.class);
        declaredConstructor.setAccessible(true);
        // 3.调用反射方法初始化对象
        EnumSingleton enumSingleton = declaredConstructor.newInstance("zhangsan", 11);
        enumSingleton.add();

    }
}

在这里插入图片描述
使用java的反射技术执行枚举类的有参数构造函数,反射的newInstance()方法强制限制如果是枚举类型,会抛出该异常。
在这里插入图片描述

6 每特教育精讲设计模式总结

设计模式总结

策略模式:
官方描述(定义一系列算法,把他们封装起来,并且使它们可以相互替换)
白话文描述:有共同的抽象行为,具体不同的行为称为不同的策略,最终可以使用Context上下文获取对应策略。
应用场景:解决多重if判断问题、聚合支付平台、第三方联合登陆、调用多个不同短信接口等。

责任链模式:
官方描述:(将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。)
白话文描述:每一个业务模块之间相互依赖有关联,每个关联模块称作为handler(处理器),使用上一个handler引用到下一个hanlder实现一个链表。
应用场景: 权限控制、网关权限控制、审批、风控系统等。

模版方法:
官方描述:定义一个算法结构,而将一些步骤延迟到子类实现。
白话文描述: 提前定义好整体的骨架,共同抽象的行为定义在父类中,不同的行为让子类实现。(重复代码抽取到父类里面,不同业务的具体实现交给子类进行处理)
应用场景:支付异步回调重构、Servlet实现

装饰模式:
官方描述:动态的给对象添加新的功能。
白话文描述: 在不改变原有对象的基础上附加功能,相比生成子类更灵活。
应用场景:IO流

代理模式:
官方描述:为其他对象提供一个代理以便控制这个对象的访问。
白话文描述: 通过代理控制对象的访问,调用对象中的方法,在方法调用之前或之后可以做其他操作
应用场景:AOP、事务、日志、权限控制

观察者模式:
官方描述: 对象间的一对多的依赖关系。
白话文描述:在对象之间定义一对多的依赖,当一个对象改变状态时,依赖它的对象收到通知并自动更新。 其实就是发布订阅模式,发布者发布消息,订阅者获取消息,订阅了就能收到消息,没订阅就收不到消息。
应用场景:发布订阅、事件通知、 Zookeeper、事件监听操作

门面(外观)模式:
官方描述: 对外提供一个统一的方法,来访问子系统中的一群接口。
白话文描述:把一些复杂的流程封装成一个接口供给外部用户更简单的使用。

状态模式:
官方描述: 允许一个对象在其对象内部状态改变时改变它的行为。
注意:状态模式与策略模式本质上没有很大区别,主要根据行为划分,如果有共同抽象行为使用策略模式,没有共同行为就使用状态模式。

适配器模式:
官方描述: 将一个类的方法接口转换成客户所希望的另一个接口。
应用场景:mybatis日志收集、提供接口转换。

单例模式
官方描述: 保证在一个jvm中只能有一个实例。

什么是单例–>单例中有七种写法–>七种写法对比–>反射机制可以破解单例–>最靠谱单例 枚举–>枚举底层是如何实现–>为什么反射不能破解枚举

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值