你刚刚做到了。
最后,计算机只知道比特。剩下的就是代码,以及查看代码的人,是如何构成的。位是 0 或 1。如果您购买的是 4GB RAM 的计算机,那么您的计算机可以记住其中的 34359738368 个。
这有点笨拙,因此 AMD,或英特尔,或台积电,或任何烘焙你的芯片的人,都在芯片的设计中加入了 8 个一组(对于某些工作,64 组甚至更高) )。但这就是它结束的地方。这只是一点点,真的。负数?那是什么? 2?你说的这个2是什么。我只知道 0 和 1。
所以这也很笨拙,所以我们人类不想说:这个字节保存值 00000101。我们只会说“保存 5”。
bits = decimal
00000000 = 0
00000001 = 1
00000010 = 2
00000011 = 3
00000100 = 4
00000101 = 5
... and so on
这很好,但是-1 呢?我们只有0 和1。没有 - 那么我们该怎么做呢?
这就是有趣的地方。这是一个约定,而不是计算机中的东西。有一个东西叫做二进制补码:我们都同意检查第一位。如果它是 1,那么我们称之为-X,其中 X 是通过应用以下算法找到的:翻转每一位(所有 0 变为 1,所有 1 变为 0),并将其加 1。
11111011 = -5.
Why? Well, flip every bit: 00000100
then add 1 to it : 00000101
which is 5.
但这会立即消耗掉我们所能代表的一半。毕竟,我们现在可以在一个字节中存储的最大数字是 127:01111111,即 127。如果我们在这个数字上加 1,那么我们就得到 10000000,但是嘿,它以 1 位开头,所以假设我们都同意这意味着它是负数,这意味着 1000000 是 -128(有点奇怪)。
有时这很烦人或不值得。所以有时我们都同意这个数字根本不可能是负数,1000000 只是 128。而11111111 只是 255。
计算机不知道。 255 是11111111,-1 也是。那么11111111 是什么?电脑不知道。它甚至不知道2 是什么。它只知道零和一,就计算机而言,11111111 就是它。 (数学计算出 + 和 - '正常工作',不管我们是否规定这些数字被视为有符号的二进制补码,很酷,嗯?试试看!如果 11111011 既是 -5 也是 251根据读取数字的人的意见,会发生什么?-5 + 2 是 -3。251 + 2 是 253。-3 和 253 归结为相同的位序列。只是一个例子。这个顺便说一句,这就是为什么我们会做奇怪的“翻转所有位并添加 1”的东西。所以 + 和 - 可以正常工作,而无论您认为这些位是“有符号”还是“无符号”,您都不需要传递。
在 java 中,除了char(它是一种数字类型。你会认为它代表一个字符,但实际上不是)之外的所有 数字类型都是有符号的。 byte 是“有符号的 8 位数字”(因此,可以表示从 -128 到 +127,包括在内)。 char 是唯一的例外,它是一个“无符号 16 位数字”,因此可以保存从 0 到 65535,包括在内。这只是如果你例如调用System.out.println((char) 65);,println 方法会将该数字解释为:“在 unicode 表中查找并打印您在其中找到的任何内容”,从而打印“A”。这是特定 println 方法的源代码的一部分,它与 java 中的 char 类型无关,它只是“0 到 65535 之间的数字”。
因此,当您在 java 中打印包含 0xFF, 0xFF 的字节数组时,因为 java 同意我们认为它已签名,它会打印 -1、-1。但这只是 0xFF、0xFF 的 java-ese。您的字节数组包含 0xFF、0xFF,因为 在位级别 -1 和 255 是完全相同的数字。无论如何,对于字节。其他所有的(char、short、int、long)都不是这样。
回顾一下:
byte x = (byte) 200;
byte x = (byte) 0xC8;
byte x = -56;
在所有这些情况下,x 最终都持有位 11001000。 没有办法区分。你不能问系统:那么,呃,这个x等于200,还是0xC8,还是-56?是用什么设置的?因为计算机不知道 - 编译器将上述所有代码转换为完全相同的最终结果,即 11001000。
255 是 -1。