Java基本类型运算会静默溢出,应优先用Math.exact系列方法抛异常、手动边界检查、升级为long或BigInteger来主动防范。

Java 中基本数据类型运算不会主动报错,而是静默溢出——比如 int 加到最大值再加 1,直接变成负数。所谓“防止报错”,其实是防止静默错误导致逻辑失控。关键不是等它出错再捕获,而是提前设防、主动拦截。
用 Math.exact 系列方法让溢出变异常
Java 8+ 提供了带校验的算术方法,一旦越界就抛 ArithmeticException,把隐性风险显性化:
-
Math.addExact(a, b)、Math.multiplyExact(a, b)、Math.subtractExact(a, b) - 对
long类型也有对应重载,如Math.multiplyExact(long a, long b) - 适合金额计算、库存扣减、分页偏移量等强一致性场景
注意:除法和取模没有
exact版本,需单独检查除零或符号边界。
运算前手动判断边界(无异常开销)
在性能敏感或无法抛异常的路径(如底层工具类、高频循环),可预判:
- 加法:
a > 0 && b > 0 && a > Integer.MAX_VALUE - b - 乘法(正数):
b != 0 && a > Integer.MAX_VALUE / b - 减法(a - b 溢出为正):
a < 0 && b > 0 && a < Integer.MIN_VALUE + b
手动检查要覆盖正负组合,建议封装成工具方法复用,避免重复写错逻辑。
优先升级数据类型,从源头降低风险
很多溢出其实源于过早锁死小类型:
- ID、时间戳、计数器、文件偏移量 → 直接用
long,别等出事再改 - 字面量参与运算时加后缀:
10000000000L,避免编译器按int解析 - 别写
(long)(a * b)—— 错在a * b已在int层溢出;应写(long) a * b或a * 1L * b
大数场景直接换 BigInteger
当数值范围完全不可控(如阶乘、密码学、超大订单号拼接):
- 构造用字符串:
new BigInteger("123456789012345678901234567890") - 运算全靠方法:
add()、multiply()、pow(),不支持+-* - 比较用
compareTo(),相等用equals(),别用== - 需转回基本类型时,用
longValueExact()(溢出会抛异常),不用longValue()(静默截断)
不复杂但容易忽略

















