反射

Catalogue   

反射(Reflection)的作用:

  • 反射让开发人员可以通过外部类的全路径名创建对象,并使用这些类,实现一些扩展的功能。
  • 反射让开发人员可以枚举出类的全部成员,包括构造函数、属性、方法。以帮助开发者写出正确的代码。
  • 测试时可以利用反射API访问类的私有成员,以保证测试代码覆盖率。

API

获取Class对象

1
2
3
4
5
6
7
8
9
10

//获取Class的方法

Class clz = Class.forName("java.lang.String");

Class clz = String.class;

String str = new String("Hello");
Class clz = str.getClass();

创建对象

如果默认无参构造函数是public,则可以直接使用class.newInstance(),而如果没有,则需要使用getDeclaredConstructor。

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

class Rectangle {
public int x;
public int y;
}

class User {
private String name;
private int age;

public User(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

public class TestPerformance {

public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {

Class c1 = Rectangle.class;
c1.newInstance();

Class user = User.class;
//需要填充参数
user.getDeclaredConstructor(String.class, int.class).newInstance("zhangsan", 19);
}

}

获取属性、方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//获取方法
Method[] methods = user.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName());
}

//获取属性
Field[] fields = user.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName());
}

//调用方法
User user1 = new User("lisi", 20);
Method method = user1.getClass().getMethod("setAge", int.class);
method.setAccessible(true); //如果是私有方法,需要执行这样语句
method.invoke(user1, 88);
System.out.println(user1);

这里需要注意的是,getDeclaredFields方法可以拿到私有属性,而getFields不行。

性能分析

虽然反射功能很强大,但在使用过程中要注意效率问题。因为:

  • 因为接口的通用性,Java的invoke方法是传object和object[]数组的。基本类型参数需要装箱和拆箱,产生大量额外的对象和内存开销,频繁促发GC。
  • 编译器难以对动态调用的代码提前做优化,比如方法内联。
  • 反射需要按名检索类和方法,有一定的时间开销。

我们可以进行简单的测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Class c1 = User.class;
long duration = 0;
duration = System.currentTimeMillis();
for(int i=0; i<999999999;i++) {
c1.newInstance();
}
long current = System.currentTimeMillis();
System.out.println(current - duration);

for(int i=0; i<999999999;i++) {
new User("zhangsan", 20);
}
System.out.println(System.currentTimeMillis() - current);

以上代码的输出结果,两者差距有近千倍!!!

参考