【问题标题】:JAVA and byte arraysJAVA 和字节数组
【发布时间】:2020-09-28 23:20:40
【问题描述】:

我正在尝试使用一个 API,其中有一个用于通信的套接字。请求由不同的部分组成,其中一个是标头,如下所示:

Fixed header: 2 bytes, fixed at 0xffff

通常我不擅长字节和流,因为我从未使用过它。那么我应该如何创建所述字节数组?我已经尝试了以下

byte[] header = new byte[]{(byte)0xff, (byte)0xff};

但是它们每个字节都变为-1,我认为这是因为 0xFF 转换为 255,它超出了有符号字节范围(-128 到 +127),但是我该如何创建这样的标头呢?

【问题讨论】:

    标签: java arrays byte


    【解决方案1】:

    你刚刚做到了。

    最后,计算机只知道比特。剩下的就是代码,以及查看代码的人,是如何构成的。位是 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 呢?我们只有01。没有 - 那么我们该怎么做呢?

    这就是有趣的地方。这是一个约定,而不是计算机中的东西。有一个东西叫做二进制补码:我们都同意检查第一位。如果它是 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。-3253 归结为相同的位序列。只是一个例子。这个顺便说一句,这就是为什么我们会做奇怪的“翻转所有位并添加 1”的东西。所以 + 和 - 可以正常工作,而无论您认为这些位是“有符号”还是“无符号”,您都不需要传递。

    在 java 中,除了char(它是一种数字类型。你会认为它代表一个字符,但实际上不是)之外的所有 数字类型都是有符号的。 byte 是“有符号的 8 位数字”(因此,可以表示从 -128+127,包括在内)。 char 是唯一的例外,它是一个“无符号 16 位数字”,因此可以保存从 065535,包括在内。这只是如果你例如调用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。

    【讨论】:

      【解决方案2】:

      首先,您必须知道在 Java 中所有整数类型都是有符号的。这意味着保留最高有效位来表示符号。这就是为什么在 Java 中常量 Byte.MAX_VALUE 说它可以达到 127。

      现在,这意味着您可以在一个字节中存储 8 位,但是如果您碰巧打开了符号位,那么您存储的任何内容都会被 Java 表示为负数。

      由于0xff 打开所有字节位(即11111111)而不是像您期望的那样获得255,因此您得到的是-1,因为该数字在Java 中代表-1。

      也许为了理解它,我可以向您展示这些位在 Java 中是如何工作的。想象一个只有 4 位的类型称为 nimble,其中最高有效位保留用于符号。

      如果它存在的话,这就是它在 Java 中的样子:

      Imaginary Signed Type: Nimble (4 bits)
      
      Dec. Bin.  Hex.
      --------------------
      +0   0000  0x0   
      +1   0001  0x1
      +2   0010  0x2
      +3   0011  0x3
      +4   0100  0x4
      +5   0101  0x5
      +6   0110  0x6
      +7   0111  0x7
      -8   1000  0x8
      -7   1001  0x9
      -6   1010  0xA
      -5   1011  0xB
      -4   1100  0xC
      -3   1101  0xD
      -2   1110  0xE
      -1   1111  0xF
      

      注意那些最高有效位所在的数字是如何变成负数的。如果这个灵活是无符号类型,那么它不会有负数,它可以达到 15。

      这就是为什么 Java 字节从 -128 变为 127,而不是您期望的高达 255。

      现在,当涉及到创建字节数组以发送到流时,您可以将套接字输出流包装到像DataOuputStream 这样的类型感知流中,而不是自己创建字节数组,这样您就可以发送特定类型的数据。

      例如:

      try(DataOutputStream out = new DataOutpuStream(socket.getOutputStream())) {
         dOut.writeByte((byte)0xff);
         dOut.writeByte((byte)0xff);
      }
      

      这样您就可以避免创建标头数组的所有困难。

      但归根结底,如果没问题,你就是数组。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-11-06
        • 1970-01-01
        • 2011-10-04
        • 2014-09-30
        • 1970-01-01
        • 1970-01-01
        • 2011-07-02
        • 1970-01-01
        相关资源
        最近更新 更多