《Java 100 天进阶之路》第28篇:Java反射机制原理详解
第28篇:Java反射机制原理详解
📌 系列导航:《Java 100 天进阶之路》完整目录 |
⬅️ 上一篇:第27篇:Java代理类详解 |
➡️ 下一篇:第29篇:Java Date类详解
一、核心知识点
- 反射的核心能力:运行时获取类信息、创建对象、调用方法、访问字段(包括私有)
- 获得
Class对象的三种方式:Class.forName()、对象.getClass()、类名.class - 核心 API:
Class、Method、Field、Constructor - 反射的优缺点:灵活、动态 vs 性能开销、破坏封装、安全隐患
- 反射的应用:框架(Spring、MyBatis)、注解处理、动态代理、序列化
二、通俗讲解(1分钟开心学)
1. 什么是反射?
反射就是在程序运行时,动态地获取类的信息(有哪些方法、字段、构造器),并且可以调用它们,甚至访问私有成员。就好像你在黑暗中用手电筒照一个物体,能看清它的所有细节。
2. 获得 Class 对象的三种方式
Class.forName("全限定类名"):最常用,会触发类初始化。对象.getClass():已经有实例时使用。类名.class:编译时已知类型,最直接,不触发初始化。
3. 反射能做什么?
- 创建实例(替代
new) - 调用方法(包括私有方法)
- 读写字段(包括私有字段)
- 获取注解信息
- 获取泛型类型信息
4. 反射的代价
- 性能比直接调用稍差(但现代 JVM 有优化)
- 破坏封装性,可能绕过权限检查
- 代码可读性下降,容易出错
生活类比:
正常编程就像拿着说明书组装家具(编译时已知)。反射就像你拿到一个没拆封的快递(运行时才知道里面是什么),你得用工具把它拆开,看清里面所有零件,再组装。
三、实操代码案例 + 场景说明
场景:写一个通用的
toString工具,可以打印任意对象的字段值。
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionDemo {
public static void main(String[] args) throws Exception {
// 1. 获取Class对象的三种方式
Class<?> clazz1 = Class.forName("java.lang.String");
Class<?> clazz2 = "hello".getClass();
Class<?> clazz3 = String.class;
System.out.println(clazz1 == clazz2); // true
// 2. 创建对象(调用无参构造)
Class<?> personClazz = Person.class;
Person p = (Person) personClazz.getDeclaredConstructor().newInstance();
p.setName("张三");
// 3. 调用私有方法
Method privateMethod = personClazz.getDeclaredMethod("secret", String.class);
privateMethod.setAccessible(true); // 绕过访问检查
privateMethod.invoke(p, "密码123");
// 4. 修改私有字段
Field nameField = personClazz.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(p, "李四");
System.out.println(p.getName()); // 李四
// 5. 遍历所有字段(通用toString)
System.out.println(objectToString(p));
}
// 通用toString:打印对象所有字段名和值
public static String objectToString(Object obj) throws IllegalAccessException {
StringBuilder sb = new StringBuilder();
Class<?> clazz = obj.getClass();
sb.append(clazz.getSimpleName()).append("{");
Field[] fields = clazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
fields[i].setAccessible(true);
sb.append(fields[i].getName()).append("=").append(fields[i].get(obj));
if (i < fields.length - 1) sb.append(", ");
}
sb.append("}");
return sb.toString();
}
}
class Person {
private String name;
public void setName(String name) { this.name = name; }
public String getName() { return name; }
private void secret(String key) {
System.out.println("私有方法被调用,key=" + key);
}
}
四、避坑要点
| 错误/误区 | 后果 | 正确做法 |
|---|---|---|
| 频繁调用反射(如循环中) | 性能下降 | 缓存 Method 或 Field 对象 |
忘记 setAccessible(true) |
IllegalAccessException |
访问私有成员前必须设置 |
Class.forName() 与 ClassLoader.loadClass() 混淆 |
前者会初始化静态块,后者不会 | 根据需求选择 |
| 反射调用方法参数类型不匹配 | IllegalArgumentException |
确保参数类型完全匹配(包括基本类型 vs 包装类) |
五、面试高频考点
Q1:反射的优缺点?
优点:动态性,框架基础,提高代码灵活性。缺点:性能略低,破坏封装,存在安全隐患(可访问私有数据)。
Q2:getMethod 和 getDeclaredMethod 的区别?
getMethod获取 public 方法(包括继承的);getDeclaredMethod获取本类声明的所有访问级别的方法(不包括继承)。
Q3:反射可以获取泛型参数化类型信息吗?
可以。通过
Method.getGenericParameterTypes()等获取Type对象,可得到实际类型参数(如果编译时保留了泛型信息)。
六、练习题
- 动手:写一个方法,接收一个对象,打印该对象的所有方法名称(包括私有)。
- 代码分析:利用反射创建
ArrayList<Integer>实例,并添加元素,再通过反射遍历输出。 - 简答:Spring 框架中哪些地方使用了反射?
📊 你的学习进度
- 当前:第28篇 / 共44篇 · 第四阶段:注解、反射、代理、日期(第26~31篇)
- ✅ 已完成:第1~27篇
- 📖 正在学:第28篇
- ⏳ 待学习:第29~44篇
👉 📚 完整目录 & 学习指南 | 🔥 订阅本专栏,不错过每一篇
💡 本专栏每篇都包含:避坑表 + 面试高频考点 + 练习题。每天30分钟,100天拿offer!
下一篇文章预告
《Java Date 类详解》
内容简介:Date类的缺陷、SimpleDateFormat线程不安全、java.time包(LocalDate/LocalDateTime/DateTimeFormatter)。
💡 学完这篇,你将彻底告别旧版日期API的坑,写出线程安全的日期处理代码。
📌 《Java 100 天进阶之路 | 从入门到上岗就业》 每天一篇,建议收藏 + 关注,一起100天拿offer!
👉 点击关注我,更新后第一时间收到推送!
更多推荐




所有评论(0)