整数运算与溢出
理解整数除法、取模、溢出行为,以及如何检测溢出
整数运算与溢出 是 CoddyKit 上的免费 Java Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Java Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Java Academy 课程共包含 4 节课。
整数算术与溢出
Java 整数的大小是固定的。当计算结果超过最大值或最小值时,会静默回绕,不会抛出异常。理解这一点可以避免难以发现的错误。
整数范围
每种整数类型都有由位宽决定的有限范围:
byte:-128 到 127short:-32,768 到 32,767int:-2,147,483,648 到 2,147,483,647long:-9.2 × 10^18 到 9.2 × 10^18
System.out.println(Integer.MAX_VALUE); // 2147483647
System.out.println(Integer.MIN_VALUE); // -2147483648
System.out.println(Long.MAX_VALUE); // 9223372036854775807
System.out.println(Byte.MAX_VALUE); // 127溢出:静默回绕
将 Integer.MAX_VALUE 加 1 会回绕为 Integer.MIN_VALUE。这是二进制补码算术;Java 不提供任何保证,也不会抛出异常。
int max = Integer.MAX_VALUE;
System.out.println(max + 1); // -2147483648 (overflow!)
byte b = 127;
b++; // wraps to -128
System.out.println(b); // -128
// Real bug: counting votes in a large election with int
int votes = Integer.MAX_VALUE;
votes += 100; // silently wrong
System.out.println(votes); // negative number!使用 Math.addExact 检测溢出
Java 8+ 引入了 Math.addExact()、multiplyExact() 和 subtractExact(),它们会在溢出时抛出 ArithmeticException,而不是静默回绕。
try {
int result = Math.addExact(Integer.MAX_VALUE, 1);
} catch (ArithmeticException e) {
System.out.println("Overflow detected!"); // prints this
}
try {
long safe = Math.multiplyExact(100_000L, 100_000L);
System.out.println(safe); // 10000000000
} catch (ArithmeticException e) {
System.out.println("Multiply overflow");
}整数除法与取模
整数除法会向零截断。% 运算符返回与被除数符号相同的余数。请注意除以零的情况,这会抛出 ArithmeticException。
System.out.println(10 / 3); // 3 (not 3.33)
System.out.println(10 % 3); // 1
System.out.println(-10 % 3); // -1 (sign follows dividend)
System.out.println(-10 % -3); // -1
try {
int x = 5 / 0; // ArithmeticException: / by zero
} catch (ArithmeticException e) {
System.out.println(e.getMessage()); // / by zero
}
// Float division by zero gives Infinity, not exception
System.out.println(5.0 / 0); // Infinity使用 long 处理大数运算
当数值可能超出 int 的范围时,请使用 long。请始终在 long 字面量后添加 L,以避免在赋值前发生溢出。
// Bug: multiplication done as int, then widened
long wrong = 1_000_000 * 1_000_000; // overflows int!
System.out.println(wrong); // -727379968 (wrong!)
// Fix: one operand is long
long correct = 1_000_000L * 1_000_000L;
System.out.println(correct); // 1000000000000
// Or cast first
long alsOk = (long) 1_000_000 * 1_000_000;
System.out.println(alsOk); // 1000000000000位移运算符
位移运算符可以快速实现乘以或除以 2 的幂:
n << k— 左移:乘以 2^kn >> k— 带符号右移:除以 2^kn >>> k— 无符号右移:用 0 填充
int n = 8;
System.out.println(n << 1); // 16 (8 * 2)
System.out.println(n << 2); // 32 (8 * 4)
System.out.println(n >> 1); // 4 (8 / 2)
System.out.println(n >> 2); // 2 (8 / 4)
// Check if number is power of 2
boolean isPow2 = n > 0 && (n & (n - 1)) == 0;
System.out.println(isPow2); // true按位 AND、OR、XOR
按位运算符作用于各个二进制位,常用于权限、标志位和底层协议。
int a = 0b1010; // 10
int b = 0b1100; // 12
System.out.println(Integer.toBinaryString(a & b)); // 1000 (AND = 8)
System.out.println(Integer.toBinaryString(a | b)); // 1110 (OR = 14)
System.out.println(Integer.toBinaryString(a ^ b)); // 0110 (XOR = 6)
System.out.println(Integer.toBinaryString(~a)); // ...11110101 (NOT)
// Permission flags example
int READ = 0b001;
int WRITE = 0b010;
int EXEC = 0b100;
int perms = READ | WRITE; // user has read+write
System.out.println((perms & EXEC) != 0); // false — no exec数字字面量中的下划线
Java 7+ 允许在数字字面量中使用下划线,以提高可读性。编译器会忽略这些下划线。
int million = 1_000_000;
long creditCard = 4_111_1111_1111_1111L;
double pi = 3.141_592_653_589_793;
int hex = 0xFF_EC_D1_2E;
int binary = 0b0001_0101_0110;
System.out.println(million); // 1000000
System.out.println(creditCard); // 4111111111111111使用 BigInteger 实现任意精度
当数值超出 long 的范围时,请使用 BigInteger。它不会溢出,但速度比基本类型慢。它适用于加密密钥、阶乘和天文数字等场景。
import java.math.BigInteger;
BigInteger factorial100 = BigInteger.ONE;
for (int i = 2; i <= 100; i++) {
factorial100 = factorial100.multiply(BigInteger.valueOf(i));
}
System.out.println(factorial100.toString().length() + " digits"); // 158 digits
BigInteger a = new BigInteger("999999999999999999999999999999");
BigInteger b = new BigInteger("1");
System.out.println(a.add(b)); // 1000000000000000000000000000000实践:防溢出的计数器
一种实现计数器的模式:使用 Math.addExact 安全处理溢出,并在发生溢出时回退到 Long.MAX_VALUE。
class SafeCounter {
private long count = 0;
public void increment() {
try {
count = Math.addExact(count, 1L);
} catch (ArithmeticException e) {
count = Long.MAX_VALUE; // cap at max
}
}
public long get() { return count; }
}
SafeCounter sc = new SafeCounter();
sc.increment();
sc.increment();
System.out.println(sc.get()); // 2快速检查
下面表达式的值是多少?
long result = 1_000_000 * 1_000_000; System.out.println(result);
总结:整数运算与溢出
要点:
- 整数溢出会静默回绕,默认不会抛出异常
- 使用 Math.addExact/multiplyExact/subtractExact 检测溢出
- 整数除法会向零截断;% 的符号遵循被除数
- 当中间结果可能超出 int 的范围时,使用 long 字面量(L 后缀)
- BigInteger 可以处理任意大的值,不会发生溢出
- 位移运算符可以快速替代乘以或除以 2 的幂
常见问题解答
「整数运算与溢出」课时是免费的吗?
是的 — 「整数运算与溢出」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Java Academy 课程的其余内容,请升级到 CoddyKit PRO。 Java Academy 课程共包含 4 节课。
「整数运算与溢出」这节课中我会学到什么?
理解整数除法、取模、溢出行为,以及如何检测溢出 你通过在浏览器中直接运行的动手代码来练习 Java Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Java Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Java Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「整数运算与溢出」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Java Academy 课中编写并运行代码吗?
能。每节 Java Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。