java之面向對象:子類對象的實例化過程詳解

在子類構造函數中,發現,訪問子類構造函數時,父類也運行了。緣由是什麼呢?java

在子類的構造函數裏第一行有一個默認的隱式語句:super()函數


ExtendsDemo.javathis

class Fu
{
	Fu()
	{
		System.out.println("fu run");
	}
}
class Zi extends Fu
{
	Zi()
	{
		//super();  //調用的是父類中的空參數的構造函數
		System.out.println("zu run");
	}
}
class ExtendsDemo
{
	public static void main(String[] args)
	{
		new Zi();
	}
}

輸出:

咱們也可訪問父類中帶有參數的構造函數:spa

class Fu
{
	Fu(int x)
	{
		System.out.println("fu run");
	}
}
class Zi extends Fu
{
	Zi()
	{
		super(4);  //父類有帶參數的構造函數
		System.out.println("zu run");
	}
}

子類的實例化過程:

子類中全部的構造函數默認都會訪問父類中的空參數的構造函數。固然,若是子類中指定了訪問父類帶參數的構造函數,就不會訪問父類默認的構造函數(好像是廢話哈~~)code

這就意味着若是父類中沒有默認的構造函數,子類嘗試調用父類的默認構造函數,程序就會報錯:對象

class Fu
{
	Fu(int x)   //指定了新的構造函數,默認的構造函數就沒有了
	{
		System.out.println("fu run 2");
	}
}
class Zi extends Fu
{
	Zi()
	{
		super(4);  //父類有帶參數的構造函數
		System.out.println("zu run 1");
	}
	Zi(int x)
	{
		//super();  //默認會訪問父類的構造函數
		System.out.println("zu run 2");
	}
}
class ExtendsDemo
{
	public static void main(String[] args)
	{
		new Zi(6);
	}
}

輸出:


因此這時候就須要在子類中指定調用父類帶參數的構造函數:blog

class Fu
{
	Fu(int x)   //指定了新的構造函數,默認的構造函數就沒有了
	{
		System.out.println("fu run 2");
	}
}
class Zi extends Fu
{
	Zi()
	{
		super(4);  //父類有帶參數的構造函數
		System.out.println("zu run 1");
	}
	Zi(int x)
	{
		super(x);  //默認會訪問父類的構造函數
		System.out.println("zu run 2");
	}
}
class ExtendsDemo
{
	public static void main(String[] args)
	{
		new Zi(6);
	}
}

輸出:

爲何子類默認會訪問父類的默認構造函數呢?

那是由於:子類繼承了父類,獲取到了父類中內容(屬性),因此在使用父類內容以前,要先看父類是如何對本身的內容進行初始化的,因此子類在構造對象時候,必須訪問父類的構造函數,爲了完成這個必須的動做,就在子類的構造函數中加入了super()語句、繼承

若是父類中沒有定義空參數構造函數,那麼子類的構造函數必須用super明確要調用父類中哪一個構造函數,不然子類沒法完成初始化。內存

注意:super語句必需要定義在子類構造函數的第一行,由於父類的初始化動做要先完成。class

//----------------------------------------------------------------------------------------------------------------------------------------------------------------

同時子類構造函數若是使用this調用了本類構造函數時,那麼super就沒有了,由於super和this都只能定義在第一行,因此只能有一個,可是能夠保證的是,子類中確定會有其餘的構造函數訪問父類的構造函數。

class Fu
{
	Fu(int x)   //指定了新的構造函數,默認的構造函數就沒有了
	{
		System.out.println("fu run 2");
	}
}
class Zi extends Fu
{
	Zi()
	{
		super(4);  //父類有帶參數的構造函數
		System.out.println("zu run 1");
	}
	Zi(int x)
	{
		this();
		//super(x);  //默認會訪問父類的構造函數
		System.out.println("zu run 2");
	}
}
class ExtendsDemo
{
	public static void main(String[] args)
	{
		new Zi(6);
	}
}
輸出:

注意的問題:

java中任何類默認會繼承一個根類——Object,主動繼承這個類或者不繼承這個類寫法均可以。

 
class Fu
{
	Fu()
	{
		super();
		show();
		return;
	}
	
	void show()
	{
		System.out.println("fu show");
	}
}

class Zi extends Fu
{
	int num = 8;
	Zi()
	{
		super();
		System.out.println("zi cons num..."+num);
	}
	void show()
	{
		System.out.println("zi show..."+num);
	}
}

class Demo
{
	public static void main(String[] args)
	{
		Zi z = new Zi();
		z.show();
	}
}


 

輸出:

對象實例化圖解:

經過super初始化父類內容時,子類的成員變量並未顯示初始化,等super()父類初始化完畢後,才進行子類的成員變量顯式初始化。

對象的實例化過程步驟總結:

Person p = new Person();

一、JVM會讀取指定路徑下的Person.class文件,並加載進內存。並會先加載Person的父類(若是有直接的父類的狀況下)

二、在堆內存中開闢空間,分配地址。

三、並在對象空間中,對對象中的屬性進行默認初始化。(不是顯式初始化)

四、調用對應的構造函數進行初始化。

五、在構造函數中,第一行會先調用父類的構造函數進行初始化。

六、父類初始化完畢後,在對子類的屬性進行顯式初始化。

七、再進行子類構造函數的特定初始化。

八、初始化完畢夠,將地址值賦值給引用變量。