今天咱们来聊一聊Java中的反射;反射,即在程序运行时,可以动态的知道任意Class文件中包括私有的构造函数、变量、普通函数。
一、获取class
如果我们想通过反射创建某个类的实例,那么我们需要先获取它的class,然后再创建对象。先创建一个普通的javabean。
获取class的方式一共有4种:
1.通过java的class获取
val personClass = Person::class.java
2.通过javabean的全路径,注意上面javabean,我把package特意也贴上了
val personClass = Class.forName("com.example.reflextest.Person")
3.通过已创建的对象获取
val person = Person("lisi", 104) val personClass = person.javaClass
4.通过classloader
val classLoader = this::class.java.classLoader val personClass = classLoader.loadClass("com.example.reflextest.Person")
其中第1种和第2种方式是经常使用到的。然后class创建对象,这里分2种方法
二、通过反射创建对象
1.直接用class创建
val person = personClass.newInstance() as Person
使用这种方式需要注意1点的是,它是通过javabean中的无参构造去创建的,所以要保证javabean中有无参构造,否则会出问题。
2.通过class获取javabean的构造函数创建对象
我们可以通过class获取javabean中声明的所有构造函数:
val constructors = personClass.constructors
val constructors = personClass.declaredConstructors
这两种方式返回的都是构造函数的数组。可以看到constructors获取的是所有public的构造,而declaredConstructors获取的类中声明的所有构造。也可以单独获取某个构造函数,不过前提是我们要知道这个构造函数需要的参数,以2参的构造函数为例:
val constructor = personClass.getConstructor(String::class.java, Int::class.java) // constructor.isAccessible = true val person = constructor.newInstance("张三", 103) as Person Log.e("Main","person === $person")
这里有一行注释掉的代码,它的作用是在:如果调用的方法是private修饰的私有方法,则必须加上它,否则会提示我们不能调用。getConstructor的参数是调用它需要传入的参数类型。
打印结果:
person === Person(name=张三, age=103)
三、获取类中的方法并调用
获取方法:
val methods = personClass.methods val methods = personClass.declaredMethods
返回的都是Method数组,或获取某个具体方法
val method = personClass.getMethod("print") val method = personClass.getDeclaredMethod("print")
获取方法是通过方法名和参数来获取的,获取print方法并调用
val method = personClass.getDeclaredMethod("print") method.isAccessible = true method.invoke(person)
因为print方法是private修饰的所有要加上isAccessible = true,然后执行invoke调用函数的时候,需要将调用者传进去,说明是调用的这个对象的某某方法,就像平时调用某个对象的方法意思一样person.print()。但是,注意这里有个但是,如果调用的方法是静态的(被static修饰),则执行invoke不需要传入对象实例,可以传null。
四、获取变量并赋值
获取变量对应的方法是
val fields = personClass.fields
val fields = personClass.declaredFields
通过上边的方法获取这里显然,返回的都是变量数组Filed[],获取某个变量
val field = personClass.getField("gender")
传入变量的名称。
对field赋值
field.isAccessible = true field.set(person, 3)
field.isAccessible就不再重复解释了;set方法,其实和调用方法时是一个意思,第一个参数,调用的谁?后边的参数则是要赋的值。如果是静态变量,第一个参数,谁,可以为null。
另外提一下Modifier这个类,个人理解它就是用来判断修饰符的,有兄弟说了:“isAccessible可以对private使用,public的并不需要啊,我有洁癖我就想在private的时候再使用”。那就可以用Modifier来搞,比如:
val field = personClass.getField("gender") field.isAccessible = true field.set(person, 3)
javabean中的gender是private,我们可以用Modifier来判断gender字段的修饰符是否为某种修复时:
val field = personClass.getDeclaredField("gender") if (field.modifiers == Modifier.PRIVATE) { Log.e("Main","is private") field.isAccessible = true } field.set(person, 3)
怎么样?没毛病吧,其实Modifier中有很多修饰符的定义,我们都可以用这个类来做判断等等操作,比如判断private static 2个关键字修饰的,它内部大部分常量及方法,这里就不贴出来了,感兴趣的伙伴可以自己点进去看一下源码。
在很多优秀的开源库里都用到了反射、动态代理等技术,比如retiofit;后边再整理一些关于代理和hook的文章,敬请期待。