Java整数运算溢出需从类型起点控制,乘法链首项须为long或强转;外部输入参与运算前应转long;关键业务用Math.addExact等显式检查溢出;循环索引和时间差也需防隐性溢出。

关键在于控制运算过程的类型起点,而不是只看结果变量声明为long。Java不会因为等号右边声明了long就自动把中间计算也升为long——它严格按表达式从左到右逐项推导类型。
乘法链中必须让首个常量带L或强转
Java乘法是左结合的,整个表达式类型由最左边的操作数决定。比如 60 * 24 * 3600 * 1000 全是int字面量,全程按int算,哪怕结果远超Integer.MAX_VALUE(2147483647),也会静默溢出成负数。
- 错误写法:
long millis = 60 * 24 * 3600 * 1000;→ 实际算出889032704(溢出后值) - 正确写法1:
long millis = 60L * 24 * 3600 * 1000;→ 左边第一个是long,后续全升为long - 正确写法2:
long millis = (long) 60 * 24 * 3600 * 1000;→ 强制首项为long,效果等同
涉及用户输入或配置的int参与运算前先转long
像时间差、分页偏移、持续分钟数这类来自外部的数据,即使当前值不大,也不能假设永远安全。一旦某次传入大值(如150000分钟 ≈ 104天),lastTime * 60 * 1000 就会立即溢出。
- 别写:
new Date(now + lastTime * 60 * 1000) - 改写为:
new Date(now + (long) lastTime * 60 * 1000)或new Date(now + TimeUnit.MINUTES.toMillis(lastTime)) - 更稳妥:把参数类型直接改为
long lastTime,从源头杜绝int参与
用Math.addExact等显式检查代替静默回绕
不是所有场景都适合无脑升long。当业务逻辑要求“溢出即报错”(比如计费金额、库存扣减),用Math.addExact比靠类型转换更可靠。
立即学习“Java免费学习笔记(深入)”;
-
int a = Integer.MAX_VALUE;int b = 1;int sum = Math.addExact(a, b); // 抛 ArithmeticException - 支持
add/subtract/multiply/toIntExact,覆盖主要整数运算 - 性能接近原生运算,异常信息明确含“integer overflow”,日志里一目了然
警惕隐性溢出点:循环索引和时间戳差值
溢出不只发生在显眼的大数相乘。以下场景容易被忽略:
-
for (int i = start; i < end; i += step):若end == Integer.MAX_VALUE且step > 0,i加到最后会回绕成负数,导致死循环 -
int diff = (int) (endTime - startTime):两个long时间戳相减结果可能超int范围,强制截断丢精度 - 解决方案:循环用
long i;时间差直接保留long,不强转


















