在 Java 运行时,如果我们拿到一个 Object ,它可能是某个类的实例。如果我们想使用这个类的某个方法,却不知道这个类的名字,即连强制类型转换也做不了,那如何调用我们想要执行的方法呢。解决方案是使用 Java 反射。

Class 类与实例

如果我们有两个类 Type1Type2 的实例,然后将其转换成 Object 实例。表面上我们看不出这两个 Object 实例的区别,因为我们对他们的信息一无所知,但是这只是因为他们的引用是 Object 类型的而已。

Java runtime 提供了一种数据类型,称为 Class ,Java 程序开始运行后,每次首次加载一个 class ,都会用这个 class 的信息创建一个 Class 实例,即这个 Class 实例将会包含这个类的所有信息。Object 类提供了一个接口能让我们拿到任何一个对象的 Class 实例。也就是说,对于上述的 Type1Type2 转换成 Object 后的实例,我们可以拿到它们的 Class 实例,来区分这两个实例哪一个是 Type1 的实例、哪一个是 Type2

1
2
3
4
5
6
7
8
9
10
11
class Type1 { ... }
class Type2 { ... }

Type1 type1 = new Type1();
Type2 type2 = new Type2();

Object obj1 = (Object)type1;
Object obj2 = (Object)type2;

Class cls1 = obj1.getClass();
Class cls2 = obj2.getClass();

除此之外,还可以已知类的名字,然后直接获得其 Class 实例:

1
Class cls = Type1.class;

或使用包路径格式的完整类名:

1
Class cls = Class.forName("com.vonbrank.Type1");

Class 实例访问字段

假设有一个类 Person ,定义如下:

1
2
3
4
5
6
7
class Person {
private String name;
public Person(String name) {
this.name = name;
}
}

在下面的 test 方法中,我们只能拿到它实例的 Object 引用,如果想访问 name 可以这样写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Main {
public static void main(String[] args) {
Person person = new Person("Xiao Ming");
test(person);
}

static void test(Object obj) {
Class cls = obj.getClass();
Field field = cls.getDeclaredField("name");
Object value = field.get(p);
System.out.println(value); // "Xiao Ming"
}

}

调用方法

此处我们以 String 为例展示通过反射调用对象实例的方法:

1
2
3
4
5
6
7
8
9
10
11
12
public class Main {
public static void main(String[] args) throws Exception {
// String对象:
String s = "Hello world";
// 获取String substring(int)方法,参数为int:
Method m = String.class.getMethod("substring", int.class);
// 在s对象上调用该方法并获取结果:
String r = (String) m.invoke(s, 6);
// 打印调用结果:
System.out.println(r);
}
}