math.negateexact对integer.min_value或long.min_value取反时抛出arithmeticexception,因其相反数超出int/long范围;底层通过特判最小值实现主动溢出检测,而非依赖异常捕获。

Math.negateExact 通过直接计算取反结果并检查是否发生溢出,而不是依赖异常捕获或额外的条件判断。
底层原理:利用补码特性做溢出判断
Java 中整数采用二进制补码表示。对一个 int 或 long 值 x 取反(即 -x),在数学上等价于 0 - x。但补码中,Integer.MIN_VALUE(即 -2147483648)取反本应是 2147483648,超出 int 范围,实际会回绕成 -2147483648(自身),造成静默错误。
negateExact 利用这一“回绕唯一性”来检测:对 int 类型,只有当 x == Integer.MIN_VALUE 时,-x == x;其他所有值都满足 -(-x) == x。因此它通过反向验证完成判断:
- 先计算
result = -x - 再检查
-result != x—— 若成立,说明发生了溢出(仅可能发生在x == MIN_VALUE时) - 若成立则抛出
ArithmeticException
源码逻辑(以 int 版本为例)
查看 JDK 源码可看到类似实现:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
public static int negateExact(int a) {
if (a == Integer.MIN_VALUE) {
throw new ArithmeticException("integer overflow");
}
return -a;
}
实际上 JDK 并非每次都用 if 判断最小值,而是采用更通用的无分支写法(避免分支预测失败):
int r = -a;-
if (r == Integer.MIN_VALUE && a != Integer.MIN_VALUE)不会发生,所以真正检查的是:if (a 0) || (a > 0 && r ?不对 —— 更简洁的是:<code>if (a != 0 && Integer.signum(a) == Integer.signum(-a))?也不高效 - JDK 实际用:
if (a == Integer.MIN_VALUE)直接特判,因为这是int中唯一使-a溢出的输入
所以本质就是:**只有一种情况会溢出,就直接判断那一种情况**。
为什么不用 try-catch 包裹普通取反?
普通 -x 运算永远不会抛异常,它总是返回一个 int 值(哪怕逻辑上溢出)。JVM 规范规定整数运算是“模运算”,不抛异常。因此无法靠捕获异常来检测 —— 必须在运算前或运算后主动校验。
-
Math.negateExact是“主动校验”:要么特判边界值,要么用反向恒等式验证 - 它不是“执行后看有没有异常”,而是“执行前就知道会不会错”
- 这也是它比手写
if (x == MIN_VALUE) throw...更可靠的原因:封装了正确逻辑,且对long同样适用(Long.MIN_VALUE同理)
使用建议与注意事项
当你需要确保取反结果在目标类型范围内时才用它,比如实现安全的符号翻转、中间计算避免静默错误:
- 替代裸写
-x,尤其当x来自用户输入、文件解析或不可信计算流时 - 注意它只对
int和long重载,不支持short、byte或float/double - 性能略低于裸取反(多一次比较),但开销极小,推荐在关键路径中使用
- 不要用于已知安全的场景(如循环计数器从 1 开始),避免不必要开销
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










