bigywater.tech
Java 内部原理 · 30

int 溢出真实静默环绕:Integer.MAX_VALUE + 1 真实变成负数,没有任何报错

Java 的整数运算默认不检查溢出——这句话大多数人都知道,但"静默"两个字到底有多静默,真实跑一次最有说服力:程序拿到一个彻底错误的结果,却没有任何信号提示它出过错。

2147483647+1 = -2147483648
真实运行:Integer.MAX_VALUE + 1 没有抛任何异常,结果真实环绕成了 Integer.MIN_VALUE
Math.addExact → 真实抛异常
同样会溢出的加法换成 Math.addExact,真实抛出 ArithmeticException: integer overflow,不溢出时结果完全一致

上一篇:try-with-resources 真实是逆序关闭:被抑制的异常真实附在主异常身上,不是丢了

真实验证:加 1,直接从正数最大值跳到负数最小值

int 是 32 位补码表示,最高位是符号位——加到溢出时,进位会真实"污染"到符号位上。

真实实测
Integer.MAX_VALUE = 2147483647
Integer.MAX_VALUE + 1 真实结果 = -2147483648(没有抛任何异常,静默环绕成了负数)
是不是正好等于 Integer.MIN_VALUE = true

这不是随机出错,是补码运算的真实、确定性结果——Integer.MAX_VALUE 的二进制是 0111...1(31 个 1),加 1 之后按位进位,真实变成 1000...0,而这个模式在补码里恰好就是 Integer.MIN_VALUE。这个环绕行为在 Java 语言规范里是明确定义的,不是未定义行为,但"明确定义"不代表"安全"——它只保证结果可预测,不保证结果是你想要的。

真实验证:一个更真实的场景,结果真实错了但程序毫不知情

两个看起来完全正常、单独都远小于 Integer.MAX_VALUE 的数相加,同样可能溢出。

真实实测
1500000000 + 1000000000 真实数学结果应为 = 2500000000
用 int 相加真实得到 = -1794967296(完全没有报错提示,结果已经错了但程序不知道)

15 亿和 10 亿,单独看都是很普通的数字(远小于约 21 亿的 Integer.MAX_VALUE),但相加之后的真实数学结果 25 亿已经超出了 int 能表示的范围——程序真实得到一个负数,如果这个结果后续被用来做数组下标、金额计算、或者任何逻辑判断,错误会真实、安静地传播下去,不会在发生的那一刻留下任何痕迹。

真实验证:Math.addExact 会真实喊出来

JDK 8 引入的 Math.addExact/subtractExact/multiplyExact 系列方法,专门用来在溢出真实发生时立刻抛异常。

真实实测
Math.addExact 真实抛出 ArithmeticException: integer overflow
真实实测 · 不溢出时,两者结果完全一致
100 + 200 用普通 + 真实结果 = 300
100 + 200 用 Math.addExact 真实结果 = 300(不溢出时两者完全一致)
那是不是应该所有加法都换成 addExact?不是。addExact 每次都要真实做一次溢出检查,在能确定数值范围安全(比如循环计数器、已知上限的业务量)的场景下,这是不必要的开销;它真正该用在数值范围不确定、又不能容忍静默错误的场景——比如金额计算、数组容量计算这类"错了会有真实后果、但又不方便每次都手动检查"的地方。真实的工程判断是:默认相信 int 运算,只在真正有溢出风险且后果严重的地方,主动换成检查版本。

顺带真实验证一下:long 是不是也会有同样的问题——只是范围大得多(约正负 922 亿亿),日常业务里很难真的碰到。

真实实测
Long.MAX_VALUE + 1 真实结果 = -9223372036854775808(同样静默环绕,只是需要接近 9.2×10^18 才会踩到)

分步演示:int 溢出是怎么在你没注意的地方真实发生的

每一行都是真实运行这段代码得到的结果。

int 溢出真实静默回绕,不抛异常;运算结果的类型由操作数类型决定,"转成 long 太晚"是真实存在的坑
这一步新验证的运算

    参考与说明

    • 本文全部结果均在本机真实运行 Java 17 得到,补码环绕行为由 Java 语言规范明确定义,跨 JVM 实现一致。
    • 系列开篇 Integer 缓存 那篇验证的是装箱后的引用比较问题,这篇验证的是更底层的算术运算本身,两者是 int/Integer 这个主题下不同层面的真实陷阱。
    • 没有涉及:BigInteger/BigDecimal 这类没有固定位宽限制的数值类型、编译器 -Xlint 等静态检查工具对潜在溢出的识别能力——这些留给系列后面按需再写。
    京ICP备2026067502号-1京公网安备11010502063487号