一. 简介
反射(Reflection) 是⼀种运⾏时检视(Inspect)类型信息并且修改属性,调⽤⽅法的机制.它常常⽤于如下⼀些场景中
- 运⾏时实例化类的对象,⽐如实例化⼀个Servlet类,此Servlet类的名字容器预先是不知道的
- 运⾏时调⽤⽅法,⽐如调⽤Servlet对象的service⽅法
- 运⾏时修改属性值.⽐如Apache DbUtils⼯具库可以把数据库的⼀条记录默认依据名称相同的策略赋值⼀个对象的字段
JDK中关于反射的相关类型都在java.lang.reflect包下,并不需要额外的第三⽅包来完成反射的⼯作.2. Class对象
二.Class对象
Class对象就是⼀个类被加载到jvm中后的运⾏时表现.⽽反射的⾸要任务就是得到Class对象.得到Class对象的⽅法如下
- Class.forName(“类的全称”)
- 类加载器获取
- 类的class字段
- 对象的getClass⽅法
2.1. Class.forName()
这种⽅法适合只知道类的字符串表示,也就是全称的情况.如果类还没有加载它还会加载此类,如果已经加载了就会返回已加载的class对象给你,⽐如:
Class personCls = Class.forName("com.Person");
2.2. 类的class字段
这种⽅式是此类已经预先知道的情况下就⾮常合适,⽐如下⾯的代码
Class personCls = Person.class;
Class integerCls = Integer.class;
2.2.1. 基本类型class对象
对于8个基本类型想获取其class对象信息除了可以⽤class字段的⽅式以外还可以利⽤其对应包装类型的TYPE字段来获取,⽐如:
Class intCls = int.class;
Class intCls = Integer.TYPE;
2.3. 对象的getClass⽅法
这种⽅式适合于已经有此类对象的情况下来获取类的class对象信息,⽐如下⾯的代码
Person p = new Person();
Class personCls = p.getClass();
2.4. Void类对象
java⽅法的返回类型中有⼀个特殊的值就是void,其⽤java.lang.Void类来代表.所以获取此特殊的Class对象就⽤Void的class字段或TYPE字段来实现
Class<Void> clazz = Void.class;
Class<Void> clazz = Void.TYPE;
三. Class对象的基本操作
有了Class对象之后,我们就可以利⽤它来获取各种各样的信息,主要可以获取的信息有如下⼀些
获取类的名字
- 类的⽗类类的修饰符
- 类实现的所有接
- 类的构造函数
- 类的所有字段
- 类的所有⽅法
3.1. 获取类的名字
类的名字分为简称和全称,⽐如下⾯的类其简称为Person,全称为com.Person
package com;
public class Person{
}
3.2. 获取类的修饰符
通过Class对象的getModifiers⽅法获取类的修饰符,此⽅法返回的是⼀个整数,然后依赖Modifier类的⼀系列⽅法来分析getModifiers⽅法返回整数的含义.
int modifier = clazz.getModifiers();
boolean isPublic = Modifier.isPublic(modifier);
boolean isAbs = Modifier.isAbstract(modifier);
3.3. 获取包的信息
可以通过Class对象对的getPackage()⽅法获取类的包信息,此⽅法返回的是Package类型,通过此Package类型就可以得到包的名字,⽐如:
Package pkg = clazz.getPackage();
String pkgName = pkg.getName();
3.4. 获取⽗类信息
获取⽗类信息主要是靠getSuperClass()⽅法实现,如果当前的Class对象代表的是Object类型,接⼝类型,void类型,基本类型,那么此⽅法返回null值
Class<?> superClazz = clazz.getSuperclass();
String superClassName = superClazz.getSimpleName();
3.5. 获取实现的接⼝
可以通过getInterfaces⽅法获取实现或继承的接⼝信息.如果当前的Class对象代表的是⼀个类,那么此⽅法得到是此类声明实现的所有接⼝信息,不包含其⽗类实现的接⼝信息.返回的数组中按声明的顺序排序.如果没有实现接⼝就返回⻓度为0的数组.
如果当前的Class对象代表的是⼀个接⼝,那么此⽅法返回的是此接⼝extends的所有接⼝信息,返回的数组中按照声明的接⼝顺序排序.如果没有继承任何接⼝,返回的数组是⼀个⻓度为0的数组.
如果当前的Class对象代表的是void或者基本类型,此⽅法返回⻓度为0的数组.
如果当前的Class对象代表的是数组类型,那么返回的是Cloneable和Serializable
Class<?>[] personInterfaces = clazz.getInterfaces();
3.6. 获取构造函数
通过getConstructors⽅法可以获取类的所有构造函数.⽐如下⾯的⽅法
Constructor<?>[] constructors = clazz.getConstructors();
3.7. 获取字段
字段分为类⾃⼰声明(Declare)的和从⽗类型继承过来的字段,想得到所有的字段(不区分是继承的还是⾃⼰声明的)就通过getFields⽅法,通过getDeclaredFields()⽅法可以获取此Class对象代表的类声明的所有字段,不包括继承过来的字段.⽐如下⾯的代码
Field[] fields = clazz.getDeclaredFields();
上⾯的⽅法可以得到任意修饰符的字段,静态与实例字段可以得到,public,private等访问修饰符的字段也可以得到.如果想得到某⼀个具体名字的字段可以通过getDeclaredFields(String name)⽅法获取
Field f = clazz.getDeclaredFields("字段名");
如果Class对象代表的类型没有字段或者代表的是数组类型,基本数据类型或者void类型会返回⼀个⻓度为0的数组.
返回数组中的元素并没有进⾏排序,并且并没有特定的顺序,⽐如按照声明的顺序,所以你的代码不能依赖反射中得到的字段的顺序来编写逻辑.
⽽getFields⽅法会返回⾃身和继承过来的所有public字段.并不包含其它修饰符的字段.
3.8. 获取⽅法
⽅法也分为类⾃⼰声明(Declare)的和从⽗类型继承过来的.可以通过getDeclaredMethods()⽅法获取此Class对象代表的类声明的所有⽅法,通过getMethods()⽅法获取所有的⽅法.⽐如下⾯的代码
Method[] methods = clazz.getDeclaredMethods();
如果想获得特定的⽅法,就需要传递⽅法名与参数类型,因为⽅法有重载.⽐如下⾯的代码表示取得只有⼀个String类型参数的⽅法doSth.
Method method = clazz.getDeclaredMethods("doSth",String.class)
getDeclaredMethods的第⼆个参数是可变⻓度的,因为⽅法的参数可以有多个.
如果⼀个类只有静态代码块,那么getDeclaredMethods返回的数组中不包括这个静态代码块.
如果Class对象代表的类型没有⽅法或者代表⼀个数组,基本类型,void类型,那么返回的是⻓度为0的数组.
返回数组中的元素并没有进⾏排序,并且并没有特定的顺序,⽐如按照声明的顺序,所以你的代码不能依赖反射中得到的⽅法的顺序来编写逻辑.
⽽getMethods⽅法会返回⾃身和继承过来的所有public⽅法.并不包含其它修饰符的⽅法.