📝 一句话概括
第九章是一条完整的"说明书"故事线:拿到说明书(9.1)→ 翻开说明书(9.2)→ 用说明书干活(9.3)→ 再学会贴标签(9.4)。反射和注解一合体,你就摸到了框架的门把手——Spring、JUnit 的地基,你已经全部见过。
1. 四节串线
| 节 | 一句话 | 关键词 |
|---|---|---|
| 9.1 反射概述 | 类名编译期写死是 new 的死穴,反射让你运行时"按名索骥" |
Class 对象、三把钥匙、ClassNotFoundException |
| 9.2 Class类和Class对象 | Class 类本身也是类(构造器私有、JVM 造),Class 对象=类说明书 | 四大族 get 方法、Declared 规律 |
| 9.3 反射的常见操作 | 没有类名也能造对象、拆字段、调方法 | newInstance、get/set、invoke、setAccessible |
| 9.4 注解 | 标签贴上去,程序读得懂 | @Override、@interface、@Target、RUNTIME |
主线一句话:先拿说明书,再翻说明书,再用说明书干活,最后学会贴标签——反射读注解,就是框架。
2. 概念速查 12 条
- Class 对象:JVM 加载类时自动生成、每类仅一份的"类说明书",反射一切操作的入口。
- 三把钥匙:
Class.forName("全类名")(受检异常)/类名.class/对象.getClass()——三者==同一份。 - 四大族 get 方法:类名 / 字段 / 方法 / 构造器,拿到的是反射三兄弟对象。
- Declared 规律:带
Declared= 全都要(含私有,不含父类);不带 = 只要 public(含继承)。 - 反射三兄弟:
Field(字段说明书)、Method(方法说明书)、Constructor(构造器说明书),都在java.lang.reflect包。 newInstance():反射造对象,返回Object必须强转;getConstructor(类型...)传类型、newInstance(实参)传值。f.get(s)/f.set(s, v):拆字段,必须传对象——说明书管"有什么",对象管"值是多少"。m.invoke(s, 实参):调方法,返回值也是Object。setAccessible(true):暴力反射,破 private 后门——3.3 封装的规矩,反射手里有钥匙。- 注解:贴在代码上的标签,给程序看的"注释",本身是死的,要有人读。
- 自定义注解:
@interface;属性长得像方法实为属性;无 default 必须赋值;唯一属性叫value可省名。 - 元注解:
@Target(贴纸规格)、@Retention(保质期)——RUNTIME 档才能被反射读到。
3. 代码模板 4 段(照抄能跑)
模板一:拿说明书(三选一)
Class<?> c1 = Class.forName("Student"); // 最常用,读配置就用它(抛受检异常,记得 throws 或 try)
Class<?> c2 = Student.class; // 编译期就知道类名时
Class<?> c3 = studentObj.getClass(); // 手里已有对象时
模板二:反射造对象
Constructor<?> cons = c.getConstructor(String.class, int.class); // 类型定位重载
Student s = (Student) cons.newInstance("张三", 18); // 实参 + 强转
模板三:拆字段 + 调方法
Field f = c.getDeclaredField("name");
f.setAccessible(true); // 碰私有必开
f.set(s, "波仔");
Method m = c.getDeclaredMethod("eat", String.class);
m.setAccessible(true);
m.invoke(s, "米饭");
模板四:自定义注解 + 反射扫描(迷你 JUnit 心脏)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME) // 忘了这行 = 白贴
public @interface MyTest { }
for (Method m : c.getDeclaredMethods()) {
if (m.isAnnotationPresent(MyTest.class)) {
m.invoke(obj); // 只执行贴了标签的方法
}
}
4. 名坑回顾(本章五大名坑)
Class.forName忘了异常 ——ClassNotFoundException是受检异常(5.2 的账),必须 throws 或 try-catch,躲不掉。newInstance返回值直接用 —— 它只给Object,不强转后面全报错;强转错了运行时ClassCastException。- Declared 与继承的恩怨搞反 ——
getDeclaredFields()恰恰不含父类字段;getFields()才带继承的 public。 - 私有成员忘开
setAccessible(true)——getDeclaredField只管"找到","碰"之前还要开后门,缺一IllegalAccessException。 @Retention忘写 RUNTIME —— 默认 CLASS 档,运行时标签已消失,反射一无所获还查不出错。读不到注解,先查保质期。
5. 收官条:本章的暗线
💡 分水岭
学前面八章,你是在写代码;学完这章,你第一次看清了框架怎么工作——
- "类名可以是数据":字符串 →
forName→ 对象,Spring 的配置式工厂就三步; - "标签可以被程序读":贴注解 → 反射扫描 → 自动执行,JUnit 的心脏就十几行。
反射慢、丢编译检查、破封装——框架用它是逼不得已的"以灵活换安全"。你的业务代码能 new 就 new,但读框架源码、看懂 @Autowired 背后发生了什么,从今天起你有钥匙了。
6. 跨章连线(本章收了哪些旧账)
- 第一章 Java开发入门/1.4 Java程序的运行机制:类加载造出 Class 对象——反射的物理基础
- 第二章 Java编程基础/2.6 方法:重载靠参数类型定位——
getConstructor/getDeclaredMethod同款 - 第三章 面向对象(上)/3.3 封装性:private 门禁被
setAccessible撬开 - 第四章 面向对象(下)/4.4 多态:
newInstance返回Object强转,编译看左运行看右 - 第四章 面向对象(下)/4.5 Object类:
getClass()是万物之祖留的反射后门 - 第五章 异常/5.2 运行时异常与编译时异常:
ClassNotFoundException受检 vs 反射操作异常全运行时 - 第六章 Java API/6.2 字符串类:类名就是个字符串——"类名可以是数据"
- 第七章 集合/7.8 Stream(流):
Field[]/Method[]到手,Stream 打印最顺手 - 第八章 泛型/8.1 泛型基础:类型擦除的另一面——运行时摸类型信息全靠反射
7. 毕业大题
❓ 毕业大题:手写迷你测试框架(综合 9.1~9.4 全部本领)
要求:①定义注解 @MyTest(只能贴方法、RUNTIME 档、带 author 属性默认"佚名");②写一个被测类 Calculator,三个方法——两个贴 @MyTest、一个不贴;③写测试运行器:反射扫描 Calculator 全部方法,只执行贴标方法,逐条打印 方法名 (作者) → 执行完成,最后统计 共执行 N 个测试。
参考骨架:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
String author() default "佚名";
}
public class TestRunner {
public static void main(String[] args) throws Exception {
Class<?> c = Class.forName("Calculator"); // 9.1 三把钥匙
Object obj = c.getDeclaredConstructor().newInstance(); // 9.3 造对象
int count = 0;
for (Method m : c.getDeclaredMethods()) { // 9.2 拆方法清单
if (m.isAnnotationPresent(MyTest.class)) { // 9.4 读标签
String author = m.getAnnotation(MyTest.class).author();
System.out.println(m.getName() + " (" + author + ") → 执行完成");
m.invoke(obj); // 9.3 调方法
count++;
}
}
System.out.println("共执行 " + count + " 个测试");
}
}
能独立写完,你就不是"学过反射",而是会用反射造工具了——JUnit 的第一版就这么朴素。
自检清单(全勾才算毕业):
- ☐ 能不假思索说出三把钥匙及各自的适用场景
- ☐ 能说清"带不带 Declared"的区别,并知道 Declared 不含父类
- ☐ 能默写"反射造对象三步",且知道哪里要强转、哪里传类型哪里传值
- ☐ 能解释
f.get(s)为什么必须传对象 - ☐ 能从零定义一个带属性、带两个元注解的自定义注解
- ☐ 能说清为什么反射读注解必须 RUNTIME 档
8. 下一步学什么
- ☑ 第十章 I/O · 10.1 File类 ✅ —— 点此复习:File 对象=路标(创建对象≠创建文件)、四大族方法、delete 不进回收站、Lambda 过滤器扫目录。
相关笔记
- 第九章 反射机制和注解/9.1 反射概述 · 第九章 反射机制和注解/9.2 Class类和Class对象 · 第九章 反射机制和注解/9.3 反射的常见操作 · 第九章 反射机制和注解/9.4 注解
- 第八章 泛型/8.1 泛型基础:上一章的类型擦除,在这一章得到完整答案
- 第五章 异常/5.2 运行时异常与编译时异常:本章受检/运行时异常的"重灾区",出Exception先翻家谱