版权声明:本文为博主原创文章,转载请注明出处: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) //在运行时通过反射去修改类的成员变量的访问限制

林皓伟

发表回复

您的电子邮箱地址不会被公开。