本文
首发于
🍀 永浩,
转载 请注明
来源。
4、【对线面试官】Java反射 & 动态代理.md#
今天要不来聊聊Java反射?你对Java反射了解多少?#
- 嗯,Java反射在JavaSE基础中还是很重要的。
- 简单来说,反射就是Java可以给我们在运行时获取类的信息
在初学的时候可能看不懂、又或是学不太会反射,因为初学的时候往往给的例子都是用反射创建对象,用反射去获取对象上的方法/属性什么的,感觉没多大用
- 但毕竟我已经不是以前的我了,跟以前的看法就不一样了。
- 理解反射重点就在于理解什么是「运行时」,为什么我们要在「运行时」获取类的信息
- 在当时学注解的时候,我们可以发现注解的生命周期有三个枚举值(当时我还告诉过面试官你呢~)
- 分别是SOURCE、CLASS和RUNTIME,其实一样的,RUNTIME就是对标着运行时
- 我们都知道:我们在编译器写的代码是j ava文件,经过javac编译会变成.class文件,class文件会被JVM装载运行(这里就是真正运行着我们所写的代码(虽然是被编译过的),也就所谓的运行时。
嗯,你说了那么多,就讲述了什么是运行时,还是快点进入重点吧#
- 在运行时获取类的信息,其实就是为了让我们所写的代码更具有「通用性」和「灵活性」
- 要理解反射,需要抛开我们日常写的业务代码。以更高的维度或者说是抽象的思维去看待我们所写的“工具”
- 所谓的“工具”:在单个系统使用叫做“Utils”、被多个系统使用打成jar包叫做“组件”、组件继续发展壮大就叫做“框架”
- 一个好用的“工具”是需要兼容各种情况的。
- 你肯定是不知道用该“工具“的用户传入的是什么对象,但你需要帮他们得到需要的结果。
- 例如SpringMVC你在方法上写上对象,传入的参数就会帮你封装到对象上
- Mybatis可以让我们只写接口,不写实现类,就可以执行SQL
- 你在类上加上@Component注解,Sprin g就帮你创建对象
- 这些统统都有反射的身影:约定大于配置,配置大于硬编码。
- 通过”约定“使用姿势,使用反射在运行时获取相应的信息(毕竟作为一个”工具“是真的不知道你是怎么用的),实现代码功能的「通用性」和「灵活性」
结合之前说的泛型,想问下:你应该知道泛型是会擦除的,那为什么反射能获取到泛型的信息呢?#
// 抽象类,定义泛型<T>
public abstract class BaseDao<T> {
public BaseDao(){
Class clazz = this.getClass();
ParameterizedType pt = (ParameterizedType) clazz.getGenericSuperclass();
clazz = (Class) pt.getActualTypeArguments()[0];
System.out.println(clazz);
}
}
// 实现类
public class UserDao extends BaseDao<User> {
public static void main(String[] args) {
BaseDao<User> userDao = new UserDao();
}
}
// 执行结果输出
class com.entity.User
- 嗯,这个问题我在学习的时候也想过
- 其实是这样的,可以理解为泛型擦除是有范围的,定义在类上的泛型信息是不会被擦除的。
- Java编译器仍在class文件以Signature 属性的方式保留了泛型信息
- Type作为顶级接口,Type下还有几种类型,比如TypeVariable、 ParameterizedT ype、 WildCardType、 GenericArrayType、以及Class。通过这些接口我们就可以在运行时获取泛型相关的信息。
你了解动态代理吗?#
- 嗯,了解的。动态代理其实就是代理模式的一种,代理模式是设计模式之一。
- 代理模型有静态代理和动态代理。静态代理需要自己写代理类,实现对应的接口,比较麻烦。
- 在Java中,动态代理常见的又有两种实现方式:JDK动态代理和CGLIB代理
- JDK动态代理其实就是运用了反射的机制,而CGLIB代理则用的是利用ASM框架,通过修改其字节码生成子类来处理。
- JDK动态代理会帮我们实现接口的方法,通过invokeHandler对所需要的方法进行增强。
- 动态代理这一技术在实际或者框架原理中是非常常见的
- 像上面所讲的Mybatis不用写实现类,只写接口就可以执行SQL,又或是SpringAOP等等好用的技术,实际上用的就是动态代理。