两种解释都是正确的,因为它们实际上是相同的。
让我们看看数学,看看为什么。
Java 将值存储在 byte、short、char、int 和 long 中,格式为 two's complement。
对于byte、short、int 和long,它是签名的,对于char,它是未签名的。
二的补码 格式的属性之一是,对于大多数操作,将值解释为 signed 还是 unsigned 都无关紧要。生成的位模式将是相同的。
为了简短起见,我将使用byte 进行解释,但其他类型的工作原理相同。
byte 有 8 位。最高位被解释为 sign 位。所以,位模式是这样的:
snnn nnnn
分成两组,每组 4 位称为 Nibble,在这里执行是为了纯粹的可读性。作为旁注,半字节可以用十六进制数字表示。
所以一个字节中有 8 位,每个位可以是 0 或 1。这给我们留下了 2^8 = 256 可以存储在 byte 中的不同值。
以下是一些示例值:
0000 0000 -> 0
0000 0001 -> 1
0000 0010 -> 2
0100 0000 -> 64
0111 1111 -> 127
1000 0000 -> -128
1111 1110 -> -2
1111 1111 -> -1
有符号数的 2 的补码值,即符号位已设置,是通过取 8 位的正值并减去范围来创建的,即在一个字节的情况下减去 256。
现在让我们看看如果你把-1 加上1 会发生什么。
1111 1111 -1 / 255
+ 0000 0001 1
--------------
= 1 0000 0000 -0 / 256 intermediate result
= 0000 0000 0 / 256 result after dropping excess leading bits
有溢出。结果现在需要 9 位,但字节只有 8 位,所以最重要的位丢失了。
让我们看另一个例子,-1 加上-1。
1111 1111 -1 / 255
+ 1111 1111 -1 / 255
--------------
= 1 1111 1110 -2 / 510 intermediate result
= 1111 1110 -2 / 254 result after dropping excess leading bits
或者这个,127 加上5。
0111 1111 127
+ 0000 0101 5
--------------
= 1000 0100 132 / -124
正如我们所见,前导位被丢弃了,这实际上是导致“再次从最小值开始计数”而导致其溢出的原因。