此文写于一年半以前,当时,有一位同事出了一道Java题,问下面程序的打印是什么?
public class Test {
    
        static int getValue() {
             int i = 1;        
                                        
             try {                
                     return i;
             } finally {
                     i++;
             }
        }

        static TT getTT() {
             TT t = new TT();
             try {
                     t.i = 1;            
                     return t;
             } finally {
                     t.i++;
             }
        }

        public static void main(String[] args) {
             System.out.println(getValue());
             System.out.println(getTT().i);
        }
}

class TT {
        int i;
}

打印结果是:
1
2

我当时所做的项目刚好与Java的字节码研究有关,就查看了字节码,对比了一下,并给出了字节码的解读:
public class Test {

        //static int getValue();
        //    Code:
        //     0:        iconst_1                    //1入操作栈
        //     1:        istore_0                    //1存入索引为0的本地变量栈
        //     2:        iload_0                            //1重新入栈
        //     3:        istore_2                    //通过这句,i的值已经被备份到本地变量区索引为2的slot
        //     4:        iinc     0, 1             //修改本地变量区索引为0的值做加1操作
        //     7:        iload_2                            //重新将i的值载入操作栈
        //     8:        ireturn                            //然后返回栈顶的值,所以返回为1。
        //     9:        astore_1                    //此后的解析,参见另外一个函数
        //     10:     iinc     0, 1
        //     13:     aload_1
        //     14:     athrow
        //    Exception table:
        //     from     to    target type
        //         2         4         9     any
                
        static int getValue() {
             int i = 1;        
                                        
             try {                
                     return i;
             } finally {
                     i++;
             }
        }

        static TT getTT() {
             TT t = new TT();    // 0 new #23 <TT>
                                                 // 3 dup
                                                 // 4 invokespecial #25 <TT.<init>>
                                                 // 7 astore_0
             try {
                     t.i = 1;            // 8 aload_0
                                                 // 9 iconst_1
                                                 //10 putfield #26 <TT.i>
                                                
                                                 //13 aload_0            // 将t放入操作栈
                                                 //14 astore_2         // 将t存到本地变量区索引为2的位置
                                                
                                                 //15 aload_0            // 将t再次放入操作栈
                                                 //16 dup                    // 复制一份t,放入操作栈
                                                 //17 getfield #26 <TT.i>        // 取出t.i的值,并放入操作栈
                                                 //20 iconst_1         // 将常量1入操作栈
                                                 //21 iadd                 // 将t.i与1相加,结果放入操作栈(原来栈顶的2个操作数出栈
                                                 //22 putfield #26 <TT.i> //将栈顶的操作结果赋值回t.i,相当于是t.i = t.i + 1
                                                
                     return t;         //25 aload_2            // 将对象引用t放回操作栈
                                                 //26 areturn            // 将对象引用t返回
             } finally {                    // 如果抛出异常(实际上,t.i = 1是不可能抛出异常的)    
                     t.i++;                // astore_1             // 将异常对象(如果上面的代码抛出来的话)存放到本地变量区索引为1的位置
                                                 // aload_0        // 将t放入操作栈
                                                 // dup                // 复制一份t,放入操作栈
                                                 // getfield #26 <TT.i>     // 取出t.i的值,并放入操作栈
                                                 // iconst_1             // 将常量1入操作栈
                                                 // iadd                     // 将t.i与1相加,结果放入操作栈(原来栈顶的2个操作数出栈
                                                 // putfield #26 <TT.i>     //将栈顶的操作结果赋值回t.i,相当于是t.i = t.i + 1
                                                 // aload_1                // 将之前存放在本地变量区索引为1处的 对象引用放入操作栈
                                                 // athrow                 // 将异常对象抛出
                                                 // 补充说明:如果出现异常后,虚拟机找到了handler,会把那个出栈的异常引用重新入栈。
             }
        }

        public static void main(String[] args) {
             System.out.println(getValue());
             System.out.println(getTT().i);
        }
}

class TT {
        int i;
}

看似相似的Java代码,经过字节码编译,背后依然有大不同。:)