深入Java虛擬機:Class文件實例解析
前面發了幾篇學習筆記,但是看這些東西總是感覺很"玄乎",來一篇實戰的東西來揭一下"JVM"的面紗,讓"SSH"時代的童鞋們來熟悉一下Java的"老祖爺"JVM。由于自己的水平有限,所以大家在看過程中發了什么問題,或者您有什么疑問請及時提出來,我及時解決。如果您有什么建議,那么更好大家一塊討論。
1、源碼文件
- public class LearningClassFile {
- //普通變量
- private int id1;
- //靜態變量
- private static int id2;
- //常量
- private final int id3 = 4;
- //靜態常量
- private static final int id4 = 5;
- public LearningClassFile() {
- }
- public LearningClassFile(int id1, int id2) {
- this.id1 = id1;
- this.id2 = id2;
- }
- //使用public修飾的addPub方法
- public void addPub(int a, int b) {
- int result = a + b;
- System.out.println(result);
- }
- //使用private修飾的addPri方法
- private void addPri(int a, int b) {
- int result = a + b;
- System.out.println(result);
- }
- //使用static修飾的方法
- public static void addSta() {
- int result = id2 + id4;
- System.out.println(result);
- }
- public static final void addFinal(int a, int b) {
- int result = a + b;
- System.out.println(result);
- }
- public static void main(String[] args) {
- LearningClassFile lcf = new LearningClassFile(1, 2);
- lcf.addPub(1, 2);
- lcf.addPri(1, 2);
- addSta();
- addFinal(1, 2);
- }
- }
Class文件:
- Compiled from "LearningClassFile.java"
- public class LearningClassFile extends java.lang.Object
- SourceFile: "LearningClassFile.java"
- minor version: 0
- major version: 50
- //運行時常量池:用于存放編譯期生成的各種字面量和符號引用。
- Constant pool:
- //從父類Object繼承的默認構造方法
- //觀察該方法的特征:無參,返回類型void
- const #1 = Method #13.#35; // java/lang/Object."<init>":()V
- //常量id3
- //"#7.#36; // LearningClassFile.id3:I"
- //#7:查找常量池中的類名LearningClassFile
- //#36-->"const #36 = NameAndType #17:#15;// id3:I"
- //NameAndType字面的意思是名稱和類型。即id3是變量的名稱,I表示id3是int類型
- //綜合描述:LearningClassFile中的id3是int類型
- const #2 = Field #7.#36; // LearningClassFile.id3:I
- const #3 = Field #7.#37; // LearningClassFile.id1:I
- const #4 = Field #7.#38; // LearningClassFile.id2:I
- //將System的out存儲至常量池
- //System類中out被public static final修飾的
- //"public final static PrintStream out = nullPrintStream();"
- //綜合描述:System類的out屬性是PrintStream類型
- const #5 = Field #39.#40; // java/lang/System.out:Ljava/io/PrintS
- tream;
- //將PrintStream的Println()方法存儲至常量池
- //該方法的參數為I,返回值為void
- const #6 = Method #41.#42; // java/io/PrintStream.println:(I)V
- //類LearningClassFIle
- const #7 = class #43; // LearningClassFile
- //構造函數
- //該構造函數需傳入兩個int類型的變量
- const #8 = Method #7.#44; // LearningClassFile."<init>":(II)V
- //LearningClassFile的addPub方法
- //#4-->"const #45 = NameAndType #27:#26;// addPub:(II)V"
- //#27-->"const #27 = Asciz addPub;" 方法的名稱為:addPub
- //#26-->"const #26 = Asciz (II)V;" 方法的類型:兩個int類型的參數,返回類型為void
- const #9 = Method #7.#45; // LearningClassFile.addPub:(II)V
- const #10 = Method #7.#46; // LearningClassFile.addPri:(II)V
- const #11 = Method #7.#47; // LearningClassFile.addSta:()V
- const #12 = Method #7.#48; // LearningClassFile.addFinal:(II)V
- const #13 = class #49; // java/lang/Object
- const #14 = Asciz id1;
- const #15 = Asciz I;
- const #16 = Asciz id2;
- const #17 = Asciz id3;
- //ConstantValue屬性表示一個常量字段的值
- //即final修飾的屬性
- const #18 = Asciz ConstantValue;
- //對于final修飾的常量直接將類型和值存入常量池
- const #19 = int 4;
- const #20 = Asciz id4;
- const #21 = int 5;
- const #22 = Asciz <init>;
- const #23 = Asciz ()V;
- //Code屬性只為***一個方法、實例類初始化方法或類初始化方法保存Java虛擬機指令及相關輔助信息
- //簡而言之:保存方法編譯后的指令信息
- const #24 = Asciz Code;
- //java源碼行號與編譯后的字節碼指令的對應表
- const #25 = Asciz LineNumberTable;
- const #26 = Asciz (II)V;
- const #27 = Asciz addPub;
- const #28 = Asciz addPri;
- const #29 = Asciz addSta;
- const #30 = Asciz addFinal;
- const #31 = Asciz main;
- const #32 = Asciz ([Ljava/lang/String;)V;
- //java 源碼文件
- const #33 = Asciz SourceFile;
- const #34 = Asciz LearningClassFile.java;
- const #35 = NameAndType #22:#23;// "<init>":()V
- const #36 = NameAndType #17:#15;// id3:I
- const #37 = NameAndType #14:#15;// id1:I
- const #38 = NameAndType #16:#15;// id2:I
- const #39 = class #50; // java/lang/System
- const #40 = NameAndType #51:#52;// out:Ljava/io/PrintStream;
- const #41 = class #53; // java/io/PrintStream
- const #42 = NameAndType #54:#55;// println:(I)V
- const #43 = Asciz LearningClassFile;
- const #44 = NameAndType #22:#26;// "<init>":(II)V
- const #45 = NameAndType #27:#26;// addPub:(II)V
- const #46 = NameAndType #28:#26;// addPri:(II)V
- const #47 = NameAndType #29:#23;// addSta:()V
- const #48 = NameAndType #30:#26;// addFinal:(II)V
- const #49 = Asciz java/lang/Object;
- const #50 = Asciz java/lang/System;
- const #51 = Asciz out;
- const #52 = Asciz Ljava/io/PrintStream;;
- const #53 = Asciz java/io/PrintStream;
- const #54 = Asciz println;
- const #55 = Asciz (I)V;
- {
- //默認構造方法
- public LearningClassFile();
- Code:
- Stack=2, Locals=1, Args_size=1
- 0: aload_0
- 1: invokespecial #1; //Method java/lang/Object."<init>":()V
- //將id3的引用推送至棧頂
- 4: aload_0
- //將4推送至棧頂
- 5: iconst_4
- //將4賦值給id3
- 6: putfield #2; //Field id3:I
- 9: return
- LineNumberTable:
- line 11: 0 //public LearningClassFile() {
- //對于final類型的實例變量在每個構造方法中都會進行一次初始化。
- line 7: 4 // private final int id3 = 4;
- line 12: 9 //}
- public LearningClassFile(int, int);
- Code:
- Stack=2, Locals=3, Args_size=3
- 0: aload_0
- 1: invokespecial #1; //Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: iconst_4
- 6: putfield #2; //Field id3:I
- 9: aload_0
- 10: iload_1
- 11: putfield #3; //Field id1:I
- 14: aload_0
- 15: pop
- 16: iload_2
- 17: putstatic #4; //Field id2:I
- 20: return
- LineNumberTable:
- line 14: 0 //public LearningClassFile(int id1, int id2) {
- //對于final類型的實例變量在每個構造方法中都會進行一次初始化。
- line 7: 4 // private final int id3 = 4;
- line 15: 9 // this.id1 = id1;
- line 16: 14 // this.id2 = id2;
- line 17: 20 //}
- public void addPub(int, int);
- Code:
- Stack=2, Locals=4, Args_size=3
- 0: iload_1
- 1: iload_2
- 2: iadd
- 3: istore_3
- 4: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream;
- 7: iload_3
- 8: invokevirtual #6; //Method java/io/PrintStream.println:(I)V
- 11: return
- LineNumberTable:
- line 21: 0 // int result = a + b;
- line 22: 4 // System.out.println(result);
- line 23: 11 // }
- public static void addSta();
- Code:
- Stack=2, Locals=1, Args_size=0
- //獲取靜態變量id2推送至棧頂
- 0: getstatic #4; //Field id2:I
- //直接從常量池中取出id4的值5推送至棧頂
- 3: iconst_5
- //執行相加操作
- 4: iadd
- //將計算結果推送至棧頂
- 5: istore_0
- //獲取靜態與out
- 6: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream;
- //取出計算結果
- 9: iload_0
- //調用println方法
- 10: invokevirtual #6; //Method java/io/PrintStream.println:(I)V
- //方法正常結束
- 13: return
- LineNumberTable:
- line 33: 0 // int result = id2 + id4;
- line 34: 6 // System.out.println(result);
- line 35: 13 //}
- public static final void addFinal(int, int);
- Code:
- Stack=2, Locals=3, Args_size=2
- 0: iload_0
- 1: iload_1
- 2: iadd
- 3: istore_2
- 4: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream;
- 7: iload_2
- 8: invokevirtual #6; //Method java/io/PrintStream.println:(I)V
- 11: return
- LineNumberTable:
- line 38: 0
- line 39: 4
- line 40: 11
- public static void main(java.lang.String[]);
- Code:
- Stack=4, Locals=2, Args_size=1
- //創建一個LearningClassFile對象,并將對象的引用推送至棧頂
- 0: new #7; //class LearningClassFile
- //將對象的引用進行備份推送至棧頂
- //使用原有的引用值調用實例方法,現在置于棧頂的引用值的位置將被接下來的操作覆蓋。
- 3: dup
- //將構造函數中的參數1推送至棧頂
- 4: iconst_1
- 5: iconst_2
- //執行構造方法
- 6: invokespecial #8; //Method "<init>":(II)V
- //將棧頂引用型數值存入第二個本地變量
- 9: astore_1
- 10: aload_1
- 11: iconst_1
- 12: iconst_2
- //調用實例方法
- 13: invokevirtual #9; //Method addPub:(II)V
- 16: aload_1
- 17: iconst_1
- 18: iconst_2
- 19: invokespecial #10; //Method addPri:(II)V
- //調用靜態方法
- 22: invokestatic #11; //Method addSta:()V
- 25: iconst_1
- 26: iconst_2
- 27: invokestatic #12; //Method addFinal:(II)V
- 30: return
- LineNumberTable:
- line 43: 0 // LearningClassFile lcf = new LearningClassFile(1, 2);
- line 44: 10 // lcf.addPub(1, 2);
- line 45: 16 // lcf.addPri(1, 2);
- line 46: 22 // addSta();
- line 47: 25 // addFinal(1, 2);
- line 48: 30 //}
- }
final變量和static final變量的區別:
(1)實例常量和類常量的區別
(2)初識方式不同:從class字節碼來看final修飾的變量會出現在每個構造方法中進行一次初始化;static final類型的變量必須在定義的時候進行初始化。 理解"編譯期可知,運行期不變": 編譯器可確定調用方法的版本,符合這個標準的方法主要有兩種:私有方法,靜態方法。
詳情請看:深入理解JVM讀書筆記--字節碼執行引擎。
2、final變量和static final變量的區別:
(1)實例常量和類常量的區別
(2)初始化方式不同:從class字節碼來看final修飾的變量會出現在每個構造方法中進行一次初始化;static final類型的變量必須在定義的時候進行初始化。
3、理解"編譯期可知,運行期不變":
編譯器可確定調用方法的版本,符合這個標準的方法主要有兩種:私有方法,靜態方法。
原文鏈接:http://www.cnblogs.com/focusj/archive/2012/03/05/2375357.html
【編輯推薦】