一:Java字節代碼的組織形式
類文件{
OxCAFEBABE,小版本號,大版本號,常量池大小,常量池數組,訪問控制標記,當前類信息,父類信息,實現的接口個數,實現的接口信息數組,域個數,域信息數組,方法個數,方法信息數組,屬性個數,屬性信息數組
}
二:查看方法 --- javap命令
例子:有一個Java類Demo.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文件的字節碼信息
1 D:\>javap -verbose Demo >> Demo.txt
Demo.txt:
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 2: 0 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 17: 0 79 line 18: 8 80 LocalVariableTable: 81 Start Length Slot Name Signature 82 0 9 0 this LDemo; 83 }
解析:
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
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 后字節碼信息:
Compiled from "TestString.java" public class TestString extends java.lang.Object{ public TestString(); Code: 0: aload_0 1: invokespecial #8; //Method java/lang/Object."<init>":()V 4: return public java.lang.String testString(java.lang.String, java.lang.String); Code: 0: new #16; //class java/lang/StringBuilder 3: dup 4: aload_1 5: invokestatic #18; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String; 8: invokespecial #24; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V 11: aload_2 12: invokevirtual #27; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 15: invokevirtual #31; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 18: areturn public java.lang.String testStringBuffer(java.lang.StringBuffer, java.lang.String); Code: 0: aload_1 1: aload_2 2: invokevirtual #40; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer; 5: invokevirtual #45; //Method java/lang/StringBuffer.toString:()Ljava/lang/String; 8: areturn }
從 上面編譯后的字節碼信息可以看出來,方法testString 調用了五個方法:new 、invokestatic 、invokespecial 和兩個invokevirtual ; 而testStringBuffer 方法只調用了兩個invokevirtual 方法。第一個方法比第二個方法多做了好多工作,其效率當然是要低的。而且我們從java/lang/StringBuilder.append: (Ljava/lang/String;)Ljava/lang/StringBuilder;
可以看出來其實對於String字符串合並,內部還是轉化為StringBuilder的方法調用,這是因為String是長度不可變的,所以不如直接 采用StringBuilder(與StringBuffer 長度都是可變的,只不過前者是非線程安全,后者是線程安全)進行字符串合並。
