前言
能够分析类能力的程序称为反射。Java中反射机制十分强大,反射机制可以用来:
•在运行时分析类的能力。
•在运行时查看对象,例如,编写一个 toString 方法供所有类使用。
•实现通用的数组操作代码。
•利用Method对象,通过Method对象调用运行时类的方法。
•以及在SSM框架等注入JavaBean对象等等操作
一、Class类
使用反射,必须要了解什么是Class类。
在运行时系统始终为所有对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。而保存这些信息的类称为Class。这个Class类的实例对象可以提供对应类本身的信息,比如有几种构造方法,有多少数据域,有什么行为方法等。
看代码(编码过程中有些方法的使用需要捕获异常,为了方便阅读我全部都抛出去了):
1. /**
2. * 现在有一个测试类,其中有数据域和行为方法
3. */
4. public class ReflectiveTest {
5. private String name;
6. private int data;
7. public ReflectiveTest() {}
8. public ReflectiveTest(String name, int data) {
9. this.name = name;
10. this.data = data; }
11. public String getName() {
12. return name;}
13. public void setName(String name) {
14. this.name = name; }
15. public int getData() {
16. return data;}
17. public void setData(int data) {
18. this.data = data;}
19. }
main方法中:
20. //创建测试类的对象
21. ReflectiveTest t = new ReflectiveTest();
22. /**
23. * 获取Class实例对象有几种方法:
24. * 1.是通过类对象.getClass方法
25. * 2.是通过类名.class
26. * 3.是通过Class.forName(类名),
27. * 如果类名保存在字符串中,并可在运行中改变,就可以使用这个方法获取
28. * 第三种方法可能会抛ClassNotFoundException(没有这个类)异常,这里我直接抛出去了
29. */
30. Class clazz = t.getClass();
31. Class clazz2 = ReflectiveTest.class;
32. Class clazz3 = Class.forName("ReflectiveTest");
33. //这个几个Class实例对象就保留了ReflectiveTest类中所有的信息
请注意,一个Class对象实际上表示的是一个类型,而这个类型未必一定是一种类。例如,int不是类, 但int.class是一个Class类型的对象。
Class类常用的方法有getName,用于返回类名:
/**
2. * 一个 Class 对象将表示一个特定类的属性。最常用的Class方法是getName()。这个方法将返回类的名字
3. * 如果类在一个包里,那么包的名字也会作为类的名字一起出现(这里就不演示了)
4. */
5. System.out.println(clazz.getName());//返回类的名字:ReflectiveTest
Class类还有一个常用的方法是newInstance()方法,这个方法能够动态的根据运行时类的空参构造器创造一个类对象,也可以使用 Constructor 类中的 newlnstance 方法调用对应的有参构造方法:
1. /**
2. * 一个 Class 对象将表示一个特定类的属性。最常用的Class方法是getName()。这个方法将返回类的名字
3. * 如果类在一个包里,那么包的名字也会作为类的名字一起出现(这里就不演示了)
4. */
5. System.out.println(clazz.getName());//返回类的名字:ReflectiveTest
6.
7. /**
8. * Class常用方法:newInstance():调用空参构造器创建对应类对象
9. * 如果想要调用有参构造器,必须先获取Constructor类对象,然后再调用对应的newInstance方法
10. */
11. Object o = clazz.newInstance();//无参构造
12. //返回一个ReflectiveTest类对象(Object是所有类的父类,因此可以用Object类引用指向该对象)
13. //Class.getConstructors()按顺序返回类中所有构造器方法,下标从0开始
14. Constructor[] constructor = clazz.getConstructors();
15. //根据有参构造器创建一个ReflectiveTest类对象,name="test1",data=1
16. Object test1 = constructor[1].newInstance("test1", 1);
二、利用反射检查类的结构
Java反射类库中有三个类Field、Method和Constructor分别用于描述类的数据域、方法和构造器。这三个类都有一个叫做getName的方法,用来返回对应类的名称,同时还有一些比较常用的方法等。看代码:
1. /**
2. * Field、Method、Constructor对象的获取和一些常用方法的使用
3. */
4. //返回类中的定义的所有数据域(包括私有),返回顺序不一定按照定义顺序
5. Field[] fields = clazz.getDeclaredFields();
6. //返回类中定义的所有方法,返回顺序不一定按照定义顺序
7. Method[] methods = clazz.getDeclaredMethods();
8. //返回类中定义的所有构造方法,返回顺序不一定按照定义顺序
9. Constructor[] constructors = clazz.getConstructors();
10. //Field.getType方法,用来返回描述域所属类型的Class对象
11. System.out.println(fields[0].getType());//输出第一个数据与的类型名称:class java.lang.String(String类型)
12.
13. for (Method method : methods) {
14. System.out.println(method.getReturnType());
15. //这个方法是返回的是Method对象的泛型参数,如果没有泛型,则返回长度为0的数组
16. //这个类中没有使用泛型的方法,所以全部都是长度为0的数组
17. TypeVariable[] typeParameters = method.getTypeParameters();
18. System.out.println(typeParameters.length);
19. for (int j = 0;j < typeParameters.length;j++){
20. System.out.print(typeParameters[j].getName()+" ");
21. }
22. System.out.println();
23. }
24.
25. /**
26. * 这三个类还有一个叫做 getModifiers 的方法,它将返回一个整型数值,
27. * 用不同的位开关描述public和static等这样的修饰符使用状况
28. * 例如, 可以使用 Modifier 类中的 isPublic、 isPrivate 或 isFinal
29. * 判断方法或构造器是否是 public、 private 或 final。 我们需要做的全部工作就是调用 Modifier
30. * 类的相应方法,并对返回的整型数值进行分析,另外,还可以利用 Modifier.toString方法将
31. * 修饰符打印出来
32. */
33. System.out.println(fields[0].getModifiers());
34. System.out.println(Modifier.isPrivate(fields[0].getModifiers()));
35. System.out.println(methods[0].getModifiers());
36. System.out.println(Modifier.isPublic(methods[0].getModifiers()));
在通过Field或者Method等方法获取类信息时,如果是类中私有的属性或者方法,使用前必须通过setAccessible方法,如x. setAccessible(true)调用方法之后,才能够使用类中私有的方法和属性,否则会报错。
三、调用任意方法
在上面的讨论中,我们知道了Method可以获取类中定义的方法,但是如果调用类中的这些方法呢?我们可以使用Method类提供的invoke函数来调用对应的方法。
Invoke函数签名如下:
Object invoke(Object obj, Object… args)
第一个参数obj是隐式参数,其余的对象(可变长参数args)提供了显式参数。(在Java SE 5.0以前的版本中,必须传递一个对象数组,如果没有显式参数就传递一个null)。对于静态方法,第一个参数可以被忽略,即可以将它设置为null。
1. //调用setName方法重新设置名字
2. methods[2].invoke(t,"test2");
3. System.out.println(t.getName());//输出test2
总结
这篇文章只记录了我初学反射时个人的一些见解。有些方法和概念还得要参考api文档以及其他资料等。