|
@@ -1,10 +1,10 @@
|
|
|
-# 20260813 课堂笔记 — 反射 Field 属性/字段/变量
|
|
|
|
|
|
|
+# 20260813 课堂笔记 — 反射 Field 字段反射与 Method 方法反射
|
|
|
|
|
|
|
|
- **日期**:2026-08-13
|
|
- **日期**:2026-08-13
|
|
|
- **项目**:`c260813`
|
|
- **项目**:`c260813`
|
|
|
- **包路径**:`course`
|
|
- **包路径**:`course`
|
|
|
- **作者**:WanJL
|
|
- **作者**:WanJL
|
|
|
-- **内容概览**:反射 Field(属性/字段/变量)(Demo01 演示:Class 类获取 Field 对象的 4 个 API → 获取所有属性并输出修饰符和类型 → 读取/修改 public 属性 → setAccessible(true) 暴力反射读取/修改 private 属性 → 操作静态属性传 null)+ Student 实体类(private/public/public static 字段设计 + 4 个构造方法)
|
|
|
|
|
|
|
+- **内容概览**:反射 Field(属性/字段/变量)(Demo01 演示:Class 类获取 Field 对象的 4 个 API → 获取所有属性并输出修饰符和类型 → 读取/修改 public 属性 → setAccessible(true) 暴力反射读取/修改 private 属性 → 操作静态属性传 null)+ 反射 Method(方法)(Demo02 演示:Class 类获取 Method 对象的 4 个 API → 遍历方法输出方法名/参数/返回值 → invoke 调用实例方法/带参方法/私有方法/静态方法)+ Student 实体类(private/public/public static 字段设计 + 4 个构造方法 + 7 组 getter/setter + 私有方法 read/write)
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
@@ -18,9 +18,13 @@
|
|
|
6. [Demo01 演示③:读取/修改 private 属性——暴力反射 setAccessible(true)](#6-demo01-演示③读取修改-private-属性暴力反射-setaccessibletrue)
|
|
6. [Demo01 演示③:读取/修改 private 属性——暴力反射 setAccessible(true)](#6-demo01-演示③读取修改-private-属性暴力反射-setaccessibletrue)
|
|
|
7. [Demo01 演示④:操作静态属性——对象参数传 null](#7-demo01-演示④操作静态属性对象参数传-null)
|
|
7. [Demo01 演示④:操作静态属性——对象参数传 null](#7-demo01-演示④操作静态属性对象参数传-null)
|
|
|
8. [Student 实体类解析:三种字段权限的反射目标](#8-student-实体类解析三种字段权限的反射目标)
|
|
8. [Student 实体类解析:三种字段权限的反射目标](#8-student-实体类解析三种字段权限的反射目标)
|
|
|
-9. [知识点全景总结](#9-知识点全景总结)
|
|
|
|
|
-10. [随堂练习要点](#10-随堂练习要点)
|
|
|
|
|
-11. [拓展阅读](#11-拓展阅读)
|
|
|
|
|
|
|
+9. [反射 Method 方法概述:方法的反射操作(Demo02)](#9-反射-method-方法概述方法的反射操作demo02)
|
|
|
|
|
+10. [获取 Method 对象的 4 个 API:Declared 与重载参数](#10-获取-method-对象的-4-个-apideclared-与重载参数)
|
|
|
|
|
+11. [Method 类的常用方法:invoke/setAccessible/getReturnType/getParameterTypes/getModifiers](#11-method-类的常用方法invokesetaccessiblegetreturntypegetparametertypesgetmodifiers)
|
|
|
|
|
+12. [Demo02 演示:遍历方法 + invoke 调用实例/带参/私有/静态方法](#12-demo02-演示遍历方法-invoke-调用实例带参私有静态方法)
|
|
|
|
|
+13. [知识点全景总结](#13-知识点全景总结)
|
|
|
|
|
+14. [随堂练习要点](#14-随堂练习要点)
|
|
|
|
|
+15. [拓展阅读](#15-拓展阅读)
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
@@ -398,6 +402,16 @@ public class Student{
|
|
|
this.phone = phone;
|
|
this.phone = phone;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // …… 7 组 getter/setter(id/name/age/gender/email/phone/school)见下方补充……
|
|
|
|
|
+
|
|
|
|
|
+ private void read(){
|
|
|
|
|
+ System.out.println(this.name+"读.......");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static void write(){
|
|
|
|
|
+ System.out.println("静态方法...写...............");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
public void study(){
|
|
public void study(){
|
|
|
System.out.println(name+"在学习.....");
|
|
System.out.println(name+"在学习.....");
|
|
|
}
|
|
}
|
|
@@ -425,6 +439,41 @@ public class Student{
|
|
|
| `Student(Integer id, String name, Integer age, Integer gender)` | public | 四参 |
|
|
| `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 创建对象用) |
|
|
| `Student(Integer id, String name, Integer age, Integer gender, String email, String phone)` | public | 六参(本课 Demo01 创建对象用) |
|
|
|
|
|
|
|
|
|
|
+### 代码更新:新增 7 组 getter/setter 与 2 个私有方法(Demo02 的反射目标)
|
|
|
|
|
+
|
|
|
|
|
+Student 类同步更新,新增了**每个字段对应的 getter/setter**(id/name/age/gender/email/phone/school 共 7 组,school 的 getter/setter 是 **static** 的),以及 **2 个私有方法** `read()` / `write()`——它们正是 Demo02 方法反射(Method)的调用目标:
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+// 来源:course/Student.java(代码更新新增部分)
|
|
|
|
|
+public Integer getId() { return id; }
|
|
|
|
|
+public void setId(Integer id) { this.id = id; }
|
|
|
|
|
+public String getName() { return name; }
|
|
|
|
|
+public void setName(String name) { this.name = name; }
|
|
|
|
|
+// …… age/gender/email/phone 的 getter/setter 同理 ……
|
|
|
|
|
+public static String getSchool() { return school; }
|
|
|
|
|
+public static void setSchool(String school) { Student.school = school; } // 静态 setter 用 类名.静态字段 赋值
|
|
|
|
|
+
|
|
|
|
|
+private void read(){ // private 实例方法:输出 name+"读......."
|
|
|
|
|
+ System.out.println(this.name+"读.......");
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+private static void write(){ // private 静态方法:输出"静态方法...写..............."
|
|
|
|
|
+ System.out.println("静态方法...写...............");
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+| 新增成员 | 类型 | 作用 / 对应演示 |
|
|
|
|
|
+|----------|------|----------------|
|
|
|
|
|
+| `getId/setId` ~ `getPhone/setPhone` 共 5 组 | public 实例 getter/setter | Demo02 用 `getMethod("getName")` / `getMethod("setName", String.class)` 反射调用 |
|
|
|
|
|
+| `getSchool/setSchool` | **public static** getter/setter | static 修饰的 getter/setter——调用静态方法 invoke 第一个参数传 null |
|
|
|
|
|
+| `read()` | **private 实例方法** | 暴力反射目标——`getDeclaredMethod("read")` + `setAccessible(true)` + `invoke(s)` |
|
|
|
|
|
+| `write()` | **private 静态方法** | 暴力反射 + 静态方法目标——`getDeclaredMethod("write")` + `setAccessible(true)` + `invoke(null)` |
|
|
|
|
|
+
|
|
|
|
|
+> **要点**:
|
|
|
|
|
+> - **getter/setter 是反射 Method 最典型的调用对象**:`getMethod("setName", String.class)` 反射调用 setName 等价于 `s.setName("李四")`——框架(MyBatis/Spring)反射调用 setter 给属性赋值、getter 取属性值正是本课的典型应用
|
|
|
|
|
+> - **private 方法必须暴力反射**:`read()` / `write()` 是 private,必须 `getDeclaredMethod` 拿到 + `setAccessible(true)` 放开权限才能 `invoke`
|
|
|
|
|
+> - **静态方法 invoke 传 null**:`write()` 是 static,`invoke(null)` 即可调用(与 Field 操作静态属性传 null 同一规律)
|
|
|
|
|
+
|
|
|
> **要点**:
|
|
> **要点**:
|
|
|
> - **字段刻意混合 public / private / static**:就是为了让同一份反射代码展示三种操作路径——`getField`(public)、`getDeclaredField + setAccessible`(private)、`get/set(null)`(static)
|
|
> - **字段刻意混合 public / private / static**:就是为了让同一份反射代码展示三种操作路径——`getField`(public)、`getDeclaredField + setAccessible`(private)、`get/set(null)`(static)
|
|
|
> - **本课用六参构造创建对象**:`new Student(1001, "张三", 25, "男", "zhangsan@163.com", "13636363366")`——因为 email、phone 是 public 字段,构造里可以给它们赋值
|
|
> - **本课用六参构造创建对象**:`new Student(1001, "张三", 25, "男", "zhangsan@163.com", "13636363366")`——因为 email、phone 是 public 字段,构造里可以给它们赋值
|
|
@@ -432,7 +481,235 @@ public class Student{
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
|
-## 9. 知识点全景总结
|
|
|
|
|
|
|
+## 9. 反射 Method 方法概述:方法的反射操作(Demo02)
|
|
|
|
|
+
|
|
|
|
|
+### 概念 —— Method 对象代表类中的一个方法
|
|
|
|
|
+
|
|
|
|
|
+**Method 对象代表类中的一个方法**。反射通过 `Method.invoke(对象, 参数...)` 来调用方法,**等价于 `对象.方法(参数)`**——但方法的名称和参数都是**运行时才确定**的。这是反射三大核心成员(Constructor / Field / Method)的最后一位,也是**动态代理、测试框架、注解解释器**等高级功能的基础。
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+// 来源:course/Demo02.java(类注释)
|
|
|
|
|
+/*
|
|
|
|
|
+ Method对象代表类中的一个方法,反射通过Method.invoke(对象,参数...)来调用方法,
|
|
|
|
|
+ 就等价于 对象.方法(参数)
|
|
|
|
|
+ 但是方法的名称和参数都是运行时才确定的,它就是动态代理、测试框架、注解解释器等等功能的基础。
|
|
|
|
|
+*/
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 反射调用方法 vs 普通调用方法
|
|
|
|
|
+
|
|
|
|
|
+| 对比 | 普通方式 | 反射方式 |
|
|
|
|
|
+|------|----------|----------|
|
|
|
|
|
+| 调用方法 | `对象.方法名(参数)`(如 `s.study()`) | `method.invoke(对象, 参数...)` |
|
|
|
|
|
+| 方法的名称 | 编译期写死 | **运行时才确定**(可以是字符串变量/配置) |
|
|
|
|
|
+| 私有方法 | 编译期直接报错(private 不可见) | `getDeclaredMethod` + `setAccessible(true)` 可调用 |
|
|
|
|
|
+| 静态方法 | `类名.方法名()` | `invoke(null, 参数...)` |
|
|
|
|
|
+| 应用场景 | 普通业务 | 动态代理 / 测试框架(JUnit)/ 注解解释器 / 框架调用 setter |
|
|
|
|
|
+
|
|
|
|
|
+> **要点**:
|
|
|
|
|
+> - **invoke 等价于 `对象.方法(参数)`**——反射调用方法就是"运行时动态调用"
|
|
|
|
|
+> - **方法与 Field/Constructor 套路一致**:`getMethod(s)`(public 含继承)vs `getDeclaredMethod(s)`(本类全部含 private)+ `setAccessible(true)` 暴力反射
|
|
|
|
|
+> - **典型应用**:框架反射调用实体类的 setter 给属性赋值、getter 取属性值;测试框架反射调用被测试方法;动态代理底层基于 Method.invoke
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 10. 获取 Method 对象的 4 个 API:Declared 与重载参数
|
|
|
|
|
+
|
|
|
|
|
+### 概念 —— Class 类获取 Method 对象的四个方法
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+// 来源:course/Demo02.java(类注释)
|
|
|
|
|
+/*
|
|
|
|
|
+ Class对象获取Method对象的方法:
|
|
|
|
|
+ public Method[] getMethods() 返回所有的public方法,包括父类/接口继承的,比如toString
|
|
|
|
|
+ public Method getMethod(String name, Class<?>... parameterTypes) 返回指定的单个public方法
|
|
|
|
|
+ public Method[] getDeclaredMethods() 返回所有的方法,含有private,但不包括父类/接口的方法
|
|
|
|
|
+ public Method getDeclaredMethod(String name, Class<?>... parameterTypes) 返回指定的单个方法含有private
|
|
|
|
|
+*/
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 4 个 API 对比表
|
|
|
|
|
+
|
|
|
|
|
+| 方法 | 作用 | 是否含 private | 是否含父类/接口继承 |
|
|
|
|
|
+|------|------|----------------|---------------------|
|
|
|
|
|
+| `getMethods()` | 返回**所有 public 方法**数组 | ❌ 只 public | ✅ 含父类/接口继承的(如 `toString`/`hashCode`) |
|
|
|
|
|
+| `getMethod(name, parameterTypes)` | 返回**指定的单个 public 方法** | ❌ 只 public | ✅ 含父类/接口继承的 |
|
|
|
|
|
+| `getDeclaredMethods()` | 返回**本类所有方法**数组 | ✅ 含 private | ❌ 不含父类/接口的方法 |
|
|
|
|
|
+| `getDeclaredMethod(name, parameterTypes)` | 返回**指定的单个方法** | ✅ 含 private | ❌ 不含父类/接口的方法 |
|
|
|
|
|
+
|
|
|
|
|
+> **要点**:
|
|
|
|
|
+> - **口诀与 Field/Constructor 完全一致**:**含 `Declared` 的能拿到私有(本类全部、不含父类/接口);不含 `Declared` 的只能拿 public(含父类/接口继承)**
|
|
|
|
|
+> - **`getMethods()` 会包含继承的方法**:比如 Student 没写 `toString`,但 `getMethods()` 能拿到 Object 的 `toString`/`hashCode`/`equals`/`getClass` 等——这是 Method 与 Field 的"含继承"表现(Field 的 `getFields()` 也含父类 public 字段)
|
|
|
|
|
+
|
|
|
|
|
+### 重载方法必须指明参数类型
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+// 来源:course/Demo02.java(类注释)
|
|
|
|
|
+/*
|
|
|
|
|
+ 如果遇到重载的方法(方法名相同,参数列表不同)那么就必须要指明参数的类型比如:
|
|
|
|
|
+ getMethod("setName", String.class) 中的String.class就是用来区分同名方法重载的,
|
|
|
|
|
+ 如果参数类型不写,或写错,就会抛异常。
|
|
|
|
|
+*/
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+| 写法 | 说明 |
|
|
|
|
|
+|------|------|
|
|
|
|
|
+| `getMethod("setName", String.class)` | 指定参数类型,找到 `setName(String)` 这个重载版本 |
|
|
|
|
|
+| `getMethod("setName")` | 不写参数类型——如果存在多个同名重载方法,**无法区分会抛异常**(NoSuchMethodException) |
|
|
|
|
|
+| `getMethod("getAge", Integer.class)` | 参数类型写错也会抛异常——必须与实际方法签名完全匹配 |
|
|
|
|
|
+
|
|
|
|
|
+> **要点**:
|
|
|
|
|
+> - **同名重载方法(方法名相同、参数列表不同)必须传参数类型来区分**——`String.class` 就是用来区分哪个重载版本的
|
|
|
|
|
+> - **参数类型不写或写错都会抛异常**(`NoSuchMethodException`)——所以 `getMethod("setName", String.class)` 中的 `String.class` 必不可少
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 11. Method 类的常用方法:invoke/setAccessible/getReturnType/getParameterTypes/getModifiers
|
|
|
|
|
+
|
|
|
|
|
+### 概念 —— 拿到 Method 对象后可以做什么
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+// 来源:course/Demo02.java(类注释)
|
|
|
|
|
+/*
|
|
|
|
|
+ Method对象的方法:
|
|
|
|
|
+ public Object invoke(Object obj, Object... args) 调用方法,返回方法的执行结果
|
|
|
|
|
+ obj 是某个对象,指的是执行某个对象的指定方法
|
|
|
|
|
+ args 参数列表,调用方法时需要传入的实际参数
|
|
|
|
|
+ public void setAccessible(boolean flag) 暴力反射
|
|
|
|
|
+ public Class<?> getReturnType() 获取方法的返回值类型
|
|
|
|
|
+ public Class<?>[] getParameterTypes() 获取方法的参数类型数组
|
|
|
|
|
+ public int getModifiers() 获取方法的修饰符
|
|
|
|
|
+*/
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+| 方法 | 作用 | 说明 |
|
|
|
|
|
+|------|------|------|
|
|
|
|
|
+| `invoke(Object obj, Object... args)` | **调用方法**,返回方法的执行结果 | `obj` 是执行该方法的对象,`args` 是实际参数列表;返回 `Object`(可强转) |
|
|
|
|
|
+| `setAccessible(boolean flag)` | **暴力反射**:临时取消访问权限检查 | 调用 private 方法前必须 `setAccessible(true)` |
|
|
|
|
|
+| `getReturnType()` | **获取方法的返回值类型** | 返回 `Class<?>`(如 String.class / void.class) |
|
|
|
|
|
+| `getParameterTypes()` | **获取方法的参数类型数组** | 返回 `Class<?>[]`,逐个可取参数类型 |
|
|
|
|
|
+| `getModifiers()` | **获取方法的修饰符** | 返回 int,用 `Modifier.toString()` 解析 |
|
|
|
|
|
+
|
|
|
|
|
+### invoke 的四种调用规律(重点)
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+// 来源:course/Demo02.java(类注释)
|
|
|
|
|
+/*
|
|
|
|
|
+ 注意:
|
|
|
|
|
+ 如果是实例方法(通过对象调用的) invoke(对象,参数...) 第一个参数是调用该方法的对象
|
|
|
|
|
+ 如果是类方法(使用static修饰的类方法) invoke(null,参数...) 第一个参数是null
|
|
|
|
|
+ 如果没有返回值(void):调用invoke()会返回null
|
|
|
|
|
+ 如果是private方法,那么必须要先暴力反射setAccessible(true)
|
|
|
|
|
+*/
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+| 方法类型 | invoke 写法 | 说明 |
|
|
|
|
|
+|----------|-------------|------|
|
|
|
|
|
+| **public 实例方法** | `invoke(对象, 参数...)` | 第一个参数是**调用该方法的对象**(如 `invoke(s)` 等价于 `s.getName()`) |
|
|
|
|
|
+| **static 静态方法** | `invoke(null, 参数...)` | 第一个参数是 **null**(类方法不属于对象,与 Field 操作静态属性传 null 同一规律) |
|
|
|
|
|
+| **void 无返回值方法** | `invoke(对象, 参数...)` | 调用 `invoke()` 会返回 **null**(void 方法没有返回值) |
|
|
|
|
|
+| **private 私有方法** | 先 `setAccessible(true)` 再 `invoke(...)` | 必须暴力反射放开权限,否则抛 IllegalAccessException |
|
|
|
|
|
+
|
|
|
|
|
+> **要点**:
|
|
|
|
|
+> - **invoke 第一参数 = 调用方法的对象**:实例方法传对象、**静态方法传 null**(回顾 Field 操作静态属性传 null 的同一规律)
|
|
|
|
|
+> - **invoke 返回 Object**:方法有返回值时 `invoke` 返回该返回值(可强转成目标类型);**void 方法返回 null**
|
|
|
|
|
+> - **private 方法必须暴力反射**:`getDeclaredMethod` 拿到 + `setAccessible(true)` + `invoke`——与 Field/Constructor 的暴力反射套路完全一致
|
|
|
|
|
+> - **反射三大成员统一套路**:`getDeclaredXxx` 拿私有 + `setAccessible(true)` 放开权限 + 执行操作(Constructor→newInstance / Field→get/set / Method→invoke)
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 12. Demo02 演示:遍历方法 + invoke 调用实例/带参/私有/静态方法
|
|
|
|
|
+
|
|
|
|
|
+### 概念 —— 完整走一遍"拿 Class → 遍历方法 → 调用方法"
|
|
|
|
|
+
|
|
|
|
|
+Demo02 演示了方法反射的**全部四个应用**:遍历所有方法(含参数和返回值信息)、调用 public 实例方法(无参/带参)、暴力反射调用 private 实例方法、调用 static 静态方法。
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+// 来源:course/Demo02.java
|
|
|
|
|
+public class Demo02 {
|
|
|
|
|
+ public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
|
|
|
|
+ Class<Student> studentClass = Student.class;
|
|
|
|
|
+
|
|
|
|
|
+ //1、获取所有的public方法(含父类/接口继承的,如toString)
|
|
|
|
|
+ Method[] methods = studentClass.getMethods();
|
|
|
|
|
+ for (Method method : methods){
|
|
|
|
|
+ Class<?>[] parameterTypes = method.getParameterTypes();
|
|
|
|
|
+ List<String> parameters = Arrays.stream(parameterTypes)
|
|
|
|
|
+ .map(p -> p.getSimpleName()).collect(Collectors.toList());
|
|
|
|
|
+ System.out.println(method.getName()+"("+parameters+") 返回值="+method.getReturnType().getSimpleName());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ System.out.println("========本类的所有方法==========");
|
|
|
|
|
+ // getDeclaredMethods() 遍历本类全部方法(含 private)
|
|
|
|
|
+ for (Method method: studentClass.getDeclaredMethods()){
|
|
|
|
|
+ Class<?>[] parameterTypes = method.getParameterTypes();
|
|
|
|
|
+ List<String> parameters = Arrays.stream(parameterTypes)
|
|
|
|
|
+ .map(p -> p.getSimpleName()).collect(Collectors.toList());
|
|
|
|
|
+ System.out.println(method.getName()+"("+parameters+") 返回值="+method.getReturnType().getSimpleName());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ //2、执行调用某个对象的实例方法
|
|
|
|
|
+ Student s=new Student(1001,"张三",25,"男","zhangsan@163.com","13636363366");
|
|
|
|
|
+ // 调用 public 无参实例方法 getName()——invoke 返回 Object
|
|
|
|
|
+ Method getNameMethod = studentClass.getMethod("getName");
|
|
|
|
|
+ Object name = getNameMethod.invoke(s);
|
|
|
|
|
+ System.out.println(name);
|
|
|
|
|
+
|
|
|
|
|
+ // 调用 public 带参实例方法 setName(String)——重载必须指明参数类型
|
|
|
|
|
+ Method setNameMethod = studentClass.getMethod("setName", String.class);
|
|
|
|
|
+ setNameMethod.invoke(s, "李四"); //赋值操作不需要返回值
|
|
|
|
|
+ System.out.println(s.getName());
|
|
|
|
|
+
|
|
|
|
|
+ //暴力反射,执行私有方法 read()——private 实例方法
|
|
|
|
|
+ Method readMethod = studentClass.getDeclaredMethod("read");
|
|
|
|
|
+ readMethod.setAccessible(true);
|
|
|
|
|
+ readMethod.invoke(s); //执行方法
|
|
|
|
|
+
|
|
|
|
|
+ //执行静态方法 write()——static 方法 invoke 传 null
|
|
|
|
|
+ Method writeMethod = studentClass.getDeclaredMethod("write");
|
|
|
|
|
+ writeMethod.setAccessible(true);
|
|
|
|
|
+ writeMethod.invoke(null);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 预期输出与运行要点
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+...(getMethods() 输出所有 public 方法:getId/setId/getName/setName/.../study + Object 继承的 toString/hashCode/equals/getClass 等)...
|
|
|
|
|
+========本类的所有方法==========
|
|
|
|
|
+setName([String]) 返回值=void
|
|
|
|
|
+setSchool([String]) 返回值=void
|
|
|
|
|
+write() 返回值=void
|
|
|
|
|
+getSchool() 返回值=String
|
|
|
|
|
+...
|
|
|
|
|
+张三 ← getMethod("getName").invoke(s) 返回 name 值
|
|
|
|
|
+李四 ← getMethod("setName", String.class).invoke(s, "李四") 后 s.getName()
|
|
|
|
|
+李四读....... ← 暴力反射调用 private read():name 已是"李四"
|
|
|
|
|
+静态方法...写............... ← 暴力反射调用 private static write():invoke(null)
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+| 代码 | 作用 | 说明 |
|
|
|
|
|
+|------|------|------|
|
|
|
|
|
+| `studentClass.getMethods()` | 获取**所有 public 方法**(含父类/接口继承) | 遍历打印方法名、参数类型、返回值类型 |
|
|
|
|
|
+| `method.getParameterTypes()` | 获取**参数类型数组** | `Arrays.stream(...)` 用 Stream 流把 `Class<?>` 映射成简单名 List(回顾 Stream 流知识) |
|
|
|
|
|
+| `method.getReturnType().getSimpleName()` | 获取**返回值类型**简单名 | 如 String / void |
|
|
|
|
|
+| `studentClass.getDeclaredMethods()` | 获取**本类全部方法**(含 private,不含父类/接口) | 输出比 getMethods 多出 read/write 两个私有方法 |
|
|
|
|
|
+| `studentClass.getMethod("getName")` | 获取 public 无参方法 | `invoke(s)` 等价于 `s.getName()`,返回 Object 打印 `张三` |
|
|
|
|
|
+| `studentClass.getMethod("setName", String.class)` | 获取 **public 带参方法**(指明参数类型区分重载) | `invoke(s, "李四")` 等价于 `s.setName("李四")`,赋值操作不需要返回值 |
|
|
|
|
|
+| `getDeclaredMethod("read")` + `setAccessible(true)` + `invoke(s)` | **暴力反射调用 private 实例方法** | 必须放开权限;name 已改为"李四",输出 `李四读.......` |
|
|
|
|
|
+| `getDeclaredMethod("write")` + `setAccessible(true)` + `invoke(null)` | **暴力反射调用 private 静态方法** | 静态方法 invoke 第一参数传 **null**,输出 `静态方法...写...............` |
|
|
|
|
|
+
|
|
|
|
|
+> **要点**:
|
|
|
|
|
+> - **getMethods() 含继承方法**:Student 没写 `toString`,但 getMethods() 能拿到 Object 的 `toString`/`hashCode` 等——Demo02 用 Stream 流把参数类型 `Class<?>[]` 转成 `List<String>` 简单名再打印
|
|
|
|
|
+> - **getMethod 调用实例方法**:`invoke(s)` 第一个参数是执行方法的对象;带参方法 `invoke(s, "李四")` 传实际参数
|
|
|
|
|
+> - **void 方法返回值**:`setName` 是 void,`invoke` 返回 null,不需要接收返回值
|
|
|
|
|
+> - **私有方法必须暴力反射**:`read()`/`write()` 是 private,用 `getDeclaredMethod` + `setAccessible(true)`;**静态方法 invoke 传 null**
|
|
|
|
|
+> - **与 Field 反射呼应**:操作静态成员(属性/方法)对象参数都传 null——本课 Field 操作静态属性 + Method 调用静态方法,规律统一
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 13. 知识点全景总结
|
|
|
|
|
|
|
|
| 知识点 | 核心内容 | 来源 |
|
|
| 知识点 | 核心内容 | 来源 |
|
|
|
|--------|----------|------|
|
|
|--------|----------|------|
|
|
@@ -458,12 +735,29 @@ public class Student{
|
|
|
| **反射赋值与普通访问同一份数据** | `field.set(s, ...)` 修改后 `s.email` / `Student.school` 直接访问也是新值 | 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 字段设计** | private(id/name/age/gender)+ public(email/phone)+ public static(school)——三种权限对应三段演示 | course/Student.java |
|
|
|
| **Student 构造方法** | public 无参 / private 二参 / public 四参 / public 六参——本课用六参构造创建对象,上一课演示过 Constructor 反射 | course/Student.java |
|
|
| **Student 构造方法** | public 无参 / private 二参 / public 四参 / public 六参——本课用六参构造创建对象,上一课演示过 Constructor 反射 | course/Student.java |
|
|
|
|
|
+| **Student 新增 getter/setter** | 7 组 getter/setter(id/name/age/gender/email/phone 实例 + school 静态)——Demo02 用 `getMethod("getName")`/`getMethod("setName", String.class)` 反射调用 | course/Student.java |
|
|
|
|
|
+| **Student 私有方法 read/write** | `private void read()`(实例)+ `private static void write()`(静态)——Demo02 暴力反射调用目标(`getDeclaredMethod` + `setAccessible(true)` + `invoke`) | course/Student.java |
|
|
|
| **student.study()** | Student 的实例方法 `输出 name+"在学习....."` | 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 |
|
|
|
|
|
|
|
+| **Method 概念** | Method 对象代表类中的一个方法;反射通过 `Method.invoke(对象, 参数...)` 调用方法,**等价于 `对象.方法(参数)`**,方法名称和参数运行时才确定——动态代理/测试框架/注解解释器的基础 | course/Demo02 注释 |
|
|
|
|
|
+| **getMethods()** | 返回**所有 public 方法**数组,**包括父类/接口继承的**(如 toString/hashCode/getClass) | course/Demo02 注释 |
|
|
|
|
|
+| **getMethod(name, parameterTypes)** | 返回**指定的单个 public 方法**;重载方法必须传参数类型区分(如 `getMethod("setName", String.class)`),不写/写错抛异常 | course/Demo02 注释 |
|
|
|
|
|
+| **getDeclaredMethods()** | 返回**本类所有方法**数组,**包括 private,但不包括父类/接口的方法** | course/Demo02 注释 |
|
|
|
|
|
+| **getDeclaredMethod(name, parameterTypes)** | 返回**指定的单个方法**,**包括 private** | course/Demo02 注释 |
|
|
|
|
|
+| **Method.invoke(obj, args)** | **调用方法**,返回方法的执行结果(Object 可强转);`obj` 是执行该方法的对象,`args` 是实际参数 | course/Demo02 注释 |
|
|
|
|
|
+| **Method.setAccessible(true)** | **暴力反射**:临时取消访问权限检查,调用 private 方法必须调用 | course/Demo02 注释 |
|
|
|
|
|
+| **Method.getReturnType()** | 获取方法的**返回值类型**(如 String.class / void.class) | course/Demo02 注释 |
|
|
|
|
|
+| **Method.getParameterTypes()** | 获取方法的**参数类型数组**(`Class<?>[]`) | course/Demo02 注释 |
|
|
|
|
|
+| **Method.getModifiers()** | 获取方法的修饰符(返回 int,`Modifier.toString()` 解析) | course/Demo02 注释 |
|
|
|
|
|
+| **invoke 四种调用规律** | 实例方法 `invoke(对象,参数...)` 第一参数传对象;**静态方法 `invoke(null,参数...)` 传 null**;**void 方法 invoke 返回 null**;**private 方法必须先 setAccessible(true)** | course/Demo02 注释 |
|
|
|
|
|
+| **演示:遍历方法** | `getMethods()`/`getDeclaredMethods()` 遍历 + `getParameterTypes()`(Stream 映射成简单名 List)+ `getReturnType().getSimpleName()` 输出每个方法的方法名/参数/返回值 | course/Demo02 main |
|
|
|
|
|
+| **演示:调用实例方法** | `getMethod("getName")` + `invoke(s)` 调用无参方法返回 name;`getMethod("setName", String.class)` + `invoke(s, "李四")` 调用带参方法(void 不接收返回值) | course/Demo02 main |
|
|
|
|
|
+| **演示:暴力反射私有方法** | `getDeclaredMethod("read")` + `setAccessible(true)` + `invoke(s)` 调用 private 实例方法;`getDeclaredMethod("write")` + `setAccessible(true)` + `invoke(null)` 调用 private 静态方法(传 null) | course/Demo02 main |
|
|
|
|
|
+| **反射三大成员统一套路** | Constructor(newInstance 创建对象)/ Field(get/set 读写字段)/ Method(invoke 调用方法)——**Declared 拿私有 + setAccessible(true) 放开权限**,操作静态成员(属性/方法)传 null | course/Demo01 + Demo02 |
|
|
|
|
|
+| **受检异常** | Demo01 main throws `ClassNotFoundException, NoSuchFieldException, IllegalAccessException`(Field);Demo02 main throws `NoSuchMethodException, InvocationTargetException, IllegalAccessException`(Method)——方法名/参数类型不匹配抛 NoSuchMethodException、被调方法内部抛异常时 invoke 抛 InvocationTargetException | course/Demo01 + Demo02 main |
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
|
-## 10. 随堂练习要点
|
|
|
|
|
|
|
+## 14. 随堂练习要点
|
|
|
|
|
|
|
|
- **Field 概念复述(必做)**:用一句话说清"什么是 Field 反射"——Field 对象代表类中的一个成员变量,反射通过 `get()` 读取字段值、`set()` 修改字段值;能说出框架中的应用(ORM 框架查询数据库后用 Field.set() 把数据映射到 Java 类)
|
|
- **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 的 4 个 API 背诵(必做)**:`getFields()`(所有 public 数组,含父类继承)/ `getField(String name)`(指定 public)/ `getDeclaredFields()`(本类全部含 private,不含父类)/ `getDeclaredField(String name)`(指定含 private)——背口诀:**含 Declared 的能拿私有(本类全部),不含 Declared 的只能拿 public(含父类继承)**
|
|
@@ -475,17 +769,38 @@ public class Student{
|
|
|
- **getField 拿私有字段实验**:把 Demo01 中 `clazz.getDeclaredField("name")` 改成 `clazz.getField("name")`,运行观察抛 **NoSuchFieldException**——体会"getField 只能拿 public 字段"的口诀
|
|
- **getField 拿私有字段实验**:把 Demo01 中 `clazz.getDeclaredField("name")` 改成 `clazz.getField("name")`,运行观察抛 **NoSuchFieldException**——体会"getField 只能拿 public 字段"的口诀
|
|
|
- **静态属性传 null 理解(必做)**:思考为什么静态属性 get/set 的对象参数要传 null——静态成员(静态属性/静态方法/静态代码块)不属于某个对象,而是属于整个类;对比实例属性必须传对象(不同对象字段值不同)
|
|
- **静态属性传 null 理解(必做)**:思考为什么静态属性 get/set 的对象参数要传 null——静态成员(静态属性/静态方法/静态代码块)不属于某个对象,而是属于整个类;对比实例属性必须传对象(不同对象字段值不同)
|
|
|
- **静态属性反射修改验证**:观察 `schoolField.set(null, "福建桥梁职业技术学院")` 之后 `Student.school` 直接访问输出新值——体会"反射 set 与普通访问同一份数据、静态字段全类共享"
|
|
- **静态属性反射修改验证**:观察 `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`)——套路与两者完全一致,提前预习
|
|
|
|
|
|
|
+- **与 Constructor 反射对比(必做)**:回顾上一课(08-12 反射 Constructor)——获取 API 都是"含 Declared 拿私有、不含 Declared 只拿 public",访问私有成员都要 `setAccessible(true)`;对比 Constructor(newInstance 创建对象)与 Field(get/set 读写字段)的差异
|
|
|
|
|
+- **反射三大成员串联(本课完结)**:反射三大核心成员已全部学完——**Constructor**(构造方法,08-12 创建对象)/ **Field**(字段属性,本课读写字段)/ **Method**(方法,本课调用方法)——三者套路完全一致:`getDeclaredXxx` 拿私有 + `setAccessible(true)` 放开权限 + 执行操作
|
|
|
- **getDeclaredFields 遍历练习**:写一个通用工具方法 `printFields(Object obj)`:传入任意对象,用 `getDeclaredFields()` 遍历打印所有字段的修饰符/类型/属性名——体会"反射可以做通用工具"(回顾上一课 printUtil 思路)
|
|
- **getDeclaredFields 遍历练习**:写一个通用工具方法 `printFields(Object obj)`:传入任意对象,用 `getDeclaredFields()` 遍历打印所有字段的修饰符/类型/属性名——体会"反射可以做通用工具"(回顾上一课 printUtil 思路)
|
|
|
- **受检异常处理回顾**:观察 Demo01 main 方法签名 `throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException`——getField/getDeclaredField 字段名写错抛 NoSuchFieldException、private 未放开权限操作抛 IllegalAccessException、Class.forName 类名写错抛 ClassNotFoundException——回顾 07-28 异常处理知识
|
|
- **受检异常处理回顾**:观察 Demo01 main 方法签名 `throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException`——getField/getDeclaredField 字段名写错抛 NoSuchFieldException、private 未放开权限操作抛 IllegalAccessException、Class.forName 类名写错抛 ClassNotFoundException——回顾 07-28 异常处理知识
|
|
|
|
|
+- **Method 概念复述(必做)**:用一句话说清"什么是 Method 反射"——Method 对象代表类中的一个方法,反射通过 `Method.invoke(对象, 参数...)` 调用方法,等价于 `对象.方法(参数)`,方法的名称和参数运行时才确定;能说出应用场景(动态代理、测试框架、注解解释器、框架调用 setter/getter)
|
|
|
|
|
+- **获取 Method 的 4 个 API 背诵(必做)**:`getMethods()`(所有 public 数组,含父类/接口继承如 toString)/ `getMethod(name, parameterTypes)`(指定 public)/ `getDeclaredMethods()`(本类全部含 private,不含父类/接口)/ `getDeclaredMethod(name, parameterTypes)`(指定含 private)——口诀与 Field/Constructor 完全一致:**含 Declared 的能拿私有,不含 Declared 的只能拿 public**
|
|
|
|
|
+- **重载方法区分(必做)**:理解 `getMethod("setName", String.class)` 中的 `String.class` 是**用来区分同名重载方法**的——不写参数类型或写错都会抛 NoSuchMethodException;试着重载一个 `setName(String, Integer)` 并分别用两种签名获取
|
|
|
|
|
+- **Method 常用方法背诵**:`invoke(obj, 参数...)` 调用方法 / `setAccessible(true)` 暴力反射 / `getReturnType()` 返回值类型 / `getParameterTypes()` 参数类型数组 / `getModifiers()` 修饰符
|
|
|
|
|
+- **invoke 四种调用规律背诵(必做)**:①实例方法 `invoke(对象, 参数...)` 第一参数传对象 ②**静态方法 `invoke(null, 参数...)` 传 null** ③**void 方法 invoke 返回 null** ④**private 方法必须先 setAccessible(true)**——与 Field 操作静态属性传 null 同一规律
|
|
|
|
|
+- **Demo02 运行验证(必做)**:运行 `course/Demo02`,观察输出——`getMethods()` 遍历打印所有 public 方法(含 Object 继承的 toString/hashCode/getClass 等)+ Stream 处理参数类型、`getDeclaredMethods()` 输出本类全部方法(含 read/write 私有方法)、`getMethod("getName").invoke(s)` 打印"张三"、`getMethod("setName", String.class).invoke(s, "李四")` 后打印"李四"、暴力反射 `read()` 输出"李四读......."、静态方法 `write()` 输出"静态方法...写..............."
|
|
|
|
|
+- **invoke 调用实例 vs 静态对比实验**:分别用 `getMethod("getName")`(实例)和 `getMethod("getSchool")`(静态)调用——体会实例方法 invoke 第一参数传对象 s、静态方法传 null;把静态方法的 invoke 第一参数误传成对象 s,观察它是否也能执行(static 方法本质不依赖对象,传对象也会被忽略)
|
|
|
|
|
+- **暴力反射私有方法实验(必做)**:把 Demo02 中 `readMethod.setAccessible(true)` 注释掉再运行,观察抛 **IllegalAccessException**——体会"private 方法不放开权限无法 invoke"
|
|
|
|
|
+- **getMethod 拿私有方法实验**:用 `studentClass.getMethod("read")`(不带 Declared)尝试获取 private 方法,观察抛 **NoSuchMethodException**——验证"getMethod 只能拿 public"的口诀
|
|
|
|
|
+- **void 方法返回值验证**:`setNameMethod.invoke(s, "李四")` 返回 null(void 方法),打印 `System.out.println(setNameMethod.invoke(s, "李四"))` 观察输出 null;对比 `getNameMethod.invoke(s)` 返回 "张三"——体会"无返回值方法 invoke 返回 null"
|
|
|
|
|
+- **getMethods vs getDeclaredMethods 对比**:思考两者的区别——getMethods 拿 public + 父类/接口继承;getDeclaredMethods 拿本类全部(含 private)不含父类。数一数 Student 的 getMethods() 输出里有哪些是 Object 继承来的(toString/hashCode/equals/getClass/notify/wait 等)
|
|
|
|
|
+- **InvocationTargetException 理解**:把 `read()` 方法体内故意写一个会抛异常的语句(如 `int x = 1/0`),反射 `invoke` 调用它,观察抛 **InvocationTargetException**(被调方法内部异常被包装)——回顾 07-28 异常处理知识
|
|
|
|
|
+- **练习答案参考**:课堂练习四(模拟 ORM mapToObject)的参考答案代码在 `exericse/OrmFieldMapTest.java`(含 `exericse/Student.java` 实体类)——运行观察静态字段传 null 操作 + Map 数据库行映射到 Student 对象的完整流程
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
|
-## 11. 拓展阅读
|
|
|
|
|
|
|
+## 15. 拓展阅读
|
|
|
|
|
|
|
|
- **反射 Field 在 ORM 框架中的落地**:MyBatis、Hibernate 等 ORM 框架的**结果映射(Result Mapping)**就是本课"Field.set() 把数据库数据映射到 Java 类"的经典实现——数据库表字段名对应 Java 实体属性名,查询结果通过反射 Field.set() 逐字段赋值
|
|
- **反射 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) 放开权限)
|
|
|
|
|
|
|
+- **反射三大核心成员(已全部学完)**:反射三大核心成员已全部掌握——**Constructor(构造方法,08-12 newInstance 创建对象)+ Field(字段属性,本课 get/set 读写字段)+ Method(方法,本课 invoke 调用方法)**——三者套路完全一致:`getDeclaredXxx` 拿私有 + `setAccessible(true)` 放开权限 + 执行操作;至此反射三件套(建对象/读写属性/调方法)可组合完成框架级操作
|
|
|
|
|
+- **Method 反射在动态代理中的落地**:**JDK 动态代理(Proxy + InvocationHandler)**底层就是 Method.invoke——代理对象调用任何方法都会被转发到 `InvocationHandler.invoke(proxy, method, args)`,其中 `method.invoke(target, args)` 反射调用真实对象的方法——这正是本课"Method 是动态代理的基础"论断的经典实现;后续学习 Spring AOP(基于动态代理)时会反复见到
|
|
|
|
|
+- **Method 反射在测试框架中的落地**:JUnit / TestNG 等**测试框架**通过反射 `getDeclaredMethods()` 遍历类的全部方法,找出带 `@Test` 注解的方法,再 `method.invoke(测试对象)` 执行——"注解 + 反射"是测试框架的底层原理,本课 Demo02 的遍历方法正是这个套路
|
|
|
|
|
+- **Method 反射在框架 setter/getter 调用中的落地**:Spring 的 BeanUtils / MyBatis 的结果映射都大量使用 `getMethod("set" + 属性名首字母大写, 类型.class)` + `invoke(obj, 值)` 反射调用 setter 赋值——这就是为什么实体类要写标准 getter/setter(本课 Student 新增的 7 组 getter/setter 正是为此准备)
|
|
|
|
|
+- **getParameterTypes 与重载的关联**:反射通过 `getParameterTypes()` 拿到方法的**参数类型数组**(`Class<?>[]`),这与 `getMethod(name, parameterTypes...)` 的传参一一对应——重载方法(同名不同参)在 Class 对象里其实是**多个独立的 Method 对象**,必须靠参数类型定位到具体那一个
|
|
|
|
|
+- **invoke 的参数匹配与自动装箱**:`invoke(obj, 参数...)` 传入的实际参数会与方法的参数类型匹配(基本类型自动装箱,如 setAge(Integer) 传 int 值 25 会被自动装箱成 Integer);参数类型不匹配或数量不对抛 `IllegalArgumentException`
|
|
|
|
|
+- **InvocationTargetException 的包装含义**:`invoke` 调用方法时,**如果被调方法内部抛异常,invoke 会把异常包装成 `InvocationTargetException` 抛出**(而不是直接抛原异常)——调用者需要 `getCause()` 拿到真正的异常;这是反射调用方法与普通调用的一个隐蔽差异
|
|
|
|
|
+- **Method.invoke 的 Class.newInstance 对比**:JDK9+ 推荐用 `Constructor.newInstance()`(可传参、可访问 private 构造),废弃了 `Class.newInstance()`(只能无参且权限检查严格)——同理反射调用方法统一用 `Method.invoke` 而非老旧的 `Class.getMethod` 直接裸调
|
|
|
|
|
+- **反射三件套组合的框架能力**:把本课的知识点组合起来——`getConstructor().newInstance()` 创建对象(08-12)+ `getDeclaredField().set()` 给字段赋值(Field)+ `getDeclaredMethod().invoke()` 调用方法(Method)——就是 MyBatis 结果映射、Spring Bean 初始化、测试框架执行测试用例的完整底层链路
|
|
|
- **getModifiers() 的 int 常量**:Java 反射中修饰符以 **int 位掩码(bitmask)** 表示——public=1、private=2、protected=4、static=8、final=16...`Modifier.toString()` 内部就是按位与运算拼接出字符串;`Modifier.isPublic(int)` / `Modifier.isStatic(int)` 等 isXxx 方法可单独判断某个修饰符是否存在
|
|
- **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 参数放开模块限制
|
|
- **Field.setAccessible 与 JPMS**:`setAccessible(true)` 本质是**绕过 Java 访问控制检查**;在模块化系统(JPMS)中,跨模块访问私有成员还受 `--add-opens` 参数约束——Spring 等框架在 JDK17+ 运行时需要配置 JVM 参数放开模块限制
|
|
|
- **反射的缓存优化**:反射 `getField`/`getDeclaredField` 本身有开销,框架(如 MyBatis)通常**在启动阶段一次性获取并缓存 Field 对象**,运行时只调用 `get()/set()`——这是"框架用反射但性能不差"的关键优化手段
|
|
- **反射的缓存优化**:反射 `getField`/`getDeclaredField` 本身有开销,框架(如 MyBatis)通常**在启动阶段一次性获取并缓存 Field 对象**,运行时只调用 `get()/set()`——这是"框架用反射但性能不差"的关键优化手段
|
|
@@ -494,5 +809,5 @@ public class Student{
|
|
|
- **IDEA 调试器的实现原理**:IDEA 等 IDE 的**调试器变量查看窗口**能在运行时显示对象的所有字段值(包括 private)——底层就是反射 `getDeclaredFields()` + `setAccessible(true)` + `field.get(对象)`,与本课 Demo01 的演示逻辑一致
|
|
- **IDEA 调试器的实现原理**:IDEA 等 IDE 的**调试器变量查看窗口**能在运行时显示对象的所有字段值(包括 private)——底层就是反射 `getDeclaredFields()` + `setAccessible(true)` + `field.get(对象)`,与本课 Demo01 的演示逻辑一致
|
|
|
- **JSON 序列化框架**:Jackson / Gson / Fastjson 把 Java 对象转成 JSON 字符串、或把 JSON 转回对象,底层也大量使用 Field 反射——读取对象所有字段名与值(序列化)、按 JSON 键名给对象字段赋值(反序列化)
|
|
- **JSON 序列化框架**:Jackson / Gson / Fastjson 把 Java 对象转成 JSON 字符串、或把 JSON 转回对象,底层也大量使用 Field 反射——读取对象所有字段名与值(序列化)、按 JSON 键名给对象字段赋值(反序列化)
|
|
|
- **反射破坏封装性的辩证关系**:本课 Field 反射可以访问 private 字段、修改 final 字段,既是框架能力的来源,也是"破坏封装性"的体现——日常业务代码中应遵守上一课的原则:**正常业务用普通方式访问属性,反射留给框架与中间件**
|
|
- **反射破坏封装性的辩证关系**:本课 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 等修饰符解析工具类)
|
|
|
|
|
|
|
+- **JDK 文档**:[`java.lang.Class`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Class.html)(getFields/getField/getDeclaredFields/getDeclaredField + getMethods/getMethod/getDeclaredMethods/getDeclaredMethod)、[`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.Method`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/reflect/Method.html)(invoke/setAccessible/getReturnType/getParameterTypes/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 等修饰符解析工具类)
|
|
|
|
|
|