【问题标题】:Weird behaviour of byte in javajava中字节的奇怪行为
【发布时间】:2016-03-26 16:14:30
【问题描述】:

我有这个代码:

public class First
{
    public static void main(String[] args)
    {
        byte b=50;
        byte c=b*2;  //Error    
        byte d=50*2;   //d=100
        byte e=(byte)258;   //e=258%256;
        byte f=(byte)128;  //f=128%256;
    }
}
  1. 我知道任何涉及byte 的算术表达式都是第一 提升为int,然后执行。因此我期望 d=50*2 也报错,因为50*2 的结果是int 值,我们将其存储在 byte 中,没有任何显式类型转换。 如果 Java 隐式进行强制转换,为什么 c=b*2 会报告 错误?
  2. 我知道如果我想存储一个值 不在byte 变量的范围内,那么我必须显式键入 将其转换为 byte 并且将存储的值就是该值 模 256(字节大小)。所以,byte f=(byte)128为什么会返回 -128?由于128 % 256 = 128,所以我期望的是f=128,但它返回-128。为什么?

非常感谢。

【问题讨论】:

  • 50*2 是编译时的常量。 128 超出字节范围。

标签: java int type-conversion byte type-promotion


【解决方案1】:

Java 中的自动类型提升

除了赋值之外,还有一个地方可能会发生某些类型的转换:即在表达式中。

要了解原因,请考虑以下问题。在表达式中,精度 所需的中间值有时会超出任一操作数的范围。

例如,检查以下表达式:

byte a = 40;
byte b = 50;
byte c = 100;
int d = a * b / c;

中间项a * b的结果很容易超出其任一字节操作数的范围。为了处理这类问题,Java 在计算表达式时自动将每个byteshortchar 操作数提升为int

这意味着子表达式a*b 是使用整数而不是字节执行的。因此,即使 a 和 b 都指定为字节类型,中间表达式 50 * 40 的结果 2,000 也是合法的。

尽管自动提升很有用,但它们可能会导致编译时混乱 错误。

例如,这个看似正确的代码导致了一个问题:

byte b = 50;
b = b * 2; // Error! Cannot assign an int to a byte!

代码试图将 50 * 2(一个完全有效的 byte 值)存储回 byte 变量中。但是,由于在计算表达式时操作数被自动提升为int,因此结果也被提升为int。因此,表达式的结果现在是int 类型,如果不使用cast,就不能将其分配给byte

即使在这种特殊情况下,分配的值仍然适合目标类型,也是如此。 如果您了解溢出的后果,则应使用显式强制转换,例如

byte b = 50;
b = (byte)(b * 2);

产生正确的值 100。

【讨论】:

    【解决方案2】:

    这是因为50*2; 将在编译时解析,而b*2; 将在运行时解析。因此,在编译时,Java 编译器确定 50*2; 的结果将为 100。但在 b*2; 的情况下,正如您还提到的,算术运算的结果是 int,编译器无法确定结果是否可以超过 byte 限制,因此它会抱怨并希望您通过强制转换或更改类型来确保事情发生。

    您可以使用javap -v Test.class 进行验证。见下文:

     public static void main(java.lang.String[]);
       flags: ACC_PUBLIC, ACC_STATIC
       Code:
         stack=2, locals=2, args_size=1
            0: bipush        100
            2: istore_1
            3: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
            6: iload_1
            7: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
           10: return
         LineNumberTable:
           line 4: 0
           line 5: 3
           line 6: 10
    

    关于你的第二个问题,@Eran 已经解释了使用 128 和 258 的位表示,你可以进一步阅读JLS §5.1.3. Narrowing Primitive Conversion

    有符号整数到整数类型 T 的窄化转换 简单地丢弃除 n 个最低位之外的所有位,其中 n 是数字 用于表示类型 T 的位。除了可能丢失 有关数值大小的信息,这可能会导致 结果值的符号与输入的符号不同 价值。

    【讨论】:

      【解决方案3】:

      关于你的第二个问题,当你向下转换数字类型时,输入的低位用于初始化输出。

      因此,二进制表示为0......010000000int128 变成了字节10000000(取低8 位),即-128

      另一方面,2580........01000000010,当你取低 8 位时,你会得到 00000010,即 2。

      【讨论】:

        猜你喜欢
        • 2010-10-24
        • 2023-03-28
        • 2013-10-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-06-09
        相关资源
        最近更新 更多