欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-06-15 11:33:24
...

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());

    }
}

互联网架构-精讲设计模式-011:利用反编译技术深入理解枚举单例底层实现原理
如果注释掉实体类UserEntity中的无参构造函数再执行:
互联网架构-精讲设计模式-011:利用反编译技术深入理解枚举单例底层实现原理
报错内容和枚举类反射报错一致,可大概推测枚举单例就是因为没有无参构造函数才不能被反射**。

使用反射初始化有参对象

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());
    }
}

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

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
互联网架构-精讲设计模式-011:利用反编译技术深入理解枚举单例底层实现原理
2.枚举中定义的对象都是在静态代码块中初始化的
互联网架构-精讲设计模式-011:利用反编译技术深入理解枚举单例底层实现原理
3.枚举中转换的类,是没有无参构造函数的,默认的是一个有参数的构造函数,调用父类构造函数
互联网架构-精讲设计模式-011:利用反编译技术深入理解枚举单例底层实现原理
互联网架构-精讲设计模式-011:利用反编译技术深入理解枚举单例底层实现原理
s表示为对象的名称、i表示对象的序号,数组$VALUES存放当前枚举类的所有对象。
4.valueOf方法,通过名称获取枚举类对象
互联网架构-精讲设计模式-011:利用反编译技术深入理解枚举单例底层实现原理

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();

    }
}

互联网架构-精讲设计模式-011:利用反编译技术深入理解枚举单例底层实现原理
使用java的反射技术执行枚举类的有参数构造函数,反射的newInstance()方法强制限制如果是枚举类型,会抛出该异常。
互联网架构-精讲设计模式-011:利用反编译技术深入理解枚举单例底层实现原理

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

设计模式总结

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

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

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

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

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

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

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

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

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

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

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