任何未知类型对象,都可以通过反射获取它的类信息。
1. 原理
说到反射,我们就不能不提Class
这个类:
public final class Class<T> implements java.io.Serializable, GenericDeclaration, Type, AnnotatedElement {
}
在运行一个项目的时候,JVM不会一次性的将全部class文件加载到内存中,而是在需要用到某一个class文件的时候,才将它加载到内存中,这种方式叫做JVM的动态加载。
当JVM第一次把一个class文件加载进内存的时候,会为它创建一个Class
类实例,并将它和该实例关联起来。这个Class
实例具有这个class文件的所有信息,包括它所在的包、它的名称、它继承的父类、它实现的接口、它包含的属性,以及它拥有的方法。
那么,为什么我们在聊反射之前,就一定要聊Class
类呢?因为,反射其实就是一种通过Class
实例获取它关联的class
文件信息的方法。
具体的通过代码来体现:
private void testReflection(Object obj) {
Class<?> clazz = obj.getClass();
Field[] fields = clazz.getDeclaredFields();
Method[] methods = clazz.getDeclaredMethods();
}
通过obj
的getClass()
方法,获取Class
实例,该实例有obj
的类的全部信息。如上代码,在未知obj
的真实类型的情况下,透过反射,我们获取到了obj
的类的所有属性及所有方法。
2. 作用
第一个作用,获取类的信息。在讲原理的时候,我们已经看到这样的例子了。
第二个作用,是获取某个属性的值,或者执行某个方法。代码示例如下:
我们先创建一个Desk
类:
package obj.desk;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class Desk {
/**
* 形状
*/
private String shape;
/**
* 桌子腿条数
*/
protected int legs;
/**
* 桌子高度
*/
protected double height;
/**
* 额外描述
*/
protected String desc;
/**
* 放置东西在桌子上
* @param thing
*/
public void place(String thing) {
System.out.println("place on desk");
}
/**
* 从桌子上移走东西
* @param thing
*/
public void remove(String thing) {
System.out.println("remove from desk");
}
}
然后,通过field.get(obj)
,就能拿到field
的值。其中field
是通过反射获取的,obj
是那个对象。这样做当然是有意义的,因为我们并不是所有时候都能知道field
是什么。有的时候,我们只是知道这些field
的特征,比如是某个注解修饰的属性,而我们需要通过这些特征获取field
的值。此外,通过代码field.setAccessible(true);
,我们可以关闭访问权限控制,操纵private
类型的属性,很cool是吧。
以下代码打印出String类型的属性的值:
import obj.desk.Desk;
import java.lang.reflect.Field;
public class TestWhatever {
public static void main(String[] args) throws IllegalAccessException {
Desk desk = new Desk();
desk.setDesc("this is a desk");
testReflection(desk);
}
private static void testReflection(Object obj) throws IllegalAccessException {
// 1.获取obj的Class实例
Class<?> clazz = obj.getClass();
// 2.通过Class实例获取类的全部属性
Field[] fields = clazz.getDeclaredFields();
// 3.判空
if (fields == null || fields.length == 0) {
return;
}
// 4.匹配 & 打印
for (Field field : fields) {
// 匹配String类型的属性。注意:是通过'=='判断的两个Class实例是否相同的
if (field.getType() == String.class) {
// 这行代码可以打破访问权限控制的壁垒,即使非public的属性也能访问
field.setAccessible(true);
// 通过field.get(obj)可以获取到该属性的值
String value = field.getName() + ": " + field.get(obj);
System.out.println(value);
}
}
}
}
// 输出
// shape: null
// desc: this is a desk
类似的,通过反射我们可以修改方法访问权限,可以调用方法。
import obj.desk.Desk;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TestWhatever {
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException {
Desk desk = new Desk();
desk.setDesc("this is a desk");
testReflection(desk);
}
private static void testReflection(Object obj) throws IllegalAccessException, InvocationTargetException {
// 1.获取obj的Class实例
Class<?> clazz = obj.getClass();
// 2.获取obj的类的全部属性
Method[] methods = clazz.getDeclaredMethods();
// 3.判空
if (methods == null && methods.length == 0) {
return;
}
// 4.调用所有get方法 & 打印执行结果
for (Method method : methods) {
// 找到所有get方法
if (method.getName() != null && method.getName().startsWith("get")) {
// 这行代码可以打破访问权限控制的壁垒,即使非public的方法也能访问
method.setAccessible(true);
// 通过method.invoke(obj)可以调用obj的方法
String result = method.getName() + "() return: " + method.invoke(obj);
System.out.println(result);
}
}
}
}
// 输出
// getDesc() return: this is a desk
// getLegs() return: 0
// getHeight() return: 0.0
// getShape() return: null
3. API
在我们理解了反射的原理之后,反射对于我们来说,其实只是一系列的API。而类具有什么信息,我们就有获取什么信息的API,这两个是对应的。以下是几个API示例:
3.1.获取Class实例的三种方式
// 1.通过调用对象的getClass方法获取Class实例
Class<?> clazz = obj.getClass();
// 2.通过类的静态属性获取Class实例
Class<?> clazz2 = Desk.class;
// 3.通过类全名获取Class实例
Class<?> clazz3 = Class.forName("obj.desk.Desk");
3.2.属性相关API
import obj.desk.Desk;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
public class TestWhatever {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
Desk desk = new Desk();
desk.setDesc("this is a desk");
testReflection(desk);
}
private static void testReflection(Object obj) throws IllegalAccessException {
Class<?> clazz = obj.getClass();
// 1.获取field
// 获取类的全部属性
Field[] fields = clazz.getDeclaredFields();
// 获取类的全部public属性,及父类的全部public属性
Field[] fields1 = clazz.getFields();
// 获取类的属性
Field field = null;
try {
field = clazz.getDeclaredField("desc");
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
// 获取类的public属性,或父类的public属性
Field field1 = null;
try {
field1 = clazz.getField("desc");
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
if (field == null)
return;
// 2.获取field的信息
// 获取属性类型
Class<?> type = field.getType();
System.out.println("type: " + type);
// 获取属性名
String name = field.getName();
System.out.println("name: " + name);
// 获取属性的修饰符:private, static, final,通过数值表示
int modifiers = field.getModifiers();
System.out.println("modifiers: " + modifiers);
// 3.获取属性值
field.setAccessible(true);
Object value = field.get(obj);
System.out.println("属性desc的值: " + value);
// 4.设置字段值
field.set(obj, "Set a new desc");
System.out.println("属性desc的新值: " + field.get(obj));
}
}
// 输出
// java.lang.NoSuchFieldException: desc
// at java.lang.Class.getField(Class.java:1690)
// at TestWhatever.testReflection(TestWhatever.java:35)
// at TestWhatever.main(TestWhatever.java:11)
// type: class java.lang.String
// name: desc
// modifiers: 4
// 属性desc的值: this is a desk
// 属性desc的新值: Set a new desc
3.3.方法相关API
import obj.desk.Desk;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
public class TestWhatever {
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException {
Desk desk = new Desk();
desk.setDesc("this is a desk");
testReflection(desk);
}
private static void testReflection(Object obj) throws IllegalAccessException, InvocationTargetException {
Class<?> clazz = obj.getClass();
// 1.获取method
// 获取类的全部方法
Method[] methods = clazz.getDeclaredMethods();
// 获取类的全部public方法,及父类的全部public方法
Method[] methods1 = clazz.getMethods();
// 获取类的方法
Method method = null;
try {
method = clazz.getDeclaredMethod("place", String.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
// 获取类的public方法,或父类的public方法
Method method1 = null;
try {
method1 = clazz.getMethod("place");
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
if (method == null)
return;
// 2.获取method的信息
// 获取方法返回值类型
Class<?> returnType = method.getReturnType();
System.out.println("returnType: " + returnType);
// 获取方法名
String name = method.getName();
System.out.println("name: " + name);
// 获取方法的修饰符:private, static, final,通过数值表示
int modifiers = method.getModifiers();
System.out.println("modifiers: " + modifiers);
// 获取方法参数列表
Parameter[] parameters = method.getParameters();
if (parameters != null && parameters.length > 0) {
for (Parameter parameter : parameters) {
// 获取参数的数据类型
Class<?> parameterType = parameter.getType();
System.out.println("parameterType: " + parameterType);
// 获取参数的名称(在编译后的class文件中的名称)
String parameterName = parameter.getName();
System.out.println("parameterName: " + parameterName);
}
}
// 3.调用方法
method.setAccessible(true);
method.invoke(obj, "a book");
}
}
// 输出
// java.lang.NoSuchMethodException: obj.desk.Desk.place()
// at java.lang.Class.getMethod(Class.java:1773)
// at TestWhatever.testReflection(TestWhatever.java:36)
// at TestWhatever.main(TestWhatever.java:12)
// returnType: void
// name: place
// modifiers: 1
// parameterType: class java.lang.String
// parameterName: arg0
// place a book on desk