我们都知道 「.java」 文件要经过编译变为 「.class」 文件才能执行,那么问题来了,「.class」 文件里面究竟有些啥?
我做了一个小小的实验。
![7cac7b383f4f03bd9f4cec9ab1063c7a.png](https://i-blog.csdnimg.cn/blog_migrate/1996329076da753059ee4f2ab34b3c6a.png)
![5bd87e4f07bdc586e1b60f5475239192.png](https://i-blog.csdnimg.cn/blog_migrate/aca46c377abc62c51e4446b0c91e3f86.png)
1. 随便写些简单语句
![de96fb99e58b2d3128708477654cdfc7.png](https://i-blog.csdnimg.cn/blog_migrate/9f4d33cbe1f7713a2324bb64d867778d.png)
![672050b3b813df9bd4644b569ba5f770.png](https://i-blog.csdnimg.cn/blog_migrate/38477e2bce18aa5bab2f778d9b20e3c5.png)
public class Hello { public void hello() { int num1 = 1; int num2 = 2; int num3 = 64; int num4 = num1+num2; for (int i = 1; i <= 3; i++) { if(num4 num3/=2; num4*=2; } } }}
![7cac7b383f4f03bd9f4cec9ab1063c7a.png](https://i-blog.csdnimg.cn/blog_migrate/1996329076da753059ee4f2ab34b3c6a.png)
![5bd87e4f07bdc586e1b60f5475239192.png](https://i-blog.csdnimg.cn/blog_migrate/aca46c377abc62c51e4446b0c91e3f86.png)
2. 编译,查看字节码
![de96fb99e58b2d3128708477654cdfc7.png](https://i-blog.csdnimg.cn/blog_migrate/9f4d33cbe1f7713a2324bb64d867778d.png)
![672050b3b813df9bd4644b569ba5f770.png](https://i-blog.csdnimg.cn/blog_migrate/38477e2bce18aa5bab2f778d9b20e3c5.png)
javac Hello.javajavap -c Hello.class
![7cac7b383f4f03bd9f4cec9ab1063c7a.png](https://i-blog.csdnimg.cn/blog_migrate/1996329076da753059ee4f2ab34b3c6a.png)
![5bd87e4f07bdc586e1b60f5475239192.png](https://i-blog.csdnimg.cn/blog_migrate/aca46c377abc62c51e4446b0c91e3f86.png)
3. 分析
![de96fb99e58b2d3128708477654cdfc7.png](https://i-blog.csdnimg.cn/blog_migrate/9f4d33cbe1f7713a2324bb64d867778d.png)
![672050b3b813df9bd4644b569ba5f770.png](https://i-blog.csdnimg.cn/blog_migrate/38477e2bce18aa5bab2f778d9b20e3c5.png)
实际上,基本类型的运算要广泛使用到「栈」这个数据结构,下面我一条一条来分析,千言万语尽在注释中:
0: iconst_1 //将值为int类型的常数1入栈 1: istore_1 //栈顶出栈,存在本地变量索引1处,即 num1=1 2: iconst_2 //将值为int类型的常数2入栈 3: istore_2 //栈顶出栈,存在本地变量索引2处,即 num2=2 4: bipush 64 //将值为int类型的64入栈 6: istore_3 //栈顶出栈,存在本地变量索引3处,即 num3=64 7: iload_1 //将本地变量索引1处的值入栈 8: iload_2 //将本地变量索引2处的值入栈 9: iadd //实现了一个形如“AB+”的后缀表达式计算,将结果入栈,即1+2=310: istore 4 //栈顶出栈,存在本地变量索引4处,即 num4=312: iconst_1 //将值为int类型的常数1入栈13: istore 5 //栈顶出栈,存在本地变量索引5处,即 i=115: iload 5 //循环开始17: iconst_3 //将值为int类型的常数3入栈,也就是循环终止条件18: if_icmpgt 43 //比较栈顶两个int类型数值的大小,大于的话(即i>3)跳到43行执行,21: iload 4 //将本地变量索引4处的值入栈23: iload_3 //将本地变量索引3处的值入栈24: if_icmpge 37 //比较栈顶两个int类型数值的大小,大于等于的话(即num4>=num3)跳到37行执行27: iload_3 //将本地变量索引3处的值入栈28: iconst_2 //将值为int类型的常数2入栈29: idiv //实现了一个形如“AB/”的后缀表达式计算,将结果入栈,即64/2=32(以第一次循环为例)30: istore_3 //栈顶出栈,存在本地变量索引3处,即 num3=3231: iload 4 //将本地变量索引4处的值入栈33: iconst_2 //将值为int类型的常数2入栈34: imul //实现了一个形如“AB*”的后缀表达式计算,将结果入栈,即3*2=6(以第一次循环为例)35: istore 4 //栈顶出栈,存在本地变量索引3处,即 num4=637: iinc 5, 1 //将本地变量索引5的值加上1,即 ++i40: goto 15 //回到15行重新循环43: return //循环结束
根据这段字节码的分析,总结如下:
当 int 类型取值 0~5 时,用 iconst_x 表示,特别地,当取 -1 时,用 iconst_m1 表示;当 int 类型取到 -128~127 其他的数时,用 bipush x 表示。
对于istore_x,当变量数量不大于3时只占用一位内存,后面的变量要占两位。
在不等式判断时,遇到i<=3,字节码实际上是按照 i>3 跳出 for 循环的思路执行;遇到 num4字节码实际上是按照 num4>=num3 跳出 if 判断的思路执行。
for循环中的 i++ 和 ++i 其实转换成字节码后没有任何区别。