日期:2014-05-18  浏览次数:20615 次

我们爱分享第二季 --- Javap 的使用
现在Java反编译工具或者插件非常多,也非常好用。比如我用的比较多得jd-gui。所以我们可能很少有机会使用到Javap命令。
这里我简单介绍下Javap,带各位入个门。介绍下这个命令不是使用它进行反编译,而是查看java编译器为我们生成 的字节码,通过比较字节码和源代码,我们可以发现很多的问题,一个很重要的作用就是了解很多编译器内部的工作机制。
下面我们通过具体的一个例子来简单的讲讲这个工具的作用,你不需要很深入的使用,这个简单的介绍和简单的使用就可以使你受益非浅。
首先我们先写一个非常简单的Java源文件:


class StringTest {  
    public static void main(String[] args) {  
        int i = 2;  
        int j = 3;  
    }  
}  

使用Javac 命令进行编译:

javac -g StringTest.java  

最终有两个文件:


user ~/csdn_test/session1$ls -l  
total 16  
-rw-r--r--  1 user  staff  417  4  7 21:54 StringTest.class  
-rw-r--r--  1 user  staff   99  4  7 21:53 StringTest.java  


以上步骤涉及到JAVA环境变量配置,不会的自己google,或者发帖询问。这里使用了-g命令,意思是生成所有调试信息。(javac -help可以查看)
下面我们分2步来看看javap的显示结果:
1.不带任何参数使用javap StringTest

这里我们可以看到的信息有,从哪个文件(StringTest.java)编译过来的。类里面调用两个方法,一个是默认的构造函数,一个是main方法。在没有显式声明构造方法时,会产生一个默认构造方法。这也验证了,为什么一个对象,默认总是先执行构造函数,特别是在继承的场合下,使用这个命令就可以看的很清楚了。
2.使用-c参数javap -c StringTest

带-p参数将额外的打印字节码信息。前面的和不带参数的输出一样,后面的显示了方法的具体的字节码,从这个输出里面我们又可以了解更多的内容。
首先是编译器生成的的缺省构造方法的内容为调用父类的构造方法super(), main()方法的字节码信息的内容这里简单解释下:

Code:  
   0:   iconst_2    //把2放到栈顶  
   1:   istore_1    //把栈顶的值放到局部变量1中,即i中  
   2:   iconst_3    //把3放到栈顶  
   3:   istore_2    //把栈顶的值放到局部变量1中,即j中  
   4:   return  
所以再根据开始的java源码对照,对于 int i = 2;首先它会在栈中创建一个变量为i的引用,然后查找有没有字面值为2的地址,没找到,就开辟一个存放2这个字面值的地址,然后将i指向2的地址。j也是类似的。
 延伸学习:
1、如果将main中的变量换成String 类型的,然后看看javap的结果。
比如:
String a = "a";
String b = "b";
String ab = "ab";
String ab1 = a + b;
String ab2 = "a" + "b"

2、使用更多的参数分析其结果,如javap -l xxx javap -p xxx(提示 -p会显示private相关的)


这几个参数几乎就可以构成javap的最常使用的集合,最常用的应该还是-c选项,因为可以打印字节码的信息,关于这些字节码的详细涵义在Java 虚拟机规范中定义,感兴趣的可以自己去找找相关资料。
------解决方案--------------------
现在或许都去扫墓去了吧!

------解决方案--------------------

Code:  
   0:   iconst_2    //把2放到栈顶  
   1:   istore_1    //把栈顶的值放到局部变量1中,即i中  
   2:   iconst_3    //把3放到栈顶  
   3:   istore_2    //把栈顶的值放到局部变量1中,即j中  
   4:   return 

这些是汇编吗?

------解决方案--------------------