Search code examples
javainttry-catchbyte

Why is the int in try compiled into byte


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"


Solution

  • 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.