一、反射概述
1.1 反射是什么?
Java 反射是指在程序运行时动态地获取类的信息并对其进行操作的能力。在 Java 中,每个类都有一个 Class 对象,该对象包含了该类的所有信息,包括类名、父类、方法、字段等等。通过反射,我们可以在运行时获取并操作这些信息,而不需要在编译时知道类的具体实现。
1.2 反射的作用和优劣
反射在 Java 中具有广泛的应用,比如:
- 动态创建对象
- 动态调用方法
- 动态加载类
- 获取类的信息
- 实现代理和 AOP 等等
反射的优点是非常灵活,可以在运行时根据需要动态地加载和操作类。但是反射也有一些缺点,比如:
- 反射调用方法的性能相对较低,因为需要进行额外的类型检查和方法调用。
- 反射破坏了 Java 的封装性,可以访问和修改类的私有成员和方法,导致安全风险。
- 反射使用起来比较复杂,需要理解 Java 类型系统和反射 API 的细节。
1.3 反射相关的类和接口
Java 反射的相关类和接口主要有:
- Class 类:表示一个类或接口的类型。每个对象都有一个 getClass() 方法,可以获取其对应的 Class 对象。
- Constructor 类:表示一个类的构造方法。
- Field 类:表示一个类的字段。
- Method 类:表示一个类的方法。
- Modifier 类:提供了一些方法,用于访问和修改类、字段和方法的修饰符。
二、动态类加载
2.1 动态加载类的方式
Java 可以使用两种方式动态加载类:
- Class.forName() 方法:该方法根据类的完整路径名加载类,返回对应的 Class 对象。需要注意的是,该方法会抛出 ClassNotFoundException 异常,需要进行捕获或声明抛出。
- ClassLoader 类:ClassLoader 是用于加载类的一个抽象类,Java 提供了多种实现,比如 URLClassLoader 和 AppClassLoader。使用 ClassLoader 加载类的方式更加灵活,可以从不同的位置加载类,比如本地文件系统、网络等等。
2.2 Class.forName() 和 ClassLoader 的区别
使用 Class.forName() 加载类时,会自动初始化该类,包括执行静态代码块和初始化静态成员变量。而使用 ClassLoader 加载类时,可以控制类的初始化时机,只有在需要使用类时才会进行初始化。
2.3 加载外部类和本地类的区别
Java 中的类可以分为两类:外部类和本地类。外部类是指存储在磁盘上的类文件,而本地类是指在当前程序中定义的类。
加载外部类需要指定类文件的路径,比如:
Class clazz = Class.forName("com.example.MyClass", true, ClassLoader.getSystemClassLoader());
其中第一个参数是类的完整路径名,第二个参数表示是否进行初始化,第三个参数是 ClassLoader。
加载本地类则可以直接使用 Class 对象,比如:
MyClass myObj = new MyClass();
Class clazz = myObj.getClass();
其中 myObj 是一个 MyClass 对象,通过 getClass() 方法获取该对象的 Class 对象。
三、动态调用方法
3.1 反射获取类和方法对象
在使用反射调用方法之前,需要先获取对应的类和方法对象。可以使用 Class 类的以下方法获取类对象:
- Class.forName():根据类的完整路径名加载类。
- 对象的 getClass() 方法:获取对象的类对象。
- 类的字面常量:使用类的字面常量获取类对象,比如 MyClass.class。
获取方法对象可以使用 Class 类的以下方法: