Java 反射
什么是反射
Java的反射是指程序在运行期可以拿到一个对象的所有信息。
正常情况下,如果我们要调用一个对象的方法,或者访问一个对象的字段,通常会传入对象实例:
// Main.java
import com.example.Person;
public class Main {
String getFullName(Person p) {
return p.getFirstName() + " " + p.getLastName();
}
}
Class 类
除了int等基本类型外,Java的其他类型全部都是class(包括interface)。
如何获取一个class的Class实例?有三个方法:
方法一:直接通过一个class的静态变量class获取:
Class cls = String.class;
方法二:如果我们有一个实例变量,可以通过该实例变量提供的getClass()方法获取:
String s = "Hello";
Class cls = s.getClass();
方法三:如果知道一个class的完整类名,可以通过静态方法Class.forName()获取:
Class cls = Class.forName("java.lang.String");
因为Class实例在JVM中是唯一的,所以,上述方法获取的Class实例是同一个实例。可以用==比较两个Class实例:
Class cls1 = String.class;
String s = "Hello";
Class cls2 = s.getClass();
boolean sameClass = cls1 == cls2; // true
访问字段
对任意的一个Object实例,只要我们获取了它的Class,就可以获取它的一切信息。
我们先看看如何通过Class实例获取字段信息。Class类提供了以下几个方法来获取字段:
- Field getField(name):根据字段名获取某个public的field(包括父类)
- Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
- Field[] getFields():获取所有public的field(包括父类)
- Field[] getDeclaredFields():获取当前类的所有field(不包括父类)
一个Field对象包含了一个字段的所有信息:
- getName():返回字段名称,例如,"name";
- getType():返回字段类型,也是一个Class实例,例如,String.class;
- getModifiers():返回字段的修饰符,它是一个int,不同的bit表示不同的含义。
通过Field实例可以读取或设置某个对象的字段,如果存在访问限制,要首先调用setAccessible(true)来访问非public字段。
调用方法
我们已经能通过Class实例获取所有Field对象,同样的,可以通过Class实例获取所有Method信息。Class类提供了以下几个方法来获取Method:
- Method getMethod(name, Class...):获取某个public的Method(包括父类)
- Method getDeclaredMethod(name, Class...):获取当前类的某个Method(不包括父类)
- Method[] getMethods():获取所有public的Method(包括父类)
- Method[] getDeclaredMethods():获取当前类的所有Method(不包括父类)
一个Method对象包含一个方法的所有信息:
- getName():返回方法名称,例如:"getScore";
- getReturnType():返回方法返回值类型,也是一个Class实例,例如:String.class;
- getParameterTypes():返回方法的参数类型,是一个Class数组,例如:{String.class, int.class};
- getModifiers():返回方法的修饰符,它是一个int,不同的bit表示不同的含义。
通过Method实例可以调用某个对象的方法:Object invoke(Object instance, Object... parameters);
通过设置setAccessible(true)来访问非public方法;
使用反射调用方法时,仍然遵循多态原则:即总是调用实际类型的覆写方法(如果存在)。
调用构造方法
为了调用任意的构造方法,Java的反射API提供了Constructor对象,它包含一个构造方法的所有信息,可以创建一个实例。Constructor对象和Method非常类似,不同之处仅在于它是一个构造方法,并且,调用结果总是返回实例:
import java.lang.reflect.Constructor;
public class Main {
public static void main(String[] args) throws Exception {
// 获取构造方法Integer(int):
Constructor cons1 = Integer.class.getConstructor(int.class);
// 调用构造方法:
Integer n1 = (Integer) cons1.newInstance(123);
System.out.println(n1);
// 获取构造方法Integer(String)
Constructor cons2 = Integer.class.getConstructor(String.class);
Integer n2 = (Integer) cons2.newInstance("456");
System.out.println(n2);
}
}
通过Class实例获取Constructor的方法如下:
- getConstructor(Class...):获取某个public的Constructor;
- getDeclaredConstructor(Class...):获取某个Constructor;
- getConstructors():获取所有public的Constructor;
- getDeclaredConstructors():获取所有Constructor。
注意Constructor总是当前类定义的构造方法,和父类无关,因此不存在多态的问题。
调用非public的Constructor时,必须首先通过setAccessible(true)设置允许访问。setAccessible(true)可能会失败。
获得继承关系
通过Class对象可以获取继承关系:
- Class getSuperclass():获取父类类型;
- Class[] getInterfaces():获取当前类实现的所有接口。
通过Class对象的isAssignableFrom()方法可以判断一个向上转型是否可以实现。