二十三、反射
- 在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制。通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以。
1. 访问构造方法
- 如果是访问指定的构造方法,需要根据该构造方法的入口参数的类型来访问。例如访问一个入口参数类型依次为String和int型的构造方法,通过以下两种方式均可实现:
objectClass.getDeclaredConstructors(String.class,int.class);
objectClass.getDeclaredConstructors(new Class[] {String.class,int.class});
Constructor类的常用方法就不一一列出了。
通过java.lang.reflect.Modifier类解析出getModifier()方法的返回值所表示的修饰符信息,在该类中提供了一系列用来解析的静态方法(这些静态解析方法就不一一列出了),既可以查看是否被指定的修饰符修饰,还可以以字符串的形式获得所有修饰符。
下面来看一个访问构造方法的实例:
- 首先创建一个类,在该类中声明一个String型成员和3个int型成员变量,并提供3个构造方法
package lianxi3;
public class Example_01 {
String s;
int i, i2, i3;
private Example_01() {
}
protected Example_01(String s, int i) {
this.s = s;
this.i = i;
}
public Example_01(String... strings) throws NumberFormatException {
if (0 < strings.length)
i = Integer.valueOf(strings[0]);
if (1 < strings.length)
i2 = Integer.valueOf(strings[1]);
if (2 < strings.length)
i3 = Integer.valueOf(strings[2]);
}
public void print() {
System.out.println("s=" + s);
System.out.println("i=" + i);
System.out.println("i2=" + i2);
System.out.println("i3=" + i3);
}
}
- 然后编写测试类,在该类中通过反射访问上一个类中的所有构造方法,并将该构造方法是否允许带有可变数量的参数、入口参数类型和可能抛出的异常类型信息输出到控制台
package lianxi3;
import java.lang.reflect.*;
public class Main_01 {
public static void main(String[] args) {
Example_01 example = new Example_01("10", "20", "30");
Class extends Example_01> exampleC = example.getClass();
Constructor[] declaredConstructors = exampleC.getDeclaredConstructors();
for (int i = 0; i < declaredConstructors.length; i++) {
Constructor> constructor = declaredConstructors[i];
System.out.println("查看是否允许带有可变数量的参数:" + constructor.isVarArgs());
System.out.println("该构造方法的入口参数类型依次为:");
Class[] parameterTypes = constructor.getParameterTypes();
for (int j = 0; j < parameterTypes.length; j++) {
System.out.println(" " + parameterTypes[j]);
}
System.out.println("该构造方法可能抛出的异常类型为:");
Class[] exceptionTypes = constructor.getExceptionTypes();
for (int j = 0; j < exceptionTypes.length; j++) {
System.out.println(" " + exceptionTypes[j]);
}
Example_01 example2 = null;
while (example2 == null) {
try {
if (i == 2)
example2 = (Example_01) constructor.newInstance();
else if (i == 1)
example2 = (Example_01) constructor.newInstance("7", 5);
else {
Object[] parameters = new Object[] { new String[] {
"100", "200", "300" } };
example2 = (Example_01) constructor
.newInstance(parameters);
}
} catch (Exception e) {
System.out.println("在创建对象时抛出异常,下面执行setAccessible()方法");
constructor.setAccessible(true);
}
}
if(example2!=null){
example2.print();
System.out.println();
}
}
}
}
运行结果:
2. 访问成员变量
- 通过以下一组方法访问成员变量时,将返回Field类型的对象或数组。每个Field对象代表一个成员变量,利用Field对象可以操纵相应的成员变量。
getFields();
getFields(String name);
getDeclareFields();
getDeclareFields(String name);
下面看一个访问成员变量的实例:
- 首先创建一个类,在该类中依次声明一个int、float、boolean和String型的成员变量,并将它们设置为不同的访问权限
package lianxi3;
public class Example_02 {
int i;
public float f;
protected boolean b;
private String s;
}
- 然后通过反射访问上个类中的所有成员变量,将成员变量的名称和类型信息输出到控制台,并分别将各个成员变量在修改前后的值输出到控制台
package lianxi3;
import java.lang.reflect.*;
public class Main_02 {
public static void main(String[] args) {
Example_02 example = new Example_02();
Class exampleC = example.getClass();
// 获得所有成员变量
Field[] declaredFields = exampleC.getDeclaredFields();
for (int i = 0; i < declaredFields.length; i++) {
Field field = declaredFields[i]; // 遍历成员变量
// 获得成员变量名称
System.out.println("名称为:" + field.getName());
Class fieldType = field.getType(); // 获得成员变量类型
System.out.println("类型为:" + fieldType);
boolean isTurn = true;
while (isTurn) {
// 如果该成员变量的访问权限为private,则抛出异常,即不允许访问
try {
isTurn = false;
// 获得成员变量值
System.out.println("修改前的值为:" + field.get(example));
// 判断成员变量的类型是否为int型
if (fieldType.equals(int.class)) {
System.out.println("利用方法setInt()修改成员变量的值");
field.setInt(example, 168); // 为int型成员变量赋值
// 判断成员变量的类型是否为float型
} else if (fieldType.equals(float.class)) {
System.out.println("利用方法setFloat()修改成员变量的值");
// 为float型成员变量赋值
field.setFloat(example, 99.9F);
// 判断成员变量的类型是否为boolean型
} else if (fieldType.equals(boolean.class)) {
System.out.println("利用方法setBoolean()修改成员变量的值");
// 为boolean型成员变量赋值
field.setBoolean(example, true);
} else {
System.out.println("利用方法set()修改成员变量的值");
// 可以为各种类型的成员变量赋值
field.set(example, "MWQ");
}
// 获得成员变量值
System.out.println("修改后的值为:" + field.get(example));
} catch (Exception e) {
System.out.println("在设置成员变量值时抛出异常,"
+ "下面执行setAccessible()方法!");
field.setAccessible(true); // 设置为允许访问
isTurn = true;
}
}
System.out.println();
}
}
}
运行结果:
- 可以看见在本例中,在访问权限为private的成员变量s时,需要执行setAccessible()方法,并将入口参数设为true,否则不允许访问。
3. 访问方法
- 访问方法与访问构造方法类似,只不过访问方法返回的是Method类型的对象或者数组,每一个Method对象代表一个方法。Method类的常用方法就不一一列出了。
下面来看一个访问方法的实例:
- 首先创建一个类,并编写4个典型的方法
package lianxi3;
public class Example_03 {
static void staticMethod() {
System.out.println("执行staticMethod()方法");
}
public int publicMethod(int i) {
System.out.println("执行publicMethod()方法");
return i * 100;
}
protected int protectedMethod(String s, int i)
throws NumberFormatException {
System.out.println("执行protectedMethod()方法");
return Integer.valueOf(s) + i;
}
private String privateMethod(String... strings) {
System.out.println("执行privateMethod()方法");
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < strings.length; i++) {
stringBuffer.append(strings[i]);
}
return stringBuffer.toString();
}
}
- 然后通过反射访问上个类中的所有方法,将各个方法的名称、入口参数类型、返回值类型等信息输出到控制台,并执行部分方法
package lianxi3;
import java.lang.reflect.*;
public class Main_03 {
public static void main(String[] args) {
Example_03 example = new Example_03();
Class exampleC = example.getClass();
// 获得所有方法
Method[] declaredMethods = exampleC.getDeclaredMethods();
for (int i = 0; i < declaredMethods.length; i++) {
Method method = declaredMethods[i]; // 遍历方法
System.out.println("名称为:" + method.getName()); // 获得方法名称
System.out.println("是否允许带有可变数量的参数:" + method.isVarArgs());
System.out.println("入口参数类型依次为:");
// 获得所有参数类型
Class[] parameterTypes = method.getParameterTypes();
for (int j = 0; j < parameterTypes.length; j++) {
System.out.println(" " + parameterTypes[j]);
}
// 获得方法返回值类型
System.out.println("返回值类型为:" + method.getReturnType());
System.out.println("可能抛出的异常类型有:");
// 获得方法可能抛出的所有异常类型
Class[] exceptionTypes = method.getExceptionTypes();
for (int j = 0; j < exceptionTypes.length; j++) {
System.out.println(" " + exceptionTypes[j]);
}
boolean isTurn = true;
while (isTurn) {
// 如果该方法的访问权限为private,则抛出异常,即不允许访问
try {
isTurn = false;
if("staticMethod".equals(method.getName()))
method.invoke(example); // 执行没有入口参数的方法
else if("publicMethod".equals(method.getName()))
System.out.println("返回值为:"
+ method.invoke(example, 168)); // 执行方法
else if("protectedMethod".equals(method.getName()))
System.out.println("返回值为:"
+ method.invoke(example, "7", 5)); // 执行方法
else if("privateMethod".equals(method.getName())) {
Object[] parameters = new Object[] { new String[] {
"M", "W", "Q" } }; // 定义二维数组
System.out.println("返回值为:"
+ method.invoke(example, parameters));
}
} catch (Exception e) {
System.out.println("在执行方法时抛出异常,"
+ "下面执行setAccessible()方法!");
method.setAccessible(true); // 设置为允许访问
isTurn = true;
}
}
System.out.println();
}
}
}