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中只能有一个实例。
什么是单例–>单例中有七种写法–>七种写法对比–>反射机制可以破解单例–>最靠谱单例 枚举–>枚举底层是如何实现–>为什么反射不能破解枚举