從對(duì)象的內(nèi)存角度來(lái)理解試試.
假設(shè)現(xiàn)在有一個(gè)父類(lèi)Father,它里面的變量需要占用1M內(nèi)存.有一個(gè)它的子類(lèi)Son,它里面的變量需要占用0.5M內(nèi)存.
現(xiàn)在通過(guò)代碼來(lái)看看內(nèi)存的分配情況:
Father f = new Father();//系統(tǒng)將分配1M內(nèi)存.
Son s = new Son();//系統(tǒng)將分配1.5M內(nèi)存.
因?yàn)樽宇?lèi)中有一個(gè)隱藏的引用super會(huì)指向父類(lèi)實(shí)例,所以在實(shí)例化子類(lèi)之前會(huì)先實(shí)例化一個(gè)父類(lèi),也就是說(shuō)會(huì)先執(zhí)行父類(lèi)的構(gòu)造函數(shù).
由于s中包含了父類(lèi)的實(shí)例,所以s可以調(diào)用父類(lèi)的方法.
Son s1 = s;//s1指向那1.5M的內(nèi)存.(可以理解為就是:子類(lèi)引用指向子類(lèi)對(duì)象)
Father f1 = (Father)s;//這時(shí)f1會(huì)指向那1.5M內(nèi)存中的1M內(nèi)存,即是說(shuō),f1只是指向了s中實(shí)例的父類(lèi)實(shí)例對(duì)象,所以f1只能調(diào)用父類(lèi)的方法(存儲(chǔ)在1M內(nèi)存中),而不能調(diào)用子類(lèi)的方法(存儲(chǔ)在0.5M內(nèi)存中).
Son s2 = (Son)f;//這句代碼運(yùn)行時(shí)會(huì)報(bào)ClassCastException.因?yàn)閒中只有1M內(nèi)存,而子類(lèi)的引用都必須要有1.5M的內(nèi)存,所以無(wú)法轉(zhuǎn)換.
Son s3 = (Son)f1;//這句可以通過(guò)運(yùn)行,這時(shí)s3指向那1.5M的內(nèi)存.由于f1是由s轉(zhuǎn)換過(guò)來(lái)的,所以它是有1.5M的內(nèi)存的,只是它指向的只有1M內(nèi)存.
示例:
class Father{
void print(){};
}
class Son extends Father{
void print(){System.out.println("子類(lèi)中!");}
void show(){System.out.println("show 中!");}
}
class Demo{
public static void main(String args[]){
Father obj=new Son();
obj.print();
obj.show(); //這個(gè)調(diào)用會(huì)報(bào)錯(cuò)!
}
}
1 .如果你想實(shí)現(xiàn)多態(tài),那么必須有三個(gè)條件,父類(lèi)引用,子類(lèi)對(duì)象,方法覆蓋
你這里如果Fathor類(lèi)有一個(gè)show()方法,那么形成方法覆蓋,那么此時(shí)就可以這么寫(xiě):obj.show(),此刻形成了多態(tài).
2. 沒(méi)有方法覆蓋,那你這里只能解釋為父類(lèi)引用去訪問(wèn)一個(gè)子類(lèi)的方法,當(dāng)然,父類(lèi)引用沒(méi)有這么大范圍的權(quán)限,當(dāng)然會(huì)報(bào)錯(cuò)
PS:多態(tài)實(shí)際上是一種機(jī)制,在編譯時(shí)刻,會(huì)生成一張?zhí)摂M表,來(lái)記錄所有覆蓋的方法,沒(méi)有被覆蓋的方法是不會(huì)記錄到這張表的.
若一個(gè)父類(lèi)引用調(diào)用了沒(méi)有覆蓋的子類(lèi)方法,那么是不符合該表的,那么編譯時(shí)刻就會(huì)報(bào)錯(cuò).
在執(zhí)行程序的時(shí)候,虛擬機(jī)會(huì)去這張?zhí)摂M表中找覆蓋的方法,比如引用中實(shí)際上存的是一個(gè)子類(lèi)對(duì)象引用,那么就會(huì)去找子類(lèi)中的相應(yīng)的覆蓋的方法來(lái)執(zhí)行
定義一個(gè)父類(lèi)類(lèi)型的引用指向一個(gè)子類(lèi)的對(duì)象既可以使用子類(lèi)強(qiáng)大的功能,又可以抽取父類(lèi)的共性。
所以,父類(lèi)類(lèi)型的引用可以調(diào)用父類(lèi)中定義的所有屬性和方法,而對(duì)于子類(lèi)中定義而父類(lèi)中沒(méi)有的方法,它是無(wú)可奈何的;
同時(shí),父類(lèi)中的一個(gè)方法只有在在父類(lèi)中定義而在子類(lèi)中沒(méi)有重寫(xiě)的情況下,才可以被父類(lèi)類(lèi)型的引用調(diào)用;
對(duì)多態(tài)的理解:多態(tài)體現(xiàn)在繼承中,所以需要有繼承關(guān)系,然后子類(lèi)要重寫(xiě)父類(lèi)方法,最后父類(lèi)指向子類(lèi)(父類(lèi)本身具有一些方法,這些方法被子類(lèi)重寫(xiě)了,但調(diào)用這些方法時(shí),會(huì)自動(dòng)調(diào)子類(lèi)重寫(xiě)的那些)。
多態(tài)具體表現(xiàn)在重寫(xiě)和重載,多態(tài)就是類(lèi)的多種表現(xiàn)方式,比如同名不同參,子類(lèi)重寫(xiě)父類(lèi)。
看下面這段程序:
class Father{
public void func1(){
func2();
}
//這是父類(lèi)中的func2()方法,因?yàn)橄旅娴淖宇?lèi)中重寫(xiě)了該方法
//所以在父類(lèi)類(lèi)型的引用中調(diào)用時(shí),這個(gè)方法將不再有效
//取而代之的是將調(diào)用子類(lèi)中重寫(xiě)的func2()方法
public void func2(){
System.out.println("AAA");
}
}
class Child extends Father{
//func1(int i)是對(duì)func1()方法的一個(gè)重載
//由于在父類(lèi)中沒(méi)有定義這個(gè)方法,所以它不能被父類(lèi)類(lèi)型的引用調(diào)用
//所以在下面的main方法中child.func1(68)是不對(duì)的
public void func1(int i){
System.out.println("BBB");
}
//func2()重寫(xiě)了父類(lèi)Father中的func2()方法
//如果父類(lèi)類(lèi)型的引用中調(diào)用了func2()方法,那么必然是子類(lèi)中重寫(xiě)的這個(gè)方法
public void func2(){
System.out.println("CCC");
}
}
public class PolymorphismTest {
public static void main(String[] args) {
Father child = new Child();
child.func1();//打印結(jié)果將會(huì)是什么?
}
}
上面的程序是個(gè)很典型的多態(tài)的例子。子類(lèi)Child繼承了父類(lèi)Father,并重載了父類(lèi)的func1()方法,重寫(xiě)了父類(lèi)的func2()方法。重載后的func1(int
i)和func1()不再是同一個(gè)方法,由于父類(lèi)中沒(méi)有func1(int i),那么,父類(lèi)類(lèi)型的引用child就不能調(diào)用func1(int
i)方法。而子類(lèi)重寫(xiě)了func2()方法,那么父類(lèi)類(lèi)型的引用child在調(diào)用該方法時(shí)將會(huì)調(diào)用子類(lèi)中重寫(xiě)的func2()。
那么該程序?qū)?huì)打印出什么樣的結(jié)果呢?
很顯然,應(yīng)該是“CCC”。
變量是不存在重寫(xiě)覆蓋的!
public class A { int a = 1; }
public class B extends A { int a = 2; }
測(cè)試類(lèi)里調(diào)用了這個(gè)方法void compare(){
if(super.a == this.a)
System.out.println("not overrided");
else
System.out.println("overrided");}
控制臺(tái)出來(lái)的是overrided
類(lèi)中的屬性是沒(méi)有多態(tài)性的,即你在引用上面使用屬性時(shí),系統(tǒng)只會(huì)去找引用的靜態(tài)類(lèi)型中的那個(gè)屬性,而與它的實(shí)際類(lèi)型無(wú)關(guān)。
靜態(tài)方法也是沒(méi)有多態(tài)性的。