Java 反射机制核心类详解:Class、Constructor、Method、Field
反射是 Java 中一种强大的动态编程机制,它允许程序在运行时获取类的元数据(如类名、属性、方法、构造器等),并动态操作类的成员(即使是私有成员)。这种特性让 Java 具备了极高的灵活性,也是 Spring、MyBatis 等框架的核心底层技术。
本文将深入剖析反射机制的四大核心类:Class
(类对象)、Constructor
(构造方法)、Method
(成员方法)、Field
(成员属性),结合实例和图解,帮你彻底搞懂反射的工作原理。
一、反射的 “入口”:Class 类(类对象)
Class
类是反射的基础 —— 它是所有类的 “元数据容器”。每个类在 JVM 中都有且仅有一个对应的Class
对象,通过这个对象可以获取该类的所有信息(属性、方法、构造器等)。
1. 什么是 Class 对象?
当一个类被加载到 JVM 时,JVM 会为其创建一个Class
类型的对象(注意:Class
本身也是一个类),这个对象包含了该类的完整结构信息。我们可以把Class
对象理解为 “类的说明书”,通过它能知道这个类有哪些属性、方法、构造器,以及类的继承关系等。
2. 获取 Class 对象的 3 种方式
获取Class
对象是反射操作的第一步,常用的有 3 种方式,适用场景不同:
方式 | 代码示例 | 特点 |
---|---|---|
对象调用getClass() | User user = new User(); Class<?> clazz = user.getClass(); | 需先创建对象,适合已有实例的场景 |
类名直接访问.class | Class<?> clazz = User.class; | 无需创建对象,不会触发类的初始化(仅加载) |
Class.forName() | Class<?> clazz = Class.forName("com.example.User"); | 仅需类的全限定名,会触发类的初始化(执行静态代码块),适合动态加载类 |
3. Class 类的核心方法
Class
类提供了大量方法用于获取类的元数据,核心方法如下:
- 获取类名:
getName()
(全限定名)、getSimpleName()
(简单类名)- 获取构造器:
getConstructor(Class<?>...)
(公共构造器)、getDeclaredConstructor(Class<?>...)
(所有构造器,包括私有)- 获取方法:
getMethod(String, Class<?>...)
(公共方法,包括继承的)、getDeclaredMethod(String, Class<?>...)
(本类所有方法,包括私有)- 获取属性:
getField(String)
(公共属性,包括继承的)、getDeclaredField(String)
(本类所有属性,包括私有)- 判断类类型:
isInterface()
、isEnum()
、isArray()
等
4. 示例:通过 Class 对象获取类信息
public class User {private String name;private int age;public User() {}public User(String name, int age) {this.name = name;this.age = age;}public void sayHello() {System.out.println("Hello, " + name);}
}// 测试类
public class ReflectionDemo {public static void main(String[] args) throws ClassNotFoundException {// 获取Class对象(3种方式任选)Class<?> clazz = Class.forName("com.example.User");// 获取类名System.out.println("全限定名:" + clazz.getName()); // com.example.UserSystem.out.println("简单类名:" + clazz.getSimpleName()); // User// 获取所有公共构造器Constructor<?>[] constructors = clazz.getConstructors();for (Constructor<?> c : constructors) {System.out.println("公共构造器:" + c);}// 获取所有公共方法Method[] methods = clazz.getMethods();for (Method m : methods) {System.out.println("公共方法:" + m);}}
}
5. 图解 Class 类的角色
上图展示了核心关系:
- 每个类(如
User
)在 JVM 中对应唯一的Class
对象;- 类的所有实例(
user1
、user2
)都可以通过getClass()
获取同一个Class
对象;Class
对象存储了类的完整元数据,是反射的 “入口”。
二、构造方法的 “操纵者”:Constructor 类
Constructor
类用于描述类的构造方法,通过它可以在运行时创建类的实例(即使是私有构造方法)。
1. 获取 Constructor 对象
通过Class
对象的方法获取Constructor
:
getConstructor(Class<?>... parameterTypes)
:获取公共构造器(参数为构造器的参数类型);getDeclaredConstructor(Class<?>... parameterTypes)
:获取所有构造器(包括私有);getConstructors()
:获取所有公共构造器(返回数组);getDeclaredConstructors()
:获取所有构造器(包括私有,返回数组)。
2. 创建对象:newInstance ()
获取Constructor
对象后,调用newInstance(Object... initArgs)
方法即可创建实例,参数为构造方法的入参。
注意:如果是私有构造器,需要先调用setAccessible(true)
突破访问权限(否则会抛IllegalAccessException
)。
3. 示例:通过 Constructor 创建对象
public class ReflectionDemo {public static void main(String[] args) throws Exception {Class<?> clazz = User.class;// 获取无参构造器(公共)Constructor<?> constructor1 = clazz.getConstructor();User user1 = (User) constructor1.newInstance(); // 等价于 new User()// 获取有参构造器(公共)Constructor<?> constructor2 = clazz.getConstructor(String.class, int.class);User user2 = (User) constructor2.newInstance("张三", 20); // 等价于 new User("张三", 20)// 获取私有构造器(假设User有一个私有构造器:private User(String name))Constructor<?> constructor3 = clazz.getDeclaredConstructor(String.class);constructor3.setAccessible(true); // 开启访问权限User user3 = (User) constructor3.newInstance("李四"); // 成功创建私有构造器实例}
}
4. 图解 Constructor 的作用
上图展示:
Class
对象包含类的所有构造器(通过Constructor
对象表示);- 每个
Constructor
对象对应一个构造方法,通过newInstance()
可创建实例;- 私有构造器需要通过
setAccessible(true)
开启访问权限。
三、方法的 “执行者”:Method 类
Method
类用于描述类的成员方法,通过它可以在运行时调用任意方法(包括私有方法和静态方法)。
1. 获取 Method 对象
通过Class
对象的方法获取Method
:
getMethod(String name, Class<?>... parameterTypes)
:获取公共方法(包括继承的,参数为方法名和参数类型);getDeclaredMethod(String name, Class<?>... parameterTypes)
:获取本类所有方法(包括私有,不包括继承的);getMethods()
:获取所有公共方法(包括继承的,返回数组);getDeclaredMethods()
:获取本类所有方法(包括私有,返回数组)。
2. 调用方法:invoke ()
获取Method
对象后,调用invoke(Object obj, Object... args)
方法即可执行方法:
obj
:调用方法的实例(静态方法可传null
);args
:方法的入参。
注意:私有方法需要先调用setAccessible(true)
突破权限。
3. 示例:通过 Method 调用方法
public class ReflectionDemo {public static void main(String[] args) throws Exception {Class<?> clazz = User.class;User user = new User("张三", 20);// 获取公共实例方法sayHello()Method sayHelloMethod = clazz.getMethod("sayHello");sayHelloMethod.invoke(user); // 输出:Hello, 张三// 获取私有方法(假设User有私有方法:private void setAge(int age))Method setAgeMethod = clazz.getDeclaredMethod("setAge", int.class);setAgeMethod.setAccessible(true); // 开启访问权限setAgeMethod.invoke(user, 25); // 调用私有方法// 获取静态方法(假设User有静态方法:public static void showVersion())Method showVersionMethod = clazz.getMethod("showVersion");showVersionMethod.invoke(null); // 静态方法obj传null}
}
4. 图解 Method 的作用
上图展示:
Class
对象包含类的所有方法(通过Method
对象表示);- 调用实例方法时,
invoke()
的第一个参数为实例对象;- 调用静态方法时,
invoke()
的第一个参数为null
。
四、属性的 “读写器”:Field 类
Field
类用于描述类的成员属性,通过它可以在运行时读写任意属性(包括私有属性)。
1. 获取 Field 对象
通过Class
对象的方法获取Field
:
getField(String name)
:获取公共属性(包括继承的,参数为属性名);getDeclaredField(String name)
:获取本类所有属性(包括私有,不包括继承的);getFields()
:获取所有公共属性(包括继承的,返回数组);getDeclaredFields()
:获取本类所有属性(包括私有,返回数组)。
2. 读写属性:get () 和 set ()
get(Object obj)
:获取对象obj
的该属性值(静态属性obj
传null
);set(Object obj, Object value)
:设置对象obj
的该属性值(静态属性obj
传null
)。
注意:私有属性需要先调用setAccessible(true)
突破权限。
3. 示例:通过 Field 读写属性
public class ReflectionDemo {public static void main(String[] args) throws Exception {Class<?> clazz = User.class;User user = new User("张三", 20);// 获取私有属性nameField nameField = clazz.getDeclaredField("name");nameField.setAccessible(true); // 开启访问权限String name = (String) nameField.get(user); // 读值:张三nameField.set(user, "李四"); // 设值// 获取私有属性ageField ageField = clazz.getDeclaredField("age");ageField.setAccessible(true);int age = (int) ageField.get(user); // 读值:20ageField.set(user, 25); // 设值// 获取静态属性(假设User有静态属性:public static String version)Field versionField = clazz.getField("version");String version = (String) versionField.get(null); // 静态属性obj传nullversionField.set(null, "1.0.1");}
}
4. 图解 Field 的作用
上图展示:
Class
对象包含类的所有属性(通过Field
对象表示);- 读写实例属性时,
get()
/set()
的参数为实例对象;- 读写静态属性时,
get()
/set()
的参数为null
。
五、反射的优缺点与应用场景
优点
- 动态性:运行时获取类信息并操作,突破编译期限制(如动态加载未知类);
- 灵活性:可操作私有成员,适合框架开发(如 Spring 的依赖注入);
- 通用性:统一处理不同类的对象(如 ORM 框架映射数据库字段)。
缺点
- 性能开销:反射操作绕过编译期检查,比直接调用慢(约 10-100 倍);
- 安全性问题:
setAccessible(true)
可突破访问权限,可能破坏封装性; - 代码可读性差:反射代码较繁琐,不如直接调用直观。
典型应用场景
- 框架底层:Spring IOC(通过反射创建 Bean)、MyBatis(通过反射映射结果集);
- 注解处理:JUnit 的
@Test
、Spring 的@Autowired
(通过反射解析注解);- 动态代理:AOP 的实现基础(通过反射增强方法);
- 序列化 / 反序列化:JSON 框架(如 Jackson)通过反射读写对象属性。
六、总结
反射机制的四大核心类分工明确:
Class
:反射的入口,存储类的元数据;Constructor
:操纵构造方法,创建对象;Method
:执行任意方法(包括私有、静态);Field
:读写任意属性(包括私有、静态)。
掌握反射不仅能理解框架底层原理,更能在需要动态编程的场景中(如工具类开发)发挥巨大作用。但需注意:反射是 “双刃剑”,应避免在性能敏感场景滥用,且需谨慎处理访问权限问题。
原创声明:本文为CSDN博主梵得儿SHI原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
希望本文能帮你彻底搞懂 Java 反射的核心逻辑!如果有疑问,欢迎在评论区交流~