反射
Java反射机制可以让我们在编译期(Compile Time)之外的运行期(Runtime)检查类,接口,变量以及方法的信息。反射还可以让我们在运行期实例化对象,调用方法,通过调用get/set方法获取变量的值。
通过反射API可以获取程序在运行时刻的内部结构,知道了Java类的内部 结构之后,就可以与它进行交互,包括创建新的对象和调用对象中的方法等。这种交互方式与直接在源代码中使用的效果是相同的,但是又额外提供了运行时刻的灵活性。
反射API中提供的动态代理也是非常强大的功能,可以原生实现AOP中的方法拦截功能。
使用反射的一个最大的弊端是性能比较差。相同的操作,用反射API所需的时间大概比直接的使用要慢一两个数量级。不过现在的JVM实现中,反射操作的性能已经有了很大的提升。在灵活性与性能之间,总是需要进行权衡的。应用可以在适当的时机来使用反射API。
一、动态获取类的信息
1.1 获取类的Class对象
所有类型,包括基本类型、数组,都有与之关联的Class类的对象。
1)编译期已知类的名字,通过.class获取
Class myObjectClass = MyObject.
class
;
2)编译期未知类的名字,但可在运行期获得类名的字符串,通过字符串获取
Class
clazz
= Class.forName(className);
在使用Class.forName()方法时,你必须提供一个类的全名,这个全名包括类所在的包的名字。
如果在调用Class.forName()方法时,没有在编译路径下(classpath)找到对应的类,那么将会抛出ClassNotFoundException。
1.2 获取类名
全限定类名:
String className = clazz.getName();
类的名字:
String simpleClassName = clazz.getSimpleName();
1.3 获取修饰符
int modifiers = clazz.getModifiers(); //类只有public和包访问权限
每个修饰符都是一个位标识。0表示包访问,1表示public, 2表示private,4表示protected
可使用Modifier类的静态方法来判断是否是期望的访问权限。
1.4 包信息
Package packg = clazz.getPackage();
1.5 父类
Class superclass = clazz.getSuperclass();
1.6 实现的接口集合
Class[] interfaces = clazz.getInterfaces();
注意:getInterfaces()方法仅仅只返回当前类所实现的接口。当前类的父类如果实现了接口,这些接口是不会在返回的Class集合中的,尽管实际上当前类其实已经实现了父类接口。
1.7 构造器
Constructor[] constructors = clazz.getConstructors();
1.8 方法
Method[] method = clazz.getMethods();
1.9 变量
Field[] method = clazz.getFields();//所有public的变量
1.10 注解
Annotation[] annotations = clazz.getAnnotations();
二、构造器
2.1 除了可以通过Class对象获取构造器列表,还可以根据入参类型获取指定的构造器。
Constructor constructor =
clazz.getConstructor(
new
Class[]{String.
class
});
2.2 获取指定构造器的入参类型
Class[] parameterTypes = constructor.getParameterTypes();
2.3 通过指定构造器实例化一个类
MyObject myObject = (MyObject)
constructor.newInstance(
"constructor-arg1"
);
三、成员变量
3.1 获取指定名称的成员变量
Field field = clazz.getField("someField");String fieldName = field.getName();//变量名Object fieldType = field.getType();//变量类型
3.2 get/set变量值
MyObject objectInstance =
new
MyObject();//objetInstance应该是拥有指定变量的类的实例。
Object value = field.get(objectInstance);
field.set(objetInstance, value);
如果变量是静态变量的话(public static),可以传入null做为参数而不用传递拥有该变量的类的实例。
四、成员方法
4.1 根据方法名和入参类型获取指定方法
Method method = clazz.getMethod(
"doSomething"
,
new
Class[]{String.
class
});//入参为一个String
Method method = clazz.getMethod(
"doSomething"
,
null
);//空参
Class[] parameterTypes = method.getParameterTypes();//入参列表
Class returnType = method.getReturnType();//返回参数
4.2 调用方法
Object returnValue = method.invoke(
null
,
"parameter-value1"
);//这里的null是调用方法的对象,静态方法可直接传null,第二个参数是一个可变参数列表
五、私有变量和方法
要想获取私有变量你可以调用Class.getDeclaredField(String name)方法或者Class.getDeclaredFields()方法。同理,构造方法、成员方法等也是。
六、注解
6.1 注解的简单介绍
如果说反射使得很多技术实现(动态代理、依赖注入等)有了基础,那么注解就是使这些技术实现变得平民化的基础。
Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。包含在 java.lang.annotation 包中。
要实现一个自定义注解,必须通过@interface关键字来定义。且在@interface之前,需要通过元注解来描述该注解的使用范围(@Target)、生命周期(@Retention)及其他。
声明一个方法即为注解类型定义了一个元素,方法的声明不允许有参数或throw语句,返回值类型被限定为原始数据类型、字符串String、Class、枚举enums、注解类型,或前面这些的数组,方法可以有默认值。
参考资料
http://www.infoq.com/cn/articles/cf-java-reflection-dynamic-proxy