Featured image of post 第八章 JAVA 反射机制

第八章 JAVA 反射机制

反射概述

什么是反射

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

它通过 Java 反射 API 来实现,其中最核心的类位于 java.lang.reflect 包下,如 Class、Constructor、Field 和 Method 等,这些类提供了对类和对象的运行时信息进行检查和操作的方法。反射机制一般用来解决 Java 程序运行期间,对某个实例对象一无所知的情况下,如何 调用该对象内部的方法 问题。

在创建对象或者调用方法时,传入形参时可以使用 Ctrl + P 提示需要传入的形参,利用反射获取到该方法上的所有形参进行展示。

创建对象后,使用对象调用方法或成员变量时,IDEA 会利用反射把该类中所有能调用的方法和成员变量获取出来并进行展示。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package reflection;

public class Student {
    String name;						// 成员变量

    public Student(){					// 空参构造方法
    }

    public void method(){				// 普通成员方法
        String name;					// 局部变量
        name = "a";
        System.out.println(name);
    }
}

如何区分构造方法 public Student(){} 和普通成员方法 public void method(){…} ?如何区分成员变量 public class Student{String name; …} 和局部变量 public void method(){String name; …} ?

反射(Reflection) 允许对 封装类字段(成员变量)方法构造函数 的信息进行编程访问。

反射机制原理

Java 程序在计算机中的三个阶段:① 源代码阶段,② 加载阶段,③ 运行阶段。

  1. 类加载:当 Java 程序运行时,类加载器根据类的名称查找并加载类的字节码文件。类加载器将字节码文件转换为可执行的 Java 类,并将其存储在运行时数据区域的方法区中。

  2. 创建 Class 对象:在类加载过程中,Java 虚拟机会自动创建对应的 Class 对象。这个 Class 对象包含了类的元数据信息,并提供了访问和操作类的接口。

  3. 获取 Class 对象: 我们可以通过多种方式获取 Class 对象。常见的方式有 3 种: 类.class 属性、类实例的 getClass() 方法、Class.forName()。

  4. 访问和操作:通过 Class 对象,我们可以获取类的字段、方法、构造函数等信息。我们可以使用 Field 类和 Method 类来访问和操作字段和方法,甚至可以调用私有的字段和方法。

反射的应用场景

反射机制在 Java中具有重要的作用和价值,它为我们提供了在运行时动态地获取和操作类的能力。以下是一些使用反射机制的常见场景和原因:

  1. 运行时类型检查: 反射机制允许我们在运行时获取类的信息,包括字段、方法和构造方法等。这使得我们可以进行运行时类型检查,以确保代码在处理不同类型的对象时能够正确地进行操作。

  2. 动态创建对象: 通过反射机制,我们可以在运行时动态地创建对象,而不需要在编译时知道具体的类名。这对于某些需要根据条件或配置来创建对象的情况非常有用,例如工厂模式或依赖注入框架。

  3. 访问和修改私有成员: 反射机制使我们能够绕过访问权限限制,访问和修改类的私有字段和方法。虽然这破坏了封装性原则,但在某些特定情况下,这种能力可以帮助我们进行一些特殊操作,例如单元测试、调试或框架的内部实现。

  4. 动态调用方法: 反射机制允许我们在运行时动态地调用类的方法,甚至可以根据运行时的条件来选择不同的方法。这对于实现插件化系统、处理回调函数或实现动态代理等功能非常有用。

  5. 框架和库的实现: 许多Java框架和库在其实现中广泛使用了反射机制。它们利用反射来自动发现和加载类、实现依赖注入、处理注解、配置文件解析和动态代理等。反射机制使得这些框架和库更加灵活和扩展。

需要注意的是,虽然反射机制提供了灵活性和动态性,但它也带来了一些潜在的性能开销和安全风险。因此,在使用反射时需要谨慎,并权衡其优缺点。它应该被视为一种强大的工具,用于特定的情况和需求,而不是滥用或不必要地使用。

反射优点和缺点

反射是一项强大的技术,可以让我们在运行时动态地获取和操作类的信息。然而,反射也有其优点和缺点。下面是反射的一些优缺点:

优点:

  1. 动态性:反射允许我们在运行时动态地获取和操作类的信息,而不需要在编译时确定。这为编写灵活的、可扩展的代码提供了便利。

  2. 灵活性:通过反射,我们可以绕过访问修饰符的限制,访问和修改私有成员、调用私有方法等。这为我们在特殊情况下进行一些高级操作提供了可能。

  3. 框架开发:反射在开发框架和库时非常有用。通过反射,框架可以动态地加载和实例化类,解析注解,处理回调等。这为框架提供了更大的灵活性和可扩展性。

  4. 调试和探索:反射使得我们可以在运行时探索代码背后的信息,例如获取类的结构、方法、字段等。这对于调试和理解复杂的代码非常有帮助。

缺点:

  1. 性能开销:相比于直接调用代码,使用反射会带来更高的性能开销。反射涉及到动态查找、方法调用等操作,这些操作比直接调用代码更加耗时。因此,在对性能要求较高的场景下,过度使用反射可能导致性能下降。

  2. 安全性和稳定性:反射打破了封装性和类型安全性。通过反射,我们可以绕过访问修饰符的限制,调用私有方法等。这可能导致代码的不稳定性和安全隐患。使用反射时需要格外小心,确保代码的正确性和稳定性。

  3. 可读性和可维护性:反射使得代码变得更加动态和复杂,增加了代码的复杂性和可读性的难度。使用过多的反射可能导致代码难以理解和维护,降低代码的可读性和可维护性。

class 对象

字节码文件和字节码文件对象

通过反射在获取成员变量 Field、构造方法 Constructor、成员方法 Method 时,不是在 java 文件中获取的,而是从 class 字节码文件对象中获取。

java 文件: 就是我们自己编写的 java 代码

字节码文件: 就是通过 java 文件编译之后的 class 文件

字节码文件对象: 当 class 文件加载到内存之后,虚拟机自动创建出来的对象

而我们的反射获取的是什么?字节码文件对象,这个对象在内存中是 唯一的

 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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package com.company.reflection;

public class Student {
    private String name;
    private int age;
    public double grade;

    public Student() {
    }

    private Student(String name) {
        this.name = name;
    }

    protected Student(int age) {
        this.age = age;
    }

    public Student(double grade) {
        this.grade = grade;
    }

    public Student(String name, int age, double grade) {
        this.name = name;
        this.age = age;
        this.grade = grade;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", grade=" + grade +
                '}';
    }

    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 double getGrade() {
        return grade;
    }

    public void setGrade(double grade) {
        this.grade = grade;
    }

    public void study() {
        System.out.println("学习");
    }

    private void play(String game) throws IOException, IndexOutOfBoundsException {
        System.out.println("玩" + game);
    }
    
    private String sleep(int time) {
        System.out.println(time + "点睡觉");
        if (time >= 21 && time <= 23) {
            return "早睡";
        } else {
            return "晚睡";
        }
    }
}

获取 class 对象的三种方式

① Class.forName(“全类名”);

② 类名.class

③ 对象.getClass();

 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
package com.company.reflection;

public class ReDemo1 {
    public static void main(String[] args) throws ClassNotFoundException {
        /*
         *  **① Class.forName("全类名");**
         *
         *	**② 类名.class**
         *
         *	**③ 对象.getClass();**
         */


        // 全类名 -> 包名 + 类名
        // -> 直接在类文件中选中类名,使用 copy reference 复制引用即可
        // 第一种方法最常用
        Class clazz1 = Class.forName("com.company.reflection.Student");
        System.out.println(clazz1);

        // class com.company.reflection.Student
        // 多用于参数进行传递
        Class clazz2 = Student.class;
        System.out.println(clazz2);

        // 当已经有某个类的对象时才能使用
        Student stu = new Student("张三", 18, 86.5);
        Class clazz3 = stu.getClass();
        System.out.println(clazz3);

        System.out.println(clazz1 == clazz2);
        System.out.println(clazz2 == clazz3);
    }
}

利用反射获取构造方法 Constructor

Class 类中用于获取构造方法的方法

方法名称 说明
Constructor[] getConstructors() 返回所有公共构造方法对象的数组
Constructor[] getDeclaredConstructors() 返回所有构造方法对象的数组
Constructor getConstructor(Class… parameterTypes) 返回单个公共构造方法对象
Constructor getDeclaredConstructor(Class… parameterTypes) 返回单个构造方法对象

List 表示集合里是Java类的实例,List 表示集合里的对象类型不确定,未指定。

这些方法都是定义在 Class 类中的,其中 get 表示获取。getConstructors 表示将所有 public 修饰的构造方法存入一个 Constructor[] 数组当中返回;getDeclaredConstructor 表示将所有的,即包括 public 修饰的以及 private 修饰的所有构造方法存入数组返回。

 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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package com.company.reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;

public class ReDemo2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        /*
            class类中用于获取构造方法的方法
                Constructor<?>[] getConstructors( )
                Constructor<?>[] getDeclaredConstructors()
                Constructor<T> getConstructor(class<?>... parameterTypes)
                Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

            Constructor类中用于创建对象的方法
                T newInstance(Object... initargs)
                setAccessible(boolean flag)
         */

        // 首先要获取class字节码文件
        Class clazz = Class.forName("com.company.reflection.Student");

        Constructor[] cons1 = clazz.getConstructors();
        for (Constructor con : cons1) {
            System.out.println(con);
        }

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

        Constructor[] cons2 = clazz.getDeclaredConstructors();
        for (Constructor con : cons2) {
            System.out.println(con);
        }

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

        // Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
        Constructor con1 = clazz.getDeclaredConstructor();
        System.out.println(con1);

        Constructor con2 = clazz.getDeclaredConstructor(String.class);
        System.out.println(con2);

        Constructor con3 = clazz.getDeclaredConstructor(int.class);
        System.out.println(con3);

        Constructor con4 = clazz.getDeclaredConstructor(String.class, int.class, double.class);
        System.out.println(con4);

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

        // Constructor<T> getConstructor(class<?>... parameterTypes)
        Constructor con5 = clazz.getConstructor();
        System.out.println(con5);

//        Constructor con6 = clazz.getConstructor(String.class);
//        System.out.println(con6);

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

        // 获取权限修饰符,根据权限修饰符提示可以调用的方法
        // 获取权限修饰符public
        int modifiers1 = con4.getModifiers();
        System.out.println(modifiers1);

        // 获取权限修饰符private
        int modifiers2 = con2.getModifiers();
        System.out.println(modifiers2);

        // 获取权限修饰符protect
        int modifiers3 = con3.getModifiers();
        System.out.println(modifiers3);

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

        System.out.println(con4.getParameterCount());       // 获取参数个数

        Class[] parameterTypes = con4.getParameterTypes();  // 获取参数类型
        for (Class parameterType : parameterTypes) {
            System.out.println(parameterType);
        }

        Parameter[] parameters = con4.getParameters();      // 获取该方法下的所有参数
        for (Parameter parameter : parameters) {
            System.out.println(parameter);
        }
    }
}

Modifier 类简介

Modifier 类是位于 java.lang.reflect(see) 反射包下的一个类,它提供了有关类或成员变量(字段、方法、构造等)的访问修饰符的信息。

各成员变量都会有一个 getModifiers() 方法,getModifiers() 方法返回一个包含标志的 int 值,标志描述了为数组元素应用了哪个修饰符 (private、public、protected 等)。

Modifier 类的常用方法

 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
package com.company.reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class ReDemo4 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
        Method[] methods = Student.class.getMethods();
        System.out.println(Modifier.isAbstract(methods[0].getModifiers()));
        System.out.println(Modifier.isPublic(methods[0].getModifiers()));

        boolean res = Modifier.isPrivate(Class.forName("com.company.reflection.Student").getDeclaredConstructor(String.class).getModifiers());
        System.out.println(res);
        
        /*
         *   ① Class.forName("全类名");
         * 	 ② 类名.class
         * 	 ③ 对象.getClass();
         */
        
        // Constructor con = Class.forName("com.company.reflection.Student").getDeclaredConstructor(String.class);
        // System.out.println(con);
    }
}

JAVA 反射机制中,Field 的 getModifiers() 方法返回 int 类型值表示该字段的修饰符。

修饰符 返回值
public 1
private 2
protected 4
static 8
final 16
synchronized 32
volatile 64
transient 128
native 256
interface 512
abstract 1024
strict 2048

Constructor 类中用于创建对象的方法

方法名称 说明
T newInstance(Object… initargs) 根据指定的构造方法创建对象
setAccessible(boolean flag) 设置为true,表示取消访问检查

使用 Object stuObj = con4.newInstance(“张三”, 18); 创建 Object 对象,通过 (Student) 强制类型转换可以转换为 Student 对象。在创建对象时,即使使用的是使用 Declared 返回的方法,也只能使用 public 方法,如果想要使用私有方法,需要使用 setAccessible(true) 取消权限校验。

 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
package com.company.reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class ReDemo3 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//        Class clazz = Class.forName("com.company.reflection.Student");
        Class<Student> clazz = Student.class;

        Constructor con2 = clazz.getDeclaredConstructor(String.class);

        Constructor con4 = clazz.getDeclaredConstructor(String.class, int.class, double.class);

        Object stuObj = con4.newInstance("张三", 18 , 86.5);
        System.out.println(stuObj);
        Student stu1 = (Student) stuObj;
        System.out.println(stu1);

//        Student stu2 = (Student) con2.newInstance("张三");
//        System.out.println(stu2);

        // 临时取消权限校验 方法.setAccessible(true);
        con2.setAccessible(true);
        Student stu2 = (Student) con2.newInstance("张三");
        System.out.println(stu2);
    }
}

利用反射获取成员变量 Field

Class 类中用于获取成员变量的方法

方法名称 说明
Field[] getFields() 返回所有公共成员变量对象的数组
Field[] getDeclaredFields() 返回所有成员变量对象的数组
Field getField(String name) 返回单个公共成员变量对象
Field getDeclaredField(String name) 返回单个成员变量对象
 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
56
package com.company.reflection;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class ReDemo5 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        // 先获取到class字节码文件对象
        Class<?> clazz = Class.forName("com.company.reflection.Student");

        // 获取所有公有成员变量
        Field[] fields1 = clazz.getFields();
        for (Field field : fields1) {
            System.out.println(field);
        }

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

        // 获取所有成员变量
        Field[] fields2 = clazz.getDeclaredFields();
        for (Field field : fields2) {
            System.out.println(field);
        }

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

        // 获取单个公有成员变量
        Field grade = clazz.getField("grade");
        System.out.println(grade);

        // 获取单个成员变量
        Field name = clazz.getDeclaredField("name");
        System.out.println(name);

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

        // 可以获取到成员变量的权限修饰符,也是int类型的整数
        int modifiers1 = grade.getModifiers();
        System.out.println(modifiers1);
        System.out.println(Modifier.isPublic(modifiers1));

        int modifiers2 = name.getModifiers();
        System.out.println(Modifier.isProtected(modifiers2));

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

        // 获取成员变量名
        System.out.println(name.getName());

        // 获取成员变量数据类型
        Class<?> type1 = name.getType();
        System.out.println(type1);
        Class<?> type2 = grade.getType();
        System.out.println(type2);
    }
}

Field 类中用于创建对象的方法

方法名称 说明
void set(Object obj, Object value) 赋值
object get(Object obj) 获取值
 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
package com.company.reflection;

import java.lang.reflect.Field;

public class ReDemo6 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        Class<?> clazz = Class.forName("com.company.reflection.Student");

        Field grade = clazz.getField("grade");

        Field name = clazz.getDeclaredField("name");

        // 获取成员变量记录的值(首先需要创建对象)
        Student stu = new Student("张三", 18, 86.5);
        Object v1 = grade.get(stu);
        System.out.println(v1);

        // cannot access a member of class com.company.reflection.Student with modifiers "private"
        // IllegalAccessException
//        Object v2 = name.get(stu);
//        System.out.println(v2);
        name.setAccessible(true);
        System.out.println(name.get(stu));

        // 修改对象里记录的值
        System.out.println(stu);
        name.set(stu, "李四");
        System.out.println(stu);
    }
}

利用反射获取成员方法 Method

Class 类中用于获取成员方法的方法

方法名称 说明
Method[] getMethods() 返回所有公共成员方法对象的数组,包括继承的
Method[] getDeclaredMethods() 返回所有成员方法对象的数组,不包括继承的
Method getMethod(String name, Class… parameterTypes) 返回单个公共成员方法对象
Method getDeclaredMethod(String name, Class… parameterTypes) 返回单个成员方法对象
 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
56
57
58
59
60
61
package com.company.reflection;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;

public class ReDemo7 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
        Class<?> clazz = Class.forName("com.company.reflection.Student");

        Method[] publicMethods1 = clazz.getMethods();
        for (Method publicMethod : publicMethods1) {
            // getMethods()获取的方法对象包含父类中所有的公共方法,因此会返回一些没有定义的public方法
            System.out.println(publicMethod);
        }

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

        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            // getDeclaredMethods()不能获取父类中的方法,但可以获取本类中私有的方法
            System.out.println(declaredMethod);
        }

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

        // 获取单个方法
        // Method play = clazz.getMethod("play", String.class);
        // NoSuchMethodException

        Method play = clazz.getDeclaredMethod("play", String.class);
        System.out.println(play);
        Method study = clazz.getMethod("study", null);
        System.out.println(study);
        
        // 获取方法修饰符
        System.out.println(Modifier.isPrivate(play.getModifiers()));

        // 获取方法名字
        System.out.println(play.getName());

        // 获取方法形参相关
        System.out.println(play.getParameterCount());

        for (Class<?> parameterType : play.getParameterTypes()) {
            System.out.println(parameterType);
        }

        for (Parameter parameter : play.getParameters()) {
            System.out.println(parameter);
        }

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

        // 获取方法抛出的异常
        Class<?>[] exceptionTypes = play.getExceptionTypes();
        for (Class<?> exceptionType : exceptionTypes) {
            System.out.println(exceptionType);
        }
    }
}

Method 类中用于创建对象的方法

1
Object invoke(Object obj, Object... args)运行方法

参数一:用 obj 对象调用该方法

参数二:调用方法的传递的参数(如果没有就不写)

返回值:方法的返回值(如果没有就不写)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.company.reflection;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ReDemo8 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class<?> clazz = Class.forName("com.company.reflection.Student");
        Method play = clazz.getDeclaredMethod("play", String.class);
        Student stu = new Student("张三", 18, 86.5);

        // stu表示方法的调用者,"游戏"表示调用方法时传入的实际参数
        play.setAccessible(true);
        play.invoke(stu, "游戏");

        // 如果这个方法有返回值,则等于invoke方法的返回值
        Method sleep = clazz.getDeclaredMethod("sleep", int.class);
        sleep.setAccessible(true);
        String res = (String) sleep.invoke(stu, 3);
        System.out.println(res);
    }
}

综合练习

反射的作用:

① 获取一个类里面所有的信息,获取到了之后,再执行其他的业务逻辑

② 结合配置文件,动态的创建对象并调用方法

保存任意对象数据

对于任意一个对象,都可以把对象所有的字段名和值,保存到文件中去。

 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
package com.company.reflection;

public class Friend {
    private String name;
    private double salary;

    @Override
    public String toString() {
        return "Friend{" +
                "name='" + name + '\'' +
                ", salary=" + salary +
                '}';
    }

    public String getName() {
        return name;
    }

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

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public Friend() {
    }

    public Friend(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }
}

 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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package com.company.reflection;

public class People {
    private String name;
    private int age;
    private double height;
    private double weight;
    private String hobby;

    @Override
    public String toString() {
        return "People{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", height=" + height +
                ", weight=" + weight +
                ", hobby='" + hobby + '\'' +
                '}';
    }

    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 double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

    public double getWeight() {
        return weight;
    }

    public void setWeight(double weight) {
        this.weight = weight;
    }

    public String getHobby() {
        return hobby;
    }

    public void setHobby(String hobby) {
        this.hobby = hobby;
    }

    public People() {
    }

    public People(String name, int age, double height, double weight, String hobby) {
        this.name = name;
        this.age = age;
        this.height = height;
        this.weight = weight;
        this.hobby = hobby;
    }
}

 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
package com.company.reflection;

import java.io.*;
import java.lang.reflect.Field;

public class PraDemo1 {
    public static void main(String[] args) throws IllegalAccessException, IOException {
        Friend f = new Friend("张三", 8000);
        People p = new People("李四", 23, 175.3, 71.2, "篮球");

        // saveObject(f);
        saveObject(p);

    }

    // 把对象里的所有成员变量名和值保存到本地文件中
    public static void saveObject(Object obj) throws IllegalAccessException, IOException {
        // 创建本地文件
        File f1 = new File("src\\a.txt");
        if (!f1.exists()){
            f1.createNewFile();
        }

        // 创建IO流 -> 文件字符输出流 -> 缓冲字符输出流
        BufferedWriter bw = new BufferedWriter(new FileWriter(f1));

        // 获取class字节码文件
        Class<?> clazz = obj.getClass();
        // 获取所有成员变量
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            declaredField.setAccessible(true);
            // 获取成员变量的名字
            String name = declaredField.getName();
            // 获取成员变量的值
            Object value = declaredField.get(obj);
            // System.out.println(name + "=" + value);
            bw.write(name + ":" + value);
            bw.newLine();
        }

        bw.close();
    }
}

利用反射动态的创建对象和运行方法

反射可以跟配置文件结合的方式,动态的创建对象,并调用方法。

 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
package com.company.reflection;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

public class PraDemo2 {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 反射可以跟配置文件结合的方式,动态的创建对象,并调用方法。

        // 先读取配置文件中的信息
        Properties prop = new Properties();
        FileInputStream fis = new FileInputStream("src/com/company/reflection/prop.properties");
        prop.load(fis);
        fis.close();
        System.out.println(prop);

        // 获取全类名和方法名
        String className = (String) prop.get("classname");
        String methodName = (String) prop.get("method");
        System.out.println(className);
        System.out.println(methodName);

        // 利用反射创建对象并运行方法,首先获取字节码文件,再获取构造方法
        Class<?> clazz = Class.forName(className);
        Constructor<?> con = clazz.getDeclaredConstructor();

        // 使用newInstance方法创建对象
        Object o = con.newInstance();
        // 已过时方法
        // clazz.newInstance();

        // 根据方法名获取成员方法
        Method method = clazz.getDeclaredMethod(methodName);
        method.setAccessible(true);
        System.out.println(o);
        method.invoke(o);

        // 如果此时想要使用其他类中的方法,直接更改配置文件即可
    }
}

总结

反射的作用

① 获取任意一个类中的所有信息

② 结合配置文件动态创建对象

获得 class 字节码文件对象的三种方式

① class.forName(“全类名”);

② 类名.class

③ 对象.getClass();

如何获取构造方法、成员方法、成员变量

get:获取 set:设置

Constructor:构造方法 Parameter:参数

Field:成员变量 Modifiers:修饰符

Method:方法 Declared:私有的

Blog for Sandy Memories