java整数溢出默认静默回绕,math.addexact等方法在溢出时抛arithmeticexception;仅支持int/long,性能低3–5倍;tointexact严格校验无损转换;floordiv/floormod解决负数取模歧义;自定义检测易错,应优先用jdk内置方法。

Java整数溢出默认不报错,但Math.addExact会抛异常
Java里int加法超出范围不会自动报错,而是静默回绕(比如Integer.MAX_VALUE + 1变成Integer.MIN_VALUE)。这在金融、计费、索引计算等场景极危险。JDK 8起,Math类提供了带溢出检查的静态方法,本质是手动做边界判断再运算。
-
Math.addExact(a, b)、Math.multiplyExact(a, b)、Math.subtractExact(a, b)这些函数在溢出时统一抛ArithmeticException,不是RuntimeException子类的其他异常,必须显式处理 - 只支持
int和long类型,没有short或byte重载;传入short会自动提升为int,但检查逻辑仍按int规则走 - 性能比普通运算低约3–5倍(JIT优化有限),高频循环里慎用;如果已知输入范围安全,别为了“看着稳妥”硬套
用Math.toIntExact转换long要小心截断
这个方法不是做四舍五入或强制转型,而是严格校验:只有当long值能无损表示为int时才返回,否则抛ArithmeticException。它常被误当成“安全强转”,其实语义完全不同。
- 例如
Math.toIntExact(3000000000L)一定失败,因为3000000000L > Integer.MAX_VALUE(2147483647) - 和
(int) 3000000000L结果不同:(int)会静默取低32位,得到-1294967296;而toIntExact直接中断流程 - 适合用在解析外部数据(如JSON、DB字段)后做合法性兜底,比如读到一个ID字段声明为
int,但数据库存的是BIGINT,这时必须确认没越界
Math.floorDiv和Math.floorMod解决负数取模歧义
Java原生/和%对负数采用“向零截断”,导致-5 % 3 == -2,而数学上更常见的余数应是非负的(即-5 mod 3 == 1)。JDK 8引入这两个函数,行为对标Python的//和%。
-
Math.floorDiv(-5, 3)返回-2(向下取整除法),Math.floorMod(-5, 3)返回1,满足恒等式:floorDiv(x,y) * y + floorMod(x,y) == x - 它们不检测溢出:若
Math.floorDiv(Integer.MIN_VALUE, -1)会发生整数溢出,结果仍是Integer.MIN_VALUE(未抛异常),这点和addExact不同,需单独防范 - 常见于坐标归一化、环形缓冲区索引计算——比如数组长度为
n,想让i无论正负都映射到[0, n),用Math.floorMod(i, n)比(i % n + n) % n更简洁且语义清晰
自定义溢出检测别手写if边界判断
有人会自己写if (a > 0 && b > 0 && a > Integer.MAX_VALUE - b)这类逻辑,看似可控,实则极易出错:漏掉符号组合、算术中间结果本身溢出、long/int混用隐式转换等。
立即学习“Java免费学习笔记(深入)”;
- JDK的
addExact底层用的是汇编级指令(HotSpot中调用unsafe或intrinsics),比Java层if判断更可靠 - 如果项目还在用JDK 7或更早,不要自己仿写;改用
BigInteger临时包装(虽慢但绝对安全),或升级JDK - 注意:这些方法只覆盖基础四则和类型转换,像
++、+=、Arrays.stream().sum()等操作不自动触发检查,得手动替换为addExact调用
真正难的不是调哪个API,而是想清楚——这里到底需不需要溢出保护?如果只是临时中间变量、范围明确,加一层检查反而增加维护负担和运行开销。边界在哪,得结合业务语义来定,不是所有数字运算都该套Exact。









