java高級:類的初始和類實例初始次序

編譯到運行java程序大體流程

在這裏插入圖片描述

Java虛擬機的基本結構及其內存分區:

在這裏插入圖片描述

JVM中把內存分爲直接內存、方法區、Java棧、Java堆、本地方法棧、PC寄存器等。

   直接內存:就是原始的內存區

   方法區:用於存放類、接口的元數據信息,加載進來的字節碼數據都存儲在方法區

   Java棧:執行引擎運行字節碼時的運行時內存區,採用棧幀的形式保存每個方法的調用運行數據

   本地方法棧:執行引擎調用本地方法時的運行時內存區

   Java堆:運行時數據區,各種對象一般都存儲在堆上

   PC寄存器:功能如同CPU中的PC寄存器,指示要執行的字節碼指令。

JVM的功能模塊主要包括類加載器、執行引擎和垃圾回收系統


類初始化

main方法 所在類會在執行前,先加載和類初始化。類初始化是從父類到子類自上到下
 * 類初始化:先初始化父類,後子類初始
 * 類初始化<clinit>():初始化類變量顯示賦值代碼、靜態代碼塊,次序是自上到下,
 * <clinit>()只執行一次*
 * 在編譯階段,編譯器收集所有的靜態字段的賦值語句及靜態代碼塊,
 * 並按語句出現的順序拼接出一個類初始化方法<clinit>()。
 * 此時,執行引擎會調用這個方法對靜態字段進行代碼中編寫的初始化操作。

1)類加載器會在指定的classpath中找到Son.class這個文件,然後讀取字節流中的數據,將其存儲在方法區中。

2)會根據Son.class的信息建立一個Class對象,這個對象比較特殊,一般也存放在方法區中,用於作爲運行時訪問Student類的各種數據的接口。

3)必要的驗證工作,格式、語義等

4)爲Son中的靜態字段分配內存空間,也是在方法區中,並進行零初始化,即數字類型初始化爲0,boolean初始化爲false,引用類型初始化爲null等。

在Son.java中只有一個靜態字段:

private static int j=test();

此時,並不會執行賦值爲test()返回值的操作,而是將其初始化爲0。

5)由於已經加載到內存了,所以原來字節碼文件中存放的部分方法、字段等的符號引用可以解析爲其在內存中的直接引用了,而不一定非要等到真正運行時才進行解析。

6)在編譯階段,編譯器收集所有的靜態字段的賦值語句及靜態代碼塊,並按語句出現的順序拼接出一個類初始化方法()。此時,執行引擎會調用這個方法對靜態字段進行代碼中編寫的初始化操作。


類實例初始

	實例初始化<init>:先父類實例初始,後子類實例初始。
	每個類實例初始過程:1.實例成員變量 、非靜態代碼塊是 按序執行 
					2. 最後是自身構造方法, 若子類構造函數指定了使用父類的構造函數,
則父類初始該指定的構造函數

	<init>()方法,是編譯器將調用父類的<init>()的語句、構造代碼塊、實例字段賦值語句,
以及自己編寫的構造方法中的語句整合在一起生成的一個方法。保證調用父類的<init>()方法
在最開頭,自己編寫的構造方法語句在最後,而構造代碼塊及實例字段賦值語句按出現的順序
按序整合到<init>()方法中。

例子

/**
 *  父類初始化<clinit>()
 * 步驟1. j = method(); -> System.out.print("(5)");
 * 步驟2. System.out.print("(1)");
 *	父類實例初始化
 * 步驟5. i = test();
 * 步驟6.System.out.print("(3)"); -> System.out.print("(9)");
 * 步驟7.System.out.print(i);		//若不在子類中調有參的父類構造函數,則是默認的無參構造函數,System.out.print("(2)");
 */
class Father{
	private int i = test();
	private static int j = method();
	static {
		System.out.print("(1)");
	}
	public Father() {
		System.out.print("(2)");
	}
	public Father(int i) {
		System.out.print(i);
	}
	{
		System.out.print("(3)");
	}
	public int test() {
		System.out.print("(4)");
		return 0;
	}
	public static int method() {
	
		System.out.print("(5)");
		return 0;
	}
}
/**
 * 
 * 子類初始化<clinit>()
 * 步驟3. j = method(); -> System.out.print("(10)");
 * 步驟4. System.out.print("(6)");
 *	子類實例初始化
 * 步驟8. i = test();-> System.out.print("(9)");
 * 步驟9. System.out.print("(8)");
 * 步驟10.System.out.print("(7)");
 *
 */
public class Son extends Father{
	
	private int i = test();
	private static int j = method();
	static {
		System.out.print("(6)");
	}
	public Son() {
		//super(); 第一行有一個默認的調用父類的無參構造方法
		super(11); //顯示的調用父類的有參構造方法
		System.out.print("(7)");
	}
	{
		System.out.print("(8)");
	}
	public int test() {
		System.out.print("(9)");
		return 0;
	}
	public static int method() {
		System.out.print("(10)");
		return 0;
	}
	
	/**
	 * main方法 所在類會在執行前,先加載和類初始化。類初始化是從父類到子類自上到下
	 * 類初始化:先初始化父類,後子類初始
	 * 類初始化<clinit>():初始化類變量顯示賦值代碼、靜態代碼塊,次序是自上到下,<clinit>()只執行一次
	 * 
	 */
	public static void main(String[] args) {
	//實例初始化<init>:先父類實例初始,後子類實例初始。每個類實例初始過程:1.實例成員變量 、非靜態代碼塊是 按序執行 
		//2. 最後是自身構造方法,  若子類構造函數指定了使用父類的構造函數,則父類初始該指定的構造函數
		//注意;父類在初始    i = test()時是調用的子類的test(),因當前對象是Son且Son類中重學了父類的test();
		Son son1 = new Son();
		System.out.println();
		Son son2 = new Son();
	}

}

//結果
//(5)(1)(10)(6)(9)(3)11(9)(8)(7)
//(9)(3)11(9)(8)(7)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章