|
@@ -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) |
|