一文掌握反射知识

2023-09-20 反射

通过反射可以获取到任意对象的任意方法、属性,也可以构造出任意对象,可跳过private等修饰符设定的权限,在Spirng、MyBtais等框架中大量使用。

# 通过反射获取Class对象

创建Student对象,包含成员变量和构造方法,其中有一个构造方法是私有的
public class Student {

    private String sid;
    private String sname;
    public Integer age;

    public Student() {
        System.out.println("调用无参构造方法创建了一个学生对象");
    }

    public Student(String sid) {
        this.sid = sid;
        System.out.println("调用带一个参数的构造方法创建了一个学生对象");
    }

    public Student(String sid, String sname) {
        this.sid = sid;
        this.sname = sname;
        System.out.println("调用带二个参数的构造方法创建了一个学生对象");
    }

    private Student(Integer age) {
        System.out.println("调用Student类私有的构造方法创建一个学生对象");
        this.age = age;
    }
}

通过反射获取Class对象有3种方法,如下所示:

public static void main(String[] args) throws ClassNotFoundException {
    // 1.方法一 
    Class class1 = new Student().getClass(); 
    System.out.println(class1.getName()); 

    // 2.方法二 
    Class class2 = Student.class;
    System.out.println(class2.getName());

    // 3.方法三 Class.forname
    Class class3 = Class.forName("com.xk857.demo.Student");
    System.out.println(class3);

    // 4.比较3个对象,发现获取的是同一个对象
    System.out.println(class1 == class2);
    System.out.println(class1 == class3);
}

# 通过反射操作构造函数

功能 函数
获取所有公共的构造方法 getConstructors()
获取所有构造方法(包含私有、默认的等) getDeclaredConstructors()
获取单个“公共”的构造方法 getConstructor(Type.class……)
获取单个构造方法(可以是私有的等) getDeclaredConstructor(Type.class……)
调用构造方法 newInstance(param……)
绕过访问修饰符权限 setAccessible(true)

案例如下,可自行增加换行或其他输出,使其在控制台打印更加明显。

@Test
public void test1() throws ClassNotFoundException, …… {
    Class class3 = Class.forName("com.xk857.demo.Student");

    // 1.获取所有公共的构造方法
    Constructor[] constructors = class3.getConstructors();
    for (Constructor constructor : constructors) {
        System.out.println(constructor);
    }

    // 2.所有构造方法(包含私有、默认的等)
    constructors = class3.getDeclaredConstructors();
    for (Constructor constructor : constructors) {
        System.out.println(constructor);
    }

    // 3.获取单个公共”的构造方法
    Constructor constructor1 = class3.getConstructor(String.class, String.class);
    System.out.println(constructor1+"\n");

    // 4.获取单个公共”的构造方法
    Constructor declaredConstructor = class3.getDeclaredConstructor(Integer.class);
    System.out.println(declaredConstructor+"\n");

    // 绕过访问修饰符权限,使private修饰的构造方法也能被调用
    declaredConstructor.setAccessible(true);
    // 通过newInstance创建对象
    Student student = (Student) declaredConstructor.newInstance(12);
}

# 通过反射操作变量

功能 函数
获取所有公共属性 getFields()
获取所有属性(包含私有、默认的等) getDeclaredFields()
获取单个"公共"字段 getField(字段名称)
获取单个字段(包含私有、默认的等) getDeclaredField(字段名称)
先通过反射获取对象,然后给对象设置属性值 field.set(对象实例, 属性值);
绕过访问修饰符权限 field.setAccessible(true)

案例如下,可自行增加换行或其他输出,使其在控制台打印更加明显。

@Test
public void test2() throws ClassNotFoundException, ……{
    Class class3 = Class.forName("com.xk857.demo.Student");

    // 1.获取所有公有字段
    Field[] fields = class3.getFields();
    Arrays.stream(fields).forEach(System.out::println);

    // 2.获取所有字段(包含私有)
    fields = class3.getDeclaredFields();
    Arrays.stream(fields).forEach(System.out::println);

    // 3.获取单个字段
    Field field = class3.getField("age");
    System.out.println(field);

    // 4.获取单个字段(包含私有)
    field = class3.getDeclaredField("sname");

    // 5.获取到Student对象,通过反射设置private修饰的sname属性
    Student student = (Student) class3.getConstructor().newInstance();
    field.setAccessible(true);
    field.set(student, "张三");
    System.out.println(student.getSname());
}

# 通过反射操作成员方法

给Student添加两个hello方法,一个公有一个私有。
public class Student {

    private String sid;
    private String sname;
    public Integer age;

    private void setSname(String sname) {
        this.sname = sname;
    }

    public void hello() {
        System.out.println("你好!我是" + this.sname);
    }

    private void hello(String name) {
        System.out.println(name + "你好!我是" + this.sname);
    }
}

功能 函数
获取所有公有方法,包含了父类的方法也包含Object类的wait、notify等方法 getMethods()
获取所有方法,包括私有但不包括继承 getDeclaredMethods()
获取单个公共方法 getMethod(方法名称)
获取任意方法(不包含父类) getDeclaredMethod("方法名称", 参数类型.class……)
执行方法 method.invoke(对象实例, 传递参数值……);

案例如下,可自行增加换行或其他输出,使其在控制台打印更加明显。

@Test
public void test3() throws ClassNotFoundException, …… {
    Class class3 = Class.forName("com.xk857.demo.Student");
    Student student = (Student) class3.getConstructor().newInstance();

    // 1.获取所有公有方法,包含了父类的方法也包含Object类的wait、notify等方法
    Method[] methods = class3.getMethods();
    Arrays.stream(methods).forEach(System.out::println);
    System.out.println();

    // 2.获取所有方法,包括私有但不包括继承
    methods = class3.getDeclaredMethods();
    Arrays.stream(methods).forEach(System.out::println);

    // 3.获取单个公共方法,并执行
    Method method = class3.getMethod("hello");
    method.invoke(student);

    // 4.获取私有的方法并执行
    method = class3.getDeclaredMethod("hello", String.class);
    method.setAccessible(true);
    method.invoke(student, "张三");
}
上次更新: 5 个月前