Java 字节码之解析一

来源:互联网 发布:刷屏软件 编辑:程序博客网 时间:2024/06/05 02:45

转   http://15838341661-139-com.iteye.com/blog/1287866


一: Java 字节代码的组织形式

 

类文件 {

    OxCAFEBABE ,小版本号,大版本号,常量池大小,常量池数组,访问控制标记,当前类信息,父类信息,实现的接口个数,实现的接口信息数组,域个数,域信息数组,方法个数,方法信息数组,属性个数,属性信息数组

}

 

二: 查看方法 --- javap 命令

 

例子:有一个 Java 类 Demo.java


Java代码  收藏代码
  1. public class Demo {  
  2.     private String str1;  
  3.     private String str2;  
  4.     private int num1;  
  5.     private int num2;  
  6.     public static final String STATIC_DATA = "hello world";  
  7.       
  8.     private void sayHello1(){  
  9.         System.out.println("this is method1...");  
  10.     }  
  11.     private void sayHello2(){  
  12.         System.out.println("this is method2...");  
  13.     }  
  14.     public void sayHello3(){  
  15.         System.out.println("this is method3...");  
  16.     }  
  17. }  
 

 

通过 jdk 自带的反编译工具命令 javap 可以查看 class 文件的字节码信息


D:\>javap -verbose Demo >> Demo.txt


   Demo.txt :

 

Aaa代码  收藏代码
  1. Compiled from "Demo.java"  
  2. public class Demo extends java.lang.Object  
  3.   SourceFile: "Demo.java"  
  4.   minor version: 0  
  5.   major version: 49    
  6.    
  7.   Constant pool:  
  8. const #1 = class      #2;   //  Demo  
  9. const #2 = Asciz     Demo;  
  10. const #3 = class      #4;   //  java/lang/Object  
  11. const #4 = Asciz     java/lang/Object;  
  12. const #5 = Asciz     str1;  
  13. const #6 = Asciz     Ljava/lang/String;;  
  14. const #7 = Asciz     str2;  
  15. const #8 = Asciz     num1;  
  16. const #9 = Asciz     I;  
  17. const #10 = Asciz   num2;  
  18. const #11 = Asciz   STATIC_DATA;  
  19. const #12 = Asciz   ConstantValue;  
  20. const #13 = String  #14; //  hello world  
  21. const #14 = Asciz   hello world;  
  22. const #15 = Asciz   <init>;  
  23. const #16 = Asciz   ()V;  
  24. const #17 = Asciz   Code;  
  25. const #18 = Method       #3.#19;   //  java/lang/Object."<init>":()V  
  26. const #19 = NameAndType    #15:#16;//  "<init>":()V  
  27. const #20 = Asciz   LineNumberTable;  
  28. const #21 = Asciz   LocalVariableTable;  
  29. const #22 = Asciz   this;  
  30. const #23 = Asciz   LDemo;;  
  31. const #24 = Asciz   sayHello1;  
  32. const #25 = Field   #26.#28;  //  java/lang/System.out:Ljava/io/PrintStream;  
  33. const #26 = class    #27; //  java/lang/System  
  34. const #27 = Asciz   java/lang/System;  
  35. const #28 = NameAndType    #29:#30;//  out:Ljava/io/PrintStream;  
  36. const #29 = Asciz   out;  
  37. const #30 = Asciz   Ljava/io/PrintStream;;  
  38. const #31 = String  #32; //  this is method1...  
  39. const #32 = Asciz   this is method1...;  
  40. const #33 = Method       #34.#36;  //  java/io/PrintStream.println:(Ljava/lang/String;)V  
  41. const #34 = class    #35; //  java/io/PrintStream  
  42. const #35 = Asciz   java/io/PrintStream;  
  43. const #36 = NameAndType    #37:#38;//  println:(Ljava/lang/String;)V  
  44. const #37 = Asciz   println;  
  45. const #38 = Asciz   (Ljava/lang/String;)V;  
  46. const #39 = Asciz   sayHello2;  
  47. const #40 = String  #41; //  this is method2...  
  48. const #41 = Asciz   this is method2...;  
  49. const #42 = Asciz   sayHello3;  
  50. const #43 = String  #44; //  this is method3...  
  51. const #44 = Asciz   this is method3...;  
  52. const #45 = Asciz   SourceFile;  
  53. const #46 = Asciz   Demo.java;  
  54.    
  55. {  
  56. public static final java.lang.String STATIC_DATA;  
  57.   Constant value: String hello world  
  58. public Demo();  
  59.   Code:  
  60.    Stack=1, Locals=1, Args_size=1  
  61.    0:      aload_0  
  62.    1:      invokespecial  #18; //Method java/lang/Object."<init>":()V  
  63.    4:      return  
  64.   LineNumberTable:  
  65.    line 20  
  66.   LocalVariableTable:  
  67.    Start  Length  Slot  Name   Signature  
  68.    0      5      0    this       LDemo;  
  69.    
  70. public void sayHello3();  
  71.   Code:  
  72.    Stack=2, Locals=1, Args_size=1  
  73.    0:      getstatic   #25; //Field java/lang/System.out:Ljava/io/PrintStream;  
  74.    3:      ldc   #43; //String this is method3...  
  75.    5:      invokevirtual  #33; //Method java/io/PrintStream.println:(Ljava/lang/String;)V  
  76.    8:      return  
  77.   LineNumberTable:  
  78.    line 170  
  79.    line 188  
  80.   LocalVariableTable:  
  81.    Start  Length  Slot  Name   Signature  
  82.    0      9      0    this       LDemo;  
  83.    
  84. }  
 

 

解析:

1. 版本号   major version: 49   //java 版本 jdk1.6 显示的是 50 , jdk1.5 显示的是 49 , jdk1.4 显示的是 58 , 高版本能执行低版本的 class 文件  

2. 常量池 Constant pool

Method :方法

Field :字段

String :字符串

Asciz :签名如<init> 由jvm 调用,其他是不能够去调用它的

NameAndType :变量名的类型

Class :类

 

通过字节码,我们可以看到 Demo 类 继承于 java.lang.Object ,如果类中没有显式声明构造函数的话,编译器会插入一个缺省无参的构造函数 ( 构造函数在 JVM 级别是显示成 <init> 的普通函数 ) 。

 

 

三: 检测代码的效率问题

 

学习 Java 的过程中,都会了解到字符串合并时要用到 StringBuffer 来代替 String ,那下面就来通过 Java 字节码来验证两种方式的效率性。

 

例子:一个 Java 类 TestString.java

Java代码  收藏代码
  1. <strong>public class TestString {  
  2.     public String testString(String str1, String str2){  
  3.        return str1 + str2;  
  4.     }  
  5.     public String testStringBuffer(StringBuffer sb, String str){  
  6.        return sb.append(str).toString();  
  7.     }  
  8. }  
  9.  </strong>  

 

 

javap –c TestString 后字节码信息:

 

 

Java代码  收藏代码
  1. Compiled from "TestString.java"  
  2. public class TestString extends java.lang.Object{  
  3. public TestString();  
  4.   Code:  
  5.    0:      aload_0  
  6.    1:      invokespecial  #8//Method java/lang/Object."<init>":()V  
  7.    4:      return  
  8.    
  9. public java.lang.String testString(java.lang.String, java.lang.String);  
  10.   Code:  
  11.    0:      new #16//class java/lang/StringBuilder  
  12.    3:      dup  
  13.    4:      aload_1  
  14.    5:      invokestatic    #18//Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;  
  15.    8:      invokespecial  #24//Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V  
  16.    11:     aload_2  
  17.    12:    invokevirtual  #27//Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;  
  18.    15:    invokevirtual  #31//Method java/lang/StringBuilder.toString:()Ljava/lang/String;  
  19.    18:    areturn  
  20.    
  21. public java.lang.String testStringBuffer(java.lang.StringBuffer, java.lang.String);  
  22.   Code:  
  23.    0:      aload_1  
  24.    1:      aload_2  
  25.    2:      invokevirtual  #40//Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;  
  26.    5:      invokevirtual  #45//Method java/lang/StringBuffer.toString:()Ljava/lang/String;  
  27.    8:      areturn  
  28.    
  29. }  
  30.    
 

从上面编译后的字节码信息可以看出来,方法testString 调用了五个方法:new 、invokestatic 、invokespecial 和两个invokevirtual ; 而testStringBuffer 方法只调用了两个invokevirtual 方法。第一个方法比第二个方法多做了好多工作,其效率当然是要低的。而且我们从java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;

可以看出来其实对于String 字符串合并,内部还是转化为StringBuilder 的方法调用,这是因为String 是长度不可变的,所以不如直接采用StringBuilder (与StringBuffer 长度都是可变的,只不过前者是非线程安全,后者是线程安全)进行字符串合并。
 


0 0
原创粉丝点击