java算术溢出会静默回绕,应优先用math.addexact等方法主动抛异常;性能敏感处可手动边界检查;推荐升级为long或biginteger防溢出。

Java 算术运算溢出不会报错,而是静默回绕——比如 Integer.MAX_VALUE + 1 直接变成 Integer.MIN_VALUE。这种“不声不响”的错误最容易在金额计算、分页偏移、ID生成、索引校验等场景引发严重逻辑漏洞。预防的关键不是等它发生再捕获,而是在运算前主动设防。
用 Math.exact 系列方法让溢出变异常
Java 8+ 提供了带溢出检查的算术方法,一旦越界立即抛 ArithmeticException,把隐性风险显性化:
-
Math.addExact(a, b)、Math.subtractExact(a, b)、Math.multiplyExact(a, b) - 对
int和long都有重载,例如Math.multiplyExact(100000, 100000)会直接抛异常 - 适合强一致性业务,如订单金额累加、库存扣减、分页参数校验
- 注意:除法和取模没有 exact 版本,需单独检查除零或符号边界
运算前手动做边界检查
在性能敏感路径(如高频循环、底层工具类)中,避免异常开销,可提前判断是否安全:
- 加法:
a > 0 && b > 0 && a > Integer.MAX_VALUE - b(正+正溢出) - 减法:
a 0 && a (负-正溢出) - 乘法(正数):
b != 0 && a > Integer.MAX_VALUE / b;负数需分符号组合处理 - 建议封装成静态工具方法复用,避免重复写错逻辑
优先升级数据类型,从源头降低风险
很多溢出源于过早锁死小类型。中间结果只要可能超限,就该用更大类型参与运算:
- ID、时间戳、计数器、文件偏移量 → 直接声明为
long - 字面量参与运算时加后缀:
10000000000L,防止编译器按int解析 - 别写
(long)(a * b)——错在a * b已在int层溢出;应写(long)a * b或a * 1L * b -
long仍会溢出,仅降低概率;高风险场景建议配合Math.multiplyExact使用
不可控大数场景直接上 BigInteger
当数值范围完全不可预估(如阶乘、密码学、超大订单号拼接),BigInteger 是唯一可靠选择:
- 构造用字符串:
new BigInteger("123456789012345678901234567890") - 所有运算靠方法:
add()、multiply()、pow(),不支持+/*运算符 - 比较用
compareTo(),相等用equals(),别用== - 需转回基本类型时,用
longValueExact()(溢出会抛异常),不用longValue()(静默截断)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











