java反射笔记(java reflection)

仅供学习交流,如有错误请指出,如要转载请加上出处,谢谢

java是一门静态语言,一般来讲,类的定义需要在JVM运行前完成,要通过JVM编译环节,才能运行在JVM上,而java反射机制使java的类定义能够在JVM运行时动态的加载,让在某些功能上更加灵活多用,根据不同的上下文来决定类的功能

Class

在每一个类都有Class对象,在JVM编译的环节中,他会检查类的信息,这个时候就会获取该类的Class对象,它包含了该类的所有信息,获取Class对象有很多种方法。

获取Class对象

1.利用类本身的情况下

1
Class myClass = className.class;

2.利用类名的情况下

1
Class myClass = Class.forName("className");

注意:在使用类名获取Class对象时,参数名称必须是类的全称,包括包的名称,这样才能找得到

3.利用类对象的情况下

1
Class myClass = new Object().getClass();

当然,你拥有Class对象,等于你就知道该类的所有信息,包括变量,方法,修饰符,注解,甚至它的父类,实现的接口,所在包的信息,具体的方法可以参考相应的文档:http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html

Constructor

在java中,创建实例对象是根据构造器来实现的,JVM编译环节通过Constructor来检查类中构造方法的信息,Constructor拥有构造方法所有的信息,如果我们获取了Constructor对象,也就可以反射出拥有该构造器的对象。

获取Constructor对象

1.获取所有的Constructor对象

1
Constructor[] constructors = myClass.getConstructors();

2.获取指定的构造器

1
Constructor constructor = myClass.getConstructor(String.class);

getConstructor方法的参数是构造方法的参数的Class对象

实例化对象

1
2
Object obj = constructor.newInstance("参数值");

相当于SimpleReflection simpleReflection = new SimpleReflection(“参数值”),newInstance方法的参数是构造方法的参数的实例

Field

在java的JVM编译环节中,通过Field来检查类变量的信息,它拥有类变量所有的信息。

获取Field对象

公有(public)变量

1.获取所有Field

1
Field[] fields = myClass.getFields();

2.获取指定的Field

1
Field field = myClass.getField();
私有(private)变量

1.获取所有Field

1
Field[] privateFields = myClass.getDeclaredFields();

2.获取指定的Field

1
Field privateField = myClass.getDeclaredField();

获取变量属性

1.获取变量名称

1
String fieldName = field.getName();

2.获取变量类型

1
Class fieldType = field.getType();

set&get变量

公有(public)变量
1
2
Object value = field.get(new Object());
field.set(new Object(), value);
私有(private)变量

通常情况下,外部类是不能访问内部的私有变量的,因为在访问对象变量时,JVM会有一个反射访问检查(reflection access check),私有变量没有访问权限是不能访问的,在Field对象有一个setAccessible方法,true表示在外部对象的作用域里可以访问私有变量。

1
2
3
privateField.setAccessible(true);
Object value = privateField.get(new Object());
privateField.set(new Object(), value);

非静态变量的Field.get()和Field.set()方法需要指定该变量所属的对象,因为每个对象里有很多相同变量,它们独自享有一块内存,如果不指定对象,就会有歧义,而对于静态变量,可以将参数设置为NULL,因为在类加载时,是先加载静态变量,后加载构造方法,所以静态变量和对象没有必要的关系。

Method

在java的JVM编译环节中,通过Method来检查方法的信息,它拥有方法所有的信息。

获取Method对象

公有(public)方法

1.获取所有的方法

1
Method[] methods = myClass.getMethods();

2.获取指定的方法

1
Method method = myClass.getMethod("methodName", new Class[]{ String.class});

私有(private)方法

1.获取所有的方法

1
Method[] privateMethods = myClass.getDeclaredMethods();

2.获取指定的方法

1
Method privateMethod = myClass.getDeclaredMethod("methodName", new Class[]{ String.class});

获取Method信息

1.获取方法名

1
String methodName = method.getName();

2.获取返回类型

1
Class methodType = method.getReturnType();

3.获取参数类型

1
Class[] types = method.getExceptionTypes();

Method访问方法

公有(public)方法
1
Object returnValue = method.invoke(new Object(), "方法参数列表");
私有(private)方法

原理和Field一样,在JVM反射访问检查时,通过setAccessible方法来设置私有方法的访问权限。

1
2
privateMethod.setAccessible(true);
Object returnValue = privateMethod.invoke(new Object(), "方法参数列表");

方法也有静态的,所以在invoke方法中如果是静态方法可以设置成NULL.

Annotation

注解在java 5才出现,它扩展了类,属性,方法,参数等,在JVM编译时,通过Annotation来检查注解的信息

类注解

1.获取所有的注解

1
Annotation[] annotations = myClass.getAnnotations();

2.获取指定的注解

1
Annotation annotation = myClass.getAnnotation(MyAnnotation.class);

方法注解

1.获取所有的注解

1
Annotation[] annotations = method.getAnnotations();

2.获取指定的注解

1
Annotation annotation = method.getAnnotation(MyAnnotation.class);

参数注解

1
2
3
4
5
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
for(Annotation[] annotations : parameterAnnotations){
for(Annotation annotation : annotations){
}
}

变量注解

1.获取所有的注解

1
Annotation[] annotations = field.getAnnotations();

2.获取指定的注解

1
Annotation annotation = field.getAnnotation(MyAnnotation.class);

访问注解信息

1
2
3
4
if(annotation instanceof MyAnnotation){
MyAnnotation myAnnotation = (MyAnnotation) annotation;
System.out.println("name: " + myAnnotation.name());//假设注解中有name属性,就可以这样访问了
}

Array

在java中,数组是一个很特殊的对象,它不继承于Object,它们没有Object的所有属性和方法,所以它不是有某个类实例化出来的,它是由JVM动态创建的,JVM有一个Array通过反射机制来处理数组

创建数组

1
String[] arrays = (int[]) Array.newInstance(String.class, 3);

相当于String[] arrays = new String(3),newInstance方法中的参数为数组的类型和大小

访问数组

set(对数组赋值)
1
2
Array.set(arrays, 0, 'hello');
Array.set(arrays, 1, 'world');

set方法参数为别为目标数组,数组下标,值。

get(获取数组值)
1
Array.get(arrays,0);

get方法参数分别是目标数组,数组下标。

获取Class对象

通过class属性
1
Class class = String[].class;
通过forName方法
1
Class intArray = Class.forName("[I");

“[“代表的是数组,”I”代表的是int类型(针对于基本类型),这个是原生的数组创建,因为在JVM创建数组,类名就是”[I”。

而对于普通类型来讲,创建数组就需要明确类型:

1
Class intArray = Class.forName("[Ljava.lang.String;");

“[L”声明普通类型数组,”java.lang.String”表示类型(类型全称),”;”表示结束

通过getClass方法
1
Class class = arrays.getClass();

获取数组属性

获取数组类型
1
Class type = class.getComponentType();

参考

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