阶段07-JVM类加载与反射.md 13 KB

阶段七: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)

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) 放开权限。

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. 四个典型操作

// ① 遍历所有属性,输出修饰符+类型+属性名
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) ✅ ❌

重载方法必须指明参数类型(必背):

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(属性/方法统一规律)