4

K.Sierra 和 B.Bates 在他们的《SCJP 学习指南》一书中写道

"下面是合法的byte b = 27;,但仅仅是因为编译器自动将文字值缩小到一个字节。换句话说,编译器进行了强制转换。前面的代码与下面的代码相同:byte b = (byte) 27; "

在我看来,这种解释是不正确的。这两行代码是否相同?

实际上

byte b = 27;

只是一个常数。而常量的编译时变窄是这段代码有效的唯一原因。所以不需要演员表。缩小范围时,编译器只检查指定的值是否适合变量的类型。规范说:

如果变量的类型是byteshortchar,并且常量表达式的值可以用变量的类型表示,则可以使用缩小原语转换。

在第二种情况下

byte b = (byte) 27;

强制转换确实在运行时发生,并且原始值是根据特定规则计算的。编译器不关心原始类型的兼容性。例如

byte b = 5.0; // compile error
byte b = 277777777; // compile error
byte b = (byte) 5.0; // valid!
byte b = (byte) 277777777; // valid!!

这让我觉得扩大/缩小转换和铸造是根本不同的。但在各种来源中,它们经常互换使用。这个对吗?在隐式缩小转换的情况下是否会发生强制转换?

任何人都可以解释编译器在上述书中描述的情况下的真实行为吗?

4

2 回答 2

5

测试它很容易。将以下内容放入 Temp.java:

class Temp {
  public static void main(String[] argv) {
    byte b = 27;
    System.out.println(b);
  }
}

现在用你最喜欢的编译器编译它:

$ javac Temp.java

现在用 javap 转储字节码:

 $ javap -c Temp.class
 Compiled from "Temp.java"
  class Temp {
    Temp();                                                                                                                             
      Code:
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return

    public static void main(java.lang.String[]);                                                                                        
      Code:
         0: bipush        27
         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
  }

现在替换27(byte)27并再次运行。你会发现没有区别。事实上,这两个类文件将具有相同的 md5sum。

字节码中没有运行时转换,因为编译器认为不需要它,并将其优化掉。

我相信你是正确的,在语法上 linebyte b = 27与line 不同byte b = (byte) 27,但它们在语义上是相同的,因为所有标准编译器都足够聪明,可以将 line 优化为单个字节码。

于 2013-06-25T13:50:30.757 回答
2

在我们开始之前,重要的是要注意,在 java 中,所有纯数字文字都是int值。

关于允许的非强制转换常量的关键短语可以用变量的类型来表示。这只是意味着变量类型的常量“在范围内”,所以:

  • -128到byte 127
  • 对于short -32768 到 32767
  • 对于char 0 到 65535

如果转换为变量类型,“范围内”的值不会“丢失信息”。这就解释了为什么允许使用范围内的常量。

对于超出范围的值,需要显式转换,因为如果执行转换,信息将会丢失。完成缩小转换后,变量类型范围之外的位会被简单地屏蔽掉——这就是在这些情况下“丢失信息”的含义。

就好像一个常量的赋值是这样的:

byte b = nnn & 0xFF;

如果 nnn 在范围内,则掩码不会更改值,因此不会丢失任何内容,所以没问题 - 编译正常。

如果 nnn 超出范围,则信息将丢失,因此需要显式转换以确认丢失。

如果您还记得所有整数文字都是 int,那么这些规则实际上与将 int 分配给更窄的变量类型的规则没有什么不同,除非编译器在知道该值将“适合”时不允许强制转换,

于 2013-06-25T14:49:52.713 回答