java反射基础

什么是反射

Java中, 每一个对象都对应有一个Class对象, 这个Class对象记录着对象的类型信息, 也就是类的内部结构.

我们知道, 我们编写的.java文件, 是需要被编译成.class文件, 然后才能被虚拟机加载执行. 正常情况下, .class是在编译期生成并且被JVM识别. 而反射机制则将.class文件的打开和检查推迟到运行时.

简单的说, 反射机制能让你在运行时操作一个类, 这些操作可以是: 实例化该类的对象, 调用对象方法和修改对象的属性值. 至于操作的类的.class文件可以是编译期未知.

反射的概述

一个类的元素可以被拆分为下面几个元素:

  • Class: 一个类对象

  • Constructor: 构造器对象, 可以通过Class对象获得

  • Method: 方法对象, 可以通过Class对象获得

  • Fields: 字段对象, 可以通过Class对象获得

  • Annotation: 注解对象, 可以通过Class对象获得

从上面的元素可以看出, 我们如果要进行反射的话, 首先得获得类的Class对象, 进而才可以操作类的字段, 方法和构造器等.

获取Class对象

要进行反射, 我们首先要获得需要反射的类的Class对象. 而获取的方法有下面三种:

  • Class class2 = Class.forName("com.desperado.reflaction.Bean"): 调用Class类的forName(String)方法, 参数为类的全限定符号. 如果运行时找不到该类的话, 会报ClassNotFoundException异常. 这种方法一般用于编译期不能获得.class文件的情形.

  • Class class1 = Bean.class: 通过类字面量来获取Class对象.

  • Bean bean = new Bean(); Class class3 = bean.getClass();: 通过对象来获取Class对象.

获取Constructor

得到Class对象后, 我们可以利用这个Class对象来获取类的构造函数, 以此来创建实例.

获取构造方法的方法有:

  • getConstructors(): 获取所有被public修饰的构造方法.

  • getConstructor(Class<?>... parameterTypes): 返回指定参数类型的构造方法, 并且构造方法是被public修饰的.

  • getDeclaredConstructors(): 获取所有的构造方法, 与权限修饰符无关.

  • getDeclaredConstructor(Class<?>... parameterTypes): 获取指定类型的构造方法, 与权限修饰符无关.

创建实例

1
2
Class beanClass = Bean.class;
Bean b = (Bean) beanClass.newInstance();

newInstance()这个方法会调用类无参的构造函数, 如果类没有无参构造函数的话, 会报异常.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Human {
private String name;
public int age;
public Human() {}
private Human(String name, int age) {
this.name = name;
this.age = age;
}
public Human(String name) {
this.name = name;
}
}
Class<?> c = Human.class;
Constructor constructor = c.getConstructor(String.class);
Human human = (Human) constructor.newInstance("xiaohong");
Constructor priCon = c.getDeclaredConstructor(String.class, int.class);
priCon.setAccessible(true);
Human h = (Human) priCon.newInstance("xiaohong", 18);

利用getConstructor方法, 调用了Human中带有String参数的构造方法.getDeclaredConstructor可以获取私有构造方法.

获取Method

利用Class对象, 我们可以获得Method来调用Class的方法. 获得Method的方法有:

  • getMethods(): 获取所有被public修饰的方法, 包含父类和接口中的方法

  • getMethod(String name, Class<?>... parameterTypes): 获取指定namepublic方法. 包含父类的方法.

  • getDeclaredMethods(): 获取所有的方法, 包括private. 但是不包括父类的方法

  • getDeclaredMethod(String name, Class<?>... parameterTypes): 获取指定的方法, 包括private. 但是不包括父类的方法.

getMethods和getMethod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public class Human {
private String name;
private int age;
public Human() {}
private Human(String name, int age) {
this.name = name;
this.age = age;
}
public Human(String name) {
this.name = name;
}
public void humanPubMethod(){
System.out.println("humanPubMethod");
}
private void humanPriMethod() {
System.out.println("humanPriMethod");
}
}
public class XiaoHong extends Human {
private String name = "xiaohong";
public XiaoHong(String name) {
this.name = name;
}
private void priMethod() {
System.out.println("priMethod");
}
}
public static void main(String[] args) throws IllegalAccessException,
InstantiationException, NoSuchMethodException,InvocationTargetException {
Class<?> c = XiaoHong.class;
Constructor constructor = c.getConstructor(String.class);
XiaoHong xiaoHong = (XiaoHong) constructor.newInstance("xiaohong");
Method method = c.getMethod("humanPubMethod", null);
method.invoke(xiaoHong, null); //调用方法
Method[] methods = c.getMethods();
for (Method m : methods) { //遍历方法
System.out.println(m.getName());
}
}

getDeclaredMethod和getDeclaredMethods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class<?> c = XiaoHong.class;
Constructor constructor = c.getConstructor(String.class);
XiaoHong xiaoHong = (XiaoHong) constructor.newInstance("xiaohong");
Method method = c.getDeclaredMethod("priMethod", null);
method.setAccessible(true);
method.invoke(xiaoHong, null);
Method[] methods = c.getDeclaredMethods();
for (Method m : methods){
System.out.println(m.getName());
}
}

获取Fields

利用Class对象, 我们同样可以获得类的字段, 并且可以setget字段的值. 我们可以通过下面的方法获取Field对象

  • getFields(): 获取类的所有被public修饰的字段, 包括父类中的.

  • getField(String name): 获取类的指定被public修饰的字段, 包括父类中的.

  • getDeclaredFields(): 获取类的所有字段, 包括被private修饰的字段. 但是不包括父类中的.

  • getDeclaredField(String name): 获取类的指定字段, 包括被private修饰的字段. 但是不包括父类中的.

getFields和getField

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class<?> c = XiaoHong.class;
Constructor constructor = c.getConstructor(String.class);
XiaoHong xiaoHong = (XiaoHong) constructor.newInstance("xiaohong");
Field field = c.getField("pubName");
String pubName = (String) field.get(xiaoHong);
System.out.println(pubName);
Field[] fields = c.getFields();
for (Field f : fields) {
System.out.println(f.getName());
}
}

getDeclaredFields和getDeclaredField

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class<?> c = XiaoHong.class;
Constructor constructor = c.getConstructor(String.class);
XiaoHong xiaoHong = (XiaoHong) constructor.newInstance("xiaohong");
Field field = c.getDeclaredField("name");
field.setAccessible(true);
System.out.println(field.get(xiaoHong));
Field[] fields = c.getDeclaredFields();
for (Field f : fields) {
System.out.println(f.getName());
}
}

获得Annotation

由于注解可以用来标注类, 方法, 字段, 参数, 所以, Annotation可以通过这些来获取.下面以Class对象为例子. 可以通过下面两个方法获取:

  • getAnnotation(Class<A> annotationClass): 获取类指定的注解

  • getAnnotations(): 获取类标注的所有注解

getAnnotations和getAnnotation(Class annotationClass)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Class<?> c = XiaoHong.class;
Constructor constructor = c.getConstructor(String.class);
XiaoHong xiaoHong = (XiaoHong) constructor.newInstance("xiaohong");
BindView bindView = c.getAnnotation(BindView.class);
if (bindView != null) {
System.out.println(bindView.value());
}
Annotation[] annotations = c.getAnnotations();
for (Annotation a : annotations) {
if (a.annotationType().equals(BindView.class)) {
System.out.println("equal");
}
}
}

利用反射处理数组

Java反射机制通过java.lang.reflect.Array这个类来处理数组。

创建数组

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

上面是一个创建数组的例子, newInstance方法的第一个参数为: 数组存放的类型, 第二个为数组的长度.

访问数组

1
2
int a = Array.getInt(ints, 0);
Array.setInt(ints,1, 3);

对于基本类型, Array中提供了setXXgetXX方法来访问数组, 对于普通对象, 可以调用getset方法.

数组Class对象

对于获取数组的Class对象, 可以先实例化对于的数组, 再得到它的Class对象

1
2
Class aClass = Class.forName("com.desperado.reflaction.XiaoHong"); //先获得class对象
Class classArray = Array.newInstance(aClass, 0).getClass(); //再创建class对象数组, 最后获取数组class对象

反射和泛型

由于泛型会在编译期被擦除, 所以我们是不能在运行时获得泛型类的参数类型的. 但是我们却可以通过使用了参数类型的字段或者返回了带有类型参数的方法来获得一个泛型类的类型信息.

1
2
3
4
5
6
7
8
9
10
11
12
public class TClass {
private List<String> stringList = new ArrayList<>();
public List<String> getList() {
return stringList;
}
public void setList(List<String> list) {
this.stringList = list;
}
}

获取方法返回值的参数类型

1
2
3
4
5
6
7
8
9
10
Method method = TClass.class.getMethod("getList", null);
Type type = method.getGenericReturnType();
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
Type[] ts = parameterizedType.getActualTypeArguments();
for (Type t : ts) {
Class c = (Class) t;
System.out.println(c.getName());
}
}

获取方法参数的参数类型

1
2
3
4
5
6
7
8
9
10
Field field = TClass.class.getDeclaredField("stringList");
Type t = field.getGenericType();
if (t instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) t;
Type[] types = pt.getActualTypeArguments();
for (Type ty : types) {
Class c = (Class) ty;
System.out.println(c.getName());
}
}

获取字段的参数类型

1
2
3
4
5
6
7
8
9
10
11
12
Method method = TClass.class.getMethod("setList", List.class);
Type[] type = method.getGenericParameterTypes();
for (Type parT : type) {
if (parT instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) parT;
Type[] types = parameterizedType.getActualTypeArguments();
for (Type ty: types) {
Class c = (Class) ty;
System.out.println(c.getName());
}
}
}

参考资料:

Java核心编程思想

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

分享到