BruceFan's Blog

Stay hungry, stay foolish

0%

Java反射机制

反射机制

反射是Java语言的一个特性,是Java被视为动态语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括modifiers(如public,static等)、superclass(如Object)、实现之interfaces(例如Cloneable),也包括fields和methods的所有信息,并可于运行时改变fields内容或唤起methods。Java可以加载一个运行时才得知名称的class,获得其完整结构。
A Simple Example

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.lang.reflect.*;
public class DumpMethods {
public static void main(String args[]) {
try {
Class c = Class.forName(args[0]);
Method m[] = c.getDeclaredMethods();
for (int i = 0; i < m.length; i++)
System.out.println(m[i].toString());
} catch (Throwable e) {
System.err.println(e);
}
}
}

输出结果

1
2
3
4
5
6
$ java DumpMethods java.util.Stack
public java.lang.Object java.util.Stack.push(java.lang.Object)
public synchronized java.lang.Object java.util.Stack.pop()
public synchronized java.lang.Object java.util.Stack.peek()
public boolean java.util.Stack.empty()
public synchronized int java.util.Stack.search(java.lang.Object)

列出了java.util.Stack类的所有方法名和签名。
这个程序用class.forName加载特定的类,然后调用getDeclaredMethods取出类中定义的方法列表。java.lang.reflect.Method是一个代表类方法的类。

JDK中提供的Reflection API

Interfaces
AnnotatedElement
GenericArrayType
GenericDeclaration
InvocationHandler
Member
ParameterizedType
Type
TypeVariable
WildcardType
Classes
AccessibleObject
Array
Constructor
Field
Method
Modifier
Proxy
ReflectPermission
Exceptions
InvocationTargetException
MalformedParameterizedTypeException
UndeclaredThrowableException
Errors
GenericSignatureFormatError

Java反射机制提供的功能

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时调用任一个对象的方法
  • 在运行时创建新类对象

在使用Java的反射功能时,基本首先都要获取类的Class对象,再通过Class对象获取其他的对象。
用于测试的类:

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class Type {
public int pubIntField;
public String pubStringField;
private int prvIntField;

public Type() {
Log("Default Constructor");
}
Type(int arg1, String arg2) {
pubIntField = arg1;
pubStringField = arg2;
Log("Constructor with parameters");
}
public void setIntField(int val) {
this.prvIntField = val;
}
public int getIntField() {
return prvIntField;
}
private void Log(String msg) {
System.out.println("Type:" + msg);
}
}

class ExtendType extends Type {
public int pubIntExtendField;
public String pubStringExtendField;
private int prvIntExtendField;

public ExtendType() {
Log("Default Constructor");
}
ExtendType(int arg1, String arg2) {
pubIntExtendField = arg1;
pubStringExtendField = arg2;
Log("Constructor with parameters");
}
public void setIntExtendField(int field7) {
this.prvIntExtendField = field7;
}
public int getIntExtendField() {
return prvIntExtendField;
}
private void Log(String msg) {
System.out.println("ExtendType:" + msg);
}
}

1.获取类的Class对象
Class类的实例表示正在运行的Java应用程序中的类和接口。

  • 调用getClass
    1
    2
    3
    Boolean var1 = new Boolean("true");
    Class classType1 = var1.getClass();
    System.out.println(classType1);
  • 运用.class语法
    1
    2
    Class classType2 = Boolean.class;
    System.out.println(ClassType2)
  • 运用static method Class.forName()
    1
    2
    Class classType3 = Class.forName("java.lang.Boolean");
    System.out.println(classType3);
  • 运用primitive wrapper classes的TYPE语法(这里返回的是原生类型,和Boolean.class返回的不同)
    1
    2
    Class classType4 = Boolean.TYPE;
    System.out.println(classType4);

2.获取类的Fields
可以通过反射机制得到某个类的某个属性,然后改变对应于这个类的某个实例的该属性值。Java的Class类提供了几个方法获取类的属性。

  • public Field getField(String name)
    返回一个Field对象,它反映此Class对象所表示的类或接口的指定public成员字段。
  • public Field[] getFields()
    返回Field对象的数组,这些对象反映此Class对象所表示的类或接口的所有可访问public字段。
  • public Field getDeclaredField(String name)
    返回一个Field对象,它反映此Class对象所表示的类或接口的指定已声明字段。
  • public Field[] getDeclaredFields()
    返回Field对象的数组,这些对象反映出此Class对象所表示的类或接口声明的所有字段。
1
2
3
4
5
6
7
8
9
10
Class classType = ExtendType.class;
Field[] fields = classType.getFields();
for (int i = 0; i < fields.length; i++) {
System.out.println(fields[i]);
}
System.out.println();
fields = classType.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
System.out.println(fields[i]);
}

可见getFields和getDeclaredFields区别:
getFields返回的是声明为public的属性,包括父类中定义,
getDeclaredFields返回的是指定类定义的所有定义的属性,不包括父类的。

3.获取类的Method
通过反射机制得到某个类的某个方法,然后调用对应于这个类的某个实例的该方法。Class类提供了几个获取类方法的方法。

  • public Method getMethod(String name, Class<?>... parameterTypes)
    返回一个Method对象,它反映此Class对象所表示的类或接口的指定public成员方法。
  • public Method[] getMethods()
    返回Method对象的数组,这些对象反映此Class对象所表示的类或接口的public成员方法,包括继承的方法。
  • public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
    返回一个Method对象,该对象反映此Class对象所表示的类或接口的指定已声明方法。
  • public Method[] getDeclaredMethods()
    返回Method对象的数组,这些对象反映此Class对象表示的类或接口声明的所有方法,但不包括继承的方法。
1
2
3
4
5
6
7
8
9
10
Class classType = ExtendType.class;
Method[] methods = classType.getMethods();
for (int i = 0; i < methods.length; i++) {
System.out.println(methods[i]);
}
System.out.println();
methods = classType.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
System.out.println(methods[i]);
}

4.获取类的Constructor
通过反射机制得到某个类的构造器,然后调用该构造器创建该类的一个实例,Class类提供了几个方法获取类的构造器。

  • public Constructor<T> getConstructor(Class<?>... parameterType)
    返回一个Constructor对象,它反映此Class对象所表示的类的指定public构造方法。
  • public Constructor<?>[] getConstructors()
    返回Constructor对象的数组,这些对象反映此Class对象所表示的类的所有public构造方法。
  • public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
    返回一个Constructor对象,它反映此Class对象所表示的类或接口的指定构造方法。
  • public Constructor<?>[] getDeclaredConstructors()
    返回Constructor对象的数组,这些对象反映此Class对象表示的类声明的所有构造方法。
1
2
3
4
5
6
7
8
9
Constructor[] constructors = classType.getConstructors();
for (int i = 0; i < constructors.length; i++) {
System.out.println(constructors[i]);
}
System.out.println();
constructors = classType.getDeclaredConstructors();
for (int i = 0; i < constructors.length; i++) {
System.out.println(constructors[i]);
}

5.新建类的实例
通过反射机制创建新类的实例,有几种方法可以创建。

  • 调用无自变量ctor

(1)调用类的Class对象的newInstance方法,该方法会调用对象的默认构造函数,如果没有默认构造函数,会调用失败。

1
2
3
Class classType = ExtendType.class;
Object inst = classType.newInstance();
System.out.println(inst);

(2)调用默认Constructor对象的newInstance方法

1
2
3
4
Class classType = ExtendType.class;
Constructor ctor = classType.getConstructor(null);
Object inst = ctor.newInstance(null);
System.out.println(inst);
  • 调用带参数ctor

(3)调用带参数Constructor对象的newInstance方法

1
2
3
4
5
6
Class classType = ExtendType.class;
Class[] params = {int.class, String.class}; // 可以使用匿名类,下一个例子中可以看到
Constructor ctor = classType.getDeclaredConstructor(params);
Object[] arg = {new Integer(1), "123"};
Object inst = ctor.newInstance(arg);
System.out.println(inst);

6.调用类的函数
通过反射获取类Method对象,调用其Invoke方法调用此函数。

1
2
3
4
5
6
7
8
9
Class classType = ExtendType.class;
Object inst = classType.newInstance();
Method logMethod = classType.getDeclaredMethod("Log", new Class[] {String.class});
logMethod.setAccessible(true);
logMethod.invoke(inst, new Object[] {"test"});
// 输出
Type:Default Constructor
ExtendType:Default Constructor
ExtendType:test

7.设置/获取类的属性值
通过反射获取类的Field对象,调用Field方法设置或获取值。

1
2
3
4
5
6
Class classType = ExtendType.class;
Object inst = classType.newInstance();
Field intField = classType.getField("pubIntExtendField");
intField.setInt(inst, 100);
int value = intField.getInt(inst);
System.out.println(value);