轉 http://15838341661-139-com.iteye.com/blog/1287866
一: Java 字節代碼的組織形式
類文件 {
OxCAFEBABE ,小版本號,大版本號,常量池大小,常量池數組,訪問控制標記,當前類信息,父類信息,實現的接口個數,實現的接口信息數組,域個數,域信息數組,方法個數,方法信息數組,屬性個數,屬性信息數組
}
二: 查看方法 --- javap 命令
例子:有一個 Java 類 Demo.java
- public class Demo {
- private String str1;
- private String str2;
- private int num1;
- private int num2;
- public static final String STATIC_DATA = "hello world";
- private void sayHello1(){
- System.out.println("this is method1...");
- }
- private void sayHello2(){
- System.out.println("this is method2...");
- }
- public void sayHello3(){
- System.out.println("this is method3...");
- }
- }
通過 jdk 自帶的反編譯工具命令 javap 可以查看 class 文件的字節碼信息
D:\>javap -verbose Demo >> Demo.txt
Demo.txt :
- Compiled from "Demo.java"
- public class Demo extends java.lang.Object
- SourceFile: "Demo.java"
- minor version: 0
- major version: 49
- Constant pool:
- const #1 = class #2; // Demo
- const #2 = Asciz Demo;
- const #3 = class #4; // java/lang/Object
- const #4 = Asciz java/lang/Object;
- const #5 = Asciz str1;
- const #6 = Asciz Ljava/lang/String;;
- const #7 = Asciz str2;
- const #8 = Asciz num1;
- const #9 = Asciz I;
- const #10 = Asciz num2;
- const #11 = Asciz STATIC_DATA;
- const #12 = Asciz ConstantValue;
- const #13 = String #14; // hello world
- const #14 = Asciz hello world;
- const #15 = Asciz <init>;
- const #16 = Asciz ()V;
- const #17 = Asciz Code;
- const #18 = Method #3.#19; // java/lang/Object."<init>":()V
- const #19 = NameAndType #15:#16;// "<init>":()V
- const #20 = Asciz LineNumberTable;
- const #21 = Asciz LocalVariableTable;
- const #22 = Asciz this;
- const #23 = Asciz LDemo;;
- const #24 = Asciz sayHello1;
- const #25 = Field #26.#28; // java/lang/System.out:Ljava/io/PrintStream;
- const #26 = class #27; // java/lang/System
- const #27 = Asciz java/lang/System;
- const #28 = NameAndType #29:#30;// out:Ljava/io/PrintStream;
- const #29 = Asciz out;
- const #30 = Asciz Ljava/io/PrintStream;;
- const #31 = String #32; // this is method1...
- const #32 = Asciz this is method1...;
- const #33 = Method #34.#36; // java/io/PrintStream.println:(Ljava/lang/String;)V
- const #34 = class #35; // java/io/PrintStream
- const #35 = Asciz java/io/PrintStream;
- const #36 = NameAndType #37:#38;// println:(Ljava/lang/String;)V
- const #37 = Asciz println;
- const #38 = Asciz (Ljava/lang/String;)V;
- const #39 = Asciz sayHello2;
- const #40 = String #41; // this is method2...
- const #41 = Asciz this is method2...;
- const #42 = Asciz sayHello3;
- const #43 = String #44; // this is method3...
- const #44 = Asciz this is method3...;
- const #45 = Asciz SourceFile;
- const #46 = Asciz Demo.java;
- {
- public static final java.lang.String STATIC_DATA;
- Constant value: String hello world
- public Demo();
- Code:
- Stack=1, Locals=1, Args_size=1
- 0: aload_0
- 1: invokespecial #18; //Method java/lang/Object."<init>":()V
- 4: return
- LineNumberTable:
- line 2: 0
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 5 0 this LDemo;
- public void sayHello3();
- Code:
- Stack=2, Locals=1, Args_size=1
- 0: getstatic #25; //Field java/lang/System.out:Ljava/io/PrintStream;
- 3: ldc #43; //String this is method3...
- 5: invokevirtual #33; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
- 8: return
- LineNumberTable:
- line 17: 0
- line 18: 8
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 9 0 this LDemo;
- }
解析:
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
- <strong>public class TestString {
- public String testString(String str1, String str2){
- return str1 + str2;
- }
- public String testStringBuffer(StringBuffer sb, String str){
- return sb.append(str).toString();
- }
- }
- </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 長度都是可變的,只不過前者是非線程安全,後者是線程安全)進行字符串合併。