反射机制概述
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类中的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
Java 反射机制的功能
-
在运行时判断任意一个对象所属的类。
-
在运行时构造任意一个类的对象。
-
在运行时判断任意一个类所具有的成员变量和方法。
-
在运行时调用任意一个对象的方法。
- 生成动态代理。
-
Java 反射机制的应用场景
-
逆向代码 ,例如反编译
-
与注解相结合的框架 例如Retrofit
-
单纯的反射机制应用框架 例如EventBus
- 动态生成类框架 例如Gson
-
获取Class对象
在Java中,java类被JVM虚拟机加载后,系统就会该类生成一个对应的Class对象,通过该Class对象就可以访问到JVM中的这个类。
首先先创建一个Human类,在java文件或者kt文件中创建class类都是一样的:Human类
public class Human { private String name="Default"; private int age=18; public String code="8888"; public Human(){ } public Human(String name){ this.name=name; } public Human(String name,int age){ this.name=name; this.age=age; } public Human(String name,int age,String code){ this.name=name; this.age=age; this.code=code; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void sayHello(){ System.out.println("Hello"); } private void say(String str){ System.out.println("The Human "+name+" is saying \""+str+"\""); } public String talking(){ return "Goodbye"; } private void doNothing(String noThing){ } }
- 使用Class类的forName(String clazzName)静态方法。该方法需要传入字符串参数,该字符串参数的值是某个类的全限定名(必须添加完整包名):
-
val hClass=Class.forName("Human")//获取Human.class
- 调用某个类的Class属性或KClass属性来获取该类对应的Class对象:
-
val hClass:Class<Human>?=Human::class.java//获取Human类的class属性 val hClass: KClass<Human>?=Human::class//获取Human类的Kclass属性
注意:在Kotlin中::符号是引用符号,可以引用属性或类。Class和Kclass类型是不一样,里面的方式类同。
- 调用某个对象的javaClass属性:
-
val h=Human() val hClass:Class<Human>?=h.javaClass
获取Class对象的成员变量
在Kotlin中对应于Class有一种包装类型KClass,这两种Class类型的使用方式不一样:
- 获取Class对象的成员变量
-
val hClass_1=Human::class.java//获取Human类的class属性 val allFields_1:Array<Field>?=hClass_1.declaredFields//获取class对象的所有属性 val publicFields_1:Array<Field>?=hClass_1.fields//获取class对象的public属性 val nameField_1:Field=hClass_1.getDeclaredField("age")//获取class指定属性 val code_1:Field=hClass_1.getField("code")//获取class指定的public属性
- 获取KClass对象的成员变量
-
val hClass_2=Human::class val allFields_2=hClass_2.declaredMemberProperties//获取KClass对象的所有属性 val publicFields_2=hClass_2.memberProperties//获取KClass对象的public属性 val extensionFields=hClass_2.declaredMemberExtensionProperties//获取KClass对象的扩展成员变量 val nameField_2=publicFields_2.stream().filter { it.name=="name" }.findAny().get().javaField//获取KClass对象的指定属性,将KProperty属性类型转换后的类型是java中的Field类型
获取Class对象的方法
- 获取Class对象的方法
-
val methods_1 = hClass_1.declaredMethods//获取class对象的所有声明方法 val allMethods_1 = hClass_1.methods//获取class对象的所有public方法 包括父类的方法 val method_1 = hClass_1.getMethod("sayHello")//返回class对象中与指定方法名,带指定参数列表的public方法 val declareMethod_1 = hClass_1.getDeclaredMethod("say", String::class.java)//返回class对象中与指定方法名,带指定参数列表的方法
- 获取KClass对象的方法
-
val methods_2 = hClass_2.memberFunctions//获取KClass对象的所有声明方法,包括父类的方法 val allMethods_2 = hClass_2.declaredMemberFunctions//获取KClass对象的所有声明方法 val method_2 = methods_2.stream().filter { it.name == "say" }.findAny().get().javaMethod//返回KClass对象中指定方法名的方法,将KFunction方法类型转换后的类型是java中的Method类型
获取Class对象的构造函数
- 获取Class对象的构造函数
-
val allConstructors_1 = hClass_1.declaredConstructors//获取class对象的所有声明构造函数 val publicConstructors_1 = hClass_1.constructors//获取class对象的所有public构造函数 val constructor_1 = hClass_1.getDeclaredConstructor(String::class.java)//获取与指定声明参数列表类型匹配的构造函数 val publicConstructor_1 = hClass_1.getConstructor(String::class.java)//获取与指定声明参数列表类型匹配的public构造函数
- 获取KClass对象的构造函数
-
val allConstructors_2 = hClass_2.constructors//获取KClass对象的所有声明构造函数 val privateConstructors_2 = hClass_2.primaryConstructor//获取KClass对象的private私有构造函数 val constructor = allConstructors_2.stream().filter { it.parameters.size == 1 }.findAny().get().javaConstructor//获取与指定声明参数列表类型匹配的构造函数
生成类的实例对象
通过class对象和kclass对象来生成类的实例对象有两种不同的方法:
- 使用Class对象的newInstance()方法或者Kclass对象的createInstance()方法来创建对应类的实例。这种方式要求class对象的对应类中有无参默认构造函数,而调用对应的newInstance和createInstance方法创建实例的过程实际上是利用默认构造函数来创建该类的实例:
-
val hClass_1 = Human::class.java//获取Human类的class属性 val human_1=hClass_1.newInstance() as Human//强制转换为Human类 val hClass_2 = Human::class//获取Human类的Kclass属性 val human_2=hClass_2.createInstance()
- 使用class对象或者kclass对象获取指定的constructor构造函数对象,再调用该constructor构造函数对象的newInstance()方法来创建该class对象对应类的实例。通过这种方法可以选择使用指定的构造函数来创建实例:
-
val hClass_1 = Human::class.java//获取Human类的class属性 val constructor_1 = hClass_1.getDeclaredConstructor(String::class.java) val human_1=constructor_1.newInstance() val hClass_2 = Human::class//获取Human类的Kclass属性 val allConstructors_2 = hClass_2.constructors val constructor = allConstructors_2.stream().filter { it.parameters.size == 1 }.findAny().get().javaConstructor val human_2=constructor.newInstance()
调用类的方法
通过class对象或者kclass对象获取指定的method方法,返回method类型的对象,调用该method对象中的invoke(Object obj,Object…args)方法来调用函数方法,第一个参数对应调用该方法的实例对象,第二个参数对应该方法的参数,可以传递多个参数。
val hClass_1 = Human::class.java//获取Human类的class属性 val human_1=hClass_1.newInstance() as Human val declareMethod_1 = hClass_1.getDeclaredMethod("say", String::class.java) declareMethod_1.isAccessible = true//设置private私有方法可以访问 declareMethod_1.invoke(human_1, "Hello~!") val hClass_2 = Human::class//获取Human类的Kclass属性 val human_2=hClass_2.createInstance() val methods_2 = hClass_2.memberFunctions//获取class对象的所有的方法,包括父类的方法 val method_2 = methods_2.stream().filter { it.name == "say" }.findAny().get().javaMethod method_2!!.isAccessible = true//设置private私有方法可以访问 method_2.invoke(human_2, "Bingo~")
访问成员变量值
通过class对象或者kclass获取指定的Field成员变量,返回Field类型的成员变量对象。Field提供了两组方法来读取或设置成员变量的值,通过get(Object obj)方法来获取指定obj对象实例的该成员变量的值,通过set(Object obj,XXX val)方法来设置指定obj对象实例的该成员变量的值为val值。PS:对于8种原生的基本数据类型,有相对应的优化方法,如getInt,getDouble,setInt,setDouble
val hClass_1 = Human::class.java//获取Human类的class属性 val human_1=hClass_1.newInstance() as Human val code_1: Field = hClass_1.getField("code") println(code_1.get(human_1)) code_1.set(human_1,"0000") val hClass_2 = Human::class//获取Human类的Kclass属性 val human_2=hClass_2.createInstance() val publicFields_2 = hClass_2.memberProperties val nameField_2 = publicFields_2.stream().filter { it.name == "name" }.findAny().get().javaField nameField_2!!.isAccessible = true nameField_2.set(human_2, "JasonChen") println(nameField_2.get(human_2))