I find a phenomenon:
public class TryTest {
public static void main(String[] args) {
System.out.println(test1());
}
public static int test1() {
try {
int a = 127;
return a;
} catch (Exception e) {
}finally {
System.out.println("I am finally");
}
return 0;
}
}
compiled to .class:
public class TryTest {
public TryTest() {
}
public static void main(String[] args) {
System.out.println(test1());
}
public static int test1() {
try {
int a = 127;
byte var1 = a;
return var1;
} catch (Exception var5) {
} finally {
System.out.println("I am finally");
}
return 0;
}
}
why "int a" convert to "byte var1"?
Is the purpose to save memory?
Isn't this unnecessary?
I want to know how the compiler handles this.
but I find if code like this:
public static int test3() {
int a = 1;
return a;
}
compiled to .class:
public static int test3() {
int a = 1;
return a;
}
If there is no "try", it will not be compiled into "byte"
If you want to look at what something is compiled into in Java, you shouldn't look at the decompiled code. Converting a .class
file back into a .java
file involves a lot of interpretation (one could even say guessing) and should not be taken as an indication what the actual compilation looks like. Look at the javap -v
output instead, that will show you the actual bytecode. The bytecode of your method looks like this (I removed some unnecessary detail, run it yourself to check it all):
public static int test1();
descriptor: ()I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=0
0: bipush 127
2: istore_0
3: iload_0
4: istore_1
5: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
8: ldc #5 // String I am finally
10: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: iload_1
14: ireturn
15: astore_0
16: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
19: ldc #5 // String I am finally
21: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
24: goto 38
27: astore_2
28: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
31: ldc #5 // String I am finally
33: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
36: aload_2
37: athrow
38: iconst_0
39: ireturn
Exception table:
from to target type
0 5 15 Class java/lang/Exception
0 5 27 any
Nothing in there indicates that anything is stored in a byte
(iload_*
and istore_*
load and store integer values).
The only byte-specific instruction is bipush
which pushes a byte value, but it will be extended to an int
value on the stack. This simply saves a couple of bytes over sipush
(push a short
constant) or ldc
(which would require the value to be stored in the constant pool).
The istore_1
is used to remember the value to be returned while the finally block is executed (8-10). Then we use iload_1
to load it back out and return it.
The decompiler probably thinks that this is an actual variable, when it's just a synthetic constructed caused by the try-catch-finally construct.
Also, you might notice that the bytecode looks horribly inefficient, but that' simply because the javac
compiler does a very straight-forward translation into bytecode and really doesn't do any optimizations. Any actual optimizations (such as never actually storing any values in any variables but simply returning the constant value 127
) will be done by the JVM at runtime.