# 反射

# 一、Java反射机制概述

# 1.1、概述

Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内 部属性及方法。

加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看 到类的结构,所以,我们形象的称之为:反射

  • 正常的方式:引入需要的全类名 -> 通过new实例化 -> 取得实例化对象
  • 反射方式:实例化对象 -> 反射相关API -> 得到完整的全类名

关于动态语言和静态语言:

  • 动态语言,是一类在运行时可以改变其结构的语言。例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构。例如:Python、JavaScript、C#
  • 静态语言,与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、 C++。
  • Java不是动态语言,但Java可以称之为准动态语言。即Java有一定的动态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。

# 1.2、反射的应用

在框架开发中,都是基于配置文件开发。

在配置文件中配置了类,可以通过反射得到类中的所有内容,可以通过这些内容实现对象的创建,属性的修改,方法的调用。

类中的所有内容:

  • 属性
  • 构造方法
    • 无参构造方法
    • 有参构造方法
  • 普通方法
public class Person {
    private String name;
    private int age;
    private String addr;

    //默认构造方法
    //带参数的构造方法
	//get和set

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

public class MyTest1 {
    public static void main(String[] args) {
        //创建对象
        Person person = new Person();
        //调用对象的方法
        person.run();
    }
}

public class MyTest2 {
    public static void main(String[] args) throws Exception {
        //获取Class类的对象
        Class c1 = Class.forName("com.qfedu.test17.Person");
        //通过默认方法创建Person对象
        Object o = c1.newInstance();
        //获取Method
        Method run = c1.getMethod("run", null);
        //执行run方法
        run.invoke(o, 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
26
27
28
29
30
31
32
33
34
35

# 1.3、反射涉及的类

java.lang.Class:代表一个类

java.lang.reflect.Method:代表类的方法

java.lang.reflect.Field:代表类的成员变量

java.lang.reflect.Constructor:代表类的构造器

# 二、Class类及反射的原理

Class本身也是一个类,一个加载的类在JVM中只会有一个Class对象,一个Class对象对应的是一个加载到JVM中的一个.class文件。

通过Class可以完整地得到一个类中的所有被加载的结构。Class类是反射的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象。

# 三、反射相关操作

# 3.1、获取Class类的实例(重点)

  • 类名.class效率最高
  • 对象.getClass
  • Class.forName("全类名")可能出现异常
public class MyTest3 {
    public static void main(String[] args) throws ClassNotFoundException {
        //获取Class类对象的三种方式
        //类名.class
        Class<Person> c1 = Person.class;
        System.out.println(c1);

        //对象.getClass() getClass()是Object类中的方法
        Person p = new Person();
        Class<? extends Person> c2 = p.getClass();
        System.out.println(c2);

        //Class.forName(全类名)
        Class<?> c3 = Class.forName("com.qfedu.test17.Person");
        System.out.println(c3);

        System.out.println(c1 == c2);
        System.out.println(c2 == c3);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

哪些类型可以有Class对象:

  • class
  • interface接口
  • []数组
  • enum枚举
  • annotation注解
  • 基本数据类型
  • void

# 3.2、创建对象(重点)

要创建类的对象,可以new,不使用new,如何创建?

# 3.2.1、操作无参数的构造方法

Object newInstance():调用默认构造函数,返回该Class对象的一个实例

public class MyTest4 {
    public static void main(String[] args) throws Exception {
        //获取Class对象
        Class<?> c = Class.forName("com.qfedu.test17.Person");
        //操作默认的构造方法创建对象
        Object o = c.newInstance();
        //判断o是否是Person类的对象
        System.out.println(o instanceof Person);

        Person p = (Person)o;
        p.run();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 3.2.2、操作有参数的构造方法

Constructor<T> getConstructor(Class<?>... parameterTypes):取得本类的指定形参类型的构造器

public class MyTest5 {
    public static void main(String[] args) throws Exception {
        //获取Class对象
        Class<?> c = Class.forName("com.qfedu.test17.Person");
        //获取有参的构造方法
        Constructor<?> constructor = c.getConstructor(String.class, int.class, String.class);
        //调用有参的构造方法创建对象
        Object o = constructor.newInstance("zs", 12, "qd");
        //判断o是否是Person类的对象
        System.out.println(o instanceof Person);

        Person p = (Person)o;
        p.run();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 3.3、获取运行时类的指定结构(重点)

# 3.3.1、获取属性

在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()get()方法就可以完成设置和取得属性内容的操作。

通过Class类的对象获取Field

  • public Field getField(String name):返回此Class对象表示的类或接口的指定的publicField
  • public Field getDeclaredField(String name):返回此Class对象表示的类或接口的指定的Field

Field中:

  • public Object get(Object obj):取得指定对象obj上此Field的属性内容;
  • public void set(Object obj,Object value): 设置指定对象obj上此Field的属性内容。
public class MyTest6 {
    public static void main(String[] args) throws Exception {
        //获取Class对象
        Class<?> c = Class.forName("com.qfedu.test17.Person");
        //操作默认的构造方法创建对象
        Object o = c.newInstance();
        //判断o是否是Person类的对象
        System.out.println(o instanceof Person);

        //获取属性Field
        Field name = c.getDeclaredField("name");
        //设置允许访问
        name.setAccessible(true);
        //设置属性
        name.set(o, "Tom");
        System.out.println(o);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 3.3.2、获取方法

public class MyTest7 {
    public static void main(String[] args) throws Exception {
        //获取Class对象
        Class<?> c = Class.forName("com.qfedu.test17.Person");
        //操作默认的构造方法创建对象
        Object o = c.newInstance();
        //判断o是否是Person类的对象
        System.out.println(o instanceof Person);

        Method method = c.getDeclaredMethod("run", null);
        //执行方法
        method.invoke(o,null);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 3.3.3、综合案例

创建配置文件,设置类和要运行的方法,通过反射的方式运行配置文件中的方法

import java.io.FileInputStream;
import java.lang.reflect.Method;
import java.util.Properties;

public class MyTest8 {
    public static void main(String[] args) throws Exception {
        //加载并解析配置文件
        Properties prop = new Properties();
        prop.load(new FileInputStream("a.properties"));

        String myclass = prop.getProperty("myclass");
        String mymethod = prop.getProperty("mymethod");

        //System.out.println(myclass);
        //System.out.println(mymethod);

        //获取Class类的对象
        Class<?> c = Class.forName(myclass);
        //创建Class类的对象的实例对象
        Object o = c.newInstance();
        //获取Method
        Method method = c.getMethod(mymethod, null);
        //执行method
        method.invoke(o,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
26

配置文件

myclass=com.qfedu.test17.Person
mymethod=run
1
2

# 3.4、获取运行时类的完整结构

完整结构包括的内容:

  • 实现的全部接口
  • 所继承的父类
  • 全部的构造器
  • 全部的方法
  • 全部的Field

获取实现的全部接口:

  • public Class[] getInterfaces():获取此对象所表示的类或接口实现的接口

获得所继承的父类:

  • public Class getSuperclass()

获取全部的构造方法:

  • public Constructor[] getConstructors()
  • public Constructor[] getDeclaredConstructors()

获取全部的方法:

  • public Method[] getDeclaredMethods()
  • public Method[] getMethods()

获取全部的Field

  • public Field[] getFields()
  • public Field[] getDeclaredFields()
上次更新: 2024/4/13