Java反射

12/20/2021 Java反射

Java反射

Java高级是面向高级语法部分,主要是服务于一些高级的操作,例如文件、异常、注解、数据结构、多任务、网络通信和数据库等。

Java的反射(reflection)机制是指在程序运行状态中,可以获取和调用任意类的属性和方法。这种动态获取类的信息和动态调用的方法的功能称为Java语言的反射机制。

# 类加载器

# 类加载

当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。

  • 加载

    就是指将class文件读入内存,并为之创建一个Class对象。

  • 连接

    • 验证:是否有正确的内部结构,并和其他类协调一致
    • 准备:负责为类的静态成员分配内存,并设置默认初始化值
    • 解析:将类的二进制数据中的符号引用替换为直接引用
  • 初始化

# 类初始化

  1. 创建类的实例
  2. 类的静态变量,或者为静态变量赋值
  3. 类的静态方法
  4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
  5. 初始化某个类的子类
  6. 直接使用java.exe命令来运行某个主类

# 类加载器

负责将.class文件加载到内存中,并为之生成对应的class对象。

# 类加载器的组成

  • Bootstrap ClassLoader 根类加载器

    也被称为引导类加载器,负责Java核心类的加载。

  • Extension ClassLoader 扩展类加载器

    负责JRE的扩展目录中jar包的加载。在JDK中JRE的lib目录下ext目录

  • System ClassLoader 系统类加载器

    负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。

# 反射

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

想要解剖一个类,就必须先要获取该类的字节码文件对象。而解剖的关键就是使用Class类中的方法。因此先要对Class类进行一定的了解。

# Class类型

阅读API的Class类得知,Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。

获取Class对象的三种方式:

  • 通过Object类的getClass()方法

    Person p = new Person();
    Class c = p.getClass();
    
    1
    2
  • 通过类名.class获取字节码文件对象(任何数据类型都具备一个class静态属性)

    Class c2 = Person.class;
    
    1
  • 通过Class类中的方法(将类名作为字符串传递给Class类中的静态方法forName)

    Class c3 = Class.forName("Person");
    
    1

注意:第三种和前两种的区别。前两种你必须明确Person类型,后面是指定这种类型的字符串就行,这种扩展更强。我不需要知道你的类,我只提供字符串,按照配置文件加载就可以了。

Person类

public class Person05 {
    // 成员变量
    public String name;
    public int age;
    private String address;

    // 构造方法
    public Person05() {
        System.out.println("空参数构造方法");
    }

    public Person05(String name) {
        this.name = name;
        System.out.println("带有String的构造方法");
    }

    private Person05(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("带有String,int的构造方法");
    }

    public Person05(String name, int age, String address){
        this.name = name;
        this.age = age;
        this.address = address;
        System.out.println("带有String, int, String的构造方法");
    }

    //成员方法
    //没有返回值没有参数的方法
    public void method1(){
        System.out.println("没有返回值没有参数的方法");
    }
    //没有返回值,有参数的方法
    public void method2(String name){
        System.out.println("没有返回值,有参数的方法 name= "+ name);
    }
    //有返回值,没有参数
    public int method3(){
        System.out.println("有返回值,没有参数的方法");
        return 123;
    }
    //有返回值,有参数的方法
    public String method4(String name){
        System.out.println("有返回值,有参数的方法");
        return "哈哈" + name;
    }
    //私有方法
    private void method5(){
        System.out.println("私有方法");
    }

    @Override
    public String toString() {
        return "Person05 [name=" + name + ", age=" + age + ", address=" + address+ "]";
    }
}
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

Class类获取演示

/*
 * 获取.class字节码文件对象的方式
 * 		1:通过Object类中的getObject()方法
 * 		2: 通过 类名.class 获取到字节码文件对象
 * 		3: 反射中的方法,
 * 			public static Class<?> forName(String className) throws ClassNotFoundException
 * 			返回与带有给定字符串名的类或接口相关联的 Class 对象 
 */
public class ReflectDemo01 {
    public static void main(String[] args) throws ClassNotFoundException {
        // 1. 通过Object类中的getClass()方法
        Person05 p5 = new Person05();
        Class<? extends Person05> c1 = p5.getClass();
        System.out.println("c1 = " + c1);

        // 2. 通过类名.class 获取字节码对象
        Class<Person05> c2 = Person05.class;
        System.out.println("c2 = " + c2);

        //3. 反射中的方法
        Class<?> c3 = Class.forName("Person05");
        System.out.println("c3 = " + c3);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 通过反射获取构造方法并使用

在反射机制中,把类中的成员(构造方法、成员方法、成员变量)都封装成了对应的类进行表示。其中,构造方法使用类Constructor表示。可以通过Class类提供的方法获取构造方法。

  • 返回一个构造方法

    public Constructor<T> getConstructor(Class<?>... parameterTypes)  //获取public修饰, 指定参数类型所对应的构造方法
    public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) //获取指定参数类型所对应的构造方法(包含私有的)
    
    1
    2
  • 返回多个构造方法

    public Constructor<?>[] getConstructors() //获取所有的public 修饰的构造方法
    public Constructor<?>[] getDeclaredConstructors() //获取所有的构造方法(包含私有的)
    
    1
    2
import java.lang.reflect.Constructor;

/**
 * 反射演示:通过反射获取构造方法并使用
 */
public class ReflectDemo02 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
        // 获取Class对象
        Class<?> c = Class.forName("Person05");

        // 获取所有的构造方法
        Constructor<?>[] cons = c.getDeclaredConstructors();
        for (Constructor con : cons) {
            System.out.println(con);
        }

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

        // 获取一个构造方法

        // 空参构造 public Person()
        Constructor<?> con1 = c.getConstructor(null);
        System.out.println(con1);

        // 单个参数构造 public Person(String name)
        Constructor<?> con2 = c.getConstructor(String.class);
        System.out.println(con2);

        // 私有多个参数构造 private Person(String name, int age)
        Constructor<?> con3 = c.getDeclaredConstructor(String.class, int.class);
        System.out.println(con3);

        // 公共多个参数构造 public Person(String name, int age, String address)
        Constructor<?> con4 = c.getDeclaredConstructor(String.class, int.class, String.class);
        System.out.println(con4);
    }
}
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

# 获取公共构造方法和创建对象

获取构造方法和创建对象,步骤如下:

  1. 获取到Class对象

  2. 获取指定的构造方法

  3. 通过构造方法Constructor中的方法,创建对象

    public T newInstance(Object... initargs)

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

/**
 * 反射演示:通过反射获取公共构造方法并使用
 */
public class ReflectDemo03 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 1.获取Class对象
        Class<?> c = Class.forName("Person05");

        // 2. 获取指定的构造方法
        // 空参构造 public Person5()
        Constructor<?> con = c.getConstructor(null);

        // 公共多个参数构造 public Person5(String name, int age, String address)
        Constructor<?> con1 = c.getConstructor(String.class, int.class, String.class);

        // 3. 通过构造方法类中newInstance的方法,创建对象
        Object obj = con.newInstance(null); // 空参数构造
        Object obj1 = con1.newInstance("小米", 25, "长沙");
        System.out.println(obj);
        System.out.println(obj1);
    }
}
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

# 获取私有构造方法和创建对象

AccessibleObject类是Field、Method和Constructor对象的父类。它提供了将反射的对象标记为在使用时取消默认Java语言访问控制检查的能力。

对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用Field、Method或Constructor对象来设置或者获取字段、调用方法、或者创建和初始化类的新实例的时候,会执行访问检查。常用的方法如下:

 public void setAccessible(boolean flag) throws SecurityException 
1
  • 参数值为true则表示反射的对象在使用时应该取消Java语言访问检查。
  • 参数为false则表示反射的对象要实施Java语言访问检查。

获取私有构造方法和创建对象,步骤如下:

  1. 获取到Class对象
  2. 获取指定的构造方法
  3. 暴力访问, 通过setAccessible(boolean flag)方法
  4. 通过构造方法类Constructor中的方法,创建对象 public T newInstance(Object... initargs)
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * 反射演示:通过反射获取私有构造方法并使用
 */
public class ReflectDemo04 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 1.获取Class对象
        Class<?> c = Class.forName("Person05");

        // 2. 获取指定的构造方法
        // 空参构造 private Person5(String name, int age)
        Constructor<?> con = c.getDeclaredConstructor(String.class, int.class);

        //3. 暴力反射
        con.setAccessible(true); // 取消Java语言访问检查

        // 4. 通过构造方法类中newInstance的方法,创建对象
        Object obj = con.newInstance("小米", 24); // 空参数构造
        System.out.println(obj);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 通过反射获取成员变量并使用

在反射机制中,把类中的成员变量使用类Field表示。可通过Class类中提供的方法获取成员变量。

  • 返回一个成员变量

    public Field getField(String name) //获取指定的 public修饰的变量
    public Field getDeclaredField(String name) //获取指定的任意变量
    
    1
    2
  • 返回多个成员变量

    public Field[] getFields() //获取所有public 修饰的变量
    public Field[] getDeclaredFields() //获取所有的 变量 (包含私有)
    
    1
    2
import java.lang.reflect.Field;

/**
 * 反射演示:通过反射获取成员变量
 */
public class ReflectDemo05 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        // 获取Class对象
        Class<?> c = Class.forName("Person05");

        // 获取成员变量
        // 多个公共变量
        Field[] fields = c.getFields();
        for(Field field: fields) {
            System.out.println(field);
        }
        System.out.println("--------------------------");

        // 单个公共变量
        Field ageField = c.getField("age");
        System.out.println(ageField);

        // 多个私有变量
        Field[] declaredFields = c.getDeclaredFields();
        for(Field field : declaredFields) {
            System.out.println(field);
        }

        // 单个私有变量
        Field addressField = c.getDeclaredField("address");
        System.out.println(addressField);
    }
}
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

# 获取成员变量和进行赋值和取值操作

获取成员变量,步骤如下:

  1. 获取Class对象

  2. 获取构造方法

  3. 通过构造方法,创建对象

  4. 获取指定的成员变量(私有成员变量,通过setAccessible(boolean flag)方法暴力访问)

  5. 通过方法,给指定对象的指定成员变量赋值或者获取值

    • public void set(Object obj, Object value)

      在指定对象obj中,将此 Field 对象表示的成员变量设置为指定的新值

    • public Object get(Object obj)

      返回指定对象obj中,此 Field 对象表示的成员变量的值

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

/**
 * 反射演示:通过反射获取成员变量并使用
 */
public class ReflectDemo06 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        // 1.获取Class对象
        Class<?> c = Class.forName("Person05");
        // 2.获取构造方法
        //public Person(String name)
        Constructor con = c.getConstructor(String.class);
        //3,通过构造方法,创建对象
        Object obj = con.newInstance("小明");
        //4,获取指定的成员变量
        //public String name;
        Field nameField = c.getField("name");
        //public int age;
        Field ageField = c.getField("age");
        //private String address;
        Field addressField = c.getDeclaredField("address");
        addressField.setAccessible(true); //取消 Java 语言访问检查

        //5. 通过方法,给指定对象的指定成员变量赋值或者获取值
        System.out.println("name = " + nameField.get(obj));
        System.out.println("age = " + ageField.get(obj));
        System.out.println("address = " + addressField.get(obj));

        // 赋值
        ageField.set(obj, 23);
        addressField.set(obj, "凯莉广场");

        System.out.println("------------------------");
        System.out.println("name = "+ nameField.get(obj));
        System.out.println("age = "+ ageField.get(obj));
        System.out.println("address = "+ addressField.get(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
31
32
33
34
35
36
37
38
39
40

# 通过反射获取成员方法并使用

在反射机制中,把类中的成员方法使用类Method表示。可以通过Class类中提供的方法获取成员方法。

  • 返回一个方法

    public Method getMethod(String name, Class<?>... parameterTypes) //获取public 修饰的方法
    public Method getDeclaredMethod(String name, Class<?>... parameterTypes) //获取任意的方法,包含私有的
        // 参数1: name 要查找的方法名称; 参数2: parameterTypes 该方法的参数类型
    
    1
    2
    3
  • 返回多个方法

    public Method[] getMethods() //获取本类与父类中所有public 修饰的方法
    public Method[] getDeclaredMethods() //获取本类中所有的方法(包含私有的)
    
    1
    2
import java.lang.reflect.Method;

/**
 * 反射演示:通过反射获取成员方法
 */
public class ReflectDemo07 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
        //获取Class对象
        Class c = Class.forName("Person05");

        // 获取多个公共方法
        Method[] methods = c.getMethods();
        for(Method method : methods) {
            System.out.println(method);
        }

        System.out.println("---------------");
        // 获取单个公共方法
        // public void method1() 没有返回值没有参数的方法
        Method method1 = c.getMethod("method1", null);
        // public void method2(String name) 没有返回值,有参数的方法
        Method method2 = c.getMethod("method2", String.class);
        // public String method3() 有返回值,没有参数
        Method method3 = c.getMethod("method3", null);
        // public String method4(String name) 有返回值,有参数
        Method method4 = c.getMethod("method4", String.class);

        System.out.println(method1);
        System.out.println(method2);
        System.out.println(method3);
        System.out.println(method4);

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

        // 获取多个私有方法
        Method[] declaredMethods = c.getDeclaredMethods();
        for(Method method : declaredMethods){
            System.out.println(method);
        }

        // 获取单个私有方法
        Method method5 = c.getDeclaredMethod("method5", null);
        System.out.println(method5);

    }
}
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

# 获取公共成员方法和调用

获取公共成员方法和调用,步骤如下:

  1. 获取Class对象
  2. 获取构造方法
  3. 通过构造方法,创建对象
  4. 获取指定的方法
  5. 执行找到的方法
    • public Object invoke(Object obj, Object... args) 执行指定对象obj中,当前Method对象所代表的方法,方法要传入的参数通过args指定。
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 反射演示:通过反射获取公共成员方法并调用
 */
public class ReflectDemo08 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 1. 获取Class对象
        Class<?> c = Class.forName("Person05");
        //2,获取构造方法
        //public Person(String name, int age, String address){
        Constructor con = c.getConstructor(String.class, int.class, String.class);
        //3,通过构造方法,创建对象
        Object obj = con.newInstance("小明", 23, "哈尔滨");
        // 4.获取指定方法
        // public void method1()  没有返回值没有参数的方法
        Method m1 = c.getMethod("method1", null);

        //public String method4(String name)
        Method m4 = c.getMethod("method4", String.class);

        // 5. 执行方法
        m1.invoke(obj, null);

        Object result = m4.invoke(obj, "snake8859");
        System.out.println("result = " + result);
    }
}
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

# 获取私有方法和调用

获取私有成员方法和调用,步骤如下:

  1. 获取Class对象
  2. 获取构造方法
  3. 通过构造方法,创建对象
  4. 获取指定的方法
  5. 开启暴力访问
  6. 执行找到的方法
    • public Object invoke(Object obj, Object... args) 执行指定对象obj中,当前Method对象所代表的方法,方法要传入的参数通过args指定。
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 反射演示:通过反射获取私有成员方法并调用
 */
public class ReflectDemo09 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 1. 获取Class对象
        Class<?> c = Class.forName("Person05");
        //2,获取构造方法
        //public Person(String name, int age, String address){
        Constructor con = c.getConstructor(String.class, int.class, String.class);
        //3,通过构造方法,创建对象
        Object obj = con.newInstance("小明", 23, "哈尔滨");
        // 4.获取指定方法
        //private void method5(){  没有返回值没有参数的四月方法
        Method m5 = c.getDeclaredMethod("method5", null);
        // 5.开启暴力访问
        m5.setAccessible(true);
        // 6. 执行方法
        m5.invoke(obj, null);
    }
}
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

# 总结

# 如何获取.Class文件对象

  1. 通过Object类 getClass()方法获取 Class对象
  2. 通过类名.class 方式 获取 Class对象
  3. 通过反射的方式, Class.forName(String classname) 获取Class对象 public static Class<?> forName(String className)throws ClassNotFoundException 返回与带有给定字符串名的类或接口相关联的 Class 对象

# 通过反射、获取构造方法和创建对象

# 构造方法获取

  • 获取指定的构造方法 public Constructor<T> getConstructor(Class<?>... parameterTypes):获取指定的public修饰的构造方法 public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes):获取指定的构造方法,包含私有的
  • 获取所有的构造方法 public Constructor<?>[] getConstructors() :获取所有的public 修饰的构造方法 public Constructor<?>[] getDeclaredConstructors(): 获取所有的构造方法,包含私有的

# 创建对象

  • 公共构造方法

    1. 获取字节码文件对象
    2. 通过字节码文件对象 ,获取到指定的构造方法 getConstructor(参数);
    3. 通过构造方法,创建对象 public T newInstance(Object... initargs)
  • 私有构造方式

    1. 获取字节码文件对象

    2. 通过字节码文件对象 ,获取到指定的构造方法 getDeclaredConstructor (参数);

    3. 暴力访问

      con.setAccessible(true);

    4. 通过构造方法,创建对象 public T newInstance(Object... initargs)

# 通过反射,获取成员变量和赋值取值

# 成员变量获取

  • 获取指定的成员变量 public Field getField(String name) 获取public修饰的成员变量 public Field getDeclaredField(String name) 获取任意的成员变量,包含私有
  • 获取所有的成员变量 public Field[] getFields() 获取所有public修饰的成员变量 public Field[] getDeclaredFields() 获取所有的成员变量,包含私有

# 赋值和取值

  • 公共
    1. 获取字节码文件对象
    2. 获取构造方法,创建对象
    3. 获取指定的成员变量
    4. 对成员变量赋值\获取值操作 public void set(Object obj, Object value) 赋值 public Object get(Object obj) 获取值
  • 私有
    1. 获取字节码文件对象
    2. 获取构造方法,创建对象
    3. 获取指定的成员变量
    4. 开启暴力访问
    5. 对成员变量赋值\获取值操作 public void set(Object obj, Object value) 赋值 public Object get(Object obj) 获取值

# 通过反射,获取成员方法和调用

# 成员方法获取

  • 获取指定的方法 public Method getMethod(String name, Class<?>... parameterTypes) 获取指定的public方法 public Method getDeclaredMethod(String name, Class<?>... parameterTypes) 获取指定的任意方法,包含私有的
  • 获取所有的方法 public Method[] getMethods() 获取本类与父类中所有public 修饰的方法 public Method[] getDeclaredMethods()获取本类中所有的方法,包含私有的

# 成员方法调用

  • 公共

    1. 获取Class对象

    2. 获取构造方法,创建对象

    3. 获取指定的public方法

    4. 执行方法

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

  • 私有

    1. 获取Class对象

    2. 获取构造方法,创建对象

    3. 获取指定的private方法

    4. 开启暴力访问

    5. 执行方法

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

Last Updated: 11/21/2022, 10:03:43 PM