Java 基础反射(Reflection)详解与实战案例
·
Java 基础反射(Reflection)详解与实战案例
一、反射概述
反射(Reflection)是 Java 语言的一种高级特性,它允许程序在运行时获取类的信息(如类名、属性、方法、构造器等),并能动态操作类的成员(如创建对象、调用方法、修改属性等)。
反射的核心是 java.lang.Class 类和 java.lang.reflect 包下的 API(如 Constructor、Field、Method 等)。通过这些 API,我们可以突破编译期的限制,实现动态编程。
为什么需要反射?
- 框架开发的基础(如 Spring 的 IOC、MyBatis 的 mapper 映射);
- 动态调用类的方法或修改属性,提升代码灵活性;
- 适配不同版本的类或第三方库,增强程序扩展性。
二、反射核心 API
| 类 / 接口 | 作用 | 常用方法举例 |
|---|---|---|
Class |
代表类的字节码对象,反射的入口 | getFields()、getMethods() |
Constructor |
代表类的构造方法 | newInstance()、getParameterTypes() |
Field |
代表类的成员变量 | get()、set()、setAccessible() |
Method |
代表类的成员方法 | invoke()、getReturnType() |
三、反射实战案例
3.1 准备工作:定义示例类
为了演示反射的使用,我们先定义一个 User 类作为操作目标:
package com.example.reflection;
import java.io.Serializable;
/**
* 用户类(用于反射演示)
*/
public class User implements Serializable {
// 私有属性
private String username;
private int age;
// 公有属性
public String address;
// 无参构造
public User() {
}
// 有参构造
public User(String username, int age) {
this.username = username;
this.age = age;
}
// 私有构造
private User(String username) {
this.username = username;
}
// 普通方法
public String getUsername() {
return username;
}
private void setUsername(String username) {
this.username = username;
}
// 静态方法
public static void printInfo() {
System.out.println("这是 User 类的静态方法");
}
@Override
public String toString() {
return "User{username='" + username + "', age=" + age + ", address='" + address + "'}";
}
}
3.2 案例 1:获取 Class 对象(反射的入口)
Class 对象是反射的基础,获取 Class 对象有 3 种方式:
public class ReflectionDemo1 {
public static void main(String[] args) throws ClassNotFoundException {
// 方式 1:通过对象的 getClass() 方法(需先创建对象)
User user = new User();
Class<?> clazz1 = user.getClass();
System.out.println("方式 1:" + clazz1.getName()); // 输出:com.example.reflection.User
// 方式 2:通过 类名.class(编译期确定,无需创建对象)
Class<?> clazz2 = User.class;
System.out.println("方式 2:" + clazz2.getName()); // 输出:com.example.reflection.User
// 方式 3:通过 Class.forName("全类名")(最常用,动态加载类)
Class<?> clazz3 = Class.forName("com.example.reflection.User");
System.out.println("方式 3:" + clazz3.getName()); // 输出:com.example.reflection.User
// 验证 3 种方式获取的是同一个 Class 对象
System.out.println(clazz1 == clazz2); // true
System.out.println(clazz1 == clazz3); // true
}
}
说明:
- 方式 1 需先创建对象,适用于已实例化的对象;
- 方式 2 无需创建对象,适用于编译期已知的类;
- 方式 3 通过类的全路径名动态加载,适用于编译期未知的类(如框架配置文件中指定的类)。
3.3 案例 2:通过反射获取类的信息
利用 Class 对象可以获取类的完整信息,包括类名、父类、接口、构造器、方法、属性等。
public class ReflectionDemo2 {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> clazz = Class.forName("com.example.reflection.User");
// 1. 获取类的基本信息
System.out.println("类名(全路径):" + clazz.getName()); // com.example.reflection.User
System.out.println("简单类名:" + clazz.getSimpleName()); // User
System.out.println("父类:" + clazz.getSuperclass().getName()); // java.lang.Object
System.out.println("实现的接口:");
for (Class<?> iface : clazz.getInterfaces()) {
System.out.println(" - " + iface.getName()); // java.io.Serializable
}
// 2. 获取构造器(getConstructors() 只能获取公有构造,getDeclaredConstructors() 可获取所有构造)
System.out.println("\n所有构造器:");
for (Constructor<?> constructor : clazz.getDeclaredConstructors()) {
System.out.print(" 构造器:");
// 打印参数类型
Class<?>[] paramTypes = constructor.getParameterTypes();
for (Class<?> paramType : paramTypes) {
System.out.print(paramType.getSimpleName() + " ");
}
System.out.println();
}
// 3. 获取成员方法(getMethods() 含父类公有方法,getDeclaredMethods() 只含本类所有方法)
System.out.println("\n所有方法:");
for (Method method : clazz.getDeclaredMethods()) {
System.out.print(" 方法:" + method.getName() + ",返回值:" + method.getReturnType().getSimpleName() + ",参数:");
Class<?>[] paramTypes = method.getParameterTypes();
for (Class<?> paramType : paramTypes) {
System.out.print(paramType.getSimpleName() + " ");
}
System.out.println();
}
// 4. 获取成员变量(getFields() 只能获取公有属性,getDeclaredFields() 可获取所有属性)
System.out.println("\n所有属性:");
for (Field field : clazz.getDeclaredFields()) {
System.out.println(" 属性:" + field.getName() + ",类型:" + field.getType().getSimpleName());
}
}
}
输出结果片段:
类名(全路径):com.example.reflection.User
简单类名:User
父类:java.lang.Object
实现的接口:
- java.io.Serializable
所有构造器:
构造器:
构造器:String int
构造器:String
所有方法:
方法:getUsername,返回值:String,参数:
方法:setUsername,返回值:void,参数:String
方法:printInfo,返回值:void,参数:
方法:toString,返回值:String,参数:
所有属性:
属性:username,类型:String
属性:age,类型:int
属性:address,类型:String
3.4 案例 3:通过反射创建对象
反射可以动态创建类的实例,支持调用无参构造和有参构造。
public class ReflectionDemo3 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
Class<?> clazz = Class.forName("com.example.reflection.User");
// 方式 1:调用无参构造(需确保类有无参构造,否则抛异常)
User user1 = (User) clazz.newInstance(); // JDK 9+ 已过时,推荐用 getConstructor().newInstance()
System.out.println("无参构造创建:" + user1); // User{username='null', age=0, address='null'}
// 方式 2:调用有参构造(需指定参数类型)
Constructor<?> constructor = clazz.getConstructor(String.class, int.class); // 获取公有有参构造
User user2 = (User) constructor.newInstance("张三", 20); // 传入参数
System.out.println("有参构造创建:" + user2); // User{username='张三', age=20, address='null'}
// 方式 3:调用私有构造(需用 getDeclaredConstructor() 并设置可访问)
Constructor<?> privateConstructor = clazz.getDeclaredConstructor(String.class); // 获取私有构造
privateConstructor.setAccessible(true); // 突破私有访问限制
User user3 = (User) privateConstructor.newInstance("李四");
System.out.println("私有构造创建:" + user3); // User{username='李四', age=0, address='null'}
}
}
说明:
clazz.newInstance()仅支持无参构造,且要求构造器为公有;- 调用私有构造或有参构造时,需先通过
getDeclaredConstructor(参数类型...)获取构造器,再调用setAccessible(true)取消访问检查(针对私有成员),最后用newInstance(参数值...)创建对象。
3.5 案例 4:通过反射调用方法
反射可以动态调用对象的方法(包括公有方法、私有方法、静态方法)
public class ReflectionDemo4 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
Class<?> clazz = Class.forName("com.example.reflection.User");
User user = (User) clazz.getConstructor(String.class, int.class).newInstance("王五", 25);
// 1. 调用公有方法(getUsername())
Method getUsernameMethod = clazz.getMethod("getUsername"); // 获取公有方法
String username = (String) getUsernameMethod.invoke(user); // 调用方法,传入对象
System.out.println("调用公有方法 getUsername():" + username); // 王五
// 2. 调用私有方法(setUsername())
Method setUsernameMethod = clazz.getDeclaredMethod("setUsername", String.class); // 获取私有方法
setUsernameMethod.setAccessible(true); // 允许访问私有方法
setUsernameMethod.invoke(user, "赵六"); // 传入对象和参数
System.out.println("调用私有方法后:" + user.getUsername()); // 赵六
// 3. 调用静态方法(printInfo())
Method printInfoMethod = clazz.getMethod("printInfo"); // 获取静态方法
printInfoMethod.invoke(null); // 静态方法无需传入对象,用 null 代替
}
}
输出结果:
调用公有方法 getUsername():王五
调用私有方法后:赵六
这是 User 类的静态方法
3.6 案例 5:通过反射操作属性
反射可以动态获取和修改对象的属性(包括私有属性)
public class ReflectionDemo5 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
Class<?> clazz = Class.forName("com.example.reflection.User");
User user = (User) clazz.newInstance();
// 1. 操作公有属性(address)
Field addressField = clazz.getField("address"); // 获取公有属性
addressField.set(user, "北京市"); // 设置属性值
System.out.println("公有属性 address:" + addressField.get(user)); // 北京市
// 2. 操作私有属性(username 和 age)
// 操作 username
Field usernameField = clazz.getDeclaredField("username"); // 获取私有属性
usernameField.setAccessible(true); // 允许访问私有属性
usernameField.set(user, "钱七"); // 设置值
System.out.println("私有属性 username:" + usernameField.get(user)); // 钱七
// 操作 age
Field ageField = clazz.getDeclaredField("age");
ageField.setAccessible(true);
ageField.setInt(user, 30); // 针对 int 类型的便捷方法
System.out.println("私有属性 age:" + ageField.getInt(user)); // 30
System.out.println("最终用户对象:" + user);
// 输出:User{username='钱七', age=30, address='北京市'}
}
}
四、反射的应用场景
- 动态代理:如 Spring AOP 基于反射实现方法增强;
- 框架开发:Spring IOC 通过反射创建 Bean,MyBatis 通过反射映射数据库字段与对象属性;
- 注解处理:如
@Override、@Deprecated的解析依赖反射; - 序列化与反序列化:将对象转为字节流或从字节流恢复对象时使用。
五、反射的优缺点
优点
- 灵活性高:可动态操作类和对象,适应多变的需求;
- 可扩展性强:无需修改代码即可适配新类(如插件化开发)。
缺点
- 性能开销:反射操作绕过编译期检查,运行时效率低于直接调用;
- 破坏封装:可直接访问私有成员,违反面向对象的封装性;
- 代码可读性差:反射代码较繁琐,调试难度高。
六、使用反射的注意事项
- 尽量避免在性能敏感场景使用反射(如高频调用的方法);
- 访问私有成员后,如需恢复访问权限,可再次调用
setAccessible(false); - 反射操作可能抛出多种异常(如
ClassNotFoundException、NoSuchMethodException),需妥善处理; - 避免过度依赖反射,优先使用直接调用(清晰且高效)。
七、总结
反射是 Java 中一种强大的动态编程机制,通过 Class 类和 java.lang.reflect 包的 API,我们可以在运行时获取类信息并动态操作对象。掌握反射不仅能理解框架的底层原理,还能在复杂场景下提升代码的灵活性。
但反射也存在性能和封装性问题,实际开发中需权衡利弊,合理使用。
更多推荐


所有评论(0)