更多 Java 高级知识方面的文章,请参见文集《Java 高级知识》
Java 反射
在运行时:
- 获取类的成员变量和成员方法
- 调用类的成员变量和成员方法
- 通过构造函数构造一个类的对象
缺点:性能较差
getXX() VS getDeclaredXX():
- getXX():包括继承的
- getDeclaredXX():不包括继承的
Java 反射的使用
首先通过 Class.forName("")
显示加载某个类,获得 Class
对象,然后调用 Class
对象的如下方法:
-
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
- 通过参数列表,获得某个构造方法
- 随后可以调用
Constructor
类的newInstance()
方法来创建对象
-
Method[] getDeclaredMethods()
- 获得所有的成员方法
-
Method
类包含如下方法:String getName()
Class<?>[] getParameterTypes()
Class<?>[] getExceptionTypes()
Class<?> getReturnType()
Annotation[] getDeclaredAnnotations()
Class<?> getDeclaringClass()
-
Method getDeclaredMethod(String name, Class<?>... parameterTypes)
- 通过方法名和参数列表,获得某个成员方法
- 如果只是通过方法名来获得某个成员方法,则难以处理方法重载的情况
-
Field[] getDeclaredFields()
- 获得所有的成员变量
-
Field getDeclaredField(String name)
- 通过变量名,获得某个成员变量
使用 Java 反射 API 的时候可以绕过 Java 的访问控制检查,可以访问私有成员变量和私有成员方法,只需在获取 Constructor
,Method
和 Field
之后调用 .setAccessible(true)
。
示例:
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public void print() {
System.out.println(name + "-" + age);
}
}
public static void main(String[] args) throws Exception {
// 加载类,字节码转换为 Class 对象
Class c = Class.forName("Student");
// 获取构造方法并通过构造方法构造对象
Constructor<Student> constructor = c.getDeclaredConstructor(String.class, int.class);
Student student = constructor.newInstance("Tom", 18);
// 获取字段及对应的值
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
// 设置可见性
field.setAccessible(true);
System.out.println(field.getName() + "=" + field.get(student));
}
// 获取方法及执行方法
Method[] methods = c.getDeclaredMethods();
for (Method method : methods) {
method.invoke(student);
}
}
反射中对泛型的处理
由于类型擦除机制,泛型中的类型参数在运行时是不存在的,JVM 只看到原始类型。
因此 Java5 在编译后的 .class 中添加了 Signature 属性,用来包含不在 JVM 类型系统中的类型信息,提供给反射 API 来使用。
例如:
public class Reflection_Test2 {
public static void main(String[] args) throws Exception {
// 加载类,字节码转换为 Class 对象
Class c = Class.forName("advanced.Ref");
// 获取字段及对应的值
Field field = c.getDeclaredField("map");
Type type = field.getType();
// 输出 java.util.HashMap
System.out.println(type.getTypeName());
Type genericType = field.getGenericType();
// 输出 java.util.HashMap<java.lang.String, java.lang.Integer>
System.out.println(genericType.getTypeName());
if (genericType instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) genericType;
Type[] actualTypeArguments = pt.getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
// 依次输出 java.lang.String java.lang.Integer
System.out.println(actualTypeArgument.getTypeName());
}
}
}
}
class Ref {
private HashMap<String, Integer> map;
}