1. 什么是正射

一般情况下,我们使用某个类时必定知道它是什么类,是用来做什么的。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。

1
2
Apple apple = new Apple(); //直接初始化,「正射」
apple.setPrice(4);

2. 反射

2.1 三个反射包中的类

Constructor:

代表类的单个构造方法,通过Constructor我们可执行一个类的某个构造方法(有参或者无参)来创建对象时。

Method:

代表类中的单个方法,可以用于执行类的某个普通方法,有参或无参,并可以接收返回值。

Field:

代表类中的单个属性,用于set或get属性

AccessibleObject:

以上三个类的父类,提供了构造方法,普通方法,和属性的访问控制的能力。

使用Class类中的方法可以获得该类中的所有Constructor对象,Method对象,和Field对象。但是任然无法访问私有化的构造方法,普通方法,和私有属性,此时我们可以使用他们继承父类(AccessibleObject)中的setAccessible()方法,来设置或取消访问检查,以达到访问私有对象的目的。

2.2 反射举例

上面这样子进行类对象的初始化,我们可以理解为「正」。

而反射则是一开始并不知道我要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了。这时候,我们使用 JDK 提供的反射 API 进行反射调用:

1
2
3
4
5
Class clz = Class.forName("com.chenshuyi.reflect.Apple");
Method method = clz.getMethod("setPrice", int.class);
Constructor constructor = clz.getConstructor();
Object object = constructor.newInstance();
method.invoke(object, 4);

上面例子的完整代码

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
public class Apple {

private int price;

public int getPrice() {
return price;
}

public void setPrice(int price) {
this.price = price;
}

public static void main(String[] args) throws Exception{
//正常的调用
Apple apple = new Apple();
apple.setPrice(5);
System.out.println("Apple Price:" + apple.getPrice());
//使用反射调用
Class clz = Class.forName("com.chenshuyi.api.Apple");
Method setPriceMethod = clz.getMethod("setPrice", int.class);
Constructor appleConstructor = clz.getConstructor();
Object appleObj = appleConstructor.newInstance();
setPriceMethod.invoke(appleObj, 14);
Method getPriceMethod = clz.getMethod("getPrice");
System.out.println("Apple Price:" + getPriceMethod.invoke(appleObj));
}
}

3. 反射常用API

3.1 获取反射中的Class对象

在反射中,要获取一个类或调用一个类的方法,我们首先需要获取到该类的 Class 对象。

在 Java API 中,获取 Class 类对象有三种方法:

  1. 使用 Class.forName 静态方法

    Class clz = Class.forName("java.lang.String");

  2. 使用 .class 方法 → 这种方法只适合在编译前就知道操作的 Class

    Class clz = String.class;

  3. 使用类对象的 getClass() 方法

    1
    2
    String str = new String("Hello");
    Class clz = str.getClass();

3.2 通过反射创建类对象

通过反射创建类对象主要有两种方式:

  1. 通过 Class 对象的 newInstance() 方法

    1
    2
    Class clz = Apple.class;
    Apple apple = (Apple)clz.newInstance();
  2. 通过 Constructor 对象的 newInstance() 方法

    1
    2
    3
    Class clz = Apple.class;
    Constructor constructor = clz.getConstructor();
    Apple apple = (Apple)constructor.newInstance();

    通过 Constructor 对象创建类对象可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法。下面的代码就调用了一个有参数的构造方法进行了类对象的初始化。

    1
    2
    3
    Class clz = Apple.class;
    Constructor constructor = clz.getConstructor(String.class, int.class);
    Apple apple = (Apple)constructor.newInstance("红富士", 15);

3.3 通过反射获取类属性、方法、构造器

我们通过 Class 对象的 getFields() 方法可以获取 Class 类的属性,但无法获取私有属性:

1
2
3
4
5
6
7
8
Class clz = Apple.class;
Field[] fields = clz.getFields();
for (Field field : fields) {
System.out.println(field.getName());
}

//输出结果是:
price

而如果使用 Class 对象的 getDeclaredFields() 方法则可以获取包括私有属性在内的所有属性:

1
2
3
4
5
6
7
8
9
Class clz = Apple.class;
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName());
}

//输出结果是:
name
price