前面發了幾篇學習筆記,但是看這些東西總是感覺很"玄乎",來一篇實戰的東西來揭一下"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繼承的預設構造方法//觀察該方法的特徵:無參,傳回型別voidconst #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:Iconst #3 = Field #7.#37; // LearningClassFile.id1:Iconst #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/PrintStream;//將PrintStream的Println()方法儲存至常量池//該方法的參數為I,傳回值為voidconst #6 = Method #41.#42; // java/io/PrintStream.println:(I)V//類LearningClassFIleconst #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類型的參數,傳回型別為voidconst #9 = Method #7.#45; // LearningClassFile.addPub:(II)Vconst #10 = Method #7.#46; // LearningClassFile.addPri:(II)Vconst #11 = Method #7.#47; // LearningClassFile.addSta:()Vconst #12 = Method #7.#48; // LearningClassFile.addFinal:(II)Vconst #13 = class #49; // java/lang/Objectconst #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>":()Vconst #36 = NameAndType #17:#15;// id3:Iconst #37 = NameAndType #14:#15;// id1:Iconst #38 = NameAndType #16:#15;// id2:Iconst #39 = class #50; // java/lang/Systemconst #40 = NameAndType #51:#52;// out:Ljava/io/PrintStream;const #41 = class #53; // java/io/PrintStreamconst #42 = NameAndType #54:#55;// println:(I)Vconst #43 = Asciz LearningClassFile;const #44 = NameAndType #22:#26;// "<init>":(II)Vconst #45 = NameAndType #27:#26;// addPub:(II)Vconst #46 = NameAndType #28:#26;// addPri:(II)Vconst #47 = NameAndType #29:#23;// addSta:()Vconst #48 = NameAndType #30:#26;// addFinal:(II)Vconst #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: 11public 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. 理解"編譯期可知,運行期不變": 編譯器可確定調用方法的版本,符合這個標準的方法主要有兩種:私人方法,靜態方法。詳情請看:深入理解JVM讀書筆記--位元組碼執行引擎。