Просмотр исходного кода

feat: 新增20260813反射Field字段课堂练习——练习一通用工具遍历打印任意类所有字段(getDeclaredFields+getModifiers+Modifier.toString+getType+getName,对Student.class/String.class通用验证)、练习二反射读取修改public字段(getField+get/set+封装readField/setField通用方法)、练习三暴力反射读取修改private字段(getDeclaredField+setAccessible(true)+get/set通用工具setFieldValue/getFieldValue)、练习四操作静态字段(get/set对象参数传null)+模拟ORM数据库行→Java对象映射(mapToObject通用方法:反射创建空对象+getDeclaredFields遍历+字段名匹配row+field.set逐字段映射,ORM底层原理),同步更新README课堂练习题表

WanJL 1 неделя назад
Родитель
Сommit
276d0a0287
2 измененных файлов с 357 добавлено и 0 удалено
  1. 1 0
      README.md
  2. 356 0
      课堂练习题/20260813-反射Field字段练习.md

+ 1 - 0
README.md

@@ -104,3 +104,4 @@
 | 2026-08-10 | [线程操作与生命周期练习](./课堂练习题/20260810-线程操作与生命周期练习.md) | 线程生命周期(getState 6 种状态)、线程优先级(setPriority)、守护线程(setDaemon/JVM 退出机制)、线程协作(join/interrupt/yield) |
 | 2026-08-11 | [volatile 与 Lock 锁练习](./课堂练习题/20260811-volatile与Lock锁练习.md) | volatile 可见性(程序永不退出)、volatile 不保证原子性 vs synchronized、Lock 锁基本用法(lock/unlock + finally)、Lock 进阶(tryLock / tryLock(timeout) / lockInterruptibly)、Lock 改造卖票问题(synchronized vs Lock 七维对比) |
 | 2026-08-12 | [反射练习](./课堂练习题/20260812-反射练习.md) | 反射遍历 SysUser 四个构造方法创建对象(public 无参/private 二参/public 六参/private 四参:Class.forName + getConstructor/getDeclaredConstructor + setAccessible(true) 暴力反射 + newInstance + instanceof 强转)、反射给私有属性赋值(getDeclaredField + setAccessible + field.set/get)、反射调用方法(getMethod + invoke 带参返回值)、综合实战配置文件驱动 + 反射(模拟 Spring IOC:Properties 加载类名/属性/方法名) |
+| 2026-08-13 | [反射 Field 字段练习](./课堂练习题/20260813-反射Field字段练习.md) | 反射遍历打印任意类的所有字段(getDeclaredFields + getModifiers + Modifier.toString + getType + getName,对 Student.class/String.class 通用工具)、反射读取/修改 public 字段(getField + get/set + 封装 readField/setField 通用方法)、暴力反射读取/修改 private 字段(getDeclaredField + setAccessible(true) + get/set 通用工具 setFieldValue/getFieldValue)、操作静态字段(get/set 对象参数传 null)+ 模拟 ORM 数据库行 → Java 对象映射(mapToObject 通用方法:反射创建空对象 + getDeclaredFields 遍历 + 字段名匹配 row + field.set 逐字段映射,ORM 底层原理) |

+ 356 - 0
课堂练习题/20260813-反射Field字段练习.md

@@ -0,0 +1,356 @@
+# 2026-08-13 反射 Field 字段反射练习(课堂练习用)
+
+> **说明**:练习今日新学知识点——**反射 Field(属性/字段/变量)**(Demo01:Class 类获取 Field 对象的 4 个 API——getFields / getField / getDeclaredFields / getDeclaredField、Field 类的常用方法——get() 读取 / set() 赋值 / setAccessible(true) 暴力反射 / getType() 类型 / getName() 属性名 / getModifiers() 修饰符、Modifier.toString 解析修饰符、getDeclaredFields 遍历输出全部字段的修饰符/类型/属性名、getField + get/set 读取修改 public 属性、getDeclaredField + setAccessible(true) 暴力反射读取修改 private 属性、操作静态属性 get/set 对象参数传 null、反射赋值与普通访问是同一份数据、ORM 框架用 Field.set() 把数据库数据映射到 Java 类的底层原理)。练习一写通用工具遍历打印任意类的全部字段;练习二读取/修改 public 字段;练习三暴力反射读取/修改 private 字段;练习四操作静态字段 + 模拟 ORM 框架把"数据库一行记录"映射成 Java 对象(综合实战)。
+
+---
+
+## 练习一:通用工具 —— 遍历打印任意类的所有字段
+
+**难度**:⭐⭐⭐
+**知识点**:`getDeclaredFields()`(本类全部字段,含 private、不含父类)、`field.getModifiers()` + `Modifier.toString()` 解析修饰符、`field.getType()` + `getSimpleName()` 获取类型、`field.getName()` 获取属性名、`clazz.getSimpleName()` 获取简单类名
+
+**场景描述**:课堂 Demo01 演示① 在 main 方法里写死了"打印 Student 的全部字段"。但反射最大的价值是**通用**——本练习把"打印一个类所有字段信息"封装成**通用工具方法 `printFields(Class<?> clazz)`**,传入**任意类**的 Class 对象,就能打印它的全部字段(修饰符 + 类型 + 属性名)。再分别对 `Student` 和 `String`(JDK 核心类!)调用,体会"反射不写死具体类、谁传谁打"。
+
+```java
+// 源码:course/Student.java(本练习的实体类,7 个字段:4 private + 2 public + 1 public static)
+public class Student{
+    private Integer id;
+    private String name;
+    private Integer age;
+    private String gender;
+    public String email;
+    public String phone;
+    public static String school;
+    // …… 4 个构造方法 + study() 方法
+}
+```
+
+### 案例需求
+
+写一个通用方法,传入任意类的 Class 对象,遍历打印其**所有字段**的「修饰符 + 类型 + 属性名」,分别对 Student.class 和 String.class 调用验证。
+
+### 实现步骤
+
+1. 定义通用方法 `printFields(Class<?> clazz)`——参数是任意类的 Class 对象
+2. `clazz.getDeclaredFields()` 获取本类声明的**全部字段数组**(含 private,不含父类)
+3. 遍历每个 Field 对象:`getModifiers()` 拿修饰符 int 值 → `Modifier.toString()` 解析成可读字符串(如 private / public static)
+4. `getType().getSimpleName()` 获取类型名 + `getName()` 获取属性名
+5. 拼接打印 `修饰符 + 空格 + 类型 + 空格 + 属性名`
+6. main 里分别用 `printFields(Student.class)` 和 `printFields(String.class)` 调用
+
+### 代码框架
+
+```java
+package course;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+public class FieldPrintUtilTest {
+    public static void main(String[] args) {
+        // 1. 打印 Student 的全部字段
+        // 2. 打印 String 的全部字段(JDK 核心类,看看它有哪些字段)
+    }
+
+    // 通用工具:打印任意类 clazz 的所有字段(修饰符 + 类型 + 属性名)
+    public static void printFields(Class<?> clazz) {
+        // 打印类名(clazz.getSimpleName())
+        // getDeclaredFields() 获取全部字段数组
+        // 遍历每个字段:
+        //     getModifiers() + Modifier.toString() 解析修饰符
+        //     getType().getSimpleName() 获取类型名
+        //     getName() 获取属性名
+        //     打印
+    }
+}
+```
+
+**输出示例**:
+```
+Student 类的所有字段:
+private Integer id
+private String name
+private Integer age
+private String gender
+public String email
+public String phone
+public static String school
+String 类的所有字段:
+private final byte[] value
+private final byte coder
+private int hash
+private boolean hashIsZero
+private static final long serialVersionUID
+...
+```
+
+**选做加分**:
+- 同时用 `getFields()` 再打印一遍,对比与 `getDeclaredFields()` 的差异——Student 的哪些字段消失(private 的),`getFields()` 是否列出父类 Object 的字段(Object 没有 public 字段,可观察 List/Date 等有父类字段的类对比)
+- 打印字段个数:`getDeclaredFields().length`(Student 应为 7)
+- 直接打印 `getModifiers()` 的**原始 int 值**(如 private=2、public=1、public static=9),再用 `Modifier.toString()` 对比——体会"修饰符底层是 int 位掩码"
+
+**思考题**:
+1. `getDeclaredFields()` 和 `getFields()` 有什么区别?(提示:Declared 拿本类全部含 private 不含父类;非 Declared 只拿 public 含父类继承——口诀"含 Declared 的能拿私有")
+2. `getModifiers()` 返回的是 int,为什么需要 `Modifier.toString()` 解析?它能解析出哪些字符串?(提示:private / public / public static...)
+3. `String.class` 是 JDK 核心类,为什么反射也能看到它的字段?(提示:反射对任意类都适用,包括 JDK 核心类)
+
+---
+
+## 练习二:反射读取/修改 public 字段 —— getField + get/set
+
+**难度**:⭐⭐⭐
+**知识点**:`clazz.getField("字段名")` 获取指定 public 字段、`field.get(对象)` 读取属性值、`field.set(对象, 值)` 修改属性值、反射赋值与普通访问是同一份数据
+
+**场景描述**:课堂 Demo01 演示② 用 `getField("email")` + `get()/set()` 读取、修改了 Student 的 public 字段 email。本练习要求:用反射读取/修改 Student 的 **email、phone 两个 public 字段**,修改后**用普通方式 `对象.字段名` 直接访问验证**(证明反射赋值与普通访问操作的是同一份数据),并把"读取/修改字段"封装成**通用方法 `readField` / `setField`**。
+
+### 案例需求
+
+创建 Student 对象后,用反射读取 public 字段 email、phone,再反射修改它们,并用普通访问方式验证修改生效。
+
+### 实现步骤
+
+1. `new Student(1001, "张三", 25, "男", "zhangsan@163.com", "13636363366")` 创建对象(六参构造,email/phone 构造里已赋值)
+2. `clazz.getField("email")` 获取 email 字段的 Field 对象(getField 只能拿 public 字段)
+3. `emailField.get(s)` 读取值、`emailField.set(s, "lisi@qq.com")` 修改值
+4. **用 `s.email` 普通访问验证**——反射 set 后直接访问也看到新值
+5. 同样操作 phone 字段
+6. 封装通用方法 `readField(Object obj, String fieldName)` 和 `setField(Object obj, String fieldName, Object value)`,内部就是 `getField` + `get/set`
+
+### 代码框架
+
+```java
+package course;
+
+import java.lang.reflect.Field;
+
+public class FieldPublicTest {
+    public static void main(String[] args) throws Exception {
+        // 1. 创建 Student 对象(六参构造)
+        // 2. 反射读取 email、phone 并打印
+        // 3. 反射修改 email、phone
+        // 4. 用 s.email / s.phone 普通访问验证修改生效
+    }
+
+    // 通用工具:读取对象 obj 的 public 字段 fieldName 的值
+    public static Object readField(Object obj, String fieldName) throws Exception {
+        // getField(fieldName) → field.get(obj)
+        return null;
+    }
+
+    // 通用工具:给对象 obj 的 public 字段 fieldName 赋值 value
+    public static void setField(Object obj, String fieldName, Object value) throws Exception {
+        // getField(fieldName) → field.set(obj, value)
+    }
+}
+```
+
+**输出示例**:
+```
+读取 email:zhangsan@163.com
+读取 phone:13636363366
+反射修改后:lisi@qq.com / 13900000000
+用 s.email 普通访问验证:lisi@qq.com      ← 反射 set 与普通访问是同一份数据
+用 s.phone 普通访问验证:13900000000
+```
+
+**选做加分**:
+- 用 `getFields()` 遍历打印 Student 的**所有 public 字段名**(email、phone、school——school 是 public 也会被列出)
+- 用 `getField("id")`(private 字段)尝试获取,运行观察抛 **NoSuchFieldException**——验证"getField 只能拿 public"的口诀
+- 封装好的 `readField` / `setField` 对 private 字段 name 调用试试,观察抛 **IllegalAccessException**——为练习三做铺垫
+
+**思考题**:
+1. `getField("字段名")` 里的字段名写错(不存在),会抛什么异常?(提示:NoSuchFieldException,受检异常)
+2. 反射 `field.set(s, "lisi@qq.com")` 修改后,为什么 `s.email` 直接访问也变成新值?(提示:反射操作的就是对象的真实字段内存,同一份数据)
+3. `field.get(obj)` 返回的是什么类型?给 String 字段取值需要强转吗?(提示:返回 Object,使用时通常强转)
+
+---
+
+## 练习三:暴力反射读取/修改 private 字段 —— getDeclaredField + setAccessible(true)
+
+**难度**:⭐⭐⭐⭐
+**知识点**:`clazz.getDeclaredField("字段名")` 获取字段(**含 private**)、`field.setAccessible(true)` 暴力反射(临时取消访问权限检查)、`field.get/set` 读写、通用字段读写工具(不区分权限)
+
+**场景描述**:课堂 Demo01 演示③ 对 private 字段 name 做了暴力反射——**getDeclaredField 拿到 private 字段 → setAccessible(true) 放开权限 → get/set 操作**(这就是反射"破坏封装性"的体现)。本练习要求:写一个**不区分字段权限**的通用工具 `setFieldValue(obj, fieldName, value)` / `getFieldValue(obj, fieldName)`——内部流程 `getDeclaredField → setAccessible(true) → set/get`,用这个工具给 Student 的 **4 个 private 字段(id/name/age/gender)赋值并读取**。
+
+### 案例需求
+
+写通用工具,能对**任意权限**(public/private)的字段赋值和读取;用它对 Student 的 4 个 private 字段赋值并读取验证。
+
+### 实现步骤
+
+1. 创建 Student 空对象:`Class.forName("course.Student").getConstructor().newInstance()`(复用上一课反射创建对象)
+2. 封装 `setFieldValue(Object obj, String fieldName, Object value)`:`getDeclaredField(fieldName)` → `setAccessible(true)` → `field.set(obj, value)`
+3. 封装 `getFieldValue(Object obj, String fieldName)`:同样套路 → `field.get(obj)`
+4. 用工具给 id / name / age / gender 四个 private 字段赋值
+5. 用 `getFieldValue` 读取打印验证,强转成 Student 调用 `study()` 观察 name 生效
+
+### 代码框架
+
+```java
+package course;
+
+import java.lang.reflect.Field;
+
+public class FieldPrivateTest {
+    public static void main(String[] args) throws Exception {
+        // 1. 反射创建 Student 空对象(Class.forName + getConstructor().newInstance())
+        // 2. 用 setFieldValue 给 id / name / age / gender 四个 private 字段赋值
+        // 3. 用 getFieldValue 读取四个字段打印验证
+        // 4. 强转成 Student 调用 study(),观察 name 是否生效
+    }
+
+    // 通用工具:给对象 obj 的任意权限字段 fieldName 赋值 value(getDeclaredField + setAccessible + set)
+    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
+        // getDeclaredField(fieldName)
+        // setAccessible(true)
+        // set(obj, value)
+    }
+
+    // 通用工具:读取对象 obj 的任意权限字段 fieldName 的值(getDeclaredField + setAccessible + get)
+    public static Object getFieldValue(Object obj, String fieldName) throws Exception {
+        // getDeclaredField(fieldName)
+        // setAccessible(true)
+        // get(obj)
+        return null;
+    }
+}
+```
+
+**输出示例**:
+```
+id = 1001
+name = 李四
+age = 20
+gender = 男
+李四在学习.....   ← 反射给 private name 赋值生效,study() 输出
+```
+
+**选做加分**:
+- **对比实验**:把 `setFieldValue` 里的 `setAccessible(true)` 注释掉,运行观察抛 **IllegalAccessException**——体会"private 字段不放开权限无法操作"
+- 用 `getField("name")`(不带 Declared)尝试获取 private 字段,观察抛 **NoSuchFieldException**——验证"不含 Declared 只能拿 public"
+- 在工具里加一行打印:`field.getType().getSimpleName()` + `field.getName()`,观察每次赋值时字段的类型名和属性名
+
+**思考题**:
+1. 为什么叫"暴力反射"?`setAccessible(true)` 做了什么?(提示:临时取消 Java 访问权限检查,private 也能访问)
+2. private 字段在普通代码中编译期就报错,为什么反射能访问?(提示:反射是运行时动态机制,运行时可以绕过编译期/访问控制限制)
+3. 反射能修改 private 字段,有什么隐患?(提示:破坏封装性、效率低 3~100 倍——回顾上一课"日常业务尽量不用反射")
+
+---
+
+## 练习四:综合实战 —— 操作静态字段(传 null)+ 模拟 ORM:数据库行 → Java 对象映射
+
+**难度**:⭐⭐⭐⭐⭐
+**知识点**:静态属性反射操作(`field.get(null)` / `field.set(null, 值)`——静态成员属于整个类、不传对象)、Field 反射全家桶(`getDeclaredFields` + `setAccessible` + `set`)、ORM 框架"数据库数据 → Java 类字段映射"底层原理(本课类注释:**"框架查询数据库,把数据库的数据一个一个映射到 Java 类中,用的就是 Field.set()"**)、`instanceof` 判断 + 强转
+
+**场景描述**:这是本课最重要的知识落地——**ORM 框架(MyBatis/Hibernate)的底层映射原理**。框架查询数据库后,把**一行数据库记录**(字段名 → 值)映射成 **Java 对象**(属性名 → 值),靠的就是反射 `Field.set()`。本练习做两件事:
+1. **操作静态字段**:用反射给 Student 的 `public static String school` 赋值/读取——注意**对象参数传 null**(静态成员属于整个类),再用 `Student.school` 直接访问验证
+2. **模拟 ORM 映射**:写一个**通用方法 `mapToObject(Map<String, Object> row, Class<?> clazz)`**——把模拟"数据库一行记录"的 Map 映射成任意类的对象:反射创建空对象 → 遍历该类全部字段 → 字段名在 row 中则 `field.set(obj, 值)`(自动包含 private,因为有 setAccessible)→ 返回映射好的对象
+
+### 案例需求
+
+(1)用反射读取/修改静态字段 school(对象参数传 null),验证全类共享;(2)写通用方法 mapToObject,把"数据库行"Map 映射成 Student 对象(含 private 字段),调用 study() 验证对象可用。
+
+### 实现步骤
+
+1. **静态字段部分**:
+   - `clazz.getDeclaredField("school")` + `setAccessible(true)` 获取静态字段
+   - `schoolField.get(null)` 读取(**对象参数传 null**)、`schoolField.set(null, "哈尔滨佛学院")` 修改
+   - `Student.school` 直接访问验证——反射修改静态属性全类生效
+2. **ORM 映射部分**:
+   - 创建 Map 模拟数据库一行记录:`row.put("id", 1001)`、`row.put("name", "张三")`、`row.put("age", 25)`、`row.put("gender", "男")`、`row.put("email", "zhangsan@163.com")`、`row.put("phone", "13800000000")`
+   - 写通用方法 `mapToObject(row, Student.class)`:
+     - `clazz.getConstructor().newInstance()` 反射创建空对象
+     - `getDeclaredFields()` 遍历全部字段
+     - 字段名在 row 里 → `setAccessible(true)` → `field.set(obj, row.get(fieldName))`  ← **映射核心**
+   - 用练习三的工具 `getFieldValue` 读取映射后的字段打印验证
+   - `(Student) obj` 强转 + `study()` 调用验证
+
+### 代码框架
+
+```java
+package course;
+
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Map;
+
+public class OrmFieldMapTest {
+    public static void main(String[] args) throws Exception {
+        // ===== 第 1 部分:操作静态字段 school(对象参数传 null)=====
+        // 1. 获取 school 字段 + 放开访问权限
+        // 2. schoolField.get(null) 读取、schoolField.set(null, "哈尔滨佛学院") 修改
+        // 3. Student.school 直接访问验证
+
+        // ===== 第 2 部分:模拟 ORM —— 数据库行 → Java 对象 =====
+        // 4. 创建 Map 模拟数据库一行记录(key=字段名,value=值)
+        Map<String, Object> row = new HashMap<>();
+        // row.put("id", 1001); row.put("name", "张三"); ...
+
+        // 5. 调用通用方法,把"数据库行"映射成 Student 对象
+        Object obj = mapToObject(row, Student.class);
+
+        // 6. 打印映射结果 + 强转调用 study() 验证
+    }
+
+    // 通用映射方法:把 Map(数据库一行记录)映射成 clazz 的对象 —— ORM 底层原理
+    public static Object mapToObject(Map<String, Object> row, Class<?> clazz) throws Exception {
+        // 1. 反射创建空对象(getConstructor().newInstance())
+        // 2. getDeclaredFields() 遍历该类全部字段
+        // 3. 字段名在 row 里 → setAccessible(true) → field.set(obj, row.get(fieldName))
+        // 4. 返回映射好的对象
+        return null;
+    }
+}
+```
+
+**输出示例**:
+```
+静态属性 school:哈尔滨佛学院        ← schoolField.get(null) 读取
+修改后 Student.school:福建桥梁职业技术学院   ← 反射 set(null, ...) 后直接访问验证
+
+映射结果:
+id = 1001
+name = 张三
+age = 25
+gender = 男
+email = zhangsan@163.com
+phone = 13800000000
+张三在学习.....                     ← 映射后的对象调用 study() 正常
+```
+
+**选做加分**:
+- **换实体类不改代码**:再定义一个 `Teacher` 类(含 private name、public teach() 方法),用**同一个 `mapToObject`** 传入 `Teacher.class` 和一个对应的 row——代码一行不改完成映射——体会"反射不写死类"的通用威力
+- **字段缺失跳过**:把 row 里故意少放一个字段(如不放 age),观察 mapToObject 自动跳过(字段不在 row 里就不赋值)——对应数据库某列为 NULL 的场景
+- **静态字段进阶**:思考如果 school 是全校统一的静态字段,如何用 `field.set(null, ...)` 一次性统一赋值(所有对象共享一份)
+- 打印映射过程中每个字段的**处理日志**:`"正在映射字段:name = 张三"`——直观看到 ORM 逐字段映射的过程
+
+**思考题**:
+1. 反射操作**静态字段**时,`get/set` 的对象参数为什么传 **null**?(提示:静态成员属于整个类、不属于任何对象——本课核心知识点)
+2. 为什么 ORM 框架要用反射 `Field.set()` 来映射数据库数据,而不是直接调用 setter?(提示:框架在编译期不知道实体类有哪些属性、有哪些 setter,靠反射运行时动态拿到——配置/注解驱动的解耦)
+3. 模拟的 `mapToObject` 里没有 setter、没有写死 Student 的任何一个属性名,为什么能把 private 字段都赋上值?(提示:getDeclaredFields 拿全部字段 + setAccessible(true) 放开权限)
+4. 如果数据库字段叫 `user_name`、Java 属性叫 `userName`(下划线 vs 驼峰),mapToObject 直接按字段名匹配会失败——你会怎么解决?(提示:字段名转换——把下划线转驼峰再匹配,这就是 MyBatis 的 mapUnderscoreToCamelCase 配置的底层逻辑)
+
+---
+
+# 涵盖知识点总览
+
+| 知识点 | 说明 |
+|--------|------|
+| `getDeclaredFields()` | 返回本类(不含父类)声明的**所有字段**数组,**包括 private**——遍历可打印全部字段 |
+| `getFields()` | 返回所有 **public** 字段数组,**包括从父类继承的**——与 getDeclaredFields 的核心区别 |
+| `getField("字段名")` | 返回指定的单个 **public** 字段,字段名不存在抛 NoSuchFieldException |
+| `getDeclaredField("字段名")` | 返回指定的单个字段(**包括 private**)——操作私有字段必须用它 |
+| **Declared 口诀** | 含 Declared 的能拿私有(本类全部、不含父类);不含 Declared 的只能拿 public(含父类继承) |
+| `field.get(对象)` | 读取对象 obj 的该属性值,返回 Object(使用时常强转) |
+| `field.set(对象, 值)` | 设置对象 obj 的该属性值(参数:目标对象 + 要赋的值) |
+| `field.setAccessible(true)` | **暴力反射**:临时取消访问权限检查——操作 private 字段必须调用,否则抛 IllegalAccessException |
+| `field.getType()` + `getSimpleName()` | 获取属性类型(如 Integer/String) |
+| `field.getName()` | 获取属性名 |
+| `field.getModifiers()` + `Modifier.toString()` | 获取修饰符 int 值并解析成可读字符串(private / public / public static...) |
+| **操作静态字段传 null** | 静态成员属于整个类、不属于任何对象——`field.get(null)` / `field.set(null, 值)`;修改后 `类名.属性名` 直接访问全类生效 |
+| **反射赋值与普通访问同一份数据** | `field.set(obj, ...)` 修改后,`obj.属性名` 直接访问也看到新值——反射操作的就是对象真实字段内存 |
+| **ORM 映射底层原理** | 框架查询数据库后,用 `Field.set()` 把数据库数据一个一个映射到 Java 类中(MyBatis/Hibernate 结果映射) |
+| **通用映射 mapToObject** | 反射创建空对象 + `getDeclaredFields` 遍历 + 字段名匹配 row 的 key + `setAccessible(true)` + `field.set`——模拟数据库行 → Java 对象 |
+| **反射的通用性** | 工具方法传入任意 Class/对象都能工作(打印任意类字段、读写任意对象属性)——不写死具体类,配置驱动 + 解耦 |
+| **反射的注意事项** | 日常业务尽量不用:破坏封装性、效率低 3~100 倍;学习反射为理解框架底层源码(ORM/IOC) |
+| **反射 API 的受检异常** | NoSuchFieldException(字段名不存在)/ IllegalAccessException(private 未放开权限)——必须处理(main 用 throws) |