父類引用指向子類對象指的是:java
例如父類Animal,子類Cat,Dog。其中Animal能夠是類也能夠是接口,Cat和Dog是繼承或實現Animal的子類。markdown
Animal animal = new Cat();
即聲明的是父類,實際指向的是子類的一個對象。ide
那咱們從內存角度來理解試試.
假設如今有一個父類Father,它裏面的變量須要佔用1M內存.
有一個它的子類Son,它裏面的變量須要佔用0.5M內存.
如今經過代碼來看看內存的分配狀況:函數
Father f = new Father();//系統將分配1M內存.
Son s = new Son();//系統將分配1.5M內存!
由於子類中有一個隱藏的引用super會指向父類實例,因此在實例化子類以前會先實例化一個父類,也就是說會先執行父類的構造函數.因爲s中包含了父類的實例,因此s能夠調用父類的方法.測試
Son s1 = s; //s1指向那1.5M的內存.
Father f1 = (Father)s;//這時f1會指向那1.5M內存中的1M內存
f1只是指向了s中實例的父類實例對象,因此f1只能調用父類的方法(存儲在1M內存中),而不能調用子類的方法(存儲在0.5M內存中).spa
Son s2 = (Son)f;//這句代碼運行時會報ClassCastException.
由於 f 中只有1M內存,而子類的引用都必需要有1.5M的內存,因此沒法轉換.code
Son s3 = (Son)f1;//這句能夠經過運行,這時s3指向那1.5M的內存.
因爲f1是由s轉換過來的,因此它是有1.5M的內存的,只是它指向的只有1M內存.
對象
若是可以理解對象在內存的分佈,那咱們接下來就介紹java的多態性繼承
方法的重寫、重載和動態連接構成了java的多態性。Java之因此引入多態的概念,緣由之一是它在類的繼承問題上和C++不一樣,後者容許多繼承,這確實給其帶來的很是強大的功能,可是複雜的繼承關係也給C++開發者帶來了更大的麻煩,爲了規避風險,Java只容許單繼承,派生類與基類間有IS-A的關係(即「貓」is a 「動物」)。這樣作雖然保證了繼承關係的簡單明瞭,可是勢必在功能上有很大的限制,因此,Java引入了多態性的概念以彌補這點的不足。接口
理解多態性,首先要理解的就是「向上轉型」
Animal c = new Cat();
它表示我定義了一個Animal類型的引用,指向新建的Cat類型的對象。因爲Cat是繼承自它的父類Animal,因此Animal類型的引用是能夠指向Cat類型的對象的。這就是「向上轉型」。
注意:java中「向上轉型」是自動的。可是「向下轉型」卻不是自動的。須要咱們用強制類型轉化。
Animal c = new Cat();
Cat c1 = (Cat)c; //不容許「向下轉型」,需用強制類型轉化。
那麼這樣作有什麼意義呢?由於子類是對父類的改進和擴充。定義一個父類類型的引用指向一個子類的對象既可使用子類強大的功能,又能夠抽取父類的共性。 可是父類類型的引用能夠調用父類中定義的全部屬性和方法,而對於子類中定義而父類中沒有的方法,父類引用是沒法調用的;
要作到父類引用調用子類的屬性或者方法,就還要有動態鏈接。那什麼是動態連接呢?當父類中的一個方法只有在父類中定義而在子類中沒有被重寫的狀況下,才能夠被父類類型的引用調用; 對於父類中定義的方法,若是子類中重寫了該方法,那麼父類類型的引用將會調用子類中的這個方法,這就是動態鏈接。
JAVA裏沒有多繼承,一個類只能有一個父類。而繼承的表現就是多態。一個父類能夠有多個子類,而在子類裏能夠重寫父類的方法,這樣每一個子類裏重寫的代碼不同,天然表現形式就不同。這樣用父類的變量去引用不一樣的子類,在調用這個相同的方法的時候獲得的結果和表現形式就不同了,這就是多態,相同的消息(也就是調用相同的方法)會有不一樣的結果。舉例說明:
下面看一個多態性的例子
//父類
public class Father{
//父類有一個打孩子方法
public void hitChild(){
}
}
//子類1
public class Son1 extends Father{
//重寫父類打孩子方法
public void hitChild(){
System.out.println("爲何打我?我作錯什麼了!");
}
}
//子類2
public class Son2 extends Father{
//重寫父類打孩子方法
public void hitChild(){
System.out.println("我知道錯了,別打了!");
}
}
//子類3
public class Son3 extends Father{
//重寫父類打孩子方法
public void hitChild(){
System.out.println("我跑,你打不着!");
}
}
//測試類
public class Test{
public static void main(String args[]){
Father father;
father = new Son1();
father.hitChild();
father = new Son2();
father.hitChild();
father = new Son3();
father.hitChild();
}
}
上面程序調用同一個方法,去出現不一樣的結果。這就是多態。
對於多態性的實現有必要重視方法重載(overloading)和方法重寫(override)區別
class Father{
public void func1(){
System.out.println("AAA");
}
}
class Child extends Father{
//func1(int i)是對func1()方法的一個重載,主要不是重寫!
//因爲在父類中沒有定義這個方法,因此它不能被父類類型的引用調用
//因此在下面的main方法中child.func1(68)是不對的
public void func1(int i){
System.out.println("BBB");
}
}
public class PolymorphismTest {
public static void main(String[] args) {
Father child = new Child();
child.func1(68);//錯誤
}
}
上面的程序是個很典型的多態的例子。子類Child繼承了父類Father,並重載了父類的func1()方法。重載後的func1(int i)和func1()再也不是同一個方法,因爲父類中沒有func1(int i),那麼,父類類型的引用child就不能調用func1(int i)方法。
最後,對於多態作出幾點總結
一、使用父類類型的引用指向子類的對象;
二、該引用只能調用父類中定義的方法和變量;
三、若是子類中重寫了父類中的一個方法,那麼在調用這個方法的時候,將會調用子類中的這個方法;(動態鏈接、動態調用)
四、變量不能被重寫(覆蓋),」重寫「的概念只針對方法,若是在子類中」重寫「了父類中的變量,那麼在編譯時會報錯。