跳转到主内容
极星编程网:以代码为星,赴技术山海!

详解Java中的二进制补码概念_理解负数在Java中的底层存储

Java中负数以补码存储:先取对应正数二进制,再逐位取反、加1;如-5的int补码为11111111111111111111111111111011,所有整数类型均如此,确保0唯一、加减统一、溢出自然。 Java里负数怎么存成二进制? Java所有整数类型(
byte
、
short
、
int
、
long
)都用补码表示,不是原码也不是反码。这意味着
-1
在
int
中不是
10000000 00000000 00000000 00000001
,而是全 1:
11111111 11111111 11111111 11111111
。 补码的构造规则就两条: 正数和 0:直接转二进制,高位补 0 到位宽(比如
int
补到 32 位) 负数:先算对应正数的二进制,再逐位取反,最后加 1 例如
-5
(
int
):
5 → 00000000 00000000 00000000 00000101 取反 → 11111111 11111111 11111111 11111010 +1 → 11111111 11111111 11111111 11111011
这个结果就是 JVM 实际存进内存的值。你用
Integer.toBinaryString(-5)
看到的也是它——但注意,该方法**不补前导零**,也不会显示符号位,所以输出是
11111111111111111111111111111011
(32 位),别误以为少位数。 立即学习 “ Java免费学习笔记(深入) ”; 为什么 Java 不用原码或反码? 补码唯一能同时解决三个硬需求:0 的表示唯一、加减法电路统一、溢出自然处理。原码里
+0
和
-0
是两个不同比特模式(如
0000
和
1000
),反码同样有双零问题;而补码中
0
只有一种表示(全 0),且
a + (-a)
直接得 0,无需额外判断符号。 更关键的是硬件友好:CPU 加法器不需要区分正负数,一律做二进制加法,溢出位自动丢弃。比如
Integer.MAX_VALUE + 1
得
Integer.MIN_VALUE
,这正是补码溢出的自然行为,不是 Java 特意设计的“异常”,而是底层机制使然。 常见误解: Eclipse导入Android或其他的JAVA项目的正确方法 WORD版 本文档主要讲述的是Eclipse导入Android或其他的JAVA项目的正确方法;希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看 下载
~x
(按位取反)不等于
-x
:它等于
-x - 1
,因为补码取反后还差一个 +1
Byte.MIN_VALUE
是
-128
,对应二进制
10000000
,不是
-0
—— 补码没有负零 用
Integer.toHexString
或
ByteBuffer
查看真实字节时要注意什么? 这些工具展示的是内存里的原始比特,但容易因“符号扩展”或“字节序”产生误导。 比如把
byte b = (byte) 0b10000000
(即
-128
)转成
int
再用
Integer.toHexString
:
byte b = (byte) 0b10000000; // -128 int i = b; // 符号扩展 → 0xffffff80 String s = Integer.toHexString(i); // 输出 "ffffff80",不是 "80"
这不是 bug,是 Java 自动做了符号扩展。若只想看原始 1 字节的十六进制,得屏蔽高位: 用
String.format("%02x", b & 0xFF)
→ 得
"80"
用
ByteBuffer.allocate(1).put(b).array()
拿字节数组,再逐字节处理 另一个坑:
ByteBuffer.order(ByteOrder.LITTLE_ENDIAN)
会影响多字节读写顺序,但补码定义与字节序无关——它只规定“整个数值”的二进制表示,字节序只决定这些比特怎么在内存里排布。 位运算时补码会悄悄影响结果吗? 会,尤其在右移和类型转换时。
>>
是**算术右移**:高位补符号位。所以
-8 >> 1
是
-4
,二进制从
11111000
变成
11111100
(不是
01111100
)
>>>
是**逻辑右移**:高位无条件补 0。所以
-8 >>> 1
是
2147483644
(对
int
),因为符号位被干掉了 强制类型转换可能截断:把
int
赋给
byte
,只留低 8 位,不管原值多大。例如
(byte) 200
得
-56
,因为
200
的低 8 位正好是
-56
的补码 真正容易被忽略的点是:补码让「负数的最高位恒为 1」这件事,在做掩码、解析协议、处理图像像素(如 signed vs unsigned byte)时,必须显式考虑是否要还原为无符号语义——Java 没有 unsigned 类型,你得自己用
& 0xFF
这类操作兜底。

相关文章