一、作业需求
计算一个方法执行了多少秒(或毫秒),要求使用匿名内部类实现。
这是一个典型的应用场景:在不修改原有方法代码的情况下,为方法添加执行时间统计功能。
二、什么是匿名内部类?
2.1 定义
匿名内部类(Anonymous Inner Class)是没有名字的内部类,通常用于一次性使用的场景。
2.2 基本语法
匿名内部类 语法示例
匿名内部类的标准语法格式如下,适用于直接实现接口或继承父类并重写方法:
父类/接口 变量名 = new 父类/接口() {
@Override
返回类型 方法名(参数列表) {
// 方法实现
}
};
AI写代码
java
运行
具体实现案例
实现接口示例(Runnable):
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println(“匿名内部类执行任务”);
}
};
AI写代码
java
运行
继承抽象类示例(Thread):
Thread thread = new Thread() {
@Override
public void run() {
System.out.println(“重写Thread的run方法”);
}
};
AI写代码
java
运行
使用场景说明
适用于需要一次性使用的类实现,避免单独创建子类文件
常用于事件监听、线程实现等需要快速重写方法的场景
匿名内部类会隐式持有外部类的引用,需注意内存泄漏问题
语法要点
必须实现父类或接口的所有抽象方法
可以访问外部类的final变量或等效final变量
编译后会生成独立的.class文件,命名格式为外部类$数字.class
Lambda简化场景
当接口仅含单个抽象方法时(函数式接口),可用Lambda表达式替代:
Runnable task = () -> System.out.println(“Lambda替代匿名类”);
AI写代码
java
运行
2.3 特点
特点 说明
没有类名 创建时直接定义类体
一次性使用 通常只使用一次
简化代码 避免单独定义一个类
必须实现所有抽象方法 与普通类相同
三、设计思路
3.1 核心问题
如何在不修改原有方法代码的情况下,统计方法的执行时间?
3.2 解决方案
使用模板方法设计模式 + 匿名内部类:
定义一个接口或抽象类,包含需要执行的方法
在工具类中编写计时方法,接收接口/抽象类的实现
调用时使用匿名内部类传入具体逻辑
3.3 流程图
以下是基于文本描述的计时流程实现方案,采用Java代码示例和流程图说明:
代码实现方案
// 定义函数式接口,便于匿名内部类实现
@FunctionalInterface
interface TargetMethod {
void execute();
}
public class TimeRecorder {
public static void recordExecutionTime(TargetMethod method) {
long startTime = System.nanoTime();
method.execute();
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println(“Execution time: “ + duration + “ ns”);
}
public static void main(String[] args) {
// 通过匿名内部类实现目标方法
recordExecutionTime(new TargetMethod() {
@Override
public void execute() {
// 替换为实际需要计时的代码
int sum = 0;
for (int i = 0; i < 1000000; i++) {
sum += i;
}
}
});
}
}
AI写代码
java
运行
流程图表示
text
开始计时
↓
记录开始时间 (startTime)
↓
执行目标方法 ←──── 匿名内部类提供具体实现
↓
记录结束时间 (endTime)
↓
计算耗时 = endTime - startTime
↓
输出结果
关键说明
时间精度:使用System.nanoTime()获取纳秒级时间戳,比currentTimeMillis()精度更高
函数式接口:通过接口设计使计时逻辑与业务逻辑解耦
Lambda优化:Java 8+可使用Lambda简化匿名内部类写法:
recordExecutionTime(() -> {
int sum = IntStream.range(0, 1000000).sum();
});
AI写代码
java
运行
注意事项
避免在测试方法中包含IO操作或随机因素
考虑JVM预热问题,正式测试前应先执行几次预热运行
对于微基准测试建议使用JMH等专业框架
四、代码实现
4.1 方式一:使用接口
以下是整理后的代码块格式:
/**
- 定义一个接口,包含需要计时的方法
*/
interface Task {
void execute(); // 需要执行的任务
}
/**
- 计时器工具类
/
class TimerUtil {
/*- 计算任务执行时间(毫秒)
- @param task 要执行的任务
*/
public static void measureTime(Task task) {
long startTime = System.currentTimeMillis();
task.execute();
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
System.out.println(“方法执行耗时:” + duration + “ 毫秒”);
System.out.println(“转换为秒:” + duration / 1000.0 + “ 秒”);
}
}
/**
- 测试类
*/
public class TimeTest {
public static void main(String[] args) {
TimerUtil.measureTime(new Task() {
@Override
public void execute() {
System.out.println(“开始执行任务…”);
for (int i = 0; i < 100000000; i++) {
Math.sqrt(i);
}
System.out.println(“任务执行完毕”);
}
});
}
}
AI写代码
java
运行
运行结果:
开始执行任务…
任务执行完毕
方法执行耗时:12 毫秒
转换为秒:0.012 秒
AI写代码
text
4.2 方式二:使用抽象类
/**
- 抽象类:需要计时的任务
*/
abstract class AbstractTask {
public abstract void run();
}
/**
- 计时器工具类(支持抽象类)
*/
class TimerUtil2 {
public static void measureTime(AbstractTask task) {
long startTime = System.nanoTime(); // 纳秒,更精确
task.run();
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println(“执行耗时:” + duration + “ 纳秒”);
System.out.println(“转换为毫秒:” + duration / 1000000.0 + “ 毫秒”);
}
}
/**
- 测试类
*/
public class TimeTest2 {
public static void main(String[] args) {
// 使用匿名内部类继承AbstractTask
TimerUtil2.measureTime(new AbstractTask() {
@Override
public void run() {
// 需要计时的业务逻辑
System.out.println(“正在计算100以内素数…”);
for (int i = 2; i <= 100; i++) {
boolean isPrime = true;
for (int j = 2; j <= Math.sqrt(i); j++) {
if (i % j == 0) {
isPrime = false;
break;
}
}
if (isPrime) {
System.out.print(i + “ “);
}
}
System.out.println();
}
});
}
}
AI写代码
java
运行
4.3 方式三:带返回值的方法计时
如果需要统计的方法有返回值,可以使用泛型:
/**
- 带返回值的任务接口
- @param
返回值类型
*/
interface CallableTask{
T execute();
}
/**
- 计时器工具类(支持返回值)
*/
class TimerUtil3 {
public staticT measureTime(CallableTask task, String taskName) {
System.out.println(“========== “ + taskName + “ ==========”);
long startTime = System.currentTimeMillis();
T result = task.execute();
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
System.out.println(“执行耗时:” + duration + “ 毫秒(” + duration / 1000.0 + “ 秒)”);
System.out.println(“================================”);
return result;
}
}
/**
- 测试类
*/
public class TimeTest3 {
public static void main(String[] args) {
// 计算1到100的和,并返回结果
Integer sum = TimerUtil3.measureTime(new CallableTask() {
@Override
public Integer execute() {
int total = 0;
for (int i = 1; i <= 100; i++) {
total += i;
}
System.out.println(“计算结果:” + total);
return total;
}
}, “计算1-100的和”);
System.out.println(“返回结果:” + sum);
// 计算斐波那契数列
TimerUtil3.measureTime(new CallableTask() {
@Override
public Void execute() {
System.out.println(“斐波那契数列前20项:”);
long a = 0, b = 1;
for (int i = 0; i < 20; i++) {
System.out.print(a + “ “);
long temp = a + b;
a = b;
b = temp;
}
System.out.println();
return null;
}
}, “斐波那契数列”);
}
}
AI写代码
java
运行
五、多种时间单位说明
方法 返回单位 说明
System.currentTimeMillis() 毫秒 从1970-01-01到现在的毫秒数
System.nanoTime() 纳秒 高精度计时,适合短时间测量
以下是将时间单位转换的Java代码示例:
毫秒转秒
long ms = 1234;
double seconds = ms / 1000.0; // 结果为1.234秒
AI写代码
java
运行
纳秒转毫秒
long ns = 12345678;
double ms = ns / 1000000.0; // 结果为12.345678毫秒
AI写代码
java
运行
秒转毫秒
double seconds = 1.5;
long ms = (long)(seconds * 1000); // 结果为1500毫秒
AI写代码
java
运行
毫秒转纳秒
long ms = 15;
long ns = ms * 1000000L; // 结果为15000000纳秒
AI写代码
java
运行
注意事项:
使用浮点数除法(1000.0)确保精度不丢失
大数值计算时使用L后缀防止整数溢出
从浮点转整型时需要进行显式类型转换
六、不使用匿名内部类的对比
6.1 传统方式(单独定义一个类)
// 需要单独定义一个类
class MyTask implements Task {
@Override
public void execute() {
// 业务逻辑
}
}
// 使用时
TimerUtil.measureTime(new MyTask());
AI写代码
java
运行
6.2 使用匿名内部类的方式
TimerUtil.measureTime(new Task() {
@Override
public void execute() {
// 业务逻辑
}
});
AI写代码
java
运行
代码说明
使用匿名内部类实现Task接口
measureTime方法接收Task实例并测量其execute方法的执行时间
业务逻辑需在execute方法中实现
格式要点
代码块使用三个反引号加语言类型标记
保持原代码缩进结构
注释保留在原有位置
6.3 Lambda表达式方式(Java 8+)
// 更简洁的Lambda写法(接口只有一个抽象方法时)
TimerUtil.measureTime(() -> {
// 业务逻辑
});
AI写代码
java
运行
代码说明
使用Lambda表达式简化了匿名内部类的写法
适用于函数式接口(只有一个抽象方法的接口)
大括号内可编写需要计时的业务逻辑代码
代码块格式便于在Markdown文档中突出显示
注意事项
确保TimerUtil类已正确导入
measureTime方法需接收Runnable或Callable等函数式接口参数
Lambda表达式中的业务逻辑不应过于复杂
方式 代码量 可读性 适用场景
单独定义类 多 一般 需要多处复用
匿名内部类 中 较好 一次性使用
Lambda 少 好 接口只有一个抽象方法
七、完整示例:多个方法计时
以下是转换后的代码块格式:
/**
综合示例:统计多个排序算法的执行时间
*/
public class SortTimeTest {
public static void main(String[] args) {
// 准备测试数据
int[] arr1 = generateRandomArray(10000);
int[] arr2 = arr1.clone();
// 测试冒泡排序时间
TimerUtil.measureTime(new Task() {
@Override
public void execute() {
bubbleSort(arr1.clone());
System.out.println(“冒泡排序完成”);
}
});
// 测试快速排序时间
TimerUtil.measureTime(new Task() {
@Override
public void execute() {
quickSort(arr2.clone(), 0, arr2.length - 1);
System.out.println(“快速排序完成”);
}
});
}/**
- 生成随机数组
*/
private static int[] generateRandomArray(int size) {
int[] arr = new int[size];
for (int i = 0; i < size; i++) {
arr[i] = (int) (Math.random() * size);
}
return arr;
}
/**
- 冒泡排序
*/
private static void bubbleSort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
/**
- 快速排序
*/
private static void quickSort(int[] arr, int left, int right) {
if (left >= right) return;
int pivot = partition(arr, left, right);
quickSort(arr, left, pivot - 1);
quickSort(arr, pivot + 1, right);
}
private static int partition(int[] arr, int left, int right) {
int pivot = arr[left];
int i = left, j = right;
while (i < j) {
while (i < j && arr[j] >= pivot) j–;
arr[i] = arr[j];
while (i < j && arr[i] <= pivot) i++;
arr[j] = arr[i];
}
arr[i] = pivot;
return i;
}- 生成随机数组
}
AI写代码
java
运行
八、总结
8.1 核心知识点
知识点 应用
匿名内部类 简化代码,一次性传入任务逻辑
接口/抽象类 定义任务规范
System.currentTimeMillis() 获取系统时间,计算耗时
模板方法模式 固定计时流程,任务逻辑可变
8.2 匿名内部类的使用场景
回调函数:如本作业的方法计时
事件监听:GUI编程中的按钮点击事件
线程创建:new Thread(new Runnable() {…})
集合排序:Collections.sort(list, new Comparator() {…})
8.3 作业完成情况
实现了方法执行时间计算功能
使用了匿名内部类
支持毫秒/纳秒两种精度
支持带返回值的场景