代码之家  ›  专栏  ›  技术社区  ›  K_K

当b是一个字节并且在Java中的值为-1时,为什么b>>1总是等于-1?

  •  2
  • K_K  · 技术社区  · 2 年前

    我知道 >>> 是无符号右移,并且由于 byte 在Java中签名为8位,则-1将表示为 1111 1111 。因此,对于以下代码:

    byte b = (byte) -1;
    b >>>= 1;
    System.out.println(b);
    
    

    它不应该印出来吗 127 (二进制0111 1111)?但我得到的结果总是 -1 ,无论我表演了多少次 >>> b (例如在循环中)。

    有人能解释为什么以及以什么方式我能得到我期望的结果吗(即执行无符号右移 -1 向右移动一次等于 127 对于 字节 在Java中)?我的理解错了吗?谢谢!

    1 回复  |  直到 2 年前
        1
  •  5
  •   rzwitserloot    2 年前

    您遇到了一系列非常奇怪的java设计选择,这些选择使代码成为“谎言”(它 出现 做一件事,但实际上不会做那件事)。让我们来分解一下:

    鲜为人知的java方面1: byte , short , char boolean 是低劣的

    在java中, 字节 , 短的 , 烧焦 , 布尔值 是吗 较差的 基本体,具有 int , long , double float 优越的 基元。除了数组存储和数组加载之外,没有用于下级的操作码。 java中没有将2个字节加在一起的操作码。它根本不存在。 .考虑到这一点,真的不可能做到 javac 不知道要生成什么。因此:

    byte b = 5;
    byte c = 10;
    byte d = b + c;
    

    实际上是指:

    byte b = 5;
    byte c = 10;
    byte d = (int) b + (int) c;
    

    因此,这是一个编译器错误-的结果 int + int int ,并尝试分配 int 字节 在没有显式强制转换的情况下是不允许的。

    你可以随便看看 list of java bytecodes 。你会发现FADD、DADD、IADD和LADD。但你不会发现BADD、CADD或SADD。

    鲜为人知的java方面2:复合赋值运算符自动转换。

    这个: a += b; 实际上与 a = a + b; 完全我知道这是你被教导的,但这是不正确的。事实上,它等于 a = (byte) (a + b); 在哪里 字节 是类型a。见证人:

    double d = 5.5;
    int x = 5;
    x = x + d; // obviously, compiler error. Explicit cast needed.
    

    与:

    double d = 5.5;
    int x = 5;
    x += d;
    

    完全合法。结果x是10。全流量为:

    • x是5。此转换为 双重的 因为java只能在两个相同类型的事物之间进行数学运算。
    • d 是5.5,已经是双人间了,很好。
    • DADD 执行字节码;其结果是10.5。
    • 这是铸造到 int ,导致 10 ,分配给 x .

    javap 如果你想看这个。

    将两者结合

    因此,让我们分解一下您的代码:

    byte b = (byte) -1;
    

    这使得int-1(32个“1”位),然后将其强制转换为byte,从而得到 b 具有价值 1111 1111 。到目前为止,一切都很好,这里没什么奇怪的。

    b >>>= 1;
    

    胡小子。这看起来很简单,但很复杂。它的语法糖用于:

    b = (byte) (((int) b) >>> 1);
    

    打断 那个 向下:

    • (int) b 产生32个1比特。 b 在字节中为-1,将其强制转换为int将产生-1的int,即32个1位。
    • >>> 1 然后将其设为0,后面跟着31个1比特。
    • 然后将其转换回一个字节,该字节。。是-1-8个1位。

    那么我该怎么做呢?

    不使用 字节 大体上除非在以下情况下,否则永远不要使用劣质基元:

    • 本质上,在签名(即方法的返回类型或参数类型)中将它们用作API文档。
    • 你做一个数组。 byte[] 非常有用,而且每个项目实际上只需要1个字节。

    对于所有其他事情(例如单个字段和针对数学的局部变量,例如您正在做的事情), 不要 。正如你在这里发现的那样,他们的行为并不像你想象的那样。

    只需使用int。如果你想要一个“无符号字节”,只需制作一个 int 变量,并使用它:

    byte b = (byte) -1;
    int x = b & 0xFF; // x is now 0000...0000 1111 1111.
    System.out.println(x); // prints 255
    x >>= 1;
    System.out.println(x); // prints 127
    

    字节 作为单个值(不是 字节[] -只是 字节 ) 在任何方面都没有更高效 int 甚至可能 长的 ,基于64位体系结构。Java不会分配更少的内存(因为所有东西都必须在字边界上对齐),操作也不会更快(CPU已经在64位块中工作了)。