Java繼承關係中,父類方法使用實例變量和調用實例方法的探究

面向對象編程中,某一個實例方法使用實例變量和調用其它實例方法的狀況是常見的。當存在繼承關係時,這種狀況就變得複雜起來。如下就對繼承關係中,父類的某實例方法使用實例變量和其它實例方法的狀況進行探究。由於我也是初學者,有理解不到位的地方,還請路過的朋友多多指教。java

(1)父類實例方法使用實例變量編程


  • public或protected修飾的實例變量。測試

由於在繼承關係中,public和protected修飾的實例變量對於子類的效果是相同的,因此在此合併討論。spa


public class Test {
	public static void main(String[] args) {
		System.out.println(new Parent().getName());
		System.out.println(new Child().getName());
	}
}

class Parent {
	public String name = "base";
	public String getName() {
		return name;
	}

}

class Child extends Parent {
	public String name = "child";
	
}

輸出結果:code

base
base

因而可知,父類的方法若是使用了父類的實例變量,即便在子類中從新定義了一個和該變量名稱相同的實例變量,父類的方法在經過子類調用的時候,依然使用的是父類的變量。對象


瞭解這一點後,咱們能夠作一個有趣的實驗。下面這段代碼會輸出什麼內容?繼承

public class Test {
	public static void main(String[] args) {
		Child c = new Child();
		c.name = "Child";
		System.out.println(c.name);         //(1)這裏輸出的是子類的name
		System.out.println(c.getName());    //(2)這裏輸出的時父類的name
	}
}

class Parent {
	public String name = "base";
	public String getName() {
		return name;
	}

}

class Child extends Parent {
	
}

(1)處的代碼輸出的name,是經過子類引用找到的name,而(2)出代碼調用的是繼承自父類的getName()方法,根據以前測試的結果,該方法訪問的確定是父類的name。這裏問題的關鍵在於,子類引用找到的name和父類的name是否是同一個name呢?運行的結果顯示:是同一個!內存

輸出結果get

Child
Child

有這裏能夠看出,子類繼承父類後,子類自身並未多出一個name來,其使用的name仍然是父類的name。也就是說,在內存結構中,子類繼承自父類的實例變量,仍然是父類的實例變量,只是對於子類而言是可見的而且表現爲子類自身實例變量。面向對象編程

從上述例子還能夠看到,子類在聲明一個同名成員變量後,子類使用的即是自身的成員變量了。其實此時父類的同名成員變量對子類仍然可見,而且子類能夠經過super.變量名來使用該父類的成員變量。


  • private修飾的成員變量。

其實這時已經不用進一步討論了,根據上邊的結論,父類方法不會訪問子類的public/protected員變量,那麼確定不會也不會訪問到private修飾的成員變量。


綜上所述,子類繼承自父類的實例方法能夠訪問的實例變量是父類的實例變量,對於子類自身的實例變量是不能訪問的!若是在繼承的時候,子類定義了一個和繼承自父類的實例變量名稱相同的實例變量,則會致使子類不能直接訪問父類,進而致使子類方法和父類方法對同名實例變量的操做不一致。因此,不建議子類定義和繼承自父類實例變量名稱相同的實例變量


(2)父類實例方法調用實例方法

一樣地,分public/protected和private兩種狀況進行討論。


  • public/protected修飾的實例方法。

測試的方法很簡單,只須要在對上面的例子稍微改動一下便可。

public class Test {
	public static void main(String[] args) {
		new Parent().print();
		new Child().print();
	}
}

class Parent {
	private String name = "base";
	public String getName() {
		return name;
	}
	public void print() {
		System.out.println(getName());
	}
}

class Child extends Parent {
	private String name = "child";
	public String getName() {
		return name;
	}
}

輸出結果:

base
child

很明顯,子類的getName()覆寫了父類的getName(),最終致使兩個getName()訪問的變量不一致。這裏值得注意的是,父類實例方法調用另外一個(子類可見的)實例方法,而且後者被子類重寫時,是存在多態的。並且可進一步驗證,即便print()方法的修飾符改成private,多態依然存在。


  • private修飾的實例方法

當方法有private修飾時,多態就不存在了。代碼以下

public class Test {
	public static void main(String[] args) {
		new Parent().print();
		new Child().print();
	}
}

class Parent {
	private String name = "base";
	private String getName() {
		return name;
	}
	public void print() {
		System.out.println(getName());
	}
}

class Child extends Parent {
	private String name = "child";
	private String getName() {
		return name;
	}
}

輸出結果

base
base

這也是能夠理解的,由於此時不存在方法的重寫,因此也不存在多態。


總結一下:

父類方法訪問實例變量只能是父類的實例變量,子類繼承父類時。從內存結構上考慮,能夠認爲子類實例所佔據的內存結構中內部有一份完整的父類實例內存結構父類實例中public/protected修飾的實例變量對於子類實例而言是可見的,而且能夠如同訪問自身實例變量同樣去訪問,固然前提是子類自身實例變量中沒有父類可見實例變量重名的。另外一方面,父類實例中private修飾的實例變量對於子類而言不可見

而當父類方法調用實例方法時,是可能存在多態的。具體說來,若是父類實例方法調用的實例方法被子類重寫,當經過子類引用調用前者時,前者所調用的實例方法將採用被子類重寫後的那個。然而,若是子類方法和父類方法並沒有重寫關係(private修飾),那麼這種多態效應將不存在,經過子類調用前者時,前者依然使用父類的方法。

相關文章
相關標籤/搜索