Java開發筆記(四十九)關鍵字super的用法

前面介紹瞭如何從Bird類繼承而來Swallow類,按道理子類應當繼承父類的全部要素,可是對於構造方法來講,Swallow類僅僅繼承了Bird類的默認構造方法,並未自動繼承帶參數的構造方法。若是子類想繼續使用父類的其它構造方法,就得本身重寫心儀的構造方法。例如老鷹屬於鳥類,那麼能夠編寫繼承自Bird類的Eagle類,同時要在Eagle類內部從新定義擁有多個輸入參數的構造方法,由此獲得以下所示的Eagle類代碼:html

//定義了一個繼承自鳥類的老鷹類
public class Eagle extends Bird {

	// 老鷹類重寫了帶三個參數的構造方法,則不使用沒有輸入參數的構造方法
	public Eagle(String name, int sexType, String voice) {
		// 利用super指代父類的構造方法名稱
		super(name, sexType, voice);
	}
}

注意到如上代碼用到了關鍵字super,它的字面意思是「超級的」,但並不是說它是超人,而是用super指代父類的名稱,因此這裏「super(name, sexType, voice)」實際表達的是「Bird(name, sexType, voice)」,也就是依然利用了Bird類的同名且同參數的構造方法。外部若想建立Eagle類的實例,就要調用新定義的帶三個參數的構造方法,此時建立實例的代碼以下所示:java

	// 經過構造方法設置屬性值
	private static void setConstruct() {
		// 調用Bird類帶三個參數的構造方法
		Bird cuckoo = new Bird("杜鵑", 1, "布穀");
		System.out.println(cuckoo.toString());
		// Eagle類重寫了帶三個參數的構造方法
		Eagle eagle = new Eagle("鷹" , 0, "啁啁");
		System.out.println(eagle.toString());
	}

 

在類繼承的場合,關鍵字super表示父類,對應的this表示本類。如同this的用法通常,super不但可用於構造方法,還可做爲成員屬性和成員方法的前綴,例如「super.屬性名稱」表明父類的屬性,「super.方法名稱」表明父類的方法。
在中文世界裏,性別名稱的「雄」和「雌」專用於野生動物,而家畜、家禽的性別應當採用「公」和「母」,好比公雞、公牛、母鴨、母豬等等。前述的Bird類,默認的性別名稱爲「雄」和「雌」,顯然並不適用於家禽。爲此幾種家禽從Bird類派生而來時,須要從新定義它們的性別名稱屬性,也就是重寫setSexType方法,在該方法內部另行對sexName字段賦值。以鴨子類爲例,重寫方法後的類定義代碼以下:this

//定義了一個繼承自鳥類的鴨子類
public class Duck extends Bird {
	
	// 定義一個家禽類的性別名稱
	private String sexName;

	public Duck(String name, int sex) {
		// 利用super指代父類的構造方法名稱
		super(name, sex, "嘎嘎");
	}

	public void setSexType(int sexType) {
		// 方法內部再調用自身方法,會變成遞歸調用,若是沒有退出機制就變成死循環了
		//setSexType(sexType);
		// 在方法前面添加前綴「super.」,表示這裏調用的是父類的方法
		super.setSexType(sexType);
		// 修改家禽類的性別名稱,此時父類和子類都有同名屬性sexName,不加前綴的話默認爲子類的屬性
		sexName = (sexType==0) ? "公" : "母";
		//this.sexName = (sexType==0) ? "公" : "母";
	}

	// 父類的getSexName方法須要重寫,不然父類的方法會使用父類的屬性
	public String getSexName() {
		return this.sexName;
	}

	// 父類的toString方法須要重寫,不然父類的方法會使用父類的屬性
	public String toString() {
		String desc = String.format("這是一隻%s%s,它會%3$s、%3$s地叫。", 
				this.sexName, getName(), getVoice());
		return desc;
	}
}

以上的Duck類代碼,看起來很有些奇特之處,且待下面細細道來:orm

一、因爲Bird類的sexName屬性爲private類型,表示其爲私有屬性,不可被子類訪問,所以Duck類另外定義本身的sexName屬性,好讓狸貓換太子。
二、重寫後的setSexType方法,只有sexName屬性才需額外設置,而sexType屬性仍遵循父類的處理方式,故此時要調用父類的setSexType方法,即給該方法添加前綴「super.」。
三、由於Duck類從新定義了sexName屬性,因此與sexName有關的方法都要重寫,改成讀寫當前類的屬性,不然父類的方法依舊操做父類的屬性。
再來看一個以super修飾成員屬性的例子,假若Bird類的sexName屬性爲public類型,就意味着子類也可訪問它,那麼Duck類便能經過「super.sexName」操做該屬性了。此時新定義的DuckPublic類代碼就變成下面這樣:htm

//演示同名的父類屬性、子類屬性、輸入參數三者的優先級順序
public class DuckPublic extends Bird {

	public DuckPublic(String name, int sex) {
		super(name, sex, "嘎嘎");
	}

	public void setSexType(int sexType) {
		super.setSexType(sexType);
		// 若想對父類的屬性直接賦值,則考慮把父類的屬性從private改成public
		super.sexName = (sexType==0) ? "公" : "母";
		// 父類和子類擁有同名屬性,則不帶前綴的屬性字段默認爲子類屬性
		//sexName = (sexType==0) ? "公" : "母";
		//this.sexName = (sexType==0) ? "公" : "母";
	}

	private String sexName;

	public void setSexName(String sexName) {
		// 輸入參數與類的屬性同名,則不帶前綴的參數字段默認爲輸入參數
		this.sexName = sexName;
	}
}

假設DuckPublic類也定義了同名屬性,而且另外實現了setSexName方法,因而該類裏面將會出現三個sexName,分別是:super.sexName表示父類的屬性,this.sexName表示本類的屬性,而setSexName內部的sexName表示輸入參數。要是三者同時出現兩個,一定有一個須要添加「super」或者「this」的前綴,否則編譯器哪知同名字段是啥含義?或者說,假若有一個sexName未加任何前綴,那麼編譯器應該優先認定它是父類屬性,仍是優先認定它是本類屬性,仍是優先認定它是輸入參數?對於這些可能產生字段名稱混淆的場合,Java制定了下列的優先級判斷規則:blog

一、方法內部存在同名的輸入參數,則該字段名稱默認表明輸入參數;
二、方法內部不存在同名的輸入參數,則該字段名稱默認表明本類的成員屬性;
三、方法內部不存在同名的輸入參數,且本類也未從新定義同名的成員屬性,則該字段名稱只能表明父類的成員屬性;
歸納地說,對於同名的字段名稱而言,其所表達含義的優先級順序爲:輸入參數>本類屬性>父類屬性。繼承



更多Java技術文章參見《Java開發筆記(序)章節目錄遞歸

相關文章
相關標籤/搜索