Browse Source

feat: 更新20260812反射练习第1题——改为反射遍历SysUser四个构造方法创建对象(public无参getConstructor+newInstance、public六参getConstructor(6个String.class)、private二参getDeclaredConstructor+setAccessible(true)暴力反射、private四参getDeclaredConstructor(4个String.class)+setAccessible(true)),含getConstructors/getDeclaredConstructors个数统计、instanceof判断、toString打印验证、暴力反射对比实验等,同步更新涵盖知识点总览与README练习描述,纳入SysUser.java实体类

WanJL 1 week ago
parent
commit
0bca346316
3 changed files with 188 additions and 42 deletions
  1. 1 1
      README.md
  2. 115 0
      授课代码/c260812/src/course/SysUser.java
  3. 72 41
      课堂练习题/20260812-反射练习.md

+ 1 - 1
README.md

@@ -101,4 +101,4 @@
 | 2026-08-08 | [对象序列化与转换流练习](./课堂练习题/20260808-对象序列化与转换流练习.md) | 对象序列化/反序列化、Serializable、serialVersionUID、transient、转换流字符集转码 |
 | 2026-08-08 | [对象序列化与转换流练习](./课堂练习题/20260808-对象序列化与转换流练习.md) | 对象序列化/反序列化、Serializable、serialVersionUID、transient、转换流字符集转码 |
 | 2026-08-10 | [线程操作与生命周期练习](./课堂练习题/20260810-线程操作与生命周期练习.md) | 线程生命周期(getState 6 种状态)、线程优先级(setPriority)、守护线程(setDaemon/JVM 退出机制)、线程协作(join/interrupt/yield) |
 | 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-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) | 反射创建对象(Class.forName + getConstructor + newInstance + instanceof + 强转)、反射给私有属性赋值(getDeclaredField + setAccessible + field.set/get)、反射调用方法(getMethod + invoke 带参返回值)、综合实战配置文件驱动 + 反射(模拟 Spring IOC:Properties 加载类名/属性/方法名) |
+| 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 加载类名/属性/方法名) |

+ 115 - 0
授课代码/c260812/src/course/SysUser.java

@@ -0,0 +1,115 @@
+package course;
+
+import java.util.Objects;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title SysUser
+ * @description
+ * @create 2026/8/12
+ */
+public class SysUser {
+    private String id;
+    private String username;
+    private String password;
+    private String name;
+    private String phone;
+    private String email;
+
+    public SysUser() {
+    }
+
+    private SysUser(String username, String password) {
+        this.username = username;
+        this.password = password;
+    }
+
+    public SysUser(String id, String username, String password, String name, String phone, String email) {
+        this.id = id;
+        this.username = username;
+        this.password = password;
+        this.name = name;
+        this.phone = phone;
+        this.email = email;
+    }
+
+    private SysUser(String id, String name, String phone, String email) {
+        this.id = id;
+        this.name = name;
+        this.phone = phone;
+        this.email = email;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null || this.getClass() != o.getClass()) return false;
+        SysUser sysUser = (SysUser) o;
+        return Objects.equals(id, sysUser.id) && Objects.equals(username, sysUser.username) && Objects.equals(password, sysUser.password) && Objects.equals(name, sysUser.name) && Objects.equals(phone, sysUser.phone) && Objects.equals(email, sysUser.email);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id, username, password, name, phone, email);
+    }
+
+    @Override
+    public String toString() {
+        return "SysUser{" +
+                "id='" + id + '\'' +
+                ", username='" + username + '\'' +
+                ", password='" + password + '\'' +
+                ", name='" + name + '\'' +
+                ", phone='" + phone + '\'' +
+                ", email='" + email + '\'' +
+                '}';
+    }
+}

+ 72 - 41
课堂练习题/20260812-反射练习.md

@@ -1,28 +1,41 @@
 # 2026-08-12 反射练习(课堂练习用)
 # 2026-08-12 反射练习(课堂练习用)
 
 
-> **说明**:练习今日新学知识点——**反射机制(Reflection)**(Demo04:反射概念、普通调用 vs 反射调用、为什么需要反射、反射核心类)、**获取 Class 对象三种方式**(Demo05:类名.class / 对象.getClass() / Class.forName("全类名"),同一个类三种方式拿到同一个对象)、**反射 Constructor 构造方法**(Demo06:getConstructors / getConstructor / getDeclaredConstructors / getDeclaredConstructor 获取构造方法、setAccessible(true) 暴力反射、newInstance() 反射创建对象、instanceof 判断 + 强转调用方法)。延伸使用 **Field 字段反射**(给私有属性赋值)与 **Method 方法反射**(调用私有/带参方法)——它们是反射三大核心成员(Constructor / Field / Method)的后两者。
+> **说明**:练习今日新学知识点——**反射机制(Reflection)**(Demo04:反射概念、普通调用 vs 反射调用、为什么需要反射、反射核心类)、**获取 Class 对象三种方式**(Demo05:类名.class / 对象.getClass() / Class.forName("全类名"),同一个类三种方式拿到同一个对象)、**反射 Constructor 构造方法**(Demo06:getConstructors / getConstructor / getDeclaredConstructors / getDeclaredConstructor 获取构造方法、setAccessible(true) 暴力反射、newInstance() 反射创建对象、instanceof 判断 + 强转调用方法)。练习一用反射**分别通过 SysUser 的 4 个构造方法**(public 无参 / private 二参 / public 六参 / private 四参)创建对象;练习二、三、四延伸使用 **Field 字段反射**(给私有属性赋值)与 **Method 方法反射**(调用私有/带参方法)——它们是反射三大核心成员(Constructor / Field / Method)的后两者。
 
 
 ---
 ---
 
 
-## 练习一:反射创建对象 —— 拿 Class → 拿构造 → 建对象
+## 练习一:反射创建对象 —— 遍历 SysUser 的四个构造方法建对象
 
 
 **难度**:⭐⭐⭐
 **难度**:⭐⭐⭐
-**知识点**:`Class.forName("全类名")` 获取 Class 对象、`getConstructor()` 获取 public 无参构造、`newInstance()` 反射创建对象、`instanceof` 判断类型、向下转型、调用方法
+**知识点**:`Class.forName("全类名")` 获取 Class 对象、`getConstructors()` / `getDeclaredConstructors()` 获取构造方法数组、`getConstructor(参数类型...)` / `getDeclaredConstructor(参数类型...)` 获取指定构造方法、`newInstance()` 反射创建对象、`setAccessible(true)` 暴力反射访问私有构造、`instanceof` 判断类型、向下转型、调用 toString()
 
 
-**场景描述**:课堂学了**反射最核心的用途**——拿到 Class 对象后,通过构造方法创建对象(这是 Spring IOC 等框架的底层原理)。本例完整走一遍反射创建对象的四步链:**① Class.forName 拿 Class → ② getConstructor 拿构造 → ③ newInstance 建对象 → ④ instanceof 判断 + 强转 + 调用方法**,与普通 `new Student()` 做对比。
+**场景描述**:课堂学了**反射最核心的用途**——拿到 Class 对象后,通过构造方法创建对象(这是 Spring IOC 等框架的底层原理)。`SysUser` 系统用户类一共有 **4 个构造方法**(2 个 public + 2 个 private):
+
+```java
+// SysUser 的 4 个构造方法(源码:course/SysUser.java)
+public SysUser()                                          // ① public 无参
+private SysUser(String username, String password)         // ② private 二参(用户名,密码)
+public SysUser(String id, String username, String password,
+               String name, String phone, String email)   // ③ public 六参(完整用户信息)
+private SysUser(String id, String name,
+                String phone, String email)               // ④ private 四参(基础信息)
+```
+
+本例用反射**分别通过这 4 个构造方法创建 4 个 SysUser 对象**——完整走一遍"拿 Class → 拿构造 → 建对象"的反射创建对象流程,并重点体会:**private 构造方法必须用 `getDeclaredConstructor` + `setAccessible(true)` 暴力反射**才能调用。
 
 
 ### 案例需求
 ### 案例需求
 
 
-用反射的方式创建 Student 对象,并调用它的 study() 方法。
+用反射分别通过 SysUser 的 4 个构造方法创建 4 个对象,并打印每个对象验证(`toString()`)
 
 
 ### 实现步骤
 ### 实现步骤
 
 
-1. 用 `Class.forName("course.Student")` 获取 Student 的 Class 对象
-2. 用 `clazz.getConstructor()` 获取 public 无参构造方法对象
-3. 用 `constructor.newInstance()` 反射创建 Student 对象
-4. 用 `obj instanceof Student` 判断创建的对象是不是 Student 类型
-5. 强转成 Student 后调用 `study()` 方法
-6. 思考:反射创建对象 和 `new Student()` 有什么不同?
+1. `Class.forName("course.SysUser")` 获取 SysUser 的 Class 对象
+2. `getConstructors()` 统计 public 构造个数(2 个),`getDeclaredConstructors()` 统计全部构造个数(4 个,含 private)
+3. **反射创建对象①**:`getConstructor()` 获取 public 无参构造 → `newInstance()` 创建空对象
+4. **反射创建对象②**:`getConstructor(String.class × 6)` 获取 public 六参构造 → `newInstance(id, username, password, name, phone, email)` 创建完整用户对象
+5. **反射创建对象③**:`getDeclaredConstructor(String.class, String.class)` 获取 private 二参构造 → `setAccessible(true)` → `newInstance("admin", "admin888")` 暴力反射创建对象
+6. **反射创建对象④**:`getDeclaredConstructor(String.class × 4)` 获取 private 四参构造 → `setAccessible(true)` → `newInstance(id, name, phone, email)` 暴力反射创建对象
+7. 每个对象 `instanceof SysUser` 判断 + 打印 `toString()` 验证属性
 
 
 ### 代码框架
 ### 代码框架
 
 
@@ -31,48 +44,65 @@ package course;
 
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Constructor;
 
 
-public class ReflectCreateObjectTest {
+public class ReflectCreateSysUserTest {
     public static void main(String[] args) throws Exception {
     public static void main(String[] args) throws Exception {
-        // ① 方式三:Class.forName("全类名") 获取 Student 的 Class 对象
+        // ① Class.forName("全类名") 获取 SysUser 的 Class 对象
         // TODO 1: Class<?> clazz = Class.forName("???");
         // TODO 1: Class<?> clazz = Class.forName("???");
-        Class<?> clazz = Class.forName("course.Student");
-
-        // ② 获取 public 无参构造方法对象
-        // TODO 2: Constructor<?> constructor = clazz.???();
-        Constructor<?> constructor = clazz.getConstructor();
-
-        // ③ 通过构造方法对象 反射创建 Student 对象
-        // TODO 3: Object obj = constructor.???();
-        Object obj = constructor.newInstance();
-
-        // ④ instanceof 判断创建的对象是不是 Student 类型
-        System.out.println("这个对象是 Student 类型吗?" + (obj instanceof Student));
-
-        // ⑤ 强转成 Student,调用方法
-        // TODO 4: 强转 + s.study()
-        if (obj instanceof Student) {
-            Student s = (Student) obj;
-            s.study();
-        }
+        Class<?> clazz = Class.forName("course.SysUser");
+
+        // ② 统计构造方法:getConstructors 只能拿到 public(2 个),getDeclaredConstructors 能拿到全部(4 个,含 private)
+        System.out.println("public 构造方法个数:" + clazz.getConstructors().length);
+        System.out.println("全部构造方法个数(含private):" + clazz.getDeclaredConstructors().length);
+
+        // ③ 反射创建对象①:public 无参构造
+        // TODO 2: Constructor<?> c1 = clazz.getConstructor();
+        Constructor<?> c1 = clazz.getConstructor();
+        Object obj1 = c1.newInstance();
+        System.out.println("① 无参构造:" + obj1 + "(是SysUser吗?" + (obj1 instanceof SysUser) + ")");
+
+        // ④ 反射创建对象②:public 六参构造(id, username, password, name, phone, email)
+        // TODO 3: getConstructor 传 6 个 String.class 参数类型
+        Constructor<?> c2 = clazz.getConstructor(String.class, String.class, String.class, String.class, String.class, String.class);
+        Object obj2 = c2.newInstance("1", "zhangsan", "123456", "张三", "13800000000", "zhangsan@qq.com");
+        System.out.println("② 六参构造:" + obj2);
+
+        // ⑤ 反射创建对象③:private 二参构造(username, password)—— 必须 setAccessible(true) 暴力反射!
+        // TODO 4: getDeclaredConstructor(String.class, String.class) + setAccessible(true)
+        Constructor<?> c3 = clazz.getDeclaredConstructor(String.class, String.class);
+        c3.setAccessible(true);
+        Object obj3 = c3.newInstance("admin", "admin888");
+        System.out.println("③ 私有二参构造:" + obj3);
+
+        // ⑥ 反射创建对象④:private 四参构造(id, name, phone, email)—— 同样需要暴力反射
+        // TODO 5: getDeclaredConstructor(4 个 String.class) + setAccessible(true)
+        Constructor<?> c4 = clazz.getDeclaredConstructor(String.class, String.class, String.class, String.class);
+        c4.setAccessible(true);
+        Object obj4 = c4.newInstance("2", "李四", "13900000000", "lisi@qq.com");
+        System.out.println("④ 私有四参构造:" + obj4);
     }
     }
 }
 }
 ```
 ```
 
 
 **输出示例**:
 **输出示例**:
 ```
 ```
-这个对象是 Student 类型吗?true
-null在学习.....   ← study() 打印 name+"在学习.....",无参构造 name 默认 null
+public 构造方法个数:2
+全部构造方法个数(含private):4
+① 无参构造:SysUser{id='null', username='null', password='null', name='null', phone='null', email='null'}(是SysUser吗?true)
+② 六参构造:SysUser{id='1', username='zhangsan', password='123456', name='张三', phone='13800000000', email='zhangsan@qq.com'}
+③ 私有二参构造:SysUser{id='null', username='admin', password='admin888', name='null', phone='null', email='null'}
+④ 私有四参构造:SysUser{id='2', username='null', password='null', name='李四', phone='13900000000', email='lisi@qq.com'}
 ```
 ```
 
 
 **选做加分**:
 **选做加分**:
-- 分别用三种方式获取 Class 对象(`Student.class` / `new Student().getClass()` / `Class.forName("course.Student")`),打印三个对象是否相等(`==` 结果应为 true)
-- 用 `clazz.getConstructor(Integer.class, String.class, Integer.class, String.class)` 获取 public 四参构造,`newInstance(1001, "张三", 20, "男")` 创建带属性对象
-- 对比 `new Student()` 与反射创建对象,说说"普通方式代码写死、反射方式动态"的含义
+- **暴力反射对比实验**:把对象③/④的 `setAccessible(true)` 注释掉再运行,观察抛 **IllegalAccessException**——体会"私有构造必须放开访问权限"
+- **Declared 口诀验证**:用 `getConstructor(String.class, String.class)`(不带 Declared)尝试获取 private 二参构造,观察抛 **NoSuchMethodException**——验证"不含 Declared 只能拿 public"
+- **遍历构造方法**:`for (Constructor<?> c : clazz.getDeclaredConstructors())` 打印每个构造的 `getParameterCount()`(参数个数)和 `getParameterTypes()`(参数类型),观察 4 个构造的参数差异
+- 分别用三种方式获取 Class 对象(`SysUser.class` / `new SysUser().getClass()` / `Class.forName("course.SysUser")`),打印三个对象是否相等(`==` 结果应为 true)
 
 
 **思考题**:
 **思考题**:
-1. 为什么 `newInstance()` 返回的是 `Object` 类型,还要强转才能调用 `study()`?
-2. 反射创建对象对应普通调用的哪个动作?(提示:`new 类名()`)
-3. `Class.forName("course.Student")` 会触发 Student 的静态代码块吗?(提示:会!回顾第 10 节类加载时机差异
+1. SysUser 有 2 个 private 构造方法,为什么 `getConstructor()` 拿不到它们?必须用什么方法组合?(提示:`getDeclaredConstructor` + `setAccessible(true)`,口诀"含 Declared 的才能拿私有")
+2. public 构造和 private 构造,反射创建对象的方式有什么不同?多出来的 `setAccessible(true)` 做了什么?
+3. 反射创建的对象是 `Object` 类型,怎么确认它确实是 SysUser?确认后如何调用 SysUser 的方法?(提示:`instanceof` + 强转
 
 
 ---
 ---
 
 
@@ -373,6 +403,7 @@ Student{name='配置驱动小明'}
 | 获取 Class 对象三种方式 | ①类名.class(编译期类型检查最安全)②对象.getClass()(需有对象实例)③Class.forName(最灵活)——同一个类三种方式拿到同一个对象 |
 | 获取 Class 对象三种方式 | ①类名.class(编译期类型检查最安全)②对象.getClass()(需有对象实例)③Class.forName(最灵活)——同一个类三种方式拿到同一个对象 |
 | `getConstructor()` / `newInstance()` | 获取 public 无参构造 + 反射创建对象(对应普通 `new 类名()`) |
 | `getConstructor()` / `newInstance()` | 获取 public 无参构造 + 反射创建对象(对应普通 `new 类名()`) |
 | `getConstructor(参数类型...)` | 获取指定参数类型的 public 构造方法,`newInstance(参数...)` 创建带属性对象 |
 | `getConstructor(参数类型...)` | 获取指定参数类型的 public 构造方法,`newInstance(参数...)` 创建带属性对象 |
+| 遍历类的多个构造方法创建对象 | SysUser 有 4 个构造方法(2 public + 2 private):`getConstructors()`/`getConstructor` 创建 public 构造对象、`getDeclaredConstructors()`/`getDeclaredConstructor` + `setAccessible(true)` 暴力反射创建 private 构造对象,`newInstance()` 逐个创建 |
 | `getDeclaredField("属性名")` | 获取字段 Field 对象(**含 private**);不带 Declared 的 `getField` 只能拿 public |
 | `getDeclaredField("属性名")` | 获取字段 Field 对象(**含 private**);不带 Declared 的 `getField` 只能拿 public |
 | `field.setAccessible(true)` | 暴力反射:放开私有成员的访问权限,否则操作 private 会抛 IllegalAccessException |
 | `field.setAccessible(true)` | 暴力反射:放开私有成员的访问权限,否则操作 private 会抛 IllegalAccessException |
 | `field.set(对象, 值)` / `field.get(对象)` | 反射给属性赋值 / 读取属性值——对应普通调用的 setter / getter |
 | `field.set(对象, 值)` / `field.get(对象)` | 反射给属性赋值 / 读取属性值——对应普通调用的 setter / getter |
@@ -382,6 +413,6 @@ Student{name='配置驱动小明'}
 | 反射三大核心成员 | Constructor(构造方法创建对象)/ Field(字段属性读写)/ Method(方法调用)——套路一致:Declared 拿私有 + setAccessible(true) 放开权限 |
 | 反射三大核心成员 | Constructor(构造方法创建对象)/ Field(字段属性读写)/ Method(方法调用)——套路一致:Declared 拿私有 + setAccessible(true) 放开权限 |
 | 配置驱动 + 反射 | 框架读取配置文件里的类名字符串 → Class.forName → 反射创建/赋值/调用——换类不改代码,解耦(Spring IOC 底层原理) |
 | 配置驱动 + 反射 | 框架读取配置文件里的类名字符串 → Class.forName → 反射创建/赋值/调用——换类不改代码,解耦(Spring IOC 底层原理) |
 | `Properties` 加载配置文件 | `getResourceAsStream("xxx.properties")` + `load()` 解析 + `getProperty()` 取值(回顾 Demo03) |
 | `Properties` 加载配置文件 | `getResourceAsStream("xxx.properties")` + `load()` 解析 + `getProperty()` 取值(回顾 Demo03) |
-| instanceof + 向下转型 | 反射创建的对象是 Object 类型,`instanceof Student` 判断后强转再调用方法 |
+| instanceof + 向下转型 | 反射创建的对象是 Object 类型,`instanceof SysUser`(或 Student)判断后强转再调用方法 |
 | 反射的注意事项 | 日常业务尽量不用:破坏封装性、效率比正常创建对象低 3~100 倍;学习反射为理解框架底层源码 |
 | 反射的注意事项 | 日常业务尽量不用:破坏封装性、效率比正常创建对象低 3~100 倍;学习反射为理解框架底层源码 |
 | 反射 API 的受检异常 | ClassNotFoundException / NoSuchMethodException / InstantiationException / IllegalAccessException / InvocationTargetException——必须处理(main 用 throws) |
 | 反射 API 的受检异常 | ClassNotFoundException / NoSuchMethodException / InstantiationException / IllegalAccessException / InvocationTargetException——必须处理(main 用 throws) |