`

Javap

    博客分类:
  • Java
阅读更多

javap是jdk自带的一个工具,可以反编译,也可以查看java编译器生成的字节码,是分析代码的一个好工具。

下面我们通过具体的一个例子来简单的讲讲这个工具的作用,你不需要很深入的使用,这个简单的介绍和简单的使用就可以使你受益非浅。
  源代码:
  class StringTest
  {
  public static void main(String[] args)
  {
  String result="";
  result+="ok";
  }
  }
  在反编译前你当然需要先编译这个类了:javac -g StringTest.java(使用-g参数是因为要得到下面javap -l时的输出需要使用此选项)
  编译完成后,我们在使用不同的选项看看不同的效果:
  
  1、先看看最简单的不带参数的情况:javap StringTest:
  
  Compiled from StringTest.java
  class StringTest extends java.lang.Object {
  StringTest();
  public static void main(java.lang.String[]);
  }
  不带参数的情况将答应类的public信息,包括成员和方法
  从上面的输出中我们确定了两个知识:假如类没有显示的从其它类派生那么它就是从Object派生;假如没有为类显示的申明构造方法,那么编译器将为之生成一个缺省构造方法(不带参数的构造方法)
  
  2、javap -c StringTest:
  
  Compiled from StringTest.java
  class StringTest extends java.lang.Object {
  StringTest();
  public static void main(java.lang.String[]);
  }
  Method StringTest()
  0 aload_0
  1 invokespecial #1 <Method java.lang.Object()>
  4 return
  
  Method void main(java.lang.String[])
  0 ldc #2 <String "">
  2 astore_1
  3 new #3 <Class java.lang.StringBuffer>
  6 dup
  7 invokespecial #4 <Method java.lang.StringBuffer()>
  10 aload_1
  11 invokevirtual #5 <Method java.lang.StringBuffer append(java.lang.String)>
  14 ldc #6 <String "ok">
  16 invokevirtual #5 <Method java.lang.StringBuffer append(java.lang.String)>
  19 invokevirtual #7 <Method java.lang.String toString()>
  22 astore_1
  23 return
  带-p参数将额外的打印字节码信息
  前面的和不带参数的输出一样,后面的显示了方法的具体的字节码,从这个输出里面我们又可以了解更多的内容,首先是编译器生成的的缺省构造方法的内容为调用父类的构造方法super()(需要说明的是使用DJ反编译的源代码中这个缺省构造方法没有这个调用,这可能是该反编译器的一种优化),main()方法的字节码信息的内容可以参考《使用String还是StringBuffer》一文的叙述。
  
  3、javap -l StringTest :
  
  Compiled from StringTest.java
  class StringTest extends java.lang.Object {
  StringTest();
  public static void main(java.lang.String[]);
  }
  
  Line numbers for method StringTest()
  line 1: 0
  
  Local variables for method StringTest()
  StringTest this pc=0, length=5, slot=0
  
  Line numbers for method void main(java.lang.String[])
  line 5: 0
  line 6: 3
  line 7: 23
  
  Local variables for method void main(java.lang.String[])
  java.lang.String[] args pc=0, length=24, slot=0
  java.lang.String result pc=3, length=20, slot=1
  -l参数将显示行号和局部变量表
  从上面的输出中我们可以得到方法中的变量和方法的源代码对于字节码信息的,例如对应main()方法,它的变量为输入参数args以及局部变量result,方法的源代码的第5行对应字节码的第0个偏移量,第5行对应字节码的第3个偏移量,而第7行对应字节码的第23偏移量(参看javap -c的输出前面的偏移量),第7行
实际是没有语句的,但是有一个隐含的return,而偏移量23实际对应的也是return调用
  
  4、javap -p StringTest:
  
  Compiled from StringTest.java
  class StringTest extends java.lang.Object {
  StringTest();
  public static void main(java.lang.String[]);
  }
  -p参数将额外的打印private成员和方法的信息,因为这个类没有因此输出相同
  
  这几个参数几乎就可以构成javap的最常使用的集合,最常用的应该还是-c选项,因为可以打印字节码的信息,关于这些字节码的具体涵义在Java 虚拟机规范中定义.

 

 

 

String Or Stringbuffer

Java 开发人员熟悉在一个循环中使用StringBuffer 来代替串联 String 对象能获得最佳性能。然而,多数开发人员从来没有比较两种方法产生的字节代码的区别。在 Java 开发工具包(JDK)中有一个叫做 javap 的工具可以告诉你为什么这样做可以获得最佳性能。

  Javap 将一个类和它的方法的一些转储信息输出到标准输出。该工具不把代码反编译为 java 源代码,但是它会把字节代码反汇编成为由 Java 虚拟机规范定义的字节代码指令。

  在你需要查看编译器为你或者给你做了什么的时候,或者你想要看一处代码的改动对编译后的类文件有什么影响的时候,javap 相当有用。

  现在以我们前面提到的 StringBuffer 和 String 作为一个例子。下面是一个专门为例子设计的类,它有两个方法,都返回一个由0到 n 的数字组成的 String,其中 n 由调用者提供。两个方法唯一的区别在于一个使用 String 构建结果,另外一个使用 StringBuffer 构建结果。

public class JavapTip {
     public static void main(String []args) {
     }

     private static String withStrings(int count) {
         String s = "";
         for (int i = 0; i < count; i++) {
             s += i;
         }

         return s;
     }

     private static String withStringBuffer(int count) {
         StringBuffer sb = new StringBuffer();
         for (int i = 0; i < count; i++) {
             sb.append(i);
         }

         return sb.toString();
     }
}

  现在让我们看看对这个类使用–c 选项运行 javap 的输出。-c 选项告诉 javap 反汇编在类中遇到的字节代码。

  运行方式如下:

>javap -c JavapTip

  此命令的输出为:

Method java.lang.String withStrings(int)
    0 ldc #2
    2 astore_1
    3 iconst_0
    4 istore_2
    5 goto 30
    8 new #3
   11 dup
   12 invokespecial #4
   15 aload_1
   16 invokevirtual #5
   19 iload_2
   20 invokevirtual #6
   23 invokevirtual #7
   26 astore_1
   27 iinc 2 1
   30 iload_2
   31 iload_0
   32 if_icmplt 8
   35 aload_1
   36 areturn

Method java.lang.String withStringBuffer(int)
    0 new #3
    3 dup
    4 invokespecial #4
    7 astore_1
    8 iconst_0
    9 istore_2
   10 goto 22
   13 aload_1
   14 iload_2
   15 invokevirtual #6
   18 pop
   19 iinc 2 1
   22 iload_2
   23 iload_0
   24 if_icmplt 13
   27 aload_1
   28 invokevirtual #7
   31 areturn

  如果你以前没有看过 Java 汇编器,那么这个输出对你来说就会比较难懂,但是你应该可以看到 withString 方法在每次循环的时候都新创建了一个 StringBuffer 实例。然后它将已有的 String 的当前值追加到 StringBuffer 上,然后追加循环的当前值。最后,它对 buffer 调用 toString 并将结果赋给现有的 String 引用。

  withStringBuffer 方法与这个方法正好相反,在每次循环的时候 withStringBuffer 只调用现有 StringBuffer 的 append 方法,没有创建新的对象,也没有新的 String 引用。

  在这种情况下,我们已经知道了使用 StringBuffer 代替 String 是一种好的做法,但是如果我们不知道呢?那么 javap 可以帮助我们找到答案。在这里你可以看到更详细的关于String,StringBuffer 的解释

  你并不会经常需要一个 Java 反汇编器,但是当你需要的时候,知道你自己的机器已经有一个并且用法相当简单的反汇编器当然是一件好事。如果你感兴趣,看书看看 javap 的其它选项——或许你会发现在你的环境中需要的特性。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics