# 反射:框架设计的灵魂

loading

# 一、概述

Java 反射机制 是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 Java 反射机制

用途:

在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放,这时候就可以利用 Java 的反射机制通过反射来获取所需的私有成员或是方法。

反射的好处:

  • 可以在程序运行过程中,操作这些对象
  • 可以解耦,提高程序的可扩展性

# 二、获取Class对象

# 1、Class.forName("全类名")

将字节码文件加载进内存,返回 Class 对象。多用于配置文件,将类名定义在配置文件中,通过读取配置文件加载类。

# 2、类名.class

通过类名的属性 class 获取。多用于参数的传递。

# 3、对象.getClass()

通过 Object 类的 getClass() 方法。多用于对象的获取字节码方式。

定义一个类:

public class Person {

    private String name;

    private int age;

    public String a;
    protected String b;
    String c;
    private String d;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void eat() {
        System.out.println("eat");
    }

    public void eat(String food) {
        System.out.println("eat:" + food);
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", a='" + a + '\'' +
                ", b='" + b + '\'' +
                ", c='" + c + '\'' +
                ", d='" + d + '\'' +
                '}';
    }
}
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
55
    @Test
    public void test() throws ClassNotFoundException {
        Class cls1 = Class.forName("com.jerry.reflect.Person");
        System.out.println(cls1);

        Class cls2 = Person.class;
        System.out.println(cls2);

        Person person = new Person();
        Class cls3 = person.getClass();
        System.out.println(cls3);

        // 比较三个对象
        System.out.println(cls1 == cls2);
        System.out.println(cls1 == cls3);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

执行结果:

class com.jerry.reflect.Person
class com.jerry.reflect.Person
class com.jerry.reflect.Person
true
true
1
2
3
4
5

同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的 class 对象都是同一个。

# 三、Field成员变量

# 1、设置值

  • void set(Object obj, Object value)

# 2、获取值

  • get(Object obj)

# 3、安全检查

  • setAccessible(boolean flag)

忽略访问权限修饰符的安全检查,暴力反射

# 四、Constructor构造方法

# 1、创建对象

  • T newInstance(Object... initargs)

如果使用空参数的构造方法创建对象,那么操作可以简化为:Class 对象的 newInstance() 方法

# 2、安全检查

  • setAccessible(boolean flag)

忽略访问权限修饰符的安全检查,暴力反射

# 五、Method成员方法

# 1、执行方法

  • Object invoke(Object obj, Object... args)

# 2、获取方法名称

  • String getName()

# 3、安全检查

  • setAccessible(boolean flag)

忽略访问权限修饰符的安全检查,暴力反射

# 六、Class对象功能

# 1、获取成员变量

  • Field[] getFields()

获取所有 public 修饰的成员变量

  • Field getField(String name)

获取指定名称的 public 修饰的成员变量

  • Field[] getDeclaredFields()

不考虑修饰符,获取所有的成员变量

  • Field getDeclaredField(String name)

不考虑修饰符,获取指定名称的成员变量

    @Test
    public void test() throws NoSuchFieldException, IllegalAccessException {
        Class cls = Person.class;

        // 获取 public 成员变量
        Field[] fields = cls.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }

        System.out.println("------------");

        Field a = cls.getField("a");
        // 获取 a 的值
        Person person = new Person();
        Object valueA = a.get(person);
        System.out.println(valueA);
        // 设置 a 的值
        a.set(person, "jerry");
        System.out.println(person);

        System.out.println("------------");

        // 获取所有成员变量
        Field[] declaredFields = cls.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }
        // 获取某一个成员变量
        Field d = cls.getDeclaredField("d");
        // 忽略访问权限修饰符的安全检查,暴力反射
        d.setAccessible(true);
        Object valueD = d.get(person);
        System.out.println(valueD);
    }
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

执行结果:

public java.lang.String com.jerry.reflect.Person.a
------------
null
Person{name='null', age=0, a='jerry', b='null', c='null', d='null'}
------------
private java.lang.String com.jerry.reflect.Person.name
private int com.jerry.reflect.Person.age
public java.lang.String com.jerry.reflect.Person.a
protected java.lang.String com.jerry.reflect.Person.b
java.lang.String com.jerry.reflect.Person.c
private java.lang.String com.jerry.reflect.Person.d
null
1
2
3
4
5
6
7
8
9
10
11
12

# 2、获取构造方法

  • Constructor<?>[] getConstructors()

获取所有 public 修饰的构造方法

  • Constructor<?> getConstructor(类<?>... parameterTypes)

获取指定名称的 public 修饰的构造方法

  • Constructor<?> getDeclaredConstructor(类<?>... parameterTypes)

不考虑修饰符,获取所有的构造方法

  • Constructor<?>[] getDeclaredConstructors()

不考虑修饰符,获取指定名称的构造方法

    @Test
    public void test() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class cls = Person.class;

        // 带参数的构造方法
        Constructor constructor1 = cls.getConstructor(String.class, int.class);
        System.out.println(constructor1);

        Object object1 = constructor1.newInstance("jerry", 30);
        System.out.println(object1);

        System.out.println("------------");

        // 空参数的构造方法
        Constructor constructor2 = cls.getConstructor();
        System.out.println(constructor2);

        Object object2 = constructor2.newInstance();
        System.out.println(object2);

        Object object3 = cls.newInstance();
        System.out.println(object3);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

执行结果:

public com.jerry.reflect.Person(java.lang.String,int)
Person{name='jerry', age=30, a='null', b='null', c='null', d='null'}
------------
public com.jerry.reflect.Person()
Person{name='null', age=0, a='null', b='null', c='null', d='null'}
Person{name='null', age=0, a='null', b='null', c='null', d='null'}
1
2
3
4
5
6

# 3、获取成员方法

  • Method[] getMethods()

获取所有 public 修饰的成员方法

  • Method getMethod(String name, 类<?>... parateterTypes)

获取指定名称的 public 修饰的成员方法

  • Method[] getDeclaredMethods()

不考虑修饰符,获取所有的成员方法

  • Method getDeclaredMethod(String name, 类<?>... parateterTypes)

不考虑修饰符,获取指定名称的成员方法

    @Test
    public void test() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class cls = Person.class;

        // 获取指定名称的方法
        Method eatMethod = cls.getMethod("eat");
        Person person = new Person();
        // 执行方法
        eatMethod.invoke(person);

        System.out.println("------------");

        Method eatMethod2 = cls.getMethod("eat", String.class);
        eatMethod2.invoke(person, "水果");

        System.out.println("------------");

        // 获取所有 public 方法,其中包括 Object 类的方法也可以获取到
        Method[] methods = cls.getMethods();
        for (Method method : methods) {
            System.out.println(method);
            System.out.println(method.getName());
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

执行结果:

eat
------------
eat:水果
------------
public java.lang.String com.jerry.reflect.Person.toString()
toString
public java.lang.String com.jerry.reflect.Person.getName()
getName
public void com.jerry.reflect.Person.setName(java.lang.String)
setName
public void com.jerry.reflect.Person.eat()
eat
public void com.jerry.reflect.Person.eat(java.lang.String)
eat
public int com.jerry.reflect.Person.getAge()
getAge
public void com.jerry.reflect.Person.setAge(int)
setAge
public final void java.lang.Object.wait() throws java.lang.InterruptedException
wait
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
wait
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
wait
public boolean java.lang.Object.equals(java.lang.Object)
equals
public native int java.lang.Object.hashCode()
hashCode
public final native java.lang.Class java.lang.Object.getClass()
getClass
public final native void java.lang.Object.notify()
notify
public final native void java.lang.Object.notifyAll()
notifyAll
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

# 4、获取类名

  • String getName()
    @Test
    public void test() {
        Class cls = Person.class;
        String name = cls.getName();
        System.out.println(name);
    }

1
2
3
4
5
6
7

执行结果:

com.jerry.reflect.Person
1

# 七、应用

假设有这么一个需求:

写一个“框架”,可以创建任意类的对象,并且执行其中任意方法。

# 1、实现

  • 配置文件
  • 反射

# 2、步骤

  1. 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
  2. 在程序中加载读取配置文件
  3. 使用反射技术来加载类文件进内存
  4. 创建对象
  5. 执行方法

# 3、实现

创建配置文件 config.properties

className=com.jerry.reflect.Person
methodName=eat
1
2

实现:

    /**
     * 在不改变任何代码的前提下,可以创建任意类的对象,可以执行对象的任意方法
     */
    @Test
    public void test() throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException,
            NoSuchMethodException, InvocationTargetException {
        // 创建 Properties 对象
        Properties properties = new Properties();

        // 加载配置文件
        ClassLoader classLoader = TestReflect6.class.getClassLoader();
        InputStream inputStream = classLoader.getResourceAsStream("config.properties");
        properties.load(inputStream);

        // 获取配置文件中的数据
        String className = properties.getProperty("className");
        String methodName = properties.getProperty("methodName");

        // 加载类进内存
        Class cls = Class.forName(className);

        // 创建对象
        Object object = cls.newInstance();
        // 获取方法
        Method method = cls.getMethod(methodName);
        // 执行方法
        method.invoke(object);
    }
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

执行结果:

eat
1

上次更新: 2020-08-21 09:02:51(10 小时前)