Java 反射机制

什么是反射?

Java 的反射机制可以使得程序在 Java 运行时能够检测、访问并且修改编译时完全未知的 classes,这个概念很容易和内省混淆。下面是 WiKi 上对这两个概念给出的定义:

反射(Reflection):反射是指计算机程序在运行时可以访问、检测和修改它本身状态或行为的一种能力。
内省(Introspection):内省是指计算机程序在运行检查对象类型的一种能力,通常也可以称作运行时类型检查

从上面的定义可以看出,相对于内省,反射更进一步。例如,Java 具有反射特性,而 C++ 不具有反射特性只具有内省特性。

反射有什么用途?

反射主要提供以下功能:

  1. 在运行时检查对象的类型

  2. 在运行时新建类的实例

  3. 在运行时检测对象一个类的成员变量和方法

  4. 在运行时调用对象的任意方法

  5. 在运行时修改构造函数、成员变量和方法的访问权限

  6. 等等

例如,可以使用反射获得或修改一个对象的私有成员变量,调用对象的私有方法。而要在Java中使用反射,需要用到 java.lang.reflect 包中的下面几个类:

  1. Class 类:java.lang.Class, 代表类的定义。

  2. Construcor 类:java.lang.reflect.Constructor,代表类的构造函数。

  3. Field 类:java.lang.reflect.Field,代表类的成员变量。

  4. Method 类:java.lang.reflect.Method,代表类的方法。

  5. Array 类:java.lang.reflect.Array,提供动态创建访问数组的静态方法

注:如果要反射内部类,Class.forName()时类名需要写为 “packageName.className$innerClassName”

反射的使用例子

新建类的实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* 根据类名和构造函数的参数新建一个对象实例,只能通过 public 的构造函数新建实例。
* @param className 类名
* @param args 构造函数的参数
* @param argClasses 构造函数的参数的 class type
* @return 该类的对象实例
*/
public static Object newInstance(String className, Object[] args, Class<?>[] argClasses) {
Object instance = null;
try {
Class classObj = Class.forName(className);
Constructor cons = classObj.getConstructor(argClasses);
instance = cons.newInstance(args);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return instance;
}

获得类的静态属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 获得类的静态属性值,不管是 public 或者 private 的都可以。
* @param className 类名
* @param fieldName 属性名
* @return 该类的静态属性值
*/
public static Object getStaticField(String className, String fieldName) {
Object fieldValue = null;
try {
Class classObj = Class.forName(className);
Field field = classObj.getDeclaredField(fieldName);
field.setAccessible(true);
fieldValue = field.get(null); // note: Field.get(Object obj), If this field is static, the object argument is ignored.
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return fieldValue;
}

修改类的静态属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 修改类的静态属性值,不管是 public 或者 private 的都可以。
* @param className 类名
* @param fieldName 属性名
* @param value 修改后的属性值
* @return true,如果修改成功; false,如果修改失败
*/
public static boolean setStaticField(String className, String fieldName, Object value) {
boolean result = false;
try {
Class classObj = Class.forName(className);
Field field = classObj.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(null, value);
result = true;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return result;
}

获得对象的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 获得对象的属性值,不管是 public 或者 private 的都可以,
* 但是该方法只能获得在该对象的类中所定义的成员变量的值,不能获得其父类的成员变量的值。
* @param owner 对象
* @param fieldName 对象所属类的属性名
* @return 对象所属类中的属性值
*/
public static Object getField(Object owner, String fieldName) {
Object fieldValue = null;
try {
Class classObj = owner.getClass();
Field field = classObj.getDeclaredField(fieldName);
field.setAccessible(true);
fieldValue = field.get(owner);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return fieldValue;
}

获得对象所属类的父类中的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 获得对象的属性值(属性是对象的父类中定义的),不管是 public 或者 private 的都可以。
* @param owner 对象
* @param className 对象所属类的父类的类名
* @param fieldName 对象所属类的父类的属性名
* @return 对象所属类的父类的属性值
*/
public static Object getField(Object owner, String className, String fieldName) {
Object fieldValue = null;
try {
Class classObj = Class.forName(className);
Field field = classObj.getDeclaredField(fieldName);
field.setAccessible(true);
fieldValue = field.get(owner);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
return fieldValue;
}

修改对象的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 修改对象的属性值,不管是 public 或者 private 的都可以,
* 但是该方法只能修改在该对象的类中所定义的成员变量的值,不能修改其父类的成员变量的值。
* @param owner 对象
* @param fieldName 对象所属类的属性名
* @param value 修改后的属性值
* @return true,如果修改成功; false,如果修改失败
*/
public static boolean setField(Object owner, String fieldName, Object value) {
boolean result = false;
try {
Class classObj = owner.getClass();
Field field = classObj.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(owner, value);
result = true;
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return result;
}

修改对象所属类的父类中的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* 修改对象的属性值(属性是对象的父类中定义的),不管是 public 或者 private 的都可以。
* @param owner 对象
* @param className 对象所属类的父类的类名
* @param fieldName 对象所属类的父类的属性名
* @param value 修改后的属性值
* @return true,如果修改成功; false,如果修改失败
*/
public static boolean setField(Object owner, String className, String fieldName, Object value) {
boolean result = false;
try {
Class classObj = Class.forName(className);
Field field = classObj.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(owner, value);
result = true;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
return result;
}

调用类的静态方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* 调用类的静态方法,不管是 public 或者 private 的都可以。
* @param className 类名
* @param methodName 静态方法名
* @param args 方法的参数
* @param argClasses 方法的参数的 class type
* @return 静态方法的返回值
*/
public static Object invokeStaticMethod(String className, String methodName, Object[] args, Class<?>[] argClasses) {
Object result = null;
try {
Class classObj = Class.forName(className);
Method method = classObj.getDeclaredMethod(methodName, argClasses);
method.setAccessible(true);
result = method.invoke(null, args);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return result;
}

调用对象的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* 调用对象的方法,不管是 public 或者 private 的都可以。
* 但是该方法只能调用对象所属类中定义的方法,不能调用其父类中定义的方法。
* @param owner 对象
* @param methodName 对象所属类的方法名
* @param args 方法的参数
* @param argClasses 方法的参数的 class type
* @return 方法的返回值
*/
public static Object invokeMethod(Object owner, String methodName, Object[] args, Class<?>[] argClasses) {
Object result = null;
try {
Class classObj = owner.getClass();
Method method = classObj.getDeclaredMethod(methodName, argClasses);
method.setAccessible(true);
result = method.invoke(owner, args);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return result;
}

调用对象所属类的父类的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* 调用对象的方法(方法是在对象所属类的父类中定义的),不管是 public 或者 private 的都可以。
* 如果这个方法在子类中重写的话,实际上还是调用子类的重写方法
* @param owner 对象
* @param className 对象所属类的父类的类名
* @param methodName 对象所属类的父类的方法名
* @param args 方法参数
* @param argClasses 方法的参数的 class type
* @return 方法的返回值
*/
public static Object invokeMethod(Object owner, String className, String methodName, Object[] args, Class<?>[] argClasses) {
Object result = null;
try {
Class classObj = Class.forName(className);
Method method = classObj.getDeclaredMethod(methodName, argClasses);
method.setAccessible(true);
result = method.invoke(owner, args);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return result;
}

获得对象的类

这里要区分基本类型与其对应的包装类是不一样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* first of all, int.class != Integer.class. but int.class == Integer.TYPE
* the is similarly to other primitive type, such as float, boolean, double, byte, char, short, long.
*
* ---------------------------------
* int a = 12;
* Object[] args = new Object[]{a};
* ---------------------------------
*
* in last code block, args[0].class is Integer.class because JVM cast a to Integer type in Java.
* in that case, this getClassTypes method will return wrong so if args contains primitive type(like int, boolean) you should not use this method.
*
* @param args
* @return the class type of args
*/
public static Class<?>[] getClassTypes(Object[] args) {
if(null == args || args.length == 0) {
return null;
}
Class<?>[] argClasses = new Class<?>[args.length];
for(int i = 0; i < args.length; i ++){
argClasses[i] = args[i].getClass();
}
return argClasses;
}

新建一个数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 新建一个类型的数组
* @param className 数组的类型的类名
* @param len 数组的大小
* @return 数组
*/
public static Object newArrayInstance (String className, int len) {
Object result = null;
try {
Class classObj = Class.forName(className);
result = Array.newInstance(classObj, len);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return result;
}

获得数组的元素

1
2
3
4
5
6
7
8
9
/**
* 获得数组中的一个元素
* @param array 数组
* @param index 元素在数组中的位置
* @return 数组中的这个元素
*/
public static Object getObjectInArray(Object array, int index) {
return Array.get(array, index);
}

修改数组的元素

1
2
3
4
5
6
7
8
9
/**
* 修改数组中的一个元素的值
* @param array 数组
* @param index 元素在数组中的位置
* @param value 修改后的元素值
*/
public static void setObjectInArray(Object array, int index, Object value) {
Array.set(array, index, value);
}

最后给出我个人的源代码 http://johnnyshieh.me/uploads/ReflectionUtils.java

参考链接:

END
Johnny Shieh wechat
我的公众号,不只有技术,还有咖啡和彩蛋!