一、作业需求

计算一个方法执行了多少秒(或毫秒),要求使用匿名内部类实现。

这是一个典型的应用场景:在不修改原有方法代码的情况下,为方法添加执行时间统计功能。

二、什么是匿名内部类?

2.1 定义

匿名内部类(Anonymous Inner Class)是没有名字的内部类,通常用于**一次性使用**的场景。

2.2 基本语法

匿名内部类语法示例

匿名内部类的标准语法格式如下,适用于直接实现接口或继承父类并重写方法:

父类/接口 变量名 = new 父类/接口() {
    @Override
    返回类型 方法名(参数列表) {
        // 方法实现
    }
};

具体实现案例

实现接口示例(Runnable):

Runnable task = new Runnable() {
    @Override
    public void run() {
        System.out.println("匿名内部类执行任务");
    }
};

继承抽象类示例(Thread):

Thread thread = new Thread() {
    @Override
    public void run() {
        System.out.println("重写Thread的run方法");
    }
};

使用场景说明

  • 适用于需要一次性使用的类实现,避免单独创建子类文件
  • 常用于事件监听、线程实现等需要快速重写方法的场景
  • 匿名内部类会隐式持有外部类的引用,需注意内存泄漏问题

语法要点

  • 必须实现父类或接口的所有抽象方法
  • 可以访问外部类的final变量或等效final变量
  • 编译后会生成独立的.class文件,命名格式为外部类$数字.class

Lambda简化场景

当接口仅含单个抽象方法时(函数式接口),可用Lambda表达式替代:

Runnable task = () -> System.out.println("Lambda替代匿名类");

2.3 特点

特点 说明
没有类名 创建时直接定义类体
一次性使用 通常只使用一次
简化代码 避免单独定义一个类
必须实现所有抽象方法 与普通类相同

三、设计思路

3.1 核心问题

如何在不修改原有方法代码的情况下,统计方法的执行时间?

3.2 解决方案

使用模板方法设计模式 + 匿名内部类

  1. 定义一个接口或抽象类,包含需要执行的方法

  2. 在工具类中编写计时方法,接收接口/抽象类的实现

  3. 调用时使用匿名内部类传入具体逻辑

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;
                }
            }
        });
    }
}

流程图表示

text

开始计时
    ↓
记录开始时间 (startTime)
    ↓
执行目标方法 ←──── 匿名内部类提供具体实现
    ↓
记录结束时间 (endTime)
    ↓
计算耗时 = endTime - startTime
    ↓
输出结果

关键说明

  • 时间精度:使用System.nanoTime()获取纳秒级时间戳,比currentTimeMillis()精度更高
  • 函数式接口:通过接口设计使计时逻辑与业务逻辑解耦
  • Lambda优化:Java 8+可使用Lambda简化匿名内部类写法:
    recordExecutionTime(() -> {
        int sum = IntStream.range(0, 1000000).sum();
    });
    

注意事项

  • 避免在测试方法中包含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("任务执行完毕");
            }
        });
    }
}

运行结果:

开始执行任务...
任务执行完毕
方法执行耗时:12 毫秒
转换为秒:0.012 秒

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();
            }
        });
    }
}
 

4.3 方式三:带返回值的方法计时

如果需要统计的方法有返回值,可以使用泛型:

/**
 * 带返回值的任务接口
 * @param <T> 返回值类型
 */
interface CallableTask<T> {
    T execute();
}

/**
 * 计时器工具类(支持返回值)
 */
class TimerUtil3 {
    public static <T> T measureTime(CallableTask<T> 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<Integer>() {
            @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<Void>() {
            @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;
            }
        }, "斐波那契数列");
    }
}
 

五、多种时间单位说明

方法 返回单位 说明
System.currentTimeMillis() 毫秒 从1970-01-01到现在的毫秒数
System.nanoTime() 纳秒 高精度计时,适合短时间测量

以下是将时间单位转换的Java代码示例:

毫秒转秒

long ms = 1234;
double seconds = ms / 1000.0;  // 结果为1.234秒

纳秒转毫秒

long ns = 12345678;
double ms = ns / 1000000.0;    // 结果为12.345678毫秒

秒转毫秒

double seconds = 1.5;
long ms = (long)(seconds * 1000);  // 结果为1500毫秒

毫秒转纳秒

long ms = 15;
long ns = ms * 1000000L;  // 结果为15000000纳秒

注意事项:

  • 使用浮点数除法(1000.0)确保精度不丢失
  • 大数值计算时使用L后缀防止整数溢出
  • 从浮点转整型时需要进行显式类型转换

六、不使用匿名内部类的对比

6.1 传统方式(单独定义一个类)

// 需要单独定义一个类
class MyTask implements Task {
    @Override
    public void execute() {
        // 业务逻辑
    }
}

// 使用时
TimerUtil.measureTime(new MyTask());

6.2 使用匿名内部类的方式

TimerUtil.measureTime(new Task() {
    @Override
    public void execute() {
        // 业务逻辑
    }
});

代码说明

  • 使用匿名内部类实现Task接口
  • measureTime方法接收Task实例并测量其execute方法的执行时间
  • 业务逻辑需在execute方法中实现

格式要点

  • 代码块使用三个反引号加语言类型标记
  • 保持原代码缩进结构
  • 注释保留在原有位置

6.3 Lambda表达式方式(Java 8+)

// 更简洁的Lambda写法(接口只有一个抽象方法时)
TimerUtil.measureTime(() -> {
    // 业务逻辑
});

代码说明

  • 使用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;
    }
}

八、总结

8.1 核心知识点

知识点 应用
匿名内部类 简化代码,一次性传入任务逻辑
接口/抽象类 定义任务规范
System.currentTimeMillis() 获取系统时间,计算耗时
模板方法模式 固定计时流程,任务逻辑可变

8.2 匿名内部类的使用场景

  1. 回调函数:如本作业的方法计时

  2. 事件监听:GUI编程中的按钮点击事件

  3. 线程创建new Thread(new Runnable() {...})

  4. 集合排序Collections.sort(list, new Comparator() {...})

8.3 作业完成情况

  • 实现了方法执行时间计算功能

  • 使用了匿名内部类

  • 支持毫秒/纳秒两种精度

  • 支持带返回值的场景

更多推荐