# 阶段七:JVM 类加载与反射 —— 核心知识点总纲 > 本阶段涵盖:**类加载机制与反射、反射 Field 字段反射与 Method 方法反射** > 细分文档:`按日细分/20260812-笔记.md`、`按日细分/20260813-笔记.md` > 学生需要重点理解、背诵、掌握的理论核心知识点。 --- ## 一、类加载机制(对应 08-12) ### 1. Java 程序执行全流程(必背总纲) ``` .java 源文件 --编译--> .class 字节码 --类加载器--> 内存 --> JVM 解释 --> 二进制机器码 --> 执行 ``` - **`.class` 字节码平台无关** → Java 跨平台原理 - **类加载器** = 字节码的"搬运工"(把 .class / jar 包加载进内存) ### 2. 类加载的 6 种时机(必背) | # | 触发场景 | 示例 | |---|----------|------| | ① | 创建类的实例对象 | `new Demo01()` | | ② | 调用 static 方法 | `Math.abs(-1)` | | ③ | 访问/赋值 static 变量 | `Student.school = "..."` | | ④ | 反射创建 Class 对象 | `Class.forName("...")` | | ⑤ | 初始化子类(先加载父类) | `new SubStudent()` | | ⑥ | Java.exe 运行主类 | `java course.Demo01` | > **懒加载原则**:使用时才加载。初始化子类**先触发父类加载**(先父后子)。 ### 3. 类的加载过程(3 大步 5 小步,必背) ``` 1. 加载(Loading):通过全限定名获取字节流 → 生成 java.lang.Class 对象 2. 链接(Linking) ├── 验证(Verify):检查字节流符合规范、无安全隐患 ├── 准备(Prepare):为 static 变量分配内存并设默认值(int→0) └── 解析(Resolve):符号引用 → 直接引用;用到其他类在此查找 3. 初始化(Initialization):执行静态变量赋值、静态代码块 ``` **一句话**:加载"搬文件"、验证"查安全"、准备"分默认内存"、解析"找引用的类"、初始化"跑初始化代码"。 ### 4. 执行顺序(必背) **静态代码块(类加载初始化阶段,只执行一次)→ 普通代码块(每次 new,构造前)→ 构造方法** ### 5. 三种类加载器(必背) | 类加载器 | 职责 | 父加载器 | |----------|------|----------| | BootstrapClassLoader(启动) | 最核心 JDK 类 | 无(表示为 null) | | PlatformClassLoader(平台) | JDK 特殊模块 | Bootstrap | | SystemClassLoader(系统) | **用户类路径的类** | Platform | - **BootstrapClassLoader 由 C/C++ 实现,Java 中表示为 null**;对 null 调 getParent() 空指针 - 获取:`ClassLoader.getSystemClassLoader()` → 逐级 `getParent()` ### 6. 双亲委派模型(必背) **概念**:类加载器收到加载请求**不自己先加载**,先委派给父加载器,递归到最顶层;顶层能加载就成功,顶层不行才由子加载器尝试。 - **委派向上**(子→父→最顶层);**回退向下**(顶层→子) - 核心类(java.lang.String)由顶层加载;用户类由系统类加载器加载 **为什么需要双亲委派(三原因必背):** 1. **避免类被重复加载**:同一类全 JVM 只有一个 Class 对象(唯一性 = 类 + 类加载器共同决定) 2. **保证核心类库安全**:用户写的假 `java.lang.String` 永远不会被加载 3. **保证类的唯一性**:避免多份 String 类型混乱 ### 7. 类加载器加载配置文件(getResourceAsStream + Properties) ```java ClassLoader cl = ClassLoader.getSystemClassLoader(); InputStream in = cl.getResourceAsStream("prop.properties"); // classpath 根目录 Properties pro = new Properties(); pro.load(in); String driver = pro.getProperty("db.driver"); in.close(); ``` - **Properties**:父类 Hashtable、Map 实现类;`load()` 加载、`getProperty(key)` 取值 - 意义:**配置与代码分离**(改配置不用改代码);DB 四要素 driver/url/username/password ## 二、反射机制(对应 08-12) ### 1. 概念(必背) **反射是 Java 的运行时动态机制**:运行状态中能获取任意类的所有属性方法(**包括 private**)、调用任意对象的任意属性和方法。反射是 Spring/MyBatis/Hibernate/JDBC 等**框架的基础**。 ### 2. 普通调用 vs 反射调用(5 维对比必背) | 维度 | 普通调用 | 反射调用 | |------|---------|---------| | 获取信息时机 | 编译期写死 | **运行时动态** | | 依赖关系 | 强耦合 | **解耦** | | 创建对象 | `new 类名()` | `Class.forName() + newInstance()` | | 调用方法 | `对象.方法()` | `method.invoke(对象, 参数)` | | 灵活性 | 固定 | 高度灵活 | ### 3. 反射的注意事项(必背) **日常业务开发尽量不用反射**(像开挂):①**可能破坏封装性**(能拿 private)②**效率低**(比正常创建低 3~100 倍)。 > 学习反射的目的:理解框架底层源码 + 架构/中间件做解耦适配。 ### 4. 反射工作机制(必背链条) ``` Person.java → javac → Person.class → 类加载器 → Class 对象(类的类模板) ├── Constructor → newInstance() 创建对象 ├── Field → get/set 读写字段 └── Method → invoke() 执行方法 ``` ### 5. 反射核心类 Class(反射入口)/ Constructor(创建对象)/ Field(读写字段)/ Method(调用方法)/ Modifier(解析修饰符)/ Parameter(方法参数) ### 6. 获取 Class 对象的三种方式(必背) | 方式 | 写法 | 特点 | |------|------|------| | 方式一 | `Student.class` | 最直接,编译期类型检查 | | 方式二 | `student.getClass()` | Object 方法,需有对象 | | 方式三 | `Class.forName("course.Student")` | 字符串传类名**最灵活**,**框架最常用**;写错抛 ClassNotFoundException | **三个重要验证:** 1. **殊途同归**:三种方式拿到**同一个对象**(clazz1 == clazz2 == clazz3 全 true) 2. **基本类型和数组也有 Class 对象**:`int.class == Integer.TYPE` 3. **类加载时机差异**:`Class.forName()` **会触发静态代码块**;`类名.class` 和 `对象.getClass()` 不触发 **查看类型信息:** `getName()`(全类名)/ `getSimpleName()` / `getSuperclass()` / `getPackage()` ### 7. 反射 Constructor(构造方法) **4 个获取 API:** | 方法 | 是否含 private | |------|---------------| | `getConstructors()` | ❌ 只 public | | `getConstructor(Class...)` | ❌ 只 public | | `getDeclaredConstructors()` | ✅ 含私有 | | `getDeclaredConstructor(Class...)` | ✅ 含私有 | **暴力反射:** 私有构造直接 `newInstance()` 报权限错,先 **`setAccessible(true)`** 放开权限。 ```java Constructor dc = studentClass.getDeclaredConstructor(String.class, Integer.class); dc.setAccessible(true); Object o = dc.newInstance("张三", 1001); ``` **反射受检异常:** ClassNotFoundException / NoSuchMethodException / InstantiationException / IllegalAccessException / InvocationTargetException ## 三、反射 Field 字段反射(对应 08-13) ### 1. Field 概述 **Field 对象代表类中的一个成员变量。** `get()` 读、`set()` 写。 > 框架典型应用:ORM 框架把数据库数据映射到 Java 类,**用的就是 Field.set()**(ORM 底层原理)。 ### 2. 获取 Field 的 4 个 API(必背) | 方法 | 含 private | 含父类继承 | |------|-----------|-----------| | `getFields()` | ❌ | ✅ | | `getField(name)` | ❌ | ✅ | | `getDeclaredFields()` | ✅ 本类全部 | ❌ | | `getDeclaredField(name)` | ✅ | ❌ | ### 3. Field 常用方法(必背) `get(obj)` 读值 / `set(obj, 值)` 赋值 / `setAccessible(true)` 暴力反射 / `getType()` 类型 / `getName()` 属性名 / `getModifiers()` 修饰符(int,`Modifier.toString()` 解析) ### 4. 操作静态属性(对象参数传 null,必背) | 场景 | get 参数 | set 参数 | 原因 | |------|---------|---------|------| | 实例属性 | `get(对象)` | `set(对象, 值)` | 属于某个对象 | | **静态属性** | `get(null)` | `set(null, 值)` | **属于整个类** | ### 5. 四个典型操作 ```java // ① 遍历所有属性,输出修饰符+类型+属性名 Field[] fields = clazz.getDeclaredFields(); for (Field f : fields) { System.out.println(Modifier.toString(f.getModifiers()) + " " + f.getType().getSimpleName() + " " + f.getName()); } // ② 操作 public 字段 Field emailField = clazz.getField("email"); emailField.get(s); emailField.set(s, "lisi@qq.com"); // ③ 操作 private 字段(暴力反射) Field nameField = clazz.getDeclaredField("name"); nameField.setAccessible(true); nameField.get(s); nameField.set(s, "李四"); // ④ 操作 static 字段(传 null) Field schoolField = clazz.getDeclaredField("school"); schoolField.get(null); schoolField.set(null, "新学校"); ``` ## 四、反射 Method 方法反射(对应 08-13) ### 1. Method 概述 **Method 对象代表类中的一个方法。** 反射通过 `Method.invoke(对象, 参数...)` 调用方法,**等价于 `对象.方法(参数)`**——方法名称和参数**运行时才确定**。 > 应用:动态代理、测试框架(JUnit)、注解解释器、框架调用 setter/getter。 ### 2. 获取 Method 的 4 个 API(必背) | 方法 | 含 private | 含父类/接口继承 | |------|-----------|-----------------| | `getMethods()` | ❌ | ✅(含 toString/hashCode) | | `getMethod(name, parameterTypes)` | ❌ | ✅ | | `getDeclaredMethods()` | ✅ 本类全部 | ❌ | | `getDeclaredMethod(name, parameterTypes)` | ✅ | ❌ | **重载方法必须指明参数类型(必背):** ```java getMethod("setName", String.class) // String.class 用来区分同名重载 ``` > 不写或写错参数类型都抛 `NoSuchMethodException`。 ### 3. Method 常用方法(必背) `invoke(obj, args)` 调用 / `setAccessible(true)` 暴力反射 / `getReturnType()` 返回值类型 / `getParameterTypes()` 参数类型数组 / `getModifiers()` 修饰符 ### 4. invoke 的四种调用规律(重点必背) | 方法类型 | invoke 写法 | |----------|-------------| | public 实例方法 | `invoke(对象, 参数...)` | | **static 静态方法** | `invoke(null, 参数...)` | | **void 无返回值方法** | 调用后返回 **null** | | **private 私有方法** | 先 `setAccessible(true)` 再 invoke | ## 五、反射知识全景总览(四类知识地图,必背) ### 12 个获取 API 的统一规律(3 组 × 4 个) | 成员 | 取多个(数组) | 取单个 | 含私有数组 | 含私有单个 | |------|---------------|--------|-----------|-----------| | 属性 | `getFields()` | `getField(name)` | `getDeclaredFields()` | `getDeclaredField(name)` | | 方法 | `getMethods()` | `getMethod(name, 参数类型...)` | `getDeclaredMethods()` | `getDeclaredMethod(name, 参数类型...)` | | 构造 | `getConstructors()` | `getConstructor(参数类型...)` | `getDeclaredConstructors()` | `getDeclaredConstructor(参数类型...)` | > **口诀(贯穿三组)**:**含 `Declared` 的能拿私有(本类全部),不含 `Declared` 的只能拿 public(含父类/接口继承)。** ### 每类反射对象的关键方法 | 类别 | 关注点 | 关键方法 | |------|--------|----------| | Field 属性 | 属性组成部分 | 名字 getName、修饰符 getModifiers、**值 get/set** | | Method 方法 | 方法组成部分 | 名字 getName、**执行 invoke**、返回值 getReturnType、参数 getParameterTypes、修饰符 getModifiers | | Constructor 构造 | 构造组成部分 | 修饰符 getModifiers、参数 getParameterTypes、**创建 newInstance** | ### 反射三大成员统一套路(必背) **`getDeclaredXxx` 拿私有 + `setAccessible(true)` 放开权限 + 执行操作**(Constructor→newInstance / Field→get/set / Method→invoke);**操作静态成员(属性/方法)对象参数传 null。** ## 本阶段必须背诵的核心口诀 1. **类加载 6 时机 + 懒加载原则** 2. **加载 → 链接(验证/准备/解析)→ 初始化**(3 大步 5 小步) 3. **静态代码块 → 代码块 → 构造方法** 4. **三种类加载器**:系统 → 平台 → 启动(null) 5. **双亲委派**:委派向上、回退向下;三原因(避免重复加载/核心类安全/保证唯一性) 6. **反射 = 运行时动态获取类信息 + 动态调用**;从 Class 对象出发(Constructor/Field/Method) 7. **三种获取 Class**:类名.class / 对象.getClass() / Class.forName()(最灵活、触发静态块) 8. **Declared 口诀**:含 Declared 拿私有,不含只拿 public 9. **setAccessible(true) = 暴力反射**(访问私有成员的关键一步) 10. **invoke 四规律**:实例传对象、静态传 null、void 返回 null、private 先 setAccessible 11. **反射三件套**:Constructor 建对象 + Field 读写字段 + Method 调用方法 12. **操作静态成员传 null**(属性/方法统一规律)