反射

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类的静态方法来判断是否是期望的访问权限。

image

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()方法。同理,构造方法、成员方法等也是。

image

image

image

image

image

六、注解

6.1 注解的简单介绍

如果说反射使得很多技术实现(动态代理、依赖注入等)有了基础,那么注解就是使这些技术实现变得平民化的基础。

Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。
注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。包含在 java.lang.annotation 包中。

要实现一个自定义注解,必须通过@interface关键字来定义。且在@interface之前,需要通过元注解来描述该注解的使用范围(@Target)、生命周期(@Retention)及其他。

声明一个方法即为注解类型定义了一个元素,方法的声明不允许有参数或throw语句,返回值类型被限定为原始数据类型、字符串String、Class、枚举enums、注解类型,或前面这些的数组,方法可以有默认值。

 

image

参考资料

http://www.infoq.com/cn/articles/cf-java-reflection-dynamic-proxy

http://ifeve.com/java-reflection-tutorial/

http://blog.csdn.net/u010150082/article/details/10519261

原文地址:https://www.cnblogs.com/lddbupt/p/5735340.html