Forráskód Böngészése

feat: 新增20260813课堂笔记及授课代码分析——反射Field属性/字段/变量(Demo01演示Class类获取Field对象的4个API getFields/getField/getDeclaredFields/getDeclaredField、Field类常用方法 get/set/setAccessible/getType/getName/getModifiers、Modifier.toString解析修饰符、演示①getDeclaredFields遍历输出全部7个字段的修饰符/类型/属性名、演示②getField+get/set读取修改public属性、演示③getDeclaredField+setAccessible(true)暴力反射读取修改private属性、演示④操作静态属性get/set对象参数传null、反射赋值与普通访问同一份数据、ORM框架用Field.set()把数据库数据映射到Java类的底层原理)+Student实体类(private/public/public static字段设计+4个构造方法),同步更新README授课笔记表与授课代码表

WanJL 1 hete
szülő
commit
099ba7b2df

+ 2 - 0
README.md

@@ -46,6 +46,7 @@
 | 2026-08-10 | [线程操作与线程安全](./课堂笔记/01-JavaSE基础-笔记/20260810-笔记.md) |
 | 2026-08-10 | [线程操作与线程安全](./课堂笔记/01-JavaSE基础-笔记/20260810-笔记.md) |
 | 2026-08-11 | [volatile、Lock 锁与线程池](./课堂笔记/01-JavaSE基础-笔记/20260811-笔记.md) |
 | 2026-08-11 | [volatile、Lock 锁与线程池](./课堂笔记/01-JavaSE基础-笔记/20260811-笔记.md) |
 | 2026-08-12 | [类加载机制与反射](./课堂笔记/01-JavaSE基础-笔记/20260812-笔记.md) |
 | 2026-08-12 | [类加载机制与反射](./课堂笔记/01-JavaSE基础-笔记/20260812-笔记.md) |
+| 2026-08-13 | [反射 Field 属性/字段/变量](./课堂笔记/01-JavaSE基础-笔记/20260813-笔记.md) |
 
 
 ## 二、授课代码
 ## 二、授课代码
 
 
@@ -77,6 +78,7 @@
 | 2026-08-10 | 线程操作方法(Thread.sleep 休眠、线程调度方式、CPU 时间片随机性、线程优先级)、守护线程(setDaemon、JVM 退出机制)、线程生命周期(6 种状态)、线程操作方法(join/interrupt/yield/isAlive、Object 类 wait/notify)、线程安全问题(数据竞争、卖票问题、计数器问题、线程安全三大特性)、synchronized 线程同步(监视器锁、三种写法)、多线程三种实现方式综合练习、课后作业回顾(Callable+FutureTask 带返回值求和) | `c260810` | [`course`](./授课代码/c260810/src/course/) / [`exericse`](./授课代码/c260810/src/exericse/) / [`homework0808`](./授课代码/c260810/src/homework0808/) |
 | 2026-08-10 | 线程操作方法(Thread.sleep 休眠、线程调度方式、CPU 时间片随机性、线程优先级)、守护线程(setDaemon、JVM 退出机制)、线程生命周期(6 种状态)、线程操作方法(join/interrupt/yield/isAlive、Object 类 wait/notify)、线程安全问题(数据竞争、卖票问题、计数器问题、线程安全三大特性)、synchronized 线程同步(监视器锁、三种写法)、多线程三种实现方式综合练习、课后作业回顾(Callable+FutureTask 带返回值求和) | `c260810` | [`course`](./授课代码/c260810/src/course/) / [`exericse`](./授课代码/c260810/src/exericse/) / [`homework0808`](./授课代码/c260810/src/homework0808/) |
 | 2026-08-11 | volatile 关键字(解决可见性和有序性、不保证原子性)、JVM 内存模型(工作内存 CPU 缓存 vs 主内存)、可见性问题演示(程序永不退出 VolatileDemo)、计数器问题升级(volatile 不保证原子性、synchronized 保证原子性)、Lock 锁(ReentrantLock 可重入锁:lock/unlock/tryLock/tryLock(timeout)/lockInterruptibly 四种用法、unlock 必须放 finally、公平锁/非公平锁、synchronized vs Lock 七维对比(锁读读写分离/底层实现 AQS/JDK21 版本更新)、10000 线程 count 验证)、死锁(4 个必要条件、死锁复现、避免方法固定锁顺序/tryLock 超时/ConcurrentHashMap)、线程池(ThreadPoolExecutor:7 参数、执行流程、8 任务演示、getActiveCount/getQueue/shutdown)、课后作业回顾(homework0810 Demo_03 卖票问题 synchronized 修复) | `c260811` | [`course`](./授课代码/c260811/src/course/) / [`homework0810`](./授课代码/c260811/src/homework0810/) |
 | 2026-08-11 | volatile 关键字(解决可见性和有序性、不保证原子性)、JVM 内存模型(工作内存 CPU 缓存 vs 主内存)、可见性问题演示(程序永不退出 VolatileDemo)、计数器问题升级(volatile 不保证原子性、synchronized 保证原子性)、Lock 锁(ReentrantLock 可重入锁:lock/unlock/tryLock/tryLock(timeout)/lockInterruptibly 四种用法、unlock 必须放 finally、公平锁/非公平锁、synchronized vs Lock 七维对比(锁读读写分离/底层实现 AQS/JDK21 版本更新)、10000 线程 count 验证)、死锁(4 个必要条件、死锁复现、避免方法固定锁顺序/tryLock 超时/ConcurrentHashMap)、线程池(ThreadPoolExecutor:7 参数、执行流程、8 任务演示、getActiveCount/getQueue/shutdown)、课后作业回顾(homework0810 Demo_03 卖票问题 synchronized 修复) | `c260811` | [`course`](./授课代码/c260811/src/course/) / [`homework0810`](./授课代码/c260811/src/homework0810/) |
 | 2026-08-12 | 类的加载机制(java 文件编译→字节码 class 文件→类加载器→内存→JVM 解释→二进制机器码→执行、类加载器作用(也可能是 jar 包)、类加载 6 种时机、懒加载原则、加载过程 3 大步 5 小步:加载→链接(验证/准备/解析)→初始化、静态代码块/代码块/构造方法执行顺序演示 Demo01)、类加载器(三种类加载器:BootstrapClassLoader/PlatformClassLoader/SystemClassLoader、双亲委派层级、ClassLoader.getSystemClassLoader/getParent 获取父加载器演示 Demo02)、双亲委派模型(Demo03:委派机制、为什么需要双亲委派三个原因、getResourceAsStream 加载资源配置文件 + Properties 解析 prop.properties 数据库配置)、反射机制(Demo04:反射概念、普通调用 vs 反射调用 5 维对比、为什么需要反射、反射工作机制、反射核心类)、获取 Class 对象的三种方式(Demo05:类名.class/对象.getClass/Class.forName 演示 + Student/Person/Animal 辅助类)、反射 Constructor 构造方法(Demo06:getConstructors/getDeclaredConstructors 获取构造方法 + setAccessible(true) 暴力反射 + newInstance 创建对象 + Student 新增 3 个构造方法) | `c260812` | [`course`](./授课代码/c260812/src/course/) / [`src/prop.properties`](./授课代码/c260812/src/prop.properties) |
 | 2026-08-12 | 类的加载机制(java 文件编译→字节码 class 文件→类加载器→内存→JVM 解释→二进制机器码→执行、类加载器作用(也可能是 jar 包)、类加载 6 种时机、懒加载原则、加载过程 3 大步 5 小步:加载→链接(验证/准备/解析)→初始化、静态代码块/代码块/构造方法执行顺序演示 Demo01)、类加载器(三种类加载器:BootstrapClassLoader/PlatformClassLoader/SystemClassLoader、双亲委派层级、ClassLoader.getSystemClassLoader/getParent 获取父加载器演示 Demo02)、双亲委派模型(Demo03:委派机制、为什么需要双亲委派三个原因、getResourceAsStream 加载资源配置文件 + Properties 解析 prop.properties 数据库配置)、反射机制(Demo04:反射概念、普通调用 vs 反射调用 5 维对比、为什么需要反射、反射工作机制、反射核心类)、获取 Class 对象的三种方式(Demo05:类名.class/对象.getClass/Class.forName 演示 + Student/Person/Animal 辅助类)、反射 Constructor 构造方法(Demo06:getConstructors/getDeclaredConstructors 获取构造方法 + setAccessible(true) 暴力反射 + newInstance 创建对象 + Student 新增 3 个构造方法) | `c260812` | [`course`](./授课代码/c260812/src/course/) / [`src/prop.properties`](./授课代码/c260812/src/prop.properties) |
+| 2026-08-13 | 反射 Field 属性/字段/变量(Demo01:Class 类获取 Field 对象的 4 个 API——getFields/getField/getDeclaredFields/getDeclaredField(Declared 口诀:含 Declared 拿私有本类全部、不含 Declared 只拿 public 含父类继承)、Field 类的常用方法——get/set 读写属性值/setAccessible(true) 暴力反射/getType 类型/getName 属性名/getModifiers 修饰符 + Modifier.toString 解析、演示① getDeclaredFields 遍历输出全部 7 个字段的修饰符/类型/属性名、演示② getField+get/set 读取修改 public 属性、演示③ getDeclaredField+setAccessible(true) 暴力反射读取修改 private 属性、演示④ 操作静态属性 get/set 对象参数传 null、反射赋值与普通访问同一份数据、ORM 框架用 Field.set() 把数据库数据映射到 Java 类的底层原理)+ Student 实体类(private 四字段 + public 两字段 + public static 字段 + 4 个构造方法) | `c260813` | [`course`](./授课代码/c260813/src/course/) |
 
 
 ## 三、课堂练习题
 ## 三、课堂练习题
 
 

+ 88 - 0
授课代码/c260813/src/course/Demo01.java

@@ -0,0 +1,88 @@
+package course;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title Demo01
+ * @description 反射-Field 属性/字段/变量
+ * @create 2026/8/13
+ */
+public class Demo01 {
+    /*
+        Field对象代表类中的一个成员变量。反射可以通过Field的get()读取字段值、set()修改字段值。
+        这个在很多框架当中使用的都非常的多,比如我们使用框架查询数据库,把数据库的数据一个一个映射到我们自己创建的Java类中,
+        使用的就是File.set()来进行字段赋值。
+
+    Class类的获取Field对象的方法
+        public Field[] getFields()  返回所有public修饰的字段属性,包括从父类继承的
+        public Field getField(String name)  返回指定的单个public修饰的字段属性
+        public Field[] getDeclaredFields() 返回本类(不包含父类)声明的所有的字段属性包括private
+        public Field getDeclaredField(String name) 返回指定的单个的字段属性(包括private)
+
+    File类的方法
+        public Object get(Object obj)   读取对象obj的该属性值
+        public void set(Object obj, Object value)   设置对象obj的该属性值
+        public void setAccessible(boolean flag)     暴力反射,临时取消访问权限检查
+        public Class<?> getType()   获取属性的类型
+        public String getName()     获取属性名
+        public int getModifiers()   获取属性的修饰符 后面可以使用Modifier解析
+
+
+
+
+     */
+    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
+        //获取Class对象
+        Class<Student> clazz = Student.class;
+
+        //1、获取所有属性并且输出修饰符和类型
+        System.out.println(clazz.getSimpleName()+"类的所有属性:");
+        //获取Student类型的所有属性,包含私有的,但是不包括父类的属性
+        Field[] fields = clazz.getDeclaredFields();
+        for (Field field:fields){
+            //获取属性的修饰符
+            int modifiers = field.getModifiers();
+            String mod = Modifier.toString(modifiers);  //public / private/static...
+            //获取属性的类型
+            Class<?> type = field.getType();
+            String typeName = type.getSimpleName();
+            System.out.println(mod+" "+typeName+" "+field.getName());
+        }
+
+        // 2、读取/修改 public 属性
+        Student s=new Student(1001,"张三",25,"男","zhangsan@163.com","13636363366");
+        //Class对象根据指定的属性名创建对象
+        Field emailField = clazz.getField("email");
+        //读取 s对象的public属性
+        System.out.println("读取email的值:"+emailField.get(s));
+        //设置 s 对象的public属性
+        emailField.set(s,"lisi@qq.com");
+        System.out.println("修改后的:"+s.email);
+
+        //3、 读取/修改 private属性 -- 暴力反射
+        Field nameField = clazz.getDeclaredField("name");
+        //暴力反射--临时取消权限检查
+        nameField.setAccessible(true);
+        System.out.println("读取 private name 的值:"+nameField.get(s));
+        nameField.set(s,"李四");
+        System.out.println("修改后的 private name 值:"+nameField.get(s));
+
+        Student.school="哈尔滨佛学院";
+        //4、操作静态属性 传null
+        Field schoolField = clazz.getDeclaredField("school");
+        //暴力反射--临时取消权限检查
+        nameField.setAccessible(true);
+        //如果要获取的是静态属性 就 传null
+        //静态成员(静态属性、静态方法、静态代码块)不属于某个对象,而是属于整个类的。所以不传对象参数,传null
+        Object value = schoolField.get(null);
+        System.out.println("静态属性 school :"+value);
+
+        schoolField.set(null,"福建桥梁职业技术学院");
+        System.out.println("修改后的属性 school :"+Student.school);
+
+
+    }
+}

+ 48 - 0
授课代码/c260813/src/course/Student.java

@@ -0,0 +1,48 @@
+package course;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title Student
+ * @description
+ * @create 2026/8/12
+ */
+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;
+
+    public Student() {
+    }
+
+    private Student(String name, Integer id) {
+        this.name = name;
+        this.id = id;
+    }
+
+    public Student(Integer id, String name, Integer age, String gender) {
+        this.id = id;
+        this.name = name;
+        this.age = age;
+        this.gender = gender;
+    }
+
+    public Student(Integer id, String name, Integer age, String gender, String email, String phone) {
+        this.id = id;
+        this.name = name;
+        this.age = age;
+        this.gender = gender;
+        this.email = email;
+        this.phone = phone;
+    }
+
+    public void study(){
+        System.out.println(name+"在学习.....");
+    }
+}
+

+ 498 - 0
课堂笔记/01-JavaSE基础-笔记/20260813-笔记.md

@@ -0,0 +1,498 @@
+# 20260813 课堂笔记 — 反射 Field 属性/字段/变量
+
+- **日期**:2026-08-13
+- **项目**:`c260813`
+- **包路径**:`course`
+- **作者**:WanJL
+- **内容概览**:反射 Field(属性/字段/变量)(Demo01 演示:Class 类获取 Field 对象的 4 个 API → 获取所有属性并输出修饰符和类型 → 读取/修改 public 属性 → setAccessible(true) 暴力反射读取/修改 private 属性 → 操作静态属性传 null)+ Student 实体类(private/public/public static 字段设计 + 4 个构造方法)
+
+---
+
+## 目录
+
+1. [反射 Field 概述:成员变量的反射操作](#1-反射-field-概述成员变量的反射操作)
+2. [获取 Field 对象的 4 个 API:Declared 决定能否拿到私有字段](#2-获取-field-对象的-4-个-apideclared-决定能否拿到私有字段)
+3. [Field 类的常用方法:get/set/setAccessible/getType/getName/getModifiers](#3-field-类的常用方法getsetsetaccessiblegettypegetnamegetmodifiers)
+4. [Demo01 演示①:获取所有属性并输出修饰符和类型](#4-demo01-演示①获取所有属性并输出修饰符和类型)
+5. [Demo01 演示②:读取/修改 public 属性](#5-demo01-演示②读取修改-public-属性)
+6. [Demo01 演示③:读取/修改 private 属性——暴力反射 setAccessible(true)](#6-demo01-演示③读取修改-private-属性暴力反射-setaccessibletrue)
+7. [Demo01 演示④:操作静态属性——对象参数传 null](#7-demo01-演示④操作静态属性对象参数传-null)
+8. [Student 实体类解析:三种字段权限的反射目标](#8-student-实体类解析三种字段权限的反射目标)
+9. [知识点全景总结](#9-知识点全景总结)
+10. [随堂练习要点](#10-随堂练习要点)
+11. [拓展阅读](#11-拓展阅读)
+
+---
+
+## 1. 反射 Field 概述:成员变量的反射操作
+
+### 概念 —— Field 对象代表类中的一个成员变量
+
+**Field 对象代表类中的一个成员变量(属性/字段)**。反射可以通过 Field 的 `get()` 读取字段值、`set()` 修改字段值。字段的反射操作在**框架中应用极广**:
+
+```java
+// 来源:course/Demo01.java(类注释)
+/*
+    Field对象代表类中的一个成员变量。反射可以通过Field的get()读取字段值、set()修改字段值。
+    这个在很多框架当中使用的都非常的多,比如我们使用框架查询数据库,把数据库的数据一个一个映射到我们自己创建的Java类中,
+    使用的就是File.set()来进行字段赋值。
+*/
+```
+
+> **要点**:
+> - **Field = 成员变量/属性/字段**——反射中表示类的属性信息
+> - **get() 读值、set() 赋值**——反射操作属性的两个核心方法
+> - **框架中的典型应用**:ORM 框架查询数据库,把数据库查到的数据**一个一个映射到 Java 类中,用的就是 `Field.set()` 来进行字段赋值**——这是数据库记录 → Java 对象映射(ORM)的底层原理
+
+### 反射操作字段 vs 普通操作字段
+
+| 对比 | 普通方式 | 反射方式 |
+|------|----------|----------|
+| 读取属性 | `对象.属性名`(如 `s.email`) | `field.get(对象)` |
+| 修改属性 | `对象.属性名 = 值` | `field.set(对象, 值)` |
+| 私有属性 | 编译期直接报错(private 不可见) | `getDeclaredField()` + `setAccessible(true)` 可访问 |
+| 静态属性 | `类名.属性名`(如 `Student.school`) | `field.get(null)` / `field.set(null, 值)` |
+
+> **要点**:
+> - 普通方式操作属性,**私有成员在编译期就无法访问**;反射方式可以动态拿到任意属性的 Field 对象(包括 private)
+> - 反射操作属性是"**运行时动态**"的——属性名可以是字符串变量/配置文件内容,这正是框架映射数据库字段时"字段名写死在配置里、运行时通过反射赋值"的原理
+
+---
+
+## 2. 获取 Field 对象的 4 个 API:Declared 决定能否拿到私有字段
+
+### 概念 —— Class 类获取 Field 对象的四个方法
+
+拿到 Class 对象后,用以下 4 个方法获取 Field 对象(**与上一课 Constructor 的 API 套路完全一致**):
+
+```java
+// 来源:course/Demo01.java(类注释)
+/*
+    Class类的获取Field对象的方法
+        public Field[] getFields()  返回所有public修饰的字段属性,包括从父类继承的
+        public Field getField(String name)  返回指定的单个public修饰的字段属性
+        public Field[] getDeclaredFields() 返回本类(不包含父类)声明的所有的字段属性包括private
+        public Field getDeclaredField(String name) 返回指定的单个的字段属性(包括private)
+*/
+```
+
+### 4 个 API 对比表
+
+| 方法 | 作用 | 是否含 private | 是否含父类继承的字段 |
+|------|------|----------------|---------------------|
+| `getFields()` | 返回**所有 public 修饰**的字段属性数组 | ❌ 只 public | ✅ 包括从父类继承的 |
+| `getField(String name)` | 返回**指定的单个 public** 字段属性 | ❌ 只 public | ✅ 包括从父类继承的 |
+| `getDeclaredFields()` | 返回**本类声明的所有**字段属性数组 | ✅ 含 private | ❌ 不包含父类 |
+| `getDeclaredField(String name)` | 返回**指定的单个**字段属性 | ✅ 含 private | ❌ 不包含父类 |
+
+> **要点**:
+> - **记忆口诀(与 Constructor 一致)**:**含 `Declared` 的能拿到私有(包含所有),不含 `Declared` 的只能拿 public**
+> - **`Declared`(本类声明)的语义**:只拿**本类**里声明的字段,**不包含父类**;非 `Declared` 反而**包含从父类继承的 public 字段**——这正是 `getFields()` 与 `getDeclaredFields()` 的核心区别
+> - **参数写法**:`getField(String name)` / `getDeclaredField(String name)` 传入**字段名字符串**(如 `"email"`),字段名不存在会抛 `NoSuchFieldException`(受检异常)
+
+---
+
+## 3. Field 类的常用方法:get/set/setAccessible/getType/getName/getModifiers
+
+### 概念 —— 拿到 Field 对象后可以做什么
+
+```java
+// 来源:course/Demo01.java(类注释)
+/*
+    File类的方法
+        public Object get(Object obj)   读取对象obj的该属性值
+        public void set(Object obj, Object value)   设置对象obj的该属性值
+        public void setAccessible(boolean flag)     暴力反射,临时取消访问权限检查
+        public Class<?> getType()   获取属性的类型
+        public String getName()     获取属性名
+        public int getModifiers()   获取属性的修饰符 后面可以使用Modifier解析
+*/
+```
+
+| 方法 | 作用 | 说明 |
+|------|------|------|
+| `get(Object obj)` | **读取**对象 obj 的该属性值 | 返回 `Object`,拿到的是 Object 类型,使用时常需强转 |
+| `set(Object obj, Object value)` | **设置**对象 obj 的该属性值 | 参数 ①对象 ②要赋的值 |
+| `setAccessible(boolean flag)` | **暴力反射**:临时取消访问权限检查 | 访问 private 字段前必须调用 `setAccessible(true)` |
+| `getType()` | **获取属性的类型** | 返回 `Class<?>`,可再调 `getSimpleName()` 得到类型名 |
+| `getName()` | **获取属性名** | 返回字段名字符串 |
+| `getModifiers()` | **获取属性的修饰符** | 返回 `int` 数值,需配合 `Modifier.toString()` 解析成 `public/private/static...` |
+
+### 两个核心参数规律
+
+| 场景 | get 的参数 | set 的参数 | 原因 |
+|------|-----------|-----------|------|
+| **操作实例属性** | `field.get(s)`(传**对象**) | `field.set(s, 值)`(传**对象**+值) | 实例属性属于某个对象,必须指明是"哪个对象的属性" |
+| **操作静态属性** | `field.get(null)`(传 **null**) | `field.set(null, 值)`(传 **null**+值) | 静态属性属于整个类、不属于任何对象,对象参数传 null |
+
+> **要点**:
+> - **读取/修改属性必须带"目标对象"**——因为同一个字段在不同对象上有不同的值,必须告诉反射"操作哪个对象"
+> - **静态属性传 null**:静态成员不属于某个对象、属于整个类,所以对象参数传 **null**
+> - **getModifiers() 返回的是 int 数字**,需要用 `java.lang.reflect.Modifier` 类的 `toString()` 方法解析成可读字符串(如 `private`、`public static`)
+
+---
+
+## 4. Demo01 演示①:获取所有属性并输出修饰符和类型
+
+### 概念 —— getDeclaredFields() 遍历类的全部字段
+
+反射的第一个典型应用:**拿到一个类的所有属性,并逐一输出"修饰符 + 类型 + 属性名"**——这在框架底层(打印对象信息、生成 SQL、DTO 校验)非常常用。
+
+```java
+// 来源:course/Demo01.java
+public class Demo01 {
+    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
+        //获取Class对象
+        Class<Student> clazz = Student.class;
+
+        //1、获取所有属性并且输出修饰符和类型
+        System.out.println(clazz.getSimpleName()+"类的所有属性:");
+        //获取Student类型的所有属性,包含私有的,但是不包括父类的属性
+        Field[] fields = clazz.getDeclaredFields();
+        for (Field field:fields){
+            //获取属性的修饰符
+            int modifiers = field.getModifiers();
+            String mod = Modifier.toString(modifiers);  //public / private/static...
+            //获取属性的类型
+            Class<?> type = field.getType();
+            String typeName = type.getSimpleName();
+            System.out.println(mod+" "+typeName+" "+field.getName());
+        }
+        // ……(第 2~4 部分代码见后续章节)……
+    }
+}
+```
+
+### 预期输出
+
+```
+Student类的所有属性:
+private Integer id
+private String name
+private Integer age
+private String gender
+public String email
+public String phone
+public static String school
+```
+
+### 代码拆解
+
+| 代码 | 作用 | 说明 |
+|------|------|------|
+| `Student.class` | 获取 Class 对象 | 方式一:类名.class(上一课内容,编译期类型检查最安全) |
+| `clazz.getSimpleName()` | 获取**简单类名** | 输出 `Student` |
+| `clazz.getDeclaredFields()` | 获取**本类声明的全部字段**(含 private,不含父类) | 拿到 7 个字段的 Field 数组 |
+| `field.getModifiers()` | 获取修饰符(int 数值) | 如 private=2、public=1、static=8(JVM 常量值) |
+| `Modifier.toString(modifiers)` | **把 int 修饰符解析成可读字符串** | 输出 `private` / `public static` 等 |
+| `field.getType()` | 获取属性类型 | 返回 `Class<?>`(如 Integer.class) |
+| `type.getSimpleName()` | 获取类型简单名 | 输出 `Integer` / `String` |
+| `field.getName()` | 获取属性名 | 输出 `id` / `name` / `school` 等 |
+
+> **要点**:
+> - **`getDeclaredFields()` 拿的是本类声明的全部字段(含 private),不包括父类**——所以 Student 的 7 个字段全部被打印(id/name/age/gender/email/phone/school),而父类 Object 的字段不会被打印
+> - **`getModifiers()` 返回 int 数值**,必须用 `Modifier.toString()` 解析——本课新增的 `java.lang.reflect.Modifier` 工具类专门负责解析修饰符
+> - 修饰符解析结果示例:`private`、`public`、`public static`——静态字段 school 输出为 `public static String school`
+
+---
+
+## 5. Demo01 演示②:读取/修改 public 属性
+
+### 概念 —— getField + get/set 操作公有字段
+
+对于 **public 字段**,可以用 `getField("字段名")` 获取 Field 对象,再用 `get()` 读取、`set()` 修改:
+
+```java
+// 来源:course/Demo01.java
+public class Demo01 {
+    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
+        //获取Class对象
+        Class<Student> clazz = Student.class;
+
+        // 2、读取/修改 public 属性
+        Student s=new Student(1001,"张三",25,"男","zhangsan@163.com","13636363366");
+        //Class对象根据指定的属性名创建对象
+        Field emailField = clazz.getField("email");
+        //读取 s对象的public属性
+        System.out.println("读取email的值:"+emailField.get(s));
+        //设置 s 对象的public属性
+        emailField.set(s,"lisi@qq.com");
+        System.out.println("修改后的:"+s.email);
+    }
+}
+```
+
+### 预期输出
+
+```
+读取email的值:zhangsan@163.com
+修改后的:lisi@qq.com
+```
+
+### 代码拆解
+
+| 代码 | 作用 | 说明 |
+|------|------|------|
+| `new Student(1001, "张三", 25, "男", "zhangsan@163.com", "13636363366")` | 创建 Student 对象 | 调用 public 六参构造(email、phone 是 public 字段,构造里赋值) |
+| `clazz.getField("email")` | 获取**指定的 public 字段** | 字段名不存在会抛 `NoSuchFieldException` |
+| `emailField.get(s)` | **读取** s 对象的 email 属性值 | 返回 `Object`,输出 `zhangsan@163.com` |
+| `emailField.set(s, "lisi@qq.com")` | **修改** s 对象的 email 属性值 | 反射赋值后对象属性直接改变 |
+| `s.email` | 直接访问验证 | 反射 set 之后用普通方式 `s.email` 也能看到新值——**反射赋值与普通赋值是同一份数据** |
+
+> **要点**:
+> - **getField() 只能拿 public 字段**(含父类继承的 public 字段),拿私有字段会抛异常
+> - **反射 set 与普通访问是同一份数据**:`emailField.set(s, ...)` 之后直接 `s.email` 读取,值已经改变——证明反射操作的就是对象的真实字段内存
+> - **get() 返回值类型是 Object**:使用时可能需要强转成目标类型(如 `String email = (String) emailField.get(s)`)
+
+---
+
+## 6. Demo01 演示③:读取/修改 private 属性——暴力反射 setAccessible(true)
+
+### 概念 —— getDeclaredField + setAccessible(true) 突破私有权限
+
+private 字段在普通代码中**编译期就无法访问**,但反射可以拿到它的 Field 对象。**直接 `get()`/`set()` 会因权限检查报 `IllegalAccessException`**,必须先调用 `setAccessible(true)` **临时取消访问权限检查**——这就是**暴力反射**。
+
+```java
+// 来源:course/Demo01.java
+public class Demo01 {
+    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
+        //获取Class对象
+        Class<Student> clazz = Student.class;
+
+        //3、 读取/修改 private属性 -- 暴力反射
+        Field nameField = clazz.getDeclaredField("name");
+        //暴力反射--临时取消权限检查
+        nameField.setAccessible(true);
+        System.out.println("读取 private name 的值:"+nameField.get(s));
+        nameField.set(s,"李四");
+        System.out.println("修改后的 private name 值:"+nameField.get(s));
+    }
+}
+```
+
+### 预期输出
+
+```
+读取 private name 的值:张三
+修改后的 private name 值:李四
+```
+
+### 暴力反射执行流程
+
+```
+nameField = clazz.getDeclaredField("name")   ← 获取 private 字段(getDeclaredField 能拿到)
+      ↓
+nameField.get(s) / set(s, "李四")             ← 不 setAccessible(true) 直接操作 → 抛 IllegalAccessException
+      ↓
+nameField.setAccessible(true)                 ← 暴力反射:临时取消访问权限检查
+      ↓
+get(s) 读取 / set(s, "李四") 修改              ← 权限放行,成功操作 private 字段
+```
+
+| 代码 | 作用 | 说明 |
+|------|------|------|
+| `clazz.getDeclaredField("name")` | 获取**指定的单个字段(包括 private)** | 必须用 Declared 系列才能拿到私有字段 |
+| `nameField.setAccessible(true)` | **临时取消访问权限检查** | 暴力反射的关键一步,否则对 private 字段 get/set 会报权限错误 |
+| `nameField.get(s)` | 读取 private name 属性值 | 输出 `张三` |
+| `nameField.set(s, "李四")` | 修改 private name 属性值 | 反射修改成功后,再 get 输出 `李四` |
+
+> **要点**:
+> - **获取 private 字段必须用 `getDeclaredField()`**(含 Declared),`getField()` 只能拿 public
+> - **操作 private 字段必须 `setAccessible(true)`**——这是暴力反射,也是上一课"反射可能破坏封装性"的具体体现
+> - **与上一课 Constructor 的套路完全一致**:`getDeclaredXxx` 拿私有 + `setAccessible(true)` 放开权限——反射三大成员(Constructor/Field/Method)的暴力反射操作模式相同
+
+---
+
+## 7. Demo01 演示④:操作静态属性——对象参数传 null
+
+### 概念 —— 静态成员属于整个类,操作时不传对象传 null
+
+**静态成员(静态属性、静态方法、静态代码块)不属于某个对象,而是属于整个类的**。所以反射操作静态属性时,`get()`/`set()` 的**对象参数不传对象,传 null**。
+
+```java
+// 来源:course/Demo01.java
+public class Demo01 {
+    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
+        //获取Class对象
+        Class<Student> clazz = Student.class;
+
+        Student.school="哈尔滨佛学院";
+        //4、操作静态属性 传null
+        Field schoolField = clazz.getDeclaredField("school");
+        //暴力反射--临时取消权限检查
+        nameField.setAccessible(true);
+        //如果要获取的是静态属性 就 传null
+        //静态成员(静态属性、静态方法、静态代码块)不属于某个对象,而是属于整个类的。所以不传对象参数,传null
+        Object value = schoolField.get(null);
+        System.out.println("静态属性 school :"+value);
+
+        schoolField.set(null,"福建桥梁职业技术学院");
+        System.out.println("修改后的属性 school :"+Student.school);
+    }
+}
+```
+
+### 预期输出
+
+```
+静态属性 school :哈尔滨佛学院
+修改后的属性 school :福建桥梁职业技术学院
+```
+
+### 代码拆解
+
+| 代码 | 作用 | 说明 |
+|------|------|------|
+| `Student.school = "哈尔滨佛学院"` | 普通方式给静态字段赋值 | 静态字段可用"类名.属性名"直接操作 |
+| `clazz.getDeclaredField("school")` | 获取 static 字段的 Field 对象 | school 是 `public static` 字段,同样用 Declared 系列获取 |
+| `schoolField.get(null)` | 读取静态属性——**对象参数传 null** | 静态成员不属于某个对象、属于整个类,不传对象 |
+| `schoolField.set(null, "福建桥梁职业技术学院")` | 修改静态属性——**对象参数传 null** | set 的第一参数(对象)也传 null |
+| `Student.school` | 直接访问验证 | 反射 set 后 `Student.school` 输出新值——静态属性反射修改成功 |
+
+> **要点**:
+> - **静态属性对象参数传 null**:静态成员属于整个类、不属于任何对象,`get(null)` / `set(null, 值)`——记住这个规律
+> - **反射修改静态属性后,`类名.属性名` 直接访问也生效**:`schoolField.set(null, ...)` 之后 `Student.school` 已变为新值——静态字段全类共享一份
+> - **private static 字段同样适用**:static 与 private 可以叠加(`private static`),获取/操作时既要用 `getDeclaredField` 也要 `setAccessible(true)`
+
+---
+
+## 8. Student 实体类解析:三种字段权限的反射目标
+
+### 概念 —— 一个覆盖了 public/private/static 的反射目标类
+
+`Student` 是本课的反射目标实体类,字段设计刻意覆盖了**三种权限类型**,正好对应 Demo01 的三段演示:
+
+```java
+// 来源:course/Student.java
+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;
+
+    public Student() {
+    }
+
+    private Student(String name, Integer id) {
+        this.name = name;
+        this.id = id;
+    }
+
+    public Student(Integer id, String name, Integer age, Integer gender) {
+        this.id = id;
+        this.name = name;
+        this.age = age;
+        this.gender = gender;
+    }
+
+    public Student(Integer id, String name, Integer age, Integer gender, String email, String phone) {
+        this.id = id;
+        this.name = name;
+        this.age = age;
+        this.gender = gender;
+        this.email = email;
+        this.phone = phone;
+    }
+
+    public void study(){
+        System.out.println(name+"在学习.....");
+    }
+}
+```
+
+### 字段权限设计对照表
+
+| 字段 | 权限 | 作用 / 对应演示 |
+|------|------|----------------|
+| `private Integer id` | private | 私有字段——反射 `getDeclaredFields()` 能拿到(演示①) |
+| `private String name` | private | 私有字段——暴力反射 `getDeclaredField` + `setAccessible(true)` 读取/修改(演示③) |
+| `private Integer age` | private | 私有字段——演示① 打印修饰符/类型 |
+| `private String gender` | private | 私有字段——演示① 打印修饰符/类型 |
+| `public String email` | public | 公有字段——`getField` + `get/set` 读取/修改(演示②) |
+| `public String phone` | public | 公有字段——演示① 打印修饰符/类型 |
+| `public static String school` | **public + static** | 静态字段——`get(null)` / `set(null, 值)` 操作(演示④) |
+
+### 4 个构造方法(回顾上一课反射 Constructor)
+
+| 构造方法 | 权限 | 参数 |
+|----------|------|------|
+| `Student()` | public | 无参 |
+| `Student(String name, Integer id)` | private | 二参(上一课暴力反射目标) |
+| `Student(Integer id, String name, Integer age, Integer gender)` | public | 四参 |
+| `Student(Integer id, String name, Integer age, Integer gender, String email, String phone)` | public | 六参(本课 Demo01 创建对象用) |
+
+> **要点**:
+> - **字段刻意混合 public / private / static**:就是为了让同一份反射代码展示三种操作路径——`getField`(public)、`getDeclaredField + setAccessible`(private)、`get/set(null)`(static)
+> - **本课用六参构造创建对象**:`new Student(1001, "张三", 25, "男", "zhangsan@163.com", "13636363366")`——因为 email、phone 是 public 字段,构造里可以给它们赋值
+> - **上节课已学构造方法**:4 个构造方法、`study()` 方法在上一课(08-12 反射 Constructor)已分析,本课重点是**字段(Field)**这一维度
+
+---
+
+## 9. 知识点全景总结
+
+| 知识点 | 核心内容 | 来源 |
+|--------|----------|------|
+| **Field 概念** | Field 对象代表类中的一个**成员变量(属性/字段)**;反射通过 `get()` 读值、`set()` 赋值 | course/Demo01 注释 |
+| **Field 的框架应用** | 框架查询数据库后,把数据库数据**一个一个映射到 Java 类中用的就是 Field.set()** 赋值(ORM 底层原理) | course/Demo01 注释 |
+| **getFields()** | 返回所有 **public** 字段数组,**包括从父类继承的** | course/Demo01 注释 |
+| **getField(String name)** | 返回指定的单个 **public** 字段,包括从父类继承的 | course/Demo01 注释 |
+| **getDeclaredFields()** | 返回**本类(不含父类)声明的所有**字段数组,**包括 private** | course/Demo01 注释 |
+| **getDeclaredField(String name)** | 返回指定的单个字段,**包括 private** | course/Demo01 注释 |
+| **Declared 口诀** | **含 Declared 的能拿到私有(本类全部),不含 Declared 的只能拿 public(含父类继承)**——与 Constructor 一致 | course/Demo01 注释 |
+| **Field.get(Object obj)** | **读取**对象 obj 的该属性值,返回 Object(可能需要强转) | course/Demo01 注释 |
+| **Field.set(Object obj, Object value)** | **设置**对象 obj 的该属性值(参数:对象 + 值) | course/Demo01 注释 |
+| **Field.setAccessible(true)** | **暴力反射**:临时取消访问权限检查,访问 private 字段必须调用 | course/Demo01 注释 |
+| **Field.getType()** | 获取属性的类型,返回 `Class<?>`(再调 getSimpleName() 得类型名) | course/Demo01 注释 |
+| **Field.getName()** | 获取属性名 | course/Demo01 注释 |
+| **Field.getModifiers()** | 获取属性的修饰符(返回 int),用 `Modifier.toString()` 解析 | course/Demo01 注释 |
+| **Modifier.toString(int)** | 把修饰符 int 值解析成可读字符串(private / public / public static...) | course/Demo01 main |
+| **演示①:遍历所有属性** | `getDeclaredFields()` + `field.getModifiers()` + `Modifier.toString()` + `field.getType().getSimpleName()` + `field.getName()` 输出每个字段的修饰符/类型/属性名 | course/Demo01 main |
+| **演示②:操作 public 字段** | `clazz.getField("email")` + `get(s)` 读取 + `set(s, 新值)` 修改;反射 set 后 `s.email` 直接访问同样生效 | course/Demo01 main |
+| **演示③:操作 private 字段** | `clazz.getDeclaredField("name")` + `setAccessible(true)` 暴力反射 + `get(s)`/`set(s, 新值)` | course/Demo01 main |
+| **演示④:操作 static 字段** | 静态成员属于整个类、不属于任何对象,`get(null)` / `set(null, 值)`——对象参数传 **null** | course/Demo01 main |
+| **静态属性传 null 的原理** | 静态成员(静态属性/静态方法/静态代码块)不属于某个对象,而是属于整个类的,所以不传对象参数、传 null | course/Demo01 main |
+| **反射赋值与普通访问同一份数据** | `field.set(s, ...)` 修改后 `s.email` / `Student.school` 直接访问也是新值 | course/Demo01 main |
+| **Student 字段设计** | private(id/name/age/gender)+ public(email/phone)+ public static(school)——三种权限对应三段演示 | course/Student.java |
+| **Student 构造方法** | public 无参 / private 二参 / public 四参 / public 六参——本课用六参构造创建对象,上一课演示过 Constructor 反射 | course/Student.java |
+| **student.study()** | Student 的实例方法 `输出 name+"在学习....."` | course/Student.java |
+| **受检异常** | main 方法签名 throws `ClassNotFoundException, NoSuchFieldException, IllegalAccessException`——getField 字段名不存在抛 NoSuchFieldException、private 未放开权限 get/set 抛 IllegalAccessException、forName 类名错误抛 ClassNotFoundException | course/Demo01 main |
+
+---
+
+## 10. 随堂练习要点
+
+- **Field 概念复述(必做)**:用一句话说清"什么是 Field 反射"——Field 对象代表类中的一个成员变量,反射通过 `get()` 读取字段值、`set()` 修改字段值;能说出框架中的应用(ORM 框架查询数据库后用 Field.set() 把数据映射到 Java 类)
+- **获取 Field 的 4 个 API 背诵(必做)**:`getFields()`(所有 public 数组,含父类继承)/ `getField(String name)`(指定 public)/ `getDeclaredFields()`(本类全部含 private,不含父类)/ `getDeclaredField(String name)`(指定含 private)——背口诀:**含 Declared 的能拿私有(本类全部),不含 Declared 的只能拿 public(含父类继承)**
+- **Field 常用方法背诵(必做)**:`get(对象)` 读值 / `set(对象, 值)` 赋值 / `setAccessible(true)` 暴力反射 / `getType()` 类型 / `getName()` 属性名 / `getModifiers()` 修饰符(int 需 Modifier.toString 解析)
+- **getFields vs getDeclaredFields 对比**:思考两者的区别——getFields 拿 public + 父类继承;getDeclaredFields 拿本类全部(含 private)不含父类。给 Student 加一个父类字段,分别用两种方式验证
+- **Demo01 运行验证(必做)**:运行 `course/Demo01`,观察输出——先打印 Student 全部 7 个字段(private/public/public static 三种修饰符),再验证 public email 的读取/修改、private name 的暴力反射、static school 的传 null 操作
+- **getModifiers 返回 int 理解**:打印 `emailField.getModifiers()` 直接输出一个 int 数字(如 1),用 `Modifier.toString()` 解析后才得到 `public`——理解"修饰符底层是 int 常量值"
+- **暴力反射实验(必做)**:把 Demo01 中 `nameField.setAccessible(true)` 注释掉再运行,观察抛 **IllegalAccessException**——直观体会"不 setAccessible(true) 无法访问 private 字段"(与上一课访问 private 构造方法完全一致的套路)
+- **getField 拿私有字段实验**:把 Demo01 中 `clazz.getDeclaredField("name")` 改成 `clazz.getField("name")`,运行观察抛 **NoSuchFieldException**——体会"getField 只能拿 public 字段"的口诀
+- **静态属性传 null 理解(必做)**:思考为什么静态属性 get/set 的对象参数要传 null——静态成员(静态属性/静态方法/静态代码块)不属于某个对象,而是属于整个类;对比实例属性必须传对象(不同对象字段值不同)
+- **静态属性反射修改验证**:观察 `schoolField.set(null, "福建桥梁职业技术学院")` 之后 `Student.school` 直接访问输出新值——体会"反射 set 与普通访问同一份数据、静态字段全类共享"
+- **与 Constructor 反射对比(必做)**:回顾上一课(08-12 反射 Constructor)——获取 API 都是"含 Declared 拿私有、不含 Declared 只拿 public",访问私有成员都要 `setAccessible(true)`;对比 Constructor(newInstance 创建对象)与 Field(get/set 读写属性)的差异
+- **反射三大成员串联**:到目前为止已学 **Constructor**(创建对象)和 **Field**(读写字段属性),下一个核心成员是 **Method**(调用方法,`getMethod` + `invoke`)——套路与两者完全一致,提前预习
+- **getDeclaredFields 遍历练习**:写一个通用工具方法 `printFields(Object obj)`:传入任意对象,用 `getDeclaredFields()` 遍历打印所有字段的修饰符/类型/属性名——体会"反射可以做通用工具"(回顾上一课 printUtil 思路)
+- **受检异常处理回顾**:观察 Demo01 main 方法签名 `throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException`——getField/getDeclaredField 字段名写错抛 NoSuchFieldException、private 未放开权限操作抛 IllegalAccessException、Class.forName 类名写错抛 ClassNotFoundException——回顾 07-28 异常处理知识
+
+---
+
+## 11. 拓展阅读
+
+- **反射 Field 在 ORM 框架中的落地**:MyBatis、Hibernate 等 ORM 框架的**结果映射(Result Mapping)**就是本课"Field.set() 把数据库数据映射到 Java 类"的经典实现——数据库表字段名对应 Java 实体属性名,查询结果通过反射 Field.set() 逐字段赋值
+- **反射三大核心成员进度**:本课完成了反射三大核心成员中的 **Constructor(构造方法,08-12)+ Field(字段属性,本课)**,还剩 **Method(方法)**——下一课将学习 `getMethods()` / `getDeclaredMethods()` / `getMethod(name, 参数类型)` + `method.invoke(对象, 参数)` 调用方法,套路与本课完全一致(Declared 拿私有 + setAccessible(true) 放开权限)
+- **getModifiers() 的 int 常量**:Java 反射中修饰符以 **int 位掩码(bitmask)** 表示——public=1、private=2、protected=4、static=8、final=16...`Modifier.toString()` 内部就是按位与运算拼接出字符串;`Modifier.isPublic(int)` / `Modifier.isStatic(int)` 等 isXxx 方法可单独判断某个修饰符是否存在
+- **Field.setAccessible 与 JPMS**:`setAccessible(true)` 本质是**绕过 Java 访问控制检查**;在模块化系统(JPMS)中,跨模块访问私有成员还受 `--add-opens` 参数约束——Spring 等框架在 JDK17+ 运行时需要配置 JVM 参数放开模块限制
+- **反射的缓存优化**:反射 `getField`/`getDeclaredField` 本身有开销,框架(如 MyBatis)通常**在启动阶段一次性获取并缓存 Field 对象**,运行时只调用 `get()/set()`——这是"框架用反射但性能不差"的关键优化手段
+- **setAccessible 的性能影响**:`setAccessible(true)` 不仅放开权限,还会让 JVM 为字段访问做**更快的内部优化**(绕过访问检查的反射调用性能显著提升)——所以框架总是对私有字段先 setAccessible(true) 再操作,既是"能访问"也是"更高效"
+- **字段名与数据库列名的映射**:ORM 框架中实体属性名(如 `userName`)与数据库列名(如 `user_name`)往往不一致——框架通过配置或约定(驼峰↔下划线转换)拿到字段名后,用反射 `Field.set()` 赋值,这正是"配置文件/注解驱动反射"的典型场景
+- **IDEA 调试器的实现原理**:IDEA 等 IDE 的**调试器变量查看窗口**能在运行时显示对象的所有字段值(包括 private)——底层就是反射 `getDeclaredFields()` + `setAccessible(true)` + `field.get(对象)`,与本课 Demo01 的演示逻辑一致
+- **JSON 序列化框架**:Jackson / Gson / Fastjson 把 Java 对象转成 JSON 字符串、或把 JSON 转回对象,底层也大量使用 Field 反射——读取对象所有字段名与值(序列化)、按 JSON 键名给对象字段赋值(反序列化)
+- **反射破坏封装性的辩证关系**:本课 Field 反射可以访问 private 字段、修改 final 字段,既是框架能力的来源,也是"破坏封装性"的体现——日常业务代码中应遵守上一课的原则:**正常业务用普通方式访问属性,反射留给框架与中间件**
+- **JDK 文档**:[`java.lang.Class`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Class.html)(getFields/getField/getDeclaredFields/getDeclaredField)、[`java.lang.reflect.Field`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/reflect/Field.html)(get/set/setAccessible/getType/getName/getModifiers)、[`java.lang.reflect.Modifier`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/reflect/Modifier.html)(toString/isPublic/isStatic 等修饰符解析工具类)
+