版权声明:本文为博主原创文章,转载请注明出处:https://twocups.cn/index.php/2020/01/21/15/
最近看了许多 Java 方面的书籍和网上的资料,对 Java 语言的反射机制很感兴趣。不过网上很多资料都不够全面,于是自己来总结一下,顺便复习一下 Java 语言的反射机制。
反射定义
JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 java 语言的反射机制。
反射应用场景
最基本的应用场景:程序运行时,可以动态地加载一些之前用不到所以不加载的类。这种运行时需要才加载的机制也体现了 Java 的多态性,使程序健壮地运行。
其他应用场景:
- 逆向代码,例如反编译;
- 与注解相结合的框架,例如 Retrofit;
- 单纯的反射机制应用框架,例如 EventBus 2.x;
- 动态生成类框架,例如 Gson。
获取 Class 对象方法
1. 通过 Object 类中的 getClass() 方法
2. 通过对象的静态属性 .class
3. 通过静态方法 Class.forName()
4. 通过 ClassLoader 对象的 loadClass() 方法
其中,Class.forName() 方法与 ClassLoader.loadClass() 方法的区别如下:
在 Java 中,类装载器把一个类装入 Java 虚拟机中,要经过三个步骤来完成:装载、链接和初始化,其中链接又可以分成校验、准备和解析三步。
除了解析外,其它步骤是严格按照顺序完成的,各个步骤的主要工作如下:
1. 装载:查找和导入类或接口的二进制数据;
2. 链接:执行下面的校验、准备和解析步骤,其中解析步骤是可以选择的;
3. 校验:检查导入类或接口的二进制数据的正确性;
4. 准备:给类的静态变量分配并初始化存储空间;
5. 解析:将符号引用转成直接引用;
6. 初始化:激活类的静态变量的初始化 Java 代码和静态 Java 代码块。
Class.forName(className) 方法,其实调用的方法是 Class.forName(className,true,classloader)。其中 true 参数表示在 loadClass 后必须初始化。比较下我们前面准备 jvm 加载类的知识,我们可以清晰的看到在执行过此方法后,目标对象的 static 块代码已经被执行,static 参数也已经被初始化。
再看 ClassLoader.loadClass(className) 方法,其实他调用的方法是 ClassLoader.loadClass(className,false)。其中 false 参数表示目标对象被装载后不进行链接,这就意味这不会去执行该类静态块中间的内容。
反射包 java.lang.reflect 的常用类
1. Field —— 类的域/属性
2. Method —— 类的方法
3. Constructor —— 类的构造器
4. AccessibleObject —— 类的访问限制
5. Modifier —— 类的修饰符
反射常用方法
Class类
//获得类相关的方法 forName(String className) //根据类名返回类的对象 getClassLoader() //获得类的加载器 getClasses() //返回一个数组,数组中包含该类及其父类中所有公共类和接口类的对象 getDeclaredClasses() //返回一个数组,数组中包含该类中所有类和接口类的对象 newInstance() //创建类的实例 getName() //获得类的完整路径名字 getSimpleName() //获得类的名字 getPackage() //获得类的包 getSuperclass() //获得当前类继承的父类的名字 getInterfaces() //获得当前类实现的类或是接口 //获得类中属性相关的方法 getField(String name) //获得该类及其父类指定公有的属性对象 getFields() //获得该类所有公有的属性对象 getDeclaredField(String name) //获得该类及其父类指定属性对象 getDeclaredFields() //获得该类所有属性对象 //获得类中方法相关的方法 getMethod(String name, Class...<?> parameterTypes) //获得该类及其父类指定公有的方法 getMethods() //获得该类所有公有的方法 getDeclaredMethod(String name, Class...<?> parameterTypes) //获得该类及其父类指定方法 getDeclaredMethods() //获得该类所有方法 //获得类中构造器相关的方法 getConstructor(Class...<?> parameterTypes) //获得该类及其父类与参数类型匹配的公有构造方法 getConstructors() //获得该类所有公有构造方法 getDeclaredConstructor(Class...<?> parameterTypes) //获得该类及其父类与参数类型匹配的构造方法 getDeclaredConstructors() //获得该类所有构造方法 //获得类中注解相关的方法 getAnnotation(Class<A> annotationClass) //返回该类及其父类与参数类型匹配的公有注解对象 getAnnotations() //返回该类所有公有注解对象 getDeclaredAnnotation(Class<A> annotationClass) //返回该类及其父类与参数类型匹配的所有注解对象 getDeclaredAnnotations() //返回该类所有的注解对象 //其他 isAnnotation() //如果是注解类型则返回true isAnnotationPresent(Class<? extends Annotation> annotationClass) //如果是指定类型注解类型则返回true isAnonymousClass() //如果是匿名类则返回true isArray() //如果是一个数组类则返回true isEnum() //如果是枚举类则返回true isInstance(Object obj) //如果obj是该类的实例则返回true isInterface() //如果是接口类则返回true isLocalClass() //如果是局部类则返回true isMemberClass() //如果是内部类则返回true
虽然看起来很麻烦,但是其实非常好记,抓住几个关键字就行了。
1. get+单数(例如 getField):获得该类及其父类指定公有的某样信息
2. get+复数(例如 getFields):获得该类及其父类所有公有的某样信息
3. getDeclared+单数(例如 getDeclaredField):获得该类指定的某样信息(不仅是公有信息)
4. getDeclared+复数(例如 getDeclaredFields):获得该类所有的某样信息(不仅是公有信息)
5. get+信息名称(例如 getName):从类相关的信息
6. is+信息名称(例如 isAnonymousClass):对类进行判断
Field类
equals(Object obj) //属性与obj相等则返回true get(Object obj) //获得obj中对应的属性值 set(Object obj, Object value) //设置obj中对应属性值
Method类
invoke(Object obj, Object... args) //传递object对象及参数调用该对象对应的方法
Constructor类
newInstance(Object... initargs) //根据传递的参数创建类的对象
AccessibleObject类
setAccessible(AccessibleObject[] array, boolean flag) //在运行时通过反射去修改类的成员变量的访问限制