第四次博客作业:从计算耗时到匿名内部类,一次关于代码优化的思考
- 精准计时
假设有一个复杂的算法 doSomething() ,想知道它跑完到底需要多久。最直观的想法是:
long start = System.currentTimeMillis();
doSomething(); // 执行业务逻辑
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start) + "ms");
这段代码没问题,但如果有 100 个方法需要测试时,若把这三行代码复制粘贴 100 次,显然违反了 DRY(Don't Repeat Yourself) 原则。
- 进阶思路:封装通用逻辑
为了解决重复代码的问题,想到封装一个工具方法。可以定义一个接口 Task ,里面有一个 execute() 方法。
interface Task {
void execute();
}
public class Timer {
public static void timeIt(Task task) {
long start = System.currentTimeMillis();
task.execute(); // 这里执行具体的业务逻辑
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start) + "ms");
}
}
这样一来,我们只需要调用 Timer.timeIt(...) 就可以复用计时的逻辑了。
- 痛点出现:为了用一次而写一个类?
现在问题来了:每次调用 timeIt 方法时,我们需要传入一个实现了 Task 接口的对象。
如果按照传统的面向对象写法,我们需要这样做:
新建一个文件 MyHeavyTask.java 。
让它 implements Task 。
在 execute 方法里写上那堆复杂的业务代码。
实例化 new MyHeavyTask() 传给 timeIt 。
这样显然太麻烦了! 如果这个任务只是在这里用一次,专门去创建一个完整的类文件,不仅让项目文件变得臃肿,而且阅读代码时还要跳来跳去,非常不直观。
- 救星登场:匿名内部类
这时候,匿名内部类就闪亮登场了。正如 PPT 上提到的 “匿名内部类的使用场景”,它最适合解决这种 “只需要使用一次的类” 的情况。
我们可以直接在方法调用的参数里,“现场”定义并实例化这个类:
Timer.timeIt(new Task() {
@Override
public void execute() {
// 这里直接写你的复杂业务逻辑
for(int i=0; i<100000; i++) {
Math.random();
}
}
});
Timer.timeIt(new Task() {
@Override
public void execute() {
// 这里直接写你的复杂业务逻辑
for(int i=0; i<100000; i++) {
Math.random();
}
}
});
此时没有类名,没有额外的文件,代码紧凑且逻辑连贯。这就是匿名内部类的魅力。
- 总结:什么时候该用它?
结合homework1,可以总结出匿名内部类的核心使用场景:
简化代码:当某个接口的实现类只需要使用一次时。
回调机制:像 Android 开发中的按钮点击事件 setOnClickListener ,或者这里的计时器回调,都是典型的例子。
逻辑内聚:让业务逻辑和调用它的地方紧挨在一起,提高代码的可读性。
浙公网安备 33010602011771号