深入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. 理解"編譯期可知,運行期不變": 編譯器可確定調用方法的版本,符合這個標准的方法主要有兩種:私有方法,靜態方法。詳情請看:深入理解JVM讀書筆記--字節碼執行引擎



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM