JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
然而在android中Google很多的类的某些方法不让第三方应用去调用,通过java反射机制能把这些隐藏方法获取出来并调用,三方应用上我们就很方便的去用这些方法。
我们以android中获取StorageManager类中的getVolumePaths()方法为例:
StorageManager sm = (StorageManager) getSystemService(STORAGE_SERVICE);
Class c=StorageManager.class;
Method method=c.getMethod("getVolumePaths",null);
String[] paths= (String[]) method.invoke(sm, null);
首先向大家说明一点,Class本身就是一个类,Class是该类的名称。看下面这个类的定义:
public class MyButton extends Button {…}
注意到上面的class的首字母是小写,它表示的是一种类类型,但是我们的Class是一个类,相当于上面定义的MyButton类。所以,千万不要把这里的Class做为一个类类型来理解。
Class类是整个Java反射机制的源头,获取Class对象的方法有三种:
在平时的使用,要注意对这几种方法的灵活运用,尤其是对Class.forName()方法的使用。因为在很多开发中,会直接通过类的名称取得Class类的对象。
例子中使用的是 . Class:Class c=StorageManager.class;
Class类提供了四个public方法,用于获取某个类的构造方法。
1.Constructor getConstructor(Class[] params) 根据构造函数的参2.数,返回一个具体的具有public属性的构造函数
2.Constructor getConstructors() 返回所有具有public属性的构造函数数组
3.Constructor getDeclaredConstructor(Class[] params) 根据构造函数的参数,返回一个具体的构造函数(不分public和非public属性)
4.Constructor getDeclaredConstructors() 返回该类中所有的构造函数数组(不分public和非public属性)
例如: 获取指定类的公有构造方法
Constructor[] theConstructors = c.getDeclaredConstructors();
与获取构造方法的方式相同,存在四种获取成员方法的方式。
1.Method getMethod(String name, Class[] params) 根据方法名和参数,返回一个具体的具有public属性的方法
2.Method[] getMethods() 返回所有具有public属性的方法数组
3.Method getDeclaredMethod(String name, Class[] params) 根据方法名和参数,返回一个具体的方法(不分public和非public属性)
4.Method[] getDeclaredMethods() 返回该类中的所有的方法数组(不分public和非public属性)
注意:方法getDeclaredMethods()只能获取到由当前类定义的所有方法,不能获取从父类继承的方法;方法getMethods() 不仅能获取到当前类定义的public方法,也能得到从父类继承和已经实现接口的public方法。
存在四种获取成员属性的方法:
1.Field getField(String name) 根据变量名,返回一个具体的具有public属性的成员变量
2.Field[] getFields() 返回具有public属性的成员变量的数组
3.Field getDeclaredField(String name) 根据变量名,返回一个成员变量(不分public和非public属性)
4.Field[] getDelcaredField() 返回所有成员变量组成的数组(不分public和非public属性)
Method类中包含着类的成员方法的信息。在Method类中有一个public成员函数:Object invoke(Object receiver, Object… args),参数receiver指明了调用对象,参数args指明了该方法所需要接收的参数。由于我们是在运行时动态的调用类的方法,无法提前知道该类的参数类型和返回值类型,所以传入的参数的类型是Object,返回的类型也是Object。(因为Object类是所有其他类的父类)
如果某一个方法是Java类的静态方法,那么Object receiver参数可以传入null,因为静态方法从不属于对象。
Method中的receiver代表要调用方法所在类的实例,要调用一个类的方法,首先需要一个该类的实例(当然,如果该类是static,就不需要实例了,至于原因,你懂得!)。
在得到一个类的Class对象之后,我们可以利用类Constructor去实例化该对象。Constructor支持泛型,也就是它本身应该是Constructor。这个类有一个public成员函数:T newInstance(Object… args),其中args为对应的参数,我们通过Constructor的这个方法来创建类的对象实例。
在代码LoadMethod.java和LoadMethodEx.java中,分别给出了两种实例化Class类的方法:一种是利用Constructor类调用newInstance()方法;另一种就是利用Class类本身的newInstance()方法创建一个实例。两种方法实现的效果是一样的。
// 利用newInstance()方法,获取构造方法的实例
// Class的newInstance方法,仅提供默认无参的实例化方法,类似于无参的构造方法
// Constructor的newInstance方法,提供了带参数的实例化方法,类似于含参的构造方法
Constructor ct = cls.getConstructor(null);
Object obj = ct.newInstance(null);
Object obj = cls.newInstance();
例子中的String[] paths= (String[]) method.invoke(sm, null);
sm就是StorageManager类的实例,我们通过StorageManager sm = (StorageManager) getSystemService(STORAGE_SERVICE);得到的。由于getVolumePaths()方法中没有参数,传入null就可以了;
上面说的基本都是获取方式,下面透过一个例子来实际使用他们:
首先创建一个用于反射的类并创建一些成员变量、方法等属性:
public class ReflectTest {
public String testPublicFiled;
private String testPrivateFiled;
public ReflectTest() {
}
public ReflectTest(String testPublicFiled, String testPrivateFiled) {
this.testPublicFiled = testPublicFiled;
this.testPrivateFiled = testPrivateFiled;
}
public void testPublicMethod(String name) {
}
private String testPrivateMethod() {
return null;
}
}
然后在其他地方通过反射获取这个类的信息:
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
//获取类
Class c = Class.forName("com.shenhesoft.lib.ReflectTest");
//获取继承的父类
String superClass = c.getSuperclass().getSimpleName();
System.out.println(superClass);
System.out.println("------------------------------");
//获取实现的接口
Class[] interfaces = c.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
System.out.println(interfaces[i].getSimpleName());
}
System.out.println("-----------------------------");
//获取类中所有的成员变量
Field[] fields = c.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
//参数修饰类型
String modifyName = Modifier.toString(fields[i].getModifiers());
//参数类型
String argsTypeName = fields[i].getType().getSimpleName();
//参数名
String argsName = fields[i].getName();
System.out.println(modifyName + " " + argsTypeName + " " + argsName);
}
//获取指定名称的变量
Field field = c.getDeclaredField("testPublicFiled");
//省略。。。。。。。。。
System.out.println("-------------------------------");
//获取类中所有的方法
Method[] methods = c.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
//方法修饰类型
String modifyName = Modifier.toString(methods[i].getModifiers());
//方法返回类型
String returnType = methods[i].getReturnType().getSimpleName();
//方法名
String methodName = methods[i].getName();
//方法里的参数类型
Class[] parameterTypes = methods[i].getParameterTypes();
String paramType = "";
for (int i1 = 0; i1 < parameterTypes.length; i1++) {
paramType = paramType + parameterTypes[i1].getSimpleName() + " ";
}
System.out.println(modifyName + " " + returnType + " " + methodName + " " + paramType);
}
//获取指定名称的方法
Method method = c.getDeclaredMethod("testPublicMethod", String.class);
//省略。。。。。。。。。
System.out.println("---------------------------------");
//获取类中所有的构造方法
Constructor[] constructors = c.getDeclaredConstructors();
for (int i = 0; i < constructors.length; i++) {
//获取构造方法的修饰类型
String modifyName = Modifier.toString(constructors[i].getModifiers());
//获取构造函数的参数类型
Class[] parameterTypes = constructors[i].getParameterTypes();
String paramType = "";
for (int i1 = 0; i1 < parameterTypes.length; i1++) {
paramType = paramType + parameterTypes[i1].getSimpleName() + " ";
}
System.out.println(modifyName + " " + paramType);
}
//获取指定名称的方法
Constructor constructor = c.getDeclaredConstructor(null);
//省略。。。。。。。。。
}
前面写的那么多方法在这里就用到了,我就不一一的讲了,对照着注释和前面的文字就能看懂,其实调用的方法就那么几个。
想要操作对象首先要获取到这个对象,然后才能通过反射操作这个对象,有些人可能会问,我已经有这个对象了可以直接操作为什么还要通过反射操作,这个问题的答案就是反射的核心。你有了对象可以直接操作私有的成员变量和方法吗,可以修改final修饰的成员变量吗,显然是不行的,下面我们看一下反射怎么做到这些。
修改ReflectTest类如下:
public class ReflectTest {
public String testPublicFiled;
private String testPrivateFiled;
public ReflectTest() {
}
public ReflectTest(String testPublicFiled, String testPrivateFiled) {
this.testPublicFiled = testPublicFiled;
this.testPrivateFiled = testPrivateFiled;
}
public void testPublicMethod(String name) {
System.out.println("公共方法,参数:" + name);
}
private void testPrivateMethod() {
System.out.println("私有方法");
}
public String getTestPublicFiled() {
return testPublicFiled;
}
public String getTestPrivateFiled() {
return testPrivateFiled;
}
}
下面我们直接通过反射修改ReflectTest的私有成员变量testPrivateFiled:
public static void main(String[] args) throws Exception{
//获取类
Class c = Class.forName("com.shenhesoft.lib.ReflectTest");
//创建一个对象
ReflectTest reflectTest = new ReflectTest("公共参数默认值", "私有参数默认值");
//打印私有参数的值
System.out.println(reflectTest.getTestPrivateFiled());
//获取私有的成员变量
Field field = c.getDeclaredField("testPrivateFiled");
//设置可以访问私有的成员变量,重要
field.setAccessible(true);
//获取私有成员变量的值
String s = (String) field.get(reflectTest);
System.out.println(s);
//修改私有成员变量的值
field.set(reflectTest, "修改私有方法");
//打印私有参数的值
System.out.println(reflectTest.getTestPrivateFiled());
}
运行结果:
私有参数默认值
私有参数默认值
修改私有方法
进程完成,退出码 0
可以看到我们已经成功通过反射修改了私有成员变量的值,关于通过反射修改公共成员变量我就不写了。
接下来我们看一下通过反射调用私有的方法testPrivateMethod():
public static void main(String[] args) throws Exception {
//获取类
Class c = Class.forName("com.shenhesoft.lib.ReflectTest");
//创建一个对象
ReflectTest reflectTest = new ReflectTest("公共参数默认值", "私有参数默认值");
//获取私有的方法
Method method = c.getDeclaredMethod("testPrivateMethod", null);
//设置可以访问私有的成员方法,重要
method.setAccessible(true);
//调用私有方法
method.invoke(reflectTest, null);
}
运行结果:
私有方法
进程完成,退出码 0
结果就不用多说了,接下来我们再看一下带有参数的公共方法怎么调用:
public static void main(String[] args) throws Exception {
//获取类
Class c = Class.forName("com.shenhesoft.lib.ReflectTest");
//创建一个对象
ReflectTest reflectTest = new ReflectTest("公共参数默认值", "私有参数默认值");
//获取公共的方法
Method method = c.getMethod("testPublicMethod", String.class);
//调用公共方法
method.invoke(reflectTest, "反射");
}
运行结果:
公共方法,参数:反射
进程完成,退出码 0
然后还有重要的一个操作,反射获取私有的静态成员变量和调用私有的静态方法,我们都知道静态的属性是类加载器启动的时候就已经加载了,并且全局只有一个,那我们获取的时候还需要对象吗,显然是不需要的,下面是具体的代码:
先在ReflectTest类中添加静态成员变量和方法
public class ReflectTest {
public String testPublicFiled;
private String testPrivateFiled;
private static String testStaticFiled;
public ReflectTest() {
}
public ReflectTest(String testPublicFiled, String testPrivateFiled) {
this.testPublicFiled = testPublicFiled;
this.testPrivateFiled = testPrivateFiled;
}
public void testPublicMethod(String name) {
System.out.println("公共方法,参数:" + name);
}
private void testPrivateMethod() {
System.out.println("私有方法");
}
public String getTestPublicFiled() {
return testPublicFiled;
}
public String getTestPrivateFiled() {
return testPrivateFiled;
}
public static String getTestStaticFiled() {
return testStaticFiled;
}
private static void testStatic() {
System.out.println("调用私有static方法");
}
}
public static void main(String[] args) throws Exception {
//获取类
Class c = Class.forName("com.shenhesoft.lib.ReflectTest");
Method method = c.getDeclaredMethod("testStatic", null);
method.setAccessible(true);
method.invoke(null, null);
}
运行结果:
调用私有static方法
进程完成,退出码 0
获取并修改静态成员变量的值
public static void main(String[] args) throws Exception {
//获取类
Class c = Class.forName("com.shenhesoft.lib.ReflectTest");
Field field = c.getDeclaredField("testStaticFiled");
field.setAccessible(true);
field.set(null, "修改静态的成员变量");
System.out.println(ReflectTest.getTestStaticFiled());
}
修改静态的成员变量
进程完成,退出码 0
最后在补充一个反射有参数方法的例子:
Class mClass=m.getClass();
Method method=mClass.getDeclaredMethod("setDrawerViewOffset",new Class[]{View.class,float.class});
method.invoke(m,m,0.9);
方法名为:setDrawerViewOffset;
参数类型为:VIew,float;
调用该方法时传入实例m,以及参数m(View类型),0.9(float类型);
反射私有成员变量
Class mClass=m.getClass();
//通过name获取成员变量mMinDrawerMargin
Field field=mClass.getDeclaredField("mMinDrawerMargin");
//设置私有变量可以修改
field.setAccessible(true);
//获取成员变量的值
Object value= field.get(m);
//mMinDrawerMargin原来的值
Log.i("info",value.toString());
//修改成员变量的值
field.set(m,300);
//获取修改之后成员变量的值
value=field.get(m);
//mMinDrawerMargin修改之后的值
Log.i("info",value.toString());
top:访问所有private类型的变量、方法都要设置setAccessible(true);
参考自:https://blog.csdn.net/sinat_38259539/article/details/71799078