反射用得最多的地方是在框架的开发中,平时项目中用得比较少。反射机制允许程序在运行时获取一个已知名称的类的内部信息,包括属性(Field)、方法(Method)、构造函数(Constructor)等,并且可以在运行时改变属性和调用方法。Java文件被编译后生成.class文件,Java虚拟机(JVM)会去解读.class文件,当某个类被使用时JVM会通过加载、连接和初始化三个步骤把.class文件解析为一个对象,这个对象就是java.lang.Class。每一个Java源文件最终都会有一个唯一的类对象(Class),这里需要注意的是:类的对象和类对象不一样,类的对象存在于堆中,可以创建多个;类对象存在于方法区中,每一个类对应唯一的一个类对象,为Class类型。而我们在使用反射时,首先最重要的就是获取到这个唯一的类对象。
获取类对象:
类对象的获取有三种方式:
1、类名.class 比如:User.class
2、类的对象.getClass 比如:new User().getClass
3、Class.forName(“全类名”) 比如:Class.forName(“com.example.amt.activitystartdemo.User”)
上面三种方式都可以获取到一个Class类型的类对象,在框架开发中最常用的是第三种方式。当我们获取到这个类对象后就可以调用对应的API操作类中的构造函数、属性、方法。下面我们来看看具体是怎么操作的,其实就是熟悉一些API。首先我们定义一个User类:其中有public的属性、有private的属性、有static属性、有构造函数、有public的方法、private的方法。
public class User {
public String userName;
public int userAge;
private String userPass;
private static String userTag = "USER";
public User() {
}
public User(String userName, int userAge) {
this.userName = userName;
this.userAge = userAge;
Log.i("User", "userName=" + userName + ",userAge=" + userAge);
}
private User(String userPass) {
this.userPass = userPass;
Log.i("User", "userPass=" + userPass);
}
public void testMethod(String name, int age) {
Log.i("User", "testMethod name=" + name + ",age=" + age);
}
public String getUserName() {
return userName;
}
private int getUserAge() {
return userAge;
}
}
操作构造函数(Constructor):
Class c = Class.forName("com.example.amt.activitystartdemo.User");
//获取public的构造函数
Constructor[] publicConstructors = c.getConstructors();
for (Constructor constructor : publicConstructors) {
Log.i("User", "publicConstructor:" + constructor);
}
//获取所有的构造函数
Constructor[] allConstructors = c.getDeclaredConstructors();
for (Constructor constructor : allConstructors) {
Log.i("User", "allConstructor:" + constructor);
}
//获取指定的public构造函数(通过构造函数中的参数来获取)
Constructor publicC = c.getConstructor(String.class,int.class);
Log.i("User", "constructor:" + publicC);
//通过public构造函数生成对象
Object obj = publicC.newInstance("LT",26);
//获取指定的私有构造函数(通过构造函数中的参数来获取)
Constructor privateC = c.getDeclaredConstructor(String.class);
//所有private的构造函数、属性、方法在使用前都必须设置setAccessible为true
privateC.setAccessible(true);
Object o = privateC.newInstance("123456");
其中需要注意的是我们在获取到private的构造函数后使用这个构造函数生成新对象时需要先设置setAccessible(true),否则运行会报错。这个不仅仅是在操作private的构造函数中需要这样,所有private的属性、方法在应用前都必须设置反射对象的setAccessible为true。setAccessible设置为true表示反射对象在应用时抑制Java语音访问检查,setAccessible设置为false表示反射对象在应用时强制执行Java语音访问检查。
操作属性(Field):
//获取所有public的属性
Field[] publicField = c.getFields();
for (Field field : publicField) {
Log.i("User", "publicField:" + field);
}
//获取所有属性
Field[] allField = c.getDeclaredFields();
for (Field field : allField) {
Log.i("User", "allField:" + field);
}
//获取指定的public属性(通过属性名称)
Field field = c.getField("userName");
//设置新的值
field.set(o, "newUser");
//获取属性值
Log.i("User", "userName:" + field.get(o));
//获取private static的属性
Field staticField = c.getDeclaredField("USER");
staticField.setAccessible(true);
Log.i("User", "staticField:" + staticField.getName());
当我们获取到了某个属性的反射对象后,如果对这个属性设置新的值,那么我们首先需要一个类的对象,就是对哪一个对象设置属性值,如上面代码中的对象o,这个对象o就是上面通过构造函数构造出来的新对象(Object o = privateC.newInstance(“123456”);),并且也需要记住的是如果操作的属性是private的需要先设置setAccessible(true)。
操作方法:
//获取所有的public方法
Method[] publicMethod = c.getMethods();
for (Method method : publicMethod) {
Log.i("User", "publicMethod:" + method);
}
//获取所有的方法
Method[] allMethod = c.getDeclaredMethods();
for (Method method : allMethod) {
Log.i("User", "allMethod:" + method);
}
//获取指定方法
//通过方法名+参数
Method method = c.getMethod("testMethod", String.class, int.class);
//方法调用
//通过类的对象+参数
method.invoke(o, "testName", 26);
//private方法
Field ageFiled = c.getField("userAge");
ageFiled.set(o, 28);
Method privateMethod = c.getDeclaredMethod("getUserAge");
privateMethod.setAccessible(true);
Log.i("User", "privateMethod:" + privateMethod.invoke(o));
获取某个指定的方法是需要通过方法名称+参数来获取,这里的参数是指该方法中参数的类型,比如上面的testMethod这个方法,参数的类型就是Sting.class和int.class,我们使用invoke来调用执行方法,同Field一样也需要传入一个类的对象,表示执行哪个对象的方法,invoke中除了传入类的对象外后面跟的就是具体的参数,如果这个方法没有定义参数那就只需类的对象。