什么是反射
Reflection is a feature in the Java programming language. It allows an executing Java program to examine or "introspect" upon itself, and manipulate internal properties of the program. For example, it's possible for a Java class to obtain the names of all its members and display them.
上面是 官方对 反射的介绍, 反射是Java编程语言的一个特性。 它允许正在执行的Java程序检查或“内省”自身,并操纵程序的内部属性.
即 我们可以在运行时, 拿到加载到内存中类的所有信息, 如 字段,方法和构造函数等信息,并且能够操作其属性和方法.
反射的作用
一般在两种情况下使用反射.
- 程序解耦, 提高代码的灵活度.
大部分开发各种通用框架或者开源库时, 很经常使用到 反射.
- 为了获取和修改私有构造函数的类, 或者类的私有字段和私有方法.
由于某种需求, 我们需要拿到系统或者第三方库的私有数据, 可能可以通过反射获取.
测试类
这里先给出测试类, 后续反射示例都会使用到该类.
package reflection;
public class Ref {
private static final String staticFinalField = "staticFinalField";
private final String finalField = "finalField";
private static String staticField = "staticField";
private String field = "field";
public Ref() {
}
private Ref(String field) {
this.field = field;
}
private void setField(String field) {
this.field = field;
}
private String getFinalField() {
return finalField;
}
private static void setStaticField(String field) {
staticField = field;
}
static class Inner {
}
}
Class对象
java中存在两种对象, 一种是 类对象
, 一种是 实例对象
.
实例对象通常是通过 new
, 反射 , clone
方法, 枚举初始化 以及 反序列创建出来的.
而类对象(Class)则是由 JVM 在类加载阶段生成的对象,用于保存类信息.
类对应Class对象 在JVM中是有且仅有一个与之对应.
而我们的反射信息, 就首先需要先拿到 Class对象.
我们可以通过三种途径拿到 Class对象.
- 类名.class
// 使用 类.class
Class klass = Ref.class;
- Class.forName
// 使用forName获取类对象
Class klass = Class.forName("reflection.Ref");
// 注意, 如果调用的是静态内部类, forName时需要 用$连接
klass = Class.forName("reflection.Ref$Inner");
- 对象实例.getClass
// 使用 对象.getClass
Object object = new Ref();
klass = object.getClass();
反射创建实例
- 如果类的构造器是公开且无参构造函数,则直接使用Class.newInstance
// 1. 对于 默认构造函数,且是public可以直接使用Class调用
Class klass = Class.forName("reflection.Ref");
Object object = klass.newInstance();
- 使用Constructor构造器可以创建带参且私有的构造函数
// 使用 Constructor , 可调用私有且带参的
// getConstructor() 只能调用,显式声明且公共的构造函数
// getDeclaredConstructor() 可以调用所有的构造器,包括私有的
Class klass = Class.forName("reflection.Ref");
// 设置带参的构造器
Constructor ctor = klass.getDeclaredConstructor(String.class);
// 设置私有构造可访问
ctor.setAccessible(true);
// 传入构造函数的参数
Object object = ctor.newInstance("ctorField");
注意 :
getDeclaredConstructor
方法, 可以获取私有的构造器,在操作私有的构造器时,需要调用setAccessible(true)
方法,来使私有构造方法可达.
- 获取类的所有构造函数信息
// 获取所有构造函数
// getDeclaredConstructors() 可以获取所有包括私有的构造函数
// getConstructors() 只能获取公共的构造函数
Constructor[] all = klass.getDeclaredConstructors();
for (Constructor constructor : all) {
// 获取构造函数的参数信息
Class[] types = constructor.getParameterTypes();
for (Class type : types) {
System.out.print("type : " + type.getName() + ",");
}
System.out.println("===");
}
反射操作字段
- 操作私有字段
Class klass = Class.forName("reflection.Ref");
Object object = klass.newInstance();
// 修改私有字段
// getField() 获取公共的字段, 包含父类的公共字段
// getDeclaredField() 获取私有以及公共字段,但不包括父类字段
// 可以设置 final 对象的值
Field field = klass.getDeclaredField("field");
field.setAccessible(true);
// 设置值
field.set(object, "setting");
// 输出 object对象的 field 字段的值 值为 setting
System.out.println(field.get(object));
注释 :
getDeclaredField
可以获取当前对象的私有字段, 但是无法获取其父类的字段.
getField
虽然只能获取公共字段,但是可以拿到父类的公共字段.
- 操作私有静态字段
Class klass = Class.forName("reflection.Ref");
// 修改私有静态字段
// 不可以设置 static final 的字段
field = klass.getDeclaredField("staticField");
// 私有字段,设置可达
field.setAccessible(true);
// 设置静态值, 无需传实例
field.set(null, "setStatic");
// 输出静态字段 # setStatic
System.out.println(field.get(null));
注释 : 静态字段,是属于类的字段,无需实例的支持,所以
field.set(Object obj, Object value)
,第一个参数需要的实例直接传null
.field.get(Object obj)
参数直接传null
.
- 获取所有字段
Class klass = Class.forName("reflection.Ref");
// 获取所有字段
// getFields 获取所有的公共字段,包括父类的公共字段
// getDeclaredFields 获取当前类的所有字段,包括私有字段,但不包括父类字段
Field[] fields = klass.getDeclaredFields();
for (Field f : fields) {
System.out.println(f.getName());
}
4. 操作final字段
final
修饰的变量比较特殊, 可以分为两种类型.
一种是修饰 字面量(不可变类型), 如基本类型和String对象(但包括基本类型的装箱对象以及new String()产生的对象
).
另一种就是 修饰 普通的对象.
final
修饰字面量时, 将被内联, 内联的字段反射修改无效.
观察以下代码:
public class ReflectionDemo {
public static void main(String[] args) throws Exception {
Class klass = Final.class;
Final obj = (Final) klass.newInstance();
Field finalField = klass.getDeclaredField("finalField");
// 如果无法修改final字段,尝试去掉 final 修饰符
// removeFinalModifier(finalField);
finalField.setAccessible(true);
finalField.set(obj, "setFinal");
Field inlineField = klass.getDeclaredField("inlineFinalField");
// removeFinalModifier(inlineField);
inlineField.setAccessible(true);
inlineField.set(obj, "setInline");
obj.print();
}
static class Final {
// 编译时,内联
private final String inlineFinalField = "inlineFinalField";
// 不会被内联
private final String finalField = new String("finalField");
void print() {
// 将被内联为 System.out.println("inlineFinalField");
System.out.println(inlineFinalField);
System.out.println(finalField);
}
}
// 去除对字段的 final 修饰符
static void removeFinalModifier(Field field) throws Exception {
Field modifierField = Field.class.getDeclaredField("modifiers");
modifierField.setAccessible(true);
modifierField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
}
}
// inlineFinalField
// setFinal
当final
字段被内联时, 使用到该字段的地方,直接被替换为 其字面量的值, 所以即使修改了 变量也无济于事.
因此, 只有当 final
修饰的是 普通的对象实例变量时, 反射该字段 才有意义.
操作 static final
变量
private static void staticFinalFiledTest() throws Exception {
Class klass = Ref.class;
// 静态字段无需对象实例
Field field = klass.getDeclaredField("staticFinalField");
field.setAccessible(true);
// 1. 读取 private static final 字段
String value = (String) field.get(null);
System.out.println(value);
// 2. 修改 private static final 字段
field.set(null, "setTest");
System.out.println(field.get(null));
}
//staticFinalField
//Exception in thread "main" java.lang.IllegalAccessException: Can not set static final java.lang.String field
注释 : 反射
final static
修饰的变量, 可以读取,但是无法修改. 修改时,将抛出Can not set static final int field
异常.
反射操作方法
- 操作私有方法
Class klass = Class.forName("reflection.Ref");
Object object = klass.newInstance();
// 操作私有方法
// 传入方法名, 和 方法参数类型
Method method = klass.getDeclaredMethod("setField", String.class);
method.setAccessible(true);
// 执行方法, 传入实例 和 方法参数值, 获取到方法返回值
String result = (String) method.invoke(object, "set field test");
System.out.println(result);
// set field test
- 操作静态方法
Class klass = Class.forName("reflection.Ref");
// 私有静态方法
method = klass.getDeclaredMethod("setStaticField", String.class);
method.setAccessible(true);
result = (String) method.invoke(null, "set static field");
System.out.println(result);
// set static field
注释 : 静态方法无需传入实例, 直接传
null
.
- 获取所有的方法
// 获取所有方法
// getMethods() 获取所有的公共方法,包含父类的公共方法
// getDeclaredMethods() 获取所有的方法,包括私有方法, 但不包括继承的方法
Method[] methods = klass.getDeclaredMethods();
for (Method m : methods) {
// 获取返回类型
Class returnType = m.getReturnType();
// 获取参数类型
Class[] params = m.getParameterTypes();
}
对泛型的反射
反射创建泛型对象
public class Generic {
private T t;
private Generic(T t) {
this.t = t;
}
public T getT() {
return t;
}
}
private static void newGenericTest() throws Exception {
Class klass = Class.forName("reflection.Generic");
// 由于泛型擦除的原因, 最终会被转为Object
// 所以构造函数传入 Object类对象
Constructor ctor = klass.getDeclaredConstructor(Object.class);
ctor.setAccessible(true);
// 由于Object是所有类的父类,所以这里可以传入任意对象的实例.
String ctorArg = "android";
Object instance = ctor.newInstance(ctorArg);
// 转化为目标对象,对应的目标泛型类型
Generic generic = (Generic) instance;
System.out.println(generic.getT());
// android
}
由于泛型擦除的原因,传入的泛型只能用 Object
代替. (T extends Parent
,这种形式,则由Parent
代替.)
泛型数据类型判断
根据 获取 类的 Type
可以判断是否是泛型变量,并且判断是否可以直接实例化.
public static void typeGuess(Type type) {
if (type instanceof ParameterizedType) {
// 表示参数化类型变量
// 如 Map
// 此方法可以获取, 其中参数的类型
Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments();
} else if (type instanceof GenericArrayType) {
// 表示泛型类型的数组
// 如 T[]
// 如 Map[]
} else if (type instanceof TypeVariable) {
// 泛型类型变量, 无法直接实例化, 但可以用 Object将其实例化
// 如 T
} else if (type instanceof WildcardType) {
// 通配符类型, 如果直接实例化
// 如 List
} else if (type instanceof Class) {
// 普通类型,可以直接实例化
// 如 String
}
}
获取泛型类型的方式
public static void getGenericType() throws Exception {
Class klass = XXX.class;
// 1. 类变量
Field field = klass.getDeclaredField("xxx");
// 获取泛型的类型
Type type = field.getGenericType();
// 2. 方法的返回值
Method method = klass.getDeclaredMethod("xxx");
type = method.getGenericReturnType();
// 3. 方法的参数
Type[] types = method.getGenericParameterTypes();
// 4. 构造函数的参数
Constructor ctor = klass.getDeclaredConstructor();
types = ctor.getGenericParameterTypes();
// 判断参数类型
typeGuess(type);
}