我一直在努力弄清楚Java优化的所有内容,并发现了一些有趣的东西。
第一种情况:原始类型编译时优化
public class Clazz {
public static void main(String args[]) {
final int i = 300;
new Clazz() {
void foo() {
System.out.println(i);
}
}.foo();
}
}
编译后(我使用jd-gui-0.3.5.windows
反编译二进制文件)看起来像:
public class Clazz {
public static void main(String[] args) {
int i = 300;
new Clazz() {
void foo() {
System.out.println(300);
}
}.foo();
}
}
正如所料,不是吗?编译后,i
被替换为它的值(内联优化)。因此,我希望在用它的包装器替换原始类型之后看到类似的东西,但是......
第二种情况:非原始类型编译时优化
public class Clazz {
public static void main(String args[]) {
final Integer i = 300; // replaced int with Integer
new Clazz() {
void foo() {
System.out.println(i);
}
}.foo();
}
}
编译后:
public class Clazz {
public static void main(String[] args) {
Integer i = Integer.valueOf(300);
new Clazz() {
void foo() {
System.out.println(Clazz.this);
}
}.foo();
}
}
在这种情况下,Clazz.this
是什么?我知道,它是封装Clazz
实例的引用,但在这种情况下不应该有效!我需要打印i
,但编译器建议我打印Clazz.this
而不是它,它可以工作!问题是什么? jd-gui
是否反编译错误,或者我是否遗漏了有关Java编译和优化的内容?
UPD:
Class$1
的内容:
class Clazz$1 extends Clazz {
Clazz$1(Integer paramInteger) {}
void foo() {
System.out.println(this.val$i);
}
}
答案 0 :(得分:8)
jd-gui
是否反编译错误,或者我是否遗漏了有关Java编译和优化的内容?
jd-gui
正在错误地反编译代码。
在我的JVM上,匿名类的反汇编代码如下所示:
class Clazz$1 extends Clazz {
Clazz$1(java.lang.Integer);
Code:
0: aload_0
1: aload_1
2: putfield #10 // Field val$i:Ljava/lang/Integer;
5: aload_0
6: invokespecial #12 // Method Clazz."<init>":()V
9: return
void foo();
Code:
0: getstatic #20 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #10 // Field val$i:Ljava/lang/Integer;
7: invokevirtual #26 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
10: return
}
正如您所看到的,i
的副本存储在匿名类中,名为val$i
(名称是特定于实现的)。
正是这个字段,您的反编译器似乎错误地呈现为Clazz.this
。
答案 1 :(得分:4)
您可以简单地查看字节码(javap -c Clazz$1.class
)。
使用int i = 300
:
void foo();
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: sipush 300
6: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
9: return
使用Integer i = 300
:
void foo();
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #1 // Field val$i:Ljava/lang/Integer;
7: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
10: return
所以int
是内联的,但Integer
不是。
同样为了记录,这是我从jd-gui(3.0.5)得到的:
public static void main(String[] args) {
Integer i = Integer.valueOf(300);
new Clazz() {
void foo() {
System.out.println(this.val$i);
}
}
.foo();
}
答案 2 :(得分:0)
它应该是一个最接近的方法jd-gui
错过解释Integer
如何在调用System.out.println
时将其值限制为其值。反编译算法本身必须假设对这个Integer
对象最重要的引用是什么,它选择Clazz.this
的结果。