java算术运算符静默回绕溢出,应优先使用math.exact系列方法检测;次选升级为long并显式类型转换;手动检查仅适用于禁用异常场景,高风险业务需前置校验或biginteger。

Java 的算术运算符(+、-、*)不检测也不处理溢出边界,而是按 JVM 规范静默回绕(wraparound)。比如 Integer.MAX_VALUE + 1 得到 Integer.MIN_VALUE,Byte.MAX_VALUE + 1 变成 -128。这不是 bug,是设计使然——追求性能,牺牲安全性。
关键点在于:溢出发生在计算过程中,不是赋值时;靠事后检查结果几乎无效。真正可靠的应对方式,得从运算前或运算中主动干预。
Math.exact 系列方法是最推荐的默认方案
Java 8 起内置了语义明确、零依赖、高性能的溢出检测工具:
-
Math.addExact(a, b)、Math.subtractExact(a, b)、Math.multiplyExact(a, b) -
Math.incrementExact(a)、Math.negateExact(a)、Math.toIntExact(long)
它们在溢出时直接抛 ArithmeticException,异常信息含 “integer overflow”,日志可精准定位问题点。
IDE 能静态识别这些方法可能抛异常,倒逼你写 fallback 逻辑(比如降级为 long 或提示错误)。
注意:
- 不支持
byte、short直接调用,需先升为int; -
Math.divideExact只检查除零和Integer.MIN_VALUE / -1这类确定溢出,不覆盖所有除法边界。
升级类型并确保全程高精度运算
如果中间结果预计不超过 long 范围(±9.2×10¹⁸),用 long 是最简单有效的规避方式——但必须避免隐式截断:
- ❌ 错误:
int a = 200000, b = 200000; long c = a * b;
→ 先按int相乘已溢出,再转long无意义 - ✅ 正确:
long c = (long) a * b;或long c = 200000L * 200000;
→ 至少一个操作数为long,触发长整型运算 - 字面量超
int范围必须加L后缀(如10000000000L),否则编译失败
手动边界检查仅用于特殊场景
适用于不能抛异常的环境(如高频循环、嵌入式),但检查必须放在运算前,且覆盖所有符号组合:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
加法判断(正溢出):
a > 0 && b > 0 && a > Integer.MAX_VALUE - b
加法判断(负溢出):
a
乘法更复杂,需先处理 0、Integer.MIN_VALUE、-1 等特例,再按符号分治;建议用 long 中转做除法比较,避免向零截断误差。
⚠️ 切忌事后验证:if (a + b 漏掉负溢出和混合符号场景,不可靠。
高风险场景应换用 BigInteger 或契约约束
对金额、订单ID、累计统计值等不容错场景,或输入完全不可控(如用户传参、外部接口返回)时:
-
BigInteger提供任意精度,但对象开销大、GC 压力明显,适合长期参与复杂计算,不适合简单计数; - 更优策略是前置校验:在参数进入业务逻辑前,用
Math.addExact或范围断言(如Preconditions.checkArgument(x >= 0 && x )拦截非法值。
不复杂但容易忽略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










