System.currentTimeMillis()
记录起止时间,计算差值并转换为时/分/秒格式核心实现原理
秒表的本质是记录两个时间点的差值,Java 中主要通过以下两种方式获取时间戳:
| 方法 | 特点 | 适用场景 |
|———————–|——————————————————————–|————————|
| System.currentTimeMillis()
| 返回自 1970-01-01 00:00:00 GMT 以来的毫秒数(long 型) | 通用场景,兼容性最佳 |
| Instant.now().toEpochMilli()
| Java 8+ 新 API,同样返回毫秒级时间戳 | 需高精度或现代语法时 |
| System.nanoTime()
| 提供纳秒级精度,不受系统时钟调整影响 | 性能测试等专业场景 |
⚠️ 注意:System.currentTimeMillis()
受系统时钟同步影响,而 nanoTime()
仅用于测量时间间隔,不能表示绝对时间。
基础控制台版秒表实现
✅ 核心逻辑三步走:
- 启动计时:记录起始时间戳
startTime = System.currentTimeMillis()
- 持续监测:循环检测当前时间与起始时间的差值
- 格式化输出:将毫秒差值转换为
时:分:秒:毫秒
格式
📜 完整代码示例:
import java.util.Scanner; public class StopwatchConsole { private long startTime; private boolean running; public void start() { startTime = System.currentTimeMillis(); running = true; System.out.println("计时已开始..."); } public void stop() { if (!running) return; long elapsed = System.currentTimeMillis() startTime; displayTime(elapsed); running = false; } private void displayTime(long milliseconds) { // 计算各时间单位 long hours = milliseconds / (60 60 1000); milliseconds %= (60 60 1000); long minutes = milliseconds / (60 1000); milliseconds %= (60 1000); long seconds = milliseconds / 1000; milliseconds %= 1000; // 格式化输出(补零操作) String timeStr = String.format("%02d:%02d:%02d.%03d", hours, minutes, seconds, milliseconds); System.out.println("总耗时: " + timeStr); } public static void main(String[] args) { StopwatchConsole sw = new StopwatchConsole(); Scanner scanner = new Scanner(System.in); System.out.println("输入 'start' 开始计时,'stop' 停止计时"); while (true) { String command = scanner.nextLine().trim().toLowerCase(); if (command.equals("start")) { sw.start(); } else if (command.equals("stop")) { sw.stop(); break; } } scanner.close(); } }
🔍 代码解析:
- 时间分解算法:通过整除和取余运算逐级提取小时、分钟、秒和毫秒
- 格式化技巧:
%02d
表示两位数字,不足补零(如5
→05
) - 交互设计:使用
Scanner
实现命令行控制,支持多次启停
进阶优化方向
🚀 1. 图形界面增强(Swing 实现)
import javax.swing.; import java.awt.; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class StopwatchGUI extends JFrame { private JLabel timeLabel; private long startTime; private boolean running; private Timer timer; // Swing 定时器 public StopwatchGUI() { setTitle("秒表"); setSize(400, 200); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); timeLabel = new JLabel("00:00:00.000", SwingConstants.CENTER); timeLabel.setFont(new Font("Arial", Font.BOLD, 36)); add(timeLabel, BorderLayout.CENTER); JPanel buttonPanel = new JPanel(); JButton startBtn = new JButton("开始"); JButton stopBtn = new JButton("停止"); JButton resetBtn = new JButton("重置"); // 按钮事件监听 startBtn.addActionListener(e -> { if (!running) { startTime = System.currentTimeMillis(); running = true; timer.start(); // 每 10ms 刷新一次 } }); stopBtn.addActionListener(e -> { if (running) { timer.stop(); running = false; } }); resetBtn.addActionListener(e -> { timer.stop(); running = false; timeLabel.setText("00:00:00.000"); }); buttonPanel.add(startBtn); buttonPanel.add(stopBtn); buttonPanel.add(resetBtn); add(buttonPanel, BorderLayout.SOUTH); // 创建定时器(非后台线程,注意性能) timer = new Timer(10, evt -> updateDisplay()); } private void updateDisplay() { if (!running) return; long elapsed = System.currentTimeMillis() startTime; timeLabel.setText(formatTime(elapsed)); } private String formatTime(long milliseconds) { return String.format("%02d:%02d:%02d.%03d", milliseconds / (60601000), // 小时 (milliseconds % (60601000)) / (601000), // 分钟 (milliseconds % (601000)) / 1000, // 秒 milliseconds % 1000 // 毫秒 ); } public static void main(String[] args) { SwingUtilities.invokeLater(() -> { new StopwatchGUI().setVisible(true); }); } }
💡 关键改进点:
- 实时更新机制:使用
javax.swing.Timer
替代手动循环,避免阻塞 EDT(事件分发线程) - 视觉反馈:大字体显示+红绿颜色区分运行/停止状态
- 防抖处理:禁用按钮防止重复点击导致的异常
⚙️ 2. 高精度模式(纳秒级)
public class HighPrecisionStopwatch { private long startNanos; private boolean running; public void start() { startNanos = System.nanoTime(); running = true; } public double stop() { if (!running) throw new IllegalStateException("未启动计时"); long endNanos = System.nanoTime(); running = false; return (endNanos startNanos) / 1e9; // 转换为秒 } }
👉 适用场景:科学实验、算法性能测试(需注意 CPU 频率波动的影响)
常见陷阱与解决方案
问题现象 | 根本原因 | 解决方案 |
---|---|---|
长时间运行后时间变慢 | 垃圾回收/线程调度延迟 | 改用 System.nanoTime() |
跨午夜计时出现负数 | 未处理日期进位 | 始终计算时间差而非绝对时间 |
多线程环境下数据不一致 | 非原子操作导致竞态条件 | 使用 AtomicLong 存储时间戳 |
手机端耗电过快 | 高频次唤醒 CPU | 降低刷新频率(如改为 50ms) |
相关问答 FAQs
Q1: 为什么我的秒表在 Windows 上比 Linux 慢?
A: 这是由于不同操作系统对线程优先级的处理策略不同,Java 的 Thread.sleep()
和 System.currentTimeMillis()
都依赖底层 OS 调度,解决方案:①使用 System.nanoTime()
提高精度;②在关键计算段关闭自动装箱;③采用锁消除技术减少上下文切换。
Q2: 如何实现带有分段计时功能的秒表?
A: 可通过以下方式扩展:
- 维护一个
List<Long>
存储每次点击时的中间时间戳 - 添加 “计次” 按钮,将当前时间差存入列表
- 显示最近三次/最佳的分段成绩
- 示例代码片段:
List<Long> lapTimes = new ArrayList<>();
public void recordLap() {
if (!running) return;
long current = System.currentTimeMillis();
long lapTime = current (lapTimes.isEmpty() ? startTime : lapTimes.get(lapTimes.size()-1));
lapTimes.add(lapTime);
System.out.printf(“第%d圈: %s%n”, lapTimes.size(), formatTime(lapTime));
}
---
六、
Java 实现秒表的核心在于灵活运用时间戳计算和合理的界面设计,根据需求可选择:
轻量级方案:纯控制台 + `System.currentTimeMillis()`
专业级方案:Swing/JavaFX 界面 + `System.nanoTime()`
工业级方案:结合 `Hutool` 工具库的 DateUtil 或 Joda-Time 库
实际开发中建议优先考虑可移植性和易用性,复杂场景再引入多线程和
原创文章,发布者:酷盾叔,转转请注明出处:https://www.kd.cn/ask/105390.html