1. 下面是一到Java笔试题:

 1 public class Test2
 2 {
 3     public void add(Byte b)
 4     {
 5         b = b++;
 6     }
 7     public void test()
 8     {
 9         Byte a = 127;
10         Byte b = 127;
11         add(++a);
12         System.out.print(a + " ");
13         add(b);
14         System.out.print(b + "");
15     }
16 }

2. 为方便分析起见,将打印的语句去掉,如下:

 1     public void add(Byte b)
 2     {
 3         b = b++;
 4     }
 5     public void test()
 6     {
 7         Byte a = 127;
 8         Byte b = 127;
 9         add(++a);
10         add(b);
11     }

3. 将上述代码反编译,得到如下字节码:

 1 public void add(java.lang.Byte);
 2     Code:
 3        0: aload_1
 4        1: astore_2
 5        2: aload_1
 6        3: invokevirtual #2                  // Method java/lang/Byte.byteValue:(
 7 )B
 8        6: iconst_1
 9        7: iadd
10        8: i2b
11        9: invokestatic  #3                  // Method java/lang/Byte.valueOf:(B)
12 Ljava/lang/Byte;
13       12: dup
14       13: astore_1
15       14: astore_3
16       15: aload_2
17       16: astore_1
18       17: return
19
20 public void test();
21     Code:
22        0: bipush        127
23        2: invokestatic  #3                  // Method java/lang/Byte.valueOf:(B)
24 Ljava/lang/Byte;
25        5: astore_1
26        6: bipush        127
27        8: invokestatic  #3                  // Method java/lang/Byte.valueOf:(B)
28 Ljava/lang/Byte;
29       11: astore_2
30       12: aload_0
31       13: aload_1
32       14: invokevirtual #2                  // Method java/lang/Byte.byteValue:(
33 )B
34       17: iconst_1
35       18: iadd
36       19: i2b
37       20: invokestatic  #3                  // Method java/lang/Byte.valueOf:(B)
38 Ljava/lang/Byte;
39       23: dup
40       24: astore_1
41       25: invokevirtual #4                  // Method add:(Ljava/lang/Byte;)V
42       28: aload_0
43       29: aload_2
44       30: invokevirtual #4                  // Method add:(Ljava/lang/Byte;)V
45       33: return
46 }

4. 字节码很长,看着发怵,不用怕,我们将字节码分成两部分:add方法和test方法。

5. 我们先来看add方法:

 1 add方法局部变量表
 2 下标:  0         1                2                    3
 3 标记: this   形参Byte b   Byte型临时变量tmp     Byte型临时变量tmp2
 4 值  :          -128             -128                  -127
 5 public void add(java.lang.Byte);
 6     Code:
 7        0: aload_1          // 局部变量表中下标为1的引用型局部变量b进栈
 8        1: astore_2         // 将栈顶数值赋值给局部变量表中下标为2的引用型局部变量tmp,栈顶数值出栈。
 9        2: aload_1           // 局部变量表中下标为1的引用型局部变量b进栈
10        3: invokevirtual #2 // 自动拆箱,访问栈顶元素b,调用实例方法b.byteValue获取b所指Byte
11                            // 对象的value值-128,并压栈
12        6: iconst_1           // int型常量值1进栈
13        7: iadd               // 依次弹出栈顶两int型数值1(0000 0001)、-128(1000 0000)
14                            //(byte类型自动转型为int类型)相加,并将结果-127(1000 0001)进栈
15        8: i2b               // 栈顶int值-127(1000 0001)出栈,强转成byte值-127(1000 0001),并且结果进栈
16        9: invokestatic  #3 // 自动装箱:访问栈顶元素,作为函数实参传入静态方法Byte.valueOf(byte),
17                            // 返回value值为-127的Byte对象的地址,并压栈
18       12: dup               // 复制栈顶数值,并且复制值进栈
19       13: astore_1           // 将栈顶数值赋值给局部变量表中下标为1的引用型局部变量b,栈顶数值出栈。此时b为-127
20       14: astore_3           // 将栈顶数值赋值给局部变量表中下标为3的引用型局部变量tmp2,栈顶数值出栈。此时tmp2为-127
21       15: aload_2           // 局部变量表中下标为2的引用型局部变量tmp进栈,即-128入栈
22       16: astore_1         // 将栈顶数值赋值给局部变量表中下标为1的引用型局部变量b,栈顶数值出栈。此时b为-128
23       17: return

总结一下上述过程,核心步骤为b = b++;分为三步:参考:http://blog.csdn.net/brooksychen/article/details/1624753

①把变量b的值取出来,放在一个临时变量里(我们先记作tmp);

②把变量b的值进行自加操作;

③把临时变量tmp的值作为自增运算前b的值使用,在本题中就是给变量b赋值。

到此可得出结论,add方法只是个摆设,没有任何作用,不修改实参的值。

6. 搞懂了add方法,我们接下来分析test方法:

这里需要说明两点:

(1)由于Byte类缓存了[-128,127]之间的Byte对象,故当传入的实参byte相同时,通过Byte.valueOf(byte)返回的对象是同一个对象,详见Byte源码。

(2)如果是实例方法(非static),那么局部变量表的第0位索引的Slot默认是用于传递方法所属对象实例的引用,在方法中通过this访问。详见:http://wangwengcn.iteye.com/blog/1622195

 1 test方法局部变量表
 2 下标:  0         1                2
 3 标记: this   形参Byte a   Byte型临时变量b
 4 值  :          -128             127
 5 public void test();
 6     Code:
 7        0: bipush        127        // 将一个byte型常量值推送至操作数栈栈顶
 8        2: invokestatic  #3        // 自动装箱:访问栈顶元素,作为函数实参传入静态方法Byte.valueOf(byte),
 9                                 // 返回value值为127的Byte对象的地址,并压栈
10        5: astore_1                // 将栈顶数值赋值给局部变量表中下标为1的引用型局部变量a,栈顶数值出栈。此时a为127
11        6: bipush        127        // 将一个byte型常量值推送至操作数栈栈顶
12        8: invokestatic  #3        // 自动装箱:访问栈顶元素,作为函数实参传入静态方法Byte.valueOf(byte),
13                                 // 返回value值为127的Byte对象的地址,并压栈。这里需要说明一点,
14                                 // 由于Byte类缓存了[-128,127]之间的Byte对象,故当传入的实参byte相同时,
15                                 // 通过Byte.valueOf(byte)返回的对象是同一个对象,详见Byte源码。
16       11: astore_2                // 将栈顶数值赋值给局部变量表中下标为2的引用型局部变量b,栈顶数值出栈。此时b为127
17       12: aload_0                // 局部变量表中下标为0的引用型局部变量进栈,即this,加载this主要是为了下面通过this调用add方法。
18       13: aload_1                // 局部变量表中下标为1的引用型局部变量a进栈
19       14: invokevirtual #2      // 自动拆箱,访问栈顶元素a,调用实例方法a.byteValue获取a所指Byte
20                                 // 对象的value值127,并压栈
21       17: iconst_1                // int型常量值1进栈
22       18: iadd                    // 依次弹出栈顶两int型数值1(0000 0001)、127(0111 1111)
23                                 //(byte类型自动转型为int类型)相加,并将结果128(1000 0000)进栈
24       19: i2b                    // 栈顶int值128(1000 0000)出栈,强转成byte值-128(1000 0000),并且结果进栈
25       20: invokestatic  #3      // 自动装箱:访问栈顶元素,作为函数实参传入静态方法Byte.valueOf(byte),
26                                 // 返回value值为-128的Byte对象的地址,并压栈
27       23: dup                    // 复制栈顶数值,并且复制值进栈
28       24: astore_1                // 将栈顶数值赋值给局部变量表中下标为1的引用型局部变量a,栈顶数值出栈。此时a为-128
29       25: invokevirtual #4      // 调用实例方法add:(Byte),传入的实参为栈顶元素,也即a的拷贝,前面已经分析过了,该调用不改变a的对象值
30                                 // 该实例方法的调用需要访问栈中的两个参数,一个是实参,也即a的拷贝,一个是在第12步入栈的this。
31       28: aload_0                // 局部变量表中下标为0的引用型局部变量进栈,即this,加载this主要是为了下面通过this调用add方法。
32       29: aload_2                // 局部变量表中下标为2的引用型局部变量b进栈
33       30: invokevirtual #4      // 调用实例方法add:(Byte),传入的实参为栈顶元素,也即b,前面已经分析过了,该调用不改变b的对象值
34                                 // 该实例方法的调用需要访问栈中的两个参数,一个是实参,也即b,一个是在第28步入栈的this。
35       33: return                // 函数执行到最后,b所指对象的值没有改变,仍为127。
36 }

7. 综合以上分析,原问题的输出为-128 127

8. 小结:

通过以上分析,我们发现该题综合考察了Byte自动拆/装箱、Byte对象缓存、Java编译器对i=i++的特殊处理等等,相当有难度呀。

转载来自:http://www.cnblogs.com/nailperry/p/4780354.html

从字节码角度分析Byte类型变量b++和++b相关推荐

  1. Java字节码角度分析多态原理 ——提升硬实力8

    在前面的文章中,有详细地介绍java字节码相关的知识,有兴趣的可以提前了解一下. 1.Java字节码的一段旅行经历--提升硬实力1 2.Java字节码角度分析a++ --提升硬实力2 3.Java字节 ...

  2. Java字节码角度分析:Synchronized ——提升硬实力11

    在前面的文章中,有详细地介绍java字节码相关的知识,有兴趣的可以提前了解一下. 1.Java字节码的一段旅行经历--提升硬实力1 2.Java字节码角度分析a++ --提升硬实力2 3.Java字节 ...

  3. Java字节码角度分析方法调用 ——提升硬实力7

    在前面的文章中,有详细地介绍java字节码相关的知识,有兴趣的可以提前了解一下. 1.Java字节码的一段旅行经历--提升硬实力1 2.Java字节码角度分析a++ --提升硬实力2 3.Java字节 ...

  4. java异常 字节码,Java字节码角度分析异常处理

    目录 从字节码角度来分析:异常处理 1.1 异常-catch // 从字节码角度来分析:异常处理 public class T13_ByteAnalyseException { public stat ...

  5. 【Android 插件化】Hook 插件化框架 ( 从源码角度分析加载资源流程 | Hook 点选择 | 资源冲突解决方案 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  6. 深入浅出Java复用类【从字节码角度看toString调用机制、对象代理、组合与继承、转型、final、初始化】

    这个世界上有10种人:一种是懂二进制的,一种是不懂二进制的 你觉得类是在什么时候被加载的?[访问static域时,为什么?看完9就明白了] 文章目录 1.深入理解Java中toString方法的调用机 ...

  7. Mybatis底层原理学习(二):从源码角度分析一次查询操作过程

    在阅读这篇文章之前,建议先阅读一下我之前写的两篇文章,对理解这篇文章很有帮助,特别是Mybatis新手: 写给mybatis小白的入门指南 mybatis底层原理学习(一):SqlSessionFac ...

  8. 【Java 虚拟机原理】Class 字节码二进制文件分析 七 ( 局部变量表分析 )

    文章目录 前言 一.编译生成带局部变量表的字节码文件 二.局部变量表 前言 上一篇博客 [Java 虚拟机原理]Class 字节码二进制文件分析 二 ( 常量池位置 | 常量池结构 | tag | i ...

  9. 【Java 虚拟机原理】Class 字节码二进制文件分析 六 ( 属性类型 | Code 属性 | 属性名称索引 | 属性长度 | 操作数栈最大深度 | 局部变量存储空间 | 字节码长度 )

    文章目录 前言 一.属性类型 二.Code 属性表数据结构 三.属性名称索引 四.属性长度 五.操作数栈最大深度 六.局部变量存储空间 七.字节码长度 八.存储字节码指令的一系列字节流 前言 上一篇博 ...

最新文章

  1. 联想拯救者Y9000-ubuntu-nvidia-驱动安装
  2. 云端卫士DDoS防护解决方案助力互联网金融安全
  3. 碗都交出去了,能不能分到羹?
  4. .Net应该学什么怎么学(三)
  5. linux 启动/关闭多个py脚本
  6. Depth-first Search深度优先搜索专题7
  7. Android 系统(11)---android 系统权限大全
  8. 用贪心算法来解决沙袋装箱问题
  9. 移动端日期控件 mobiscroll
  10. 【教程】PDF控件Spire.PDF 教程:在C#中加密和解密PDF文件
  11. js实现canvas在线画板
  12. 饥荒联机版连不上服务器_《饥荒》无法连接klei服务器 刷不出服务器解决办法...
  13. R语言辅导高维数据的主成分pca、 t-SNE算法降维与可视化分析案例报告
  14. 【游戏策划】之神仙道的计算公式
  15. 英语口语练习:点冰淇淋
  16. SAS学习(5)——用于文章投稿的图片导出设置
  17. 7月30日云栖精选夜读 | 王坚博士:进入空气稀薄地带
  18. 植物大战僵尸关卡金币存档修改过程
  19. 取消Eclipse的Error Reporting
  20. 电子计算机X光机,X线机

热门文章

  1. 均质机工作原理动画_玉林均质机原理组图
  2. Android基于wheelView的自定义日期选择器(可拓展样式)
  3. 登录页面记住密码如何实现?
  4. 淘宝铺货上传商品API文档
  5. Mybatis中的常用标签
  6. 全球风投在一周内向非洲金融科技注资近4亿美元
  7. webgl添加环境光示例
  8. Ubuntu16.04下TensorFlow-GPU安装记录(GTX1060显卡)
  9. 服务接口调用-OpenFeign
  10. MySQL快速复制数据库的方法