一篇文章搞懂 java 反射
简述
Java 反射机制是指在程序运行状态中,对于任意一个类,都能获取其所有属性和方法;对于任意一个对象,都能调用其任意方法和属性。这种动态获取信息与动态调用对象方法的能力,就是 Java 反射机制的核心。
反射基础
- RTTI(运行时类型识别) :区别于编译时已确定的类型,RTTI 能在运行时识别对象或类的信息,而反射是 RTTI 的重要实现方式。
- 反射的本质:将类的属性、方法、构造器等组件 "解剖" 为一个个可操作的对象,从而实现动态操作类的能力。
Class 类
Java 中每个类或接口在 JVM 中都对应一个Class
对象(包括基本数据类型和 void),它是反射的核心入口。
Class 类的特点
-
是
java.lang
包下的 final 类,实现了多个接口。 -
构造方法为
private
,仅由 JVM 创建和加载,开发者无法手动实例化。 -
每个类在 JVM 中仅有一个对应的
Class
对象,全局唯一。
kotlin
public final class Class<T> implements java.io.Serializable,
GenericDeclaration,
Type,
AnnotatedElement {
// 源码片段:构造方法私有
private Class(ClassLoader loader) {
classLoader = loader;
}
}
类加载机制
-
编译 :Java 文件编译为
.class
文件,包含类的完整信息。 -
加载 :类加载器(ClassLoader)将
.class
文件加载到内存。 -
生成 Class 对象 :JVM 为每个类创建唯一的
Class
对象,后续所有实例对象都基于该Class
对象创建。
示意图逻辑 :
本地 / 网络的.class
文件 → 类加载器 → 内存生成唯一Class
对象 → 基于Class
对象创建多个实例对象。
反射的核心类库
Java 反射主要依赖java.lang.Class
类和java.lang.reflect
包,其中关键类包括:
Constructor
:类的构造方法Field
:类的成员变量Method
:类的成员方法Modifier
:访问修饰符工具类
常用方法速查表
方法 | 说明 |
---|---|
forName(String) |
通过全限定类名获取Class 对象,立即加载并初始化类 |
getClass() |
通过对象实例获取Class 对象,返回实际类型 |
getName() |
获取类的全限定名(含包名) |
getSimpleName() |
获取类名(不含包名) |
isXXX() |
判断类型(如isInterface() 是否为接口,isEnum() 是否为枚举) |
getXXX() |
获取单个成员(如getField(String) 获取 public 字段),含父类 public 成员 |
getXXXs() |
获取成员数组(如getMethods() 获取所有 public 方法),含父类 public 成员 |
getDeclaredXXX() |
获取本类声明的单个成员(含私有),不含父类 |
getDeclaredXXXs() |
获取本类声明的所有成员(含私有),不含父类 |
newInstance() |
通过无参构造器创建实例(需类有默认构造器) |
获取 Class 对象的三种方式
-
类名.class:通过类名直接获取,编译时确定类型。
-
对象.getClass () :通过实例对象获取,运行时确定实际类型。
-
Class.forName (全限定类名) :通过类路径动态获取,适用于未知类型。
java
// 示例
public class ReflectionDemo {
public static void main(String[] args) throws ClassNotFoundException {
// 方式1:类名.class
Class<Student> clazz1 = Student.class;
// 方式2:对象.getClass()
Student student = new Student();
Class<? extends Student> clazz2 = student.getClass();
// 方式3:Class.forName()
Class<?> clazz3 = Class.forName("com.example.Student");
}
}
Constructor 类:操作构造方法
Constructor
类用于获取和调用类的构造方法,支持访问私有构造器。
示例代码
arduino
public class Student {
private String name;
public int age;
public Student() {}
public Student(int age) { this.age = age; }
private Student(String name, int age) {
this.name = name;
this.age = age;
}
}
// 反射操作构造器
public class TestConstructor {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("com.example.Student");
// 调用无参构造器
Student s1 = (Student) clazz.newInstance();
// 调用public单参构造器
Constructor<?> c1 = clazz.getConstructor(int.class);
Student s2 = (Student) c1.newInstance(18);
// 调用私有构造器(需设置可访问)
Constructor<?> c2 = clazz.getDeclaredConstructor(String.class, int.class);
c2.setAccessible(true); // 解除私有访问限制
Student s3 = (Student) c2.newInstance("Tom", 20);
}
}
Field 类:操作成员变量
Field
类用于获取和修改类的成员变量,包括私有变量。
示例代码
csharp
public class TestField {
public static void main(String[] args) throws Exception {
Class<?> clazz = Student.class;
Student student = new Student();
// 获取public字段(含父类)
Field ageField = clazz.getField("age");
ageField.set(student, 20); // 修改public字段
System.out.println(ageField.get(student)); // 输出:20
// 获取私有字段(仅本类)
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true); // 解除私有限制
nameField.set(student, "Jerry");
System.out.println(nameField.get(student)); // 输出:Jerry
}
}
Method 类:操作成员方法
Method
类用于获取和调用类的成员方法,包括私有方法。
示例代码
csharp
public class Student {
private void showName() {
System.out.println("Name: " + name);
}
public int getAge() {
return age;
}
}
// 反射操作方法
public class TestMethod {
public static void main(String[] args) throws Exception {
Class<?> clazz = Student.class;
Student student = new Student();
// 调用私有方法
Method showMethod = clazz.getDeclaredMethod("showName");
showMethod.setAccessible(true);
showMethod.invoke(student); // 输出:Name: Jerry
// 调用public方法
Method getMethod = clazz.getMethod("getAge");
int age = (int) getMethod.invoke(student);
System.out.println("Age: " + age); // 输出:20
}
}
反射的实际应用:Spring IOC
Spring 的 IOC 容器核心依赖反射实现:
- 扫描类路径下的
@Component
注解类,通过Class.forName()
获取Class
对象。 - 解析类的属性,若存在
@Autowired
注解,通过Field
类设置属性可访问(setAccessible(true)
)。 - 自动注入依赖的 Bean 实例,实现 "控制反转"。
总结
反射是 Java 动态编程的核心技术,通过操作Class
、Constructor
、Field
和Method
等类,可在运行时灵活操作类的成员。尽管反射会牺牲部分性能并打破封装性,但在框架开发(如 Spring、MyBatis)中不可或缺。掌握反射,能更深入理解 Java 的底层机制和框架原理。