说明:本次课程学习了 反射 Field(属性/字段/变量)(Demo01:Class 类获取 Field 对象的 4 个 API、Field 类的常用方法 get/set/setAccessible/getType/getName/getModifiers、Modifier.toString 解析修饰符、遍历字段、操作 public/private/static 属性)以及 反射 Method(方法)(Demo02:Class 类获取 Method 对象的 4 个 API、重载方法必须传参数类型区分、Method 类的常用方法 invoke/setAccessible/getReturnType/getParameterTypes/getModifiers、invoke 四种调用规律——实例方法传对象 / 静态方法传 null / void 返回 null / private 先 setAccessible(true) 暴力反射、遍历方法输出方法名/参数/返回值、动态代理 / 测试框架 / 注解解释器的基础)。
本作业共 2 道题(必做):基础题——暴力反射调用私有方法与静态方法(getDeclaredMethod + setAccessible(true) + invoke 实例传对象/静态传 null,封装不区分权限的 invokeAnyMethod 通用工具);进阶题(综合实战)——模拟测试框架 JUnit 简化版(getDeclaredMethods 扫描 test 开头方法 + 暴力反射调用 + InvocationTargetException.getCause() 处理 + 反射三件套)。覆盖今日"方法反射 invoke 四种调用规律 + 暴力反射 + 框架底层原理"核心知识点。
| 项目 | 要求 |
|---|---|
| 类名 | Demo_题号_你的姓名(如 Demo_01_张三、Demo_02_李四) |
| 文件头注释 | 每个 Java 文件头部需包含 @author、@title、@create 文档注释 |
| 实体类 | 使用课程代码中的 course.Student 实体类(授课代码/c260813/src/course/Student.java,含私有方法 read()/write());题目 2 需在该类中补充 3 个 private 测试方法 |
| 项目 | 要求 |
|---|---|
| 提交方式 | 将源码文件 + 运行结果截图提交至班级指定平台 |
| 截止时间 | 下次上课前 |
知识点:getDeclaredMethod("方法名") 获取私有方法、setAccessible(true) 暴力反射、invoke(对象) 调用私有实例方法、invoke(null) 调用静态方法(静态方法 invoke 传 null,与 Field 操作静态属性传 null 同一规律)、不区分权限的通用方法调用工具
场景描述:课堂 Demo02 演示③④ 调用了 Student 的私有方法 read()(实例方法)和 write()(静态方法)。本题写不区分权限的通用工具 invokeAnyMethod(Object obj, String methodName, Object... args)——内部流程 getDeclaredMethod → setAccessible(true) → invoke,用它调用 private read()(实例方法 invoke 传对象 s)和 private static write()(静态方法 invoke 传 null)——完整体会 invoke 四种调用规律中的"实例方法传对象、静态方法传 null、私有方法先暴力反射"。
题目要求:
/**
* @author 你的姓名
* @title 题目1:暴力反射调用私有方法与静态方法
* @create 2026/8/13
*/
实现步骤:
getMethod("setName", String.class) + invoke(s, "张三"),或复用下面封装好的通用工具)clazz.getDeclaredMethod("read") 获取私有实例方法 → setAccessible(true) → invoke(s) 调用(传对象 s)clazz.getDeclaredMethod("write") 获取私有静态方法 → setAccessible(true) → invoke(null) 调用(传 null)invokeAnyMethod(Object obj, String methodName, Object... args):getDeclaredMethod + setAccessible(true) + invoke(obj, args)setAccessible(true) 注释掉,运行观察异常代码框架:
package course;
import java.lang.reflect.Method;
public class Demo_01_你的姓名 {
public static void main(String[] args) throws Exception {
// 1. 创建 Student 对象,反射调用 setName("张三")
// 2. 暴力反射调用私有实例方法 read()(invoke 传对象 s)
// 3. 暴力反射调用私有静态方法 write()(invoke 传 null)
// 4. 用封装好的 invokeAnyMethod 工具调用 read(obj 传 s)/ write(obj 传 null)
}
// 通用工具:反射调用任意权限方法(getDeclaredMethod + setAccessible + invoke)
public static Object invokeAnyMethod(Object obj, String methodName, Object... args) throws Exception {
// 1. 计算参数类型数组
// 2. getDeclaredMethod(methodName, 参数类型...) 获取方法(含 private)
// 3. setAccessible(true) 暴力反射放开权限
// 4. invoke(obj, args) 调用并返回结果
return null;
}
}
输出示例:
张三读....... ← 暴力反射调用 private read()(实例方法,invoke 传对象 s,name 已是"张三")
静态方法...写............... ← 暴力反射调用 private static write()(静态方法,invoke 传 null)
张三读....... ← 通用工具调用 read()(obj 传 s)
静态方法...写............... ← 通用工具调用 write()(obj 传 null)
选做加分:
invokeAnyMethod 里的 setAccessible(true) 注释掉,运行观察抛 IllegalAccessException——体会"私有方法不放开权限无法 invoke"getMethod("read")(不带 Declared)尝试获取私有方法,运行观察抛 NoSuchMethodException——验证"getMethod 只能拿 public"的口诀invoke(s)),观察是否也能执行——体会静态方法不依赖对象,传什么都会被忽略method.getModifiers() + Modifier.toString() 打印 read/write 的修饰符,观察 private / private static思考题(将答案写在代码文件头注释或提交报告里):
setAccessible(true) 才能 invoke?(提示:Java 访问权限检查,不放开抛 IllegalAccessException——暴力反射)getDeclaredMethod 与 getMethod 的区别?调用私有方法为什么必须用前者?(提示:口诀"含 Declared 的能拿私有")知识点:反射三件套全家桶(Constructor 创建对象 + Field 给字段赋值 + Method 调用方法)、getDeclaredMethods() 遍历扫描方法、私有方法暴力反射、InvocationTargetException(invoke 包装被调方法内部异常,getCause() 取真实异常)、method.invoke 返回值、startsWith("test") 过滤测试方法
场景描述:这是"Method 是测试框架、注解解释器的基础"的完整落地——JUnit 测试框架的底层原理:扫描一个类的所有方法,找出测试方法,反射创建对象后逐个调用。本题给 Student 添加几个 private 测试方法(testAdd / testHello / testFail),写一个"迷你测试框架 MiniJUnitTest":
test 开头的方法getCause() 打印真实异常并统计"测试失败"——完整走一遍"反射三件套(创建对象/调方法)+ 暴力反射 + 异常处理"的框架级流程。
题目要求:
/**
* @author 你的姓名
* @title 题目2:模拟测试框架 JUnit 简化版
* @create 2026/8/13
*/
被测类准备:在 course.Student 中补充 3 个 private 测试方法(或复制一份 Student 到你的作业包里再添加):
// 在被测类 course/Student.java 中补充(模拟被测类的测试方法)
private void testAdd() {
System.out.println("测试:加法计算");
int a = 1 + 1;
if (a != 2) throw new RuntimeException("加法计算错误");
}
private void testHello() {
System.out.println("测试:打招呼 " + this.name);
}
private void testFail() {
throw new RuntimeException("故意失败的方法");
}
实现步骤:
testAdd()(正常通过)、testHello()(正常通过)、testFail()(故意抛异常)Student.class.getConstructor().newInstance() 反射创建被测对象clazz.getDeclaredMethods() 遍历 Student 全部方法method.getName().startsWith("test") 找出测试方法(过滤掉 read/write/study/getXxx 等)setAccessible(true) + invoke(obj) 执行InvocationTargetException → getCause() 打印真实异常信息代码框架:
package course;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Demo_02_你的姓名 {
public static void main(String[] args) throws Exception {
// 1. 反射创建被测 Student 对象(getConstructor().newInstance())
// 2. getDeclaredMethods() 遍历 Student 全部方法
// 3. 方法名以 "test" 开头 → 认定为测试方法
// 4. 对每个测试方法:setAccessible(true) + invoke(obj) 执行
// 5. 捕获 InvocationTargetException → getCause() 打印真实异常
// 6. 统计通过/失败个数
}
}
输出示例:
找到测试方法:testAdd
测试通过:testAdd
找到测试方法:testHello
测试通过:testHello
找到测试方法:testFail
测试失败:testFail,原因:故意失败的方法
测试通过 2 个,失败 1 个
选做加分:
getMethods() 扫描——观察 toString/hashCode 等 Object 继承方法也会被扫到,思考怎么过滤掉(提示:!method.getName().equals("toString") 或判断 getDeclaringClass())testAdd(int a, int b),用 invoke(obj, 3, 5) 传参调用——体会"参数如何通过 invoke 传入"boolean(true 通过 false 失败),invoke 返回 Object 强转 boolean 判断——体会"invoke 返回方法执行结果"@MyTest,扫描时用 method.isAnnotationPresent(MyTest.class) 只调用带注解的方法——这就是 JUnit 的 @Test 注解底层原理思考题(将答案写在代码文件头注释或提交报告里):
obj.testAdd() 调用?(提示:框架在编译期不知道被测类有哪些测试方法——运行期动态扫描,这就是"为什么需要反射")invoke 抛的是 InvocationTargetException 而不是原异常?(提示:invoke 把被调方法内部异常包装了一层,需要 getCause() 取真正的异常对象)startsWith("test") 过滤——只有 test 开头的方法才会被认定为测试方法)| 知识点 | 说明 |
|---|---|
getDeclaredMethod("方法名", 参数类型...) |
获取指定的单个方法(包括 private)——操作私有方法必须用它;getMethod 只能拿 public |
method.setAccessible(true) |
暴力反射:临时取消访问权限检查——调用 private 方法必须调用,否则抛 IllegalAccessException |
method.invoke(obj, 参数...) |
调用方法,返回方法执行结果(Object 可强转);等价于 对象.方法(参数) |
| invoke 四种调用规律 | ①实例方法 invoke(对象, 参数...) 第一参数传对象 ②静态方法 invoke(null, 参数...) 传 null ③void 方法 invoke 返回 null ④private 方法先 setAccessible(true) |
| 静态方法 invoke 传 null | 静态成员属于整个类、不属于任何对象——与 Field 操作静态属性传 null 同一规律 |
getDeclaredMethods() |
返回本类所有方法数组,包括 private,但不包括父类/接口的方法——扫描测试方法的入口 |
| InvocationTargetException | invoke 调用方法时被调方法内部抛异常,invoke 把它包装成 InvocationTargetException 抛出——需要 getCause() 获取真正的异常 |
| 反射三件套组合 | Constructor(newInstance 创建对象)+ Field(get/set 读写字段)+ Method(invoke 调用方法)——组合 = 框架级操作的完整底层链路 |
| 模拟测试框架(JUnit 原理) | getDeclaredMethods() 扫描方法 → startsWith("test") 过滤测试方法 → setAccessible(true) + invoke(obj) 执行 → 捕获 InvocationTargetException 统计通过/失败——JUnit 扫描 @Test 注解方法的底层套路 |
getModifiers() + Modifier.toString() |
获取方法修饰符 int 值并解析成可读字符串(private / private static...) |
| 反射 API 的受检异常 | NoSuchMethodException(方法名/参数类型不匹配)/ IllegalAccessException(private 未放开权限)/ InvocationTargetException(被调方法内部抛异常)——必须处理(main 用 throws) |