反射基础
反射机制指的是程序在运行时能够获取自身的信息。在java中,只要给定类的名字,那么就可以通过反射机制来获得类的所有属性和方法。
在运行时判断任意一个对象所属的类。
在运行时判断任意一个类所具有的成员变量和方法。
在运行时任意调用一个对象的方法。
在运行时构造任意一个类的对象。
反射就是把java类中的各种成分映射成一个个的Java对象
例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。
这里我们首先需要理解 Class类,以及类的加载机制; 然后基于此我们如何通过反射获取Class类以及类中的成员变量、方法、构造方法等。
反射的主要用途
- 框架开发:如Spring、Hibernate等Java框架通过反射实现依赖注入、动态代理等功能。
- 调试和测试:利用反射可以在运行时检查类的状态,方便进行调试和测试。
- 动态代理:通过反射实现动态代理,用于实现AOP(面向切面编程)等功能。
- 配置文件处理:读取配置文件中的类名和方法名,动态加载和执行。
反射的主要类和方法
Java反射机制主要通过以下几个核心类实现:
- Class:表示类和接口,提供了获取类的元数据的方法。
- Constructor:表示类的构造方法,允许通过反射创建类的实例。
- Field:表示类的属性,提供了访问和修改字段值的方法。
- Method:表示类的方法,允许通过反射调用方法。
Java反射的使用方法
Java反射的使用主要涉及以下几个步骤:
-
获取Class对象:
- 使用
类名.class
获取。 - 使用对象的
getClass()
方法获取。 - 使用
Class.forName("类的全限定名")
方法获取。
- 使用
-
获取类的信息:
- 通过Class对象,可以获取类的名称、包名、父类、实现的接口、构造器、字段和方法等信息。
-
调用类的方法:
- 使用
getMethod()
或getDeclaredMethod()
获取Method对象。 - 使用Method对象的
invoke()
方法调用方法。
- 使用
-
创建类的实例:
- 使用Class对象的
newInstance()
方法(已过时,建议使用Constructor的newInstance())或Constructor对象的newInstance()方法创建实例。
- 使用Class对象的
-
访问和修改字段:
- 使用
getField()
或getDeclaredField()
获取Field对象。 - 访问私有字段时,需要先调用
setAccessible(true)
方法。 - 使用Field对象的
get()
和set()
方法访问和修改字段值。
- 使用
反射机制执行的流程
反射的性能影响
反射机制在运行时进行类的检查和操作,会带来一定的性能开销。反射调用通常比直接调用要慢很多,因为它跳过了许多编译期的优化。此外,频繁使用反射可能会增加代码的复杂度,影响可读性和可维护性。因此,在性能要求较高的场景下,应尽量避免过多地使用反射,并通过缓存反射操作的结果来优化性能。
反射调用流程小结
用几句话总结反射的实现原理:
-
反射类及反射方法的获取,都是通过从列表中搜寻查找匹配的方法,所以查找性能会随类的大小方法多少而变化;
-
每个类都会有一个与之对应的Class实例,从而每个类都可以获取method反射方法,并作用到其他实例身上;
-
反射也是考虑了线程安全的,放心使用;
-
反射使用软引用relectionData缓存class信息,避免每次重新从jvm获取带来的开销;
-
反射调用多次生成新代理Accessor, 而通过字节码生存的则考虑了卸载功能,所以会使用独立的类加载器;
-
当找到需要的方法,都会copy一份出来,而不是使用原来的实例,从而保证数据隔离;
-
调度反射方法,最终是由jvm执行invoke0()执行;