I'd been trying to figure out all about Java optimizations and found something interesting.
First case: primitive type compile-time optimization
public class Clazz {
public static void main(String args[]) {
final int i = 300;
new Clazz() {
void foo() {
System.out.println(i);
}
}.foo();
}
}
After compilation (I'm using jd-gui-0.3.5.windows
to decompile binary files) it is looks like:
public class Clazz {
public static void main(String[] args) {
int i = 300;
new Clazz() {
void foo() {
System.out.println(300);
}
}.foo();
}
}
As expected, isn't it? i
was replaced with it's value (inlining optimization) after compilation. So, I expected to see something similar after replacing primitive type with it's wrapper, but...
Second case: non-primitive type compile-time optimization
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();
}
}
After compilation:
public class Clazz {
public static void main(String[] args) {
Integer i = Integer.valueOf(300);
new Clazz() {
void foo() {
System.out.println(Clazz.this);
}
}.foo();
}
}
What is Clazz.this
in this context? I know, that it is reference to enclosing instance of Clazz
, but it should not work in that case! I need to print i
, but compiler suggests me to print Clazz.this
instead of it and it works! What is the problem? Does jd-gui
decompiles incorrectly or do I missing something about Java compilation and optimization?
UPD:
Content of Class$1
:
class Clazz$1 extends Clazz {
Clazz$1(Integer paramInteger) {}
void foo() {
System.out.println(this.val$i);
}
}
The final keyword has a different purpose when applied to classes and methods. When we apply the final keyword to a class, then that class cannot be subclassed. When we apply it to a method, then that method cannot be overridden. There are no reported performance benefits of applying final to classes and methods.
final means that you can't change the object's reference to point to another reference or another object, but you can still mutate its state (using setter methods e.g). Whereas immutable means that the object's actual value can't be changed, but you can change its reference to another one.
The final modifier for finalizing the implementations of classes, methods, and variables. We can declare a method as final, once you declare a method final it cannot be overridden. So, you cannot modify a final method from a sub class.
If you prefix a class definition with final , you prevent anyone from subclassing that class. Value types don't support inheritance so it doesn't make sense to mark a struct or enum as final . The final keyword communicates to the compiler that the class cannot and should not be subclassed.
Does
jd-gui
decompiles incorrectly or do I missing something about Java compilation and optimization?
jd-gui
is decompiling the code incorrectly.
On my JVM, the disassembled code for the anonymous class looks as follows:
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
}
As you can see, a copy of i
stored in the anonymous class, in the field called val$i
(the name is implementation-specific).
It is this field that your decompiler appears to incorrectly render as Clazz.this
.
You could simply have a look at the bytecode (javap -c Clazz$1.class
).
With 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
With 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
So the int
is inlined but the Integer
is not.
Also for the record, this is what I get from 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();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With