☕ 第九章 反射机制和注解
📝 一句话概括

第九章是一条完整的"说明书"故事线:拿到说明书(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 条

  1. Class 对象:JVM 加载类时自动生成、每类仅一份的"类说明书",反射一切操作的入口。
  2. 三把钥匙:Class.forName("全类名")(受检异常)/ 类名.class / 对象.getClass()——三者 == 同一份。
  3. 四大族 get 方法:类名 / 字段 / 方法 / 构造器,拿到的是反射三兄弟对象。
  4. Declared 规律:带 Declared = 全都要(含私有,不含父类);不带 = 只要 public(含继承)。
  5. 反射三兄弟:Field(字段说明书)、Method(方法说明书)、Constructor(构造器说明书),都在 java.lang.reflect 包。
  6. newInstance():反射造对象,返回 Object 必须强转;getConstructor(类型...) 传类型、newInstance(实参) 传值。
  7. f.get(s) / f.set(s, v):拆字段,必须传对象——说明书管"有什么",对象管"值是多少"。
  8. m.invoke(s, 实参):调方法,返回值也是 Object。
  9. setAccessible(true):暴力反射,破 private 后门——3.3 封装的规矩,反射手里有钥匙。
  10. 注解:贴在代码上的标签,给程序看的"注释",本身是死的,要有人读。
  11. 自定义注解:@interface;属性长得像方法实为属性;无 default 必须赋值;唯一属性叫 value 可省名。
  12. 元注解:@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. 名坑回顾(本章五大名坑)

  1. Class.forName 忘了异常 —— ClassNotFoundException 是受检异常(5.2 的账),必须 throws 或 try-catch,躲不掉。
  2. newInstance 返回值直接用 —— 它只给 Object,不强转后面全报错;强转错了运行时 ClassCastException。
  3. Declared 与继承的恩怨搞反 —— getDeclaredFields() 恰恰不含父类字段;getFields() 才带继承的 public。
  4. 私有成员忘开 setAccessible(true) —— getDeclaredField 只管"找到","碰"之前还要开后门,缺一 IllegalAccessException。
  5. @Retention 忘写 RUNTIME —— 默认 CLASS 档,运行时标签已消失,反射一无所获还查不出错。读不到注解,先查保质期。

5. 收官条:本章的暗线

💡 分水岭

学前面八章,你是在写代码;学完这章,你第一次看清了框架怎么工作——

  • "类名可以是数据":字符串 → forName → 对象,Spring 的配置式工厂就三步;
  • "标签可以被程序读":贴注解 → 反射扫描 → 自动执行,JUnit 的心脏就十几行。

反射慢、丢编译检查、破封装——框架用它是逼不得已的"以灵活换安全"。你的业务代码能 new 就 new,但读框架源码、看懂 @Autowired 背后发生了什么,从今天起你有钥匙了。

6. 跨章连线(本章收了哪些旧账)

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 的第一版就这么朴素。

自检清单(全勾才算毕业):

8. 下一步学什么

相关笔记