导致这种“可能的精度损失”错误的原因是什么?

时间:2014-04-17 11:04:53

标签: java final

我的最终变量有问题。任何帮助将不胜感激。

这是我的第一个正常运行的代码

final int i = 90; 
byte b = i ; 
System.out.println(i);

以下是我的第二段代码,其中说明 可能会导致精确度 。这有什么不对?

final int i; 
i = 90;
byte b = i ; 
System.out.println(i);

3 个答案:

答案 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

在声明中初始化

用常量表达式初始化