1. 程式人生 > >深入研究Java中一個物件的初始化過程

深入研究Java中一個物件的初始化過程

在Java中要想例項化一個物件必須呼叫構造器,呼叫構造器這個類必須在jvm已經被載入了,在類載入和物件初始化的過程有些順序問題是值得我們去留意的

一個Java類中主要包含以下幾部分

靜態程式碼塊:在類載入完的時候就執行可以呼叫靜態成員,在整個類的生命週期只執行一次,優先於該類中的main方法執行。

靜態屬性、靜態方法:隨著類的載入而載入,類載入了靜態變數靜態方法就存在了,是所有物件所共享的。

構造程式碼塊:每次例項化物件都會執行且優先於構造方法。

構造方法:例項化物件是呼叫。

成員變數:普通變數。

成員方法:普通方法。

這些部分中構造器是必須的,一個類預設有一個無引數的構造器,如果自定義了一個有引數的構造器,那無參構造器就沒了需要顯式的寫出來

下面我們就來看下,例項化一個物件會經歷那些步驟。

package com.lcx.tj.test;
/**
 * 
 * @author
 */
class Father {
	private String name = "張三";//姓名,顯式賦值初始化
	private String job = "老師";//職業,顯式賦值初始化
	//測試預設初始化
	String String_;
	Father Father_;
	byte byte_;
	short short_;
	int int_;
	long long_;
	float float_;
	double double_;
	char char_;
	boolean boolean_;
	
	//靜態程式碼塊,在類載入完的時候就執行可以呼叫靜態成員,在整個類的生命週期只執行一次,優先於該類中的main方法執行。
	static {
		System.out.println("Father靜態程式碼塊 begin------------");
		System.out.println("呼叫靜態方法doSometing");
		doSometing();
		sex = "男";
		System.out.println("Father靜態程式碼塊 over------------");
	}
	/*
	 * 靜態屬性、靜態方法隨著類的載入而載入
	 */
	public static final String sex;
	public static void doSometing(){
		System.out.println("Father靜態方法doSometing:照顧小孩");
	}
	
	{//構造塊,每次例項化物件都會執行且優先於構造方法
		System.out.println("Father構造塊");
	}
	
	public Father(){
		System.out.println("Father無引數構造器");	
	}
	
	public Father(String name, String job) {
		System.out.println("Father有引數構造器");
		this.name = name;
		this.job = job;
	}
	
	//主方法
	public static void main(String[] args) {
		System.out.println("Father主方法");
		/**
		 * 1、類首次載入 靜態屬性靜態方法已經存在,執行靜態程式碼塊
		 * 2、例項化物件時 會先執行構造塊 然後呼叫構造器
		 * 需要注意:
		 * 呼叫構造器生成物件時,非靜態屬性會先進行賦值初始化,如果沒有賦值就會採取預設初始化,如果是物件預設初始化為null如果是基本型別會初始化為相應的值。
		 * 但是我們在普通的方法中定義了基本型別或者物件沒有進行顯式的賦值初始化就去使用 是會編譯出錯的。
		 */
		//沒有進顯式的賦值初始化就去使用 會編譯報錯的。
//		Father f8 ;
//		f8.toString();
//		int i;
//		System.out.println(i);
		
		Father f1 = new Father();
		System.out.println("Father toString():"+f1);
		Father f2 = new Father("李四", "程式設計師");
		System.out.println("Father toString():"+f2);
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getJob() {
		return job;
	}
	public void setJob(String job) {
		this.job = job;
	}

	@Override
	public String toString() {
		return "Father [name=" + name + ", job=" + job + ", String_=" + String_ + ", Father_=" + Father_ + ", byte_="
				+ byte_ + ", short_=" + short_ + ", int_=" + int_ + ", long_=" + long_ + ", float_=" + float_
				+ ", double_=" + double_ + ", char_=" + char_ + ", boolean_=" + boolean_ + "]";
	}
	
}
程式執行結果:


如果這個有一個子類呢?在例項化子類物件的時候 順序又是咋樣的呢?那我們接下再瞅瞅。

先看下子類程式碼:

class Son extends Father{
	private String grade;//班級
	
	//靜態程式碼塊,在類載入完的時候就執行可以呼叫靜態成員,在整個類的生命週期只執行一次,優先於該類中的main方法執行。
	static {
		System.out.println("Son靜態程式碼塊");
	}
	{//構造塊,每次例項化物件都會執行且優先於構造方法
		System.out.println("Son構造塊");
	}
	public Son() {
		super();
		System.out.println("Son無引數構造器");
	}

	public Son(String grade,String name,String job) {
		super(name,job);
		System.out.println("Son有引數構造器");
		this.grade = grade;
	}

	//靜態屬性
	public static final String like = "小女孩";
	//靜態方法
	public static void doSometing(String a){
		System.out.println("Son學習");
	}
	
	//主方法
	public static void main(String[] args) {
		System.out.println("Son主方法");
		Son s1 = new Son();
		System.out.println("Son toString:"+s1);
		Son s2 = new Son("五年級", "王五", "學生");
		System.out.println("Son toString:"+s2);
	}

	public String getGrade() {
		return grade;
	}

	public void setGrade(String grade) {
		this.grade = grade;
	}

	@Override
	public String toString() {
		return "Son [grade=" + grade + ", getName()=" + getName() + ", getJob()=" + getJob() + "]";
	}
	
}
程式執行結果:


最後我們總結一下初始化一個物件要經歷那些步驟,其實很簡單, 如果類是首次載入 就先載入類 然後呼叫 構造器就OK。

載入類class執行靜態程式碼塊-->類構造塊-->類構造器。

如果是子類例項化的話,其實也一樣,只不過 先有父物件在 再才有子物件,所有 先載入父類class再載入子類class 然後 先呼叫父構造器 再呼叫子構造器。

載入父類class執行父類靜態程式碼塊-->載入子類class執行子類靜態程式碼塊-->父類構造塊-->父類構造器-->子類構造塊-->子類構造器。

以上均是個人的一些理解哈,如果有不對的地方敬請指教。