是什么导致了这种“可能失去精度”的错误?

2022-09-01 20:56:35

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

这是我的第一个工作正常的代码

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

这是我的第二个代码,它说可能失去精度。这是怎么回事?

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

答案 1

我在JLS中找不到确切的原因,所以我浏览了字节码,发现原因是编译器在第二种情况下无法内联i的值,但能够在第一种情况下执行此操作。

代码如下:

final int x = 90;
System.out.println(x);

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

编译后的字节代码如下所示:

 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

因此,在第一种情况(3到5)中,它直接使用值进行打印,而在第二种情况(8到15)中,它必须将值存储到变量中,然后将其加载回堆栈。然后,打印方法将选取堆栈的最高值。90

因此,在分配的情况下:

byte x = i;

的值将在运行时从堆栈中获取,而不是由编译器内联。所以编译器不知道可能包含什么值。i

当然,这都是我的猜测。不同 JVM 上的字节码可能不同。但我有强烈的直觉,这可能是原因。

此外,JLS §4.12.4 可能与以下部分相关:

基元类型或 String 类型的变量称为常量变量,它是 final 并使用编译时常量表达式 (§15.28) 初始化。

由于在第二种情况下,变量不是由常量表达式初始化的,而是稍后分配值,因此它不再是常量变量。


答案 2

简单地说,编译器“知道”90可以容纳字节变量的第一种方式,但在第二种方式中,它无法检测到“i”的值,并且不知道它可能包含什么值,因此不确定它是否适合字节变量。


推荐