我的最终变量有问题。任何帮助将不胜感激。
这是我的第一个正常运行的代码
final int i = 90;
byte b = i ;
System.out.println(i);
以下是我的第二段代码,其中说明 可能会导致精确度 。这有什么不对?
final int i;
i = 90;
byte b = i ;
System.out.println(i);
答案 0 :(得分:25)
我在JLS中找不到确切的原因,所以我仔细检查了字节代码,发现的原因是编译器无法内联 {的值{2}} 在第二种情况下,但能够在第一种情况下执行此操作。
以下是代码:
i
编译后的字节代码如下:
final int x = 90;
System.out.println(x);
final int i;
i = 90;
System.out.println(i);
所以在第一种情况下(3到5),它直接使用值 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: bipush 90
5: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
8: bipush 90
10: istore_2
11: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
14: iload_2
15: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
18: return
进行打印,而在第二种情况下(8到15),它必须将值存储到变量中然后将其加载回堆栈。然后90
方法将选择堆栈的最高值。
所以,如果是作业:
print
byte x = i;
的值将在运行时从堆栈中获取,而不是由编译器内联。 因此编译器不知道i
可能包含的值。
当然,这是我的全部猜测。不同JVM上的字节代码可能不同。但我有强烈的直觉,这可能是原因。
此外,JLS §4.12.4可能与此相关:
基本类型或类型String的变量,是最终的并使用编译时常量表达式(第15.28节)初始化,称为常量变量。
由于在第二种情况下,变量不是由常量表达式初始化,而是稍后赋值,因此它不再是常量变量。
答案 1 :(得分:4)
简单地说,编译器“知道”90可以适应字节变量的第一种方式,但在第二种情况下,它无法检测到“i”的值,也不知道它可能包含什么值,因此不会确定它可以适合字节变量。
答案 2 :(得分:2)
在编译时,编译器可以确定最终变量的值(如果在创建时分配它),但在第二个代码中,您将在创建后分配值,而编译器将无法捕获该值。
如果你在第一个代码中使用超过127个(对于变量i)它也不会起作用。
Spec说“如果原始类型或字符串被定义为常量并且值在编译时已知,则编译器会将代码中的常量名称替换为其值。这称为编译时常量“
并且
编译时间常数为:
宣布最终
primitive或String
在声明中初始化
用常量表达式初始化