首页 > 编程开发 > Java    日期:2026-06-12 / 浏览

一、从一个 "笨办法" 说起

前几天在写一个数据处理模块时,突然想测一下某个方法的耗时。第一反应是这样的:

public void doSomething() {
    long start = System.currentTimeMillis();
    // ... 业务逻辑
    long end = System.currentTimeMillis();
    System.out.println("耗时: " + (end - start) + "ms");
}

写了几遍之后就开始烦躁了 ——每个方法都要复制粘贴这三行代码,而且一旦不需要测速了,还得一个个删掉,代码变得又脏又乱。

有没有一种更优雅的方式,既能计算方法耗时,又不污染业务代码?

二、思路:把 "测速" 抽象成一个模板

仔细一想,这其实是一个经典的模板方法模式场景:

  • 记录开始时间
  • 执行某个方法(这是变化的)
  • 记录结束时间并输出

变化的只有第 2 步,那能不能把这部分 "抽出来" 呢?

三、匿名内部类登场

定义一个抽象类,把不变的骨架写好,把变化的部分留给子类:

public abstract class TimeCalculator {
    
    public final void calculate() {
        long start = System.currentTimeMillis();
        
        // 变化的部分,交给子类实现
        doWork();
        
        long end = System.currentTimeMillis();
        System.out.println("方法执行耗时: " + (end - start) + "ms");
    }
    
    // 抽象方法,由子类实现具体业务
    public abstract void doWork();
}

然后在使用时,匿名内部类就派上用场了:

public class Demo {
    public static void main(String[] args) {
        // 匿名内部类,现场实现 doWork()
        new TimeCalculator() {
            @Override
            public void doWork() {
                // 这里写你的业务代码
                try {
                    Thread.sleep(1234); // 模拟耗时操作
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.calculate();
    }
}

输出:

四、还能更优雅吗?Lambda 表达式!

如果你用的是 Java 8+,匿名内部类还能进一步简化。给 TimeCalculator 加个函数式接口的 "马甲":

@FunctionalInterface
public interface TimeCalculator {
    
    static void calculate(TimeTask task) {
        long start = System.currentTimeMillis();
        task.execute();
        long end = System.currentTimeMillis();
        System.out.println("方法执行耗时: " + (end - start) + "ms");
    }
}

@FunctionalInterface
interface TimeTask {
    void execute();
}

使用时代码清爽到飞起:

public class Demo {
    public static void main(String[] args) {
        TimeCalculator.calculate(() -> {
            // 你的业务代码
            try {
                Thread.sleep(1500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
    }
}

五、匿名内部类的使用场景总结

通过这次实践,我梳理了匿名内部类特别适合的场景:

场景 说明
一次性实现 只需要用一次的类,没必要单独写一个文件
回调机制 如线程的 Runnable、按钮的点击事件监听
模板方法 像本文这样,把不变的部分封装,变化的部分现场实现
函数式接口 Java 8 后推荐用 Lambda 替代,但原理相通

六、知识扩展

在 Java 中,使用匿名内部类可以非常优雅地封装一段需要计时的代码,而无需显式定义具名类。核心思路是:定义一个函数式接口(或直接使用已有的 Runnable / Callable),在需要计时的地方以匿名内部类的形式传入代码块,并在方法内部记录开始和结束时间。

以下是一个简洁且符合 Java 风格的实现,包含两种常用方式:

自定义计时工具类(基于匿名内部类)

public class TimeUtil {

    /**
     * 执行一段代码并打印执行时间(毫秒)
     * @param task 需要计时的代码块,使用匿名内部类实现 Runnable
     */
    public static void measure(Runnable task) {
        long start = System.nanoTime();
        task.run();
        long end = System.nanoTime();
        System.out.printf("执行耗时: %.3f ms%n", (end - start) / 1_000_000.0);
    }

    // 使用示例
    public static void main(String[] args) {
        // 优雅地传入匿名内部类
        TimeUtil.measure(new Runnable() {
            @Override
            public void run() {
                // 这里是需要计时的业务逻辑
                for (int i = 0; i < 1_000_000; i++) {
                    Math.sqrt(i);
                }
            }
        });
    }
}

说明

  • Runnable 是 JDK 自带的功能接口,匿名内部类可直接实现其 run() 方法。
  • 方法内部使用 System.nanoTime() 获得更高精度,并转换为毫秒输出。

更灵活的版本:支持带返回值的方法(匿名内部类 + Callable)

如果被计时的代码需要返回结果,可以使用 Callable 接口。

import java.util.concurrent.Callable;

public class TimeUtil {

    public static <T> T measure(Callable<T> task) throws Exception {
        long start = System.nanoTime();
        try {
            return task.call();
        } finally {
            long end = System.nanoTime();
            System.out.printf("执行耗时: %.3f ms%n", (end - start) / 1_000_000.0);
        }
    }

    public static void main(String[] args) throws Exception {
        // 匿名内部类实现 Callable,可返回值
        String result = TimeUtil.measure(new Callable<String>() {
            @Override
            public String call() throws Exception {
                // 模拟耗时操作
                Thread.sleep(100);
                return "完成";
            }
        });
        System.out.println("返回结果: " + result);
    }
}

优势:可以捕获方法的返回值,且 finally 保证即使任务抛出异常也会打印耗时。

使用 AOP 风格的拦截器(进阶)

如果要计算任意对象某个方法的执行时间,可以用匿名内部类包装原有调用,但更优雅的方式是利用动态代理。不过动态代理仍可结合匿名内部类增强:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class TimingProxy {

    @SuppressWarnings("unchecked")
    public static <T> T proxy(T target, Class<T> interfaceType) {
        return (T) Proxy.newProxyInstance(
                interfaceType.getClassLoader(),
                new Class<?>[]{interfaceType},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        long start = System.nanoTime();
                        try {
                            return method.invoke(target, args);
                        } finally {
                            long end = System.nanoTime();
                            System.out.printf("方法 %s 耗时: %.3f ms%n",
                                    method.getName(),
                                    (end - start) / 1_000_000.0);
                        }
                    }
                }
        );
    }
}

使用时:

interface Calculator {
    int add(int a, int b);
}
// 创建原始对象
Calculator calc = (a, b) -> a + b;
// 生成带计时功能的代理
Calculator timedCalc = TimingProxy.proxy(calc, Calculator.class);
timedCalc.add(3, 5);

虽然动态代理并非直接使用匿名内部类执行计时,但 InvocationHandler 本身就是一个匿名内部类,这也算是一种优雅实践。

七、写在最后

匿名内部类看似只是 "省了一个类文件",但它的真正价值在于让代码更贴近思考方式—— 当你只想专注于 "我要做什么",而不想被 "怎么组织类" 分心时,它就是最好的选择。

当然,如果逻辑复杂、复用性高,还是乖乖写成独立类吧。技术没有银弹,只有合适的场景。

觉得上面的内容有用吗?快来点个赞吧!

点赞() 我要打赏

温馨提示 : 本站内容来自会员投稿以及互联网,所有源码及教程均为作者总结编辑,请大家在使用过程中提前做好备份,以免发生无法预知的错误,源码类教程请勿直接用于生产环境!

 可能感兴趣的文章