日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

父類引用指向子類對象

 細(xì)想生活 2015-09-15

父類引用指向子類對象指的是:

例如父類Animal,子類Cat,Dog。其中Animal可以是類也可以是接口,Cat和Dog是繼承或?qū)崿F(xiàn)Animal的子類。

Animal animal = new Cat();

即聲明的是父類,實(shí)際指向的是子類的一個(gè)對象。

那這么使用的優(yōu)點(diǎn)是什么,為什么要這么用?可以用這幾個(gè)關(guān)鍵詞來概括:多態(tài)、動態(tài)鏈接,向上轉(zhuǎn)型

也有人說這是面向接口編程,可以降低程序的耦合性,即調(diào)用者不必關(guān)心調(diào)用的是哪個(gè)對象,只需要針對接口編程就可以了,被調(diào)用者對于調(diào)用者是完全透明的。讓你更關(guān)注父類能做什么,而不去關(guān)心子類是具體怎么做的,你可以隨時(shí)替換一個(gè)子類,也就是隨時(shí)替換一個(gè)具體實(shí)現(xiàn),而不用修改其他.

以后結(jié)合設(shè)計(jì)模式(如工廠模式,代理模式)和反射機(jī)制可能有更深理解。

下面介紹java的多態(tài)性和其中的動態(tài)鏈接,向上轉(zhuǎn)型:

面向?qū)ο蟮娜齻€(gè)特征:封裝、繼承和多態(tài);

封裝隱藏了類的內(nèi)部實(shí)現(xiàn)機(jī)制,可以在不影響使用者的前提下修改類的內(nèi)部結(jié)構(gòu),同時(shí)保護(hù)了數(shù)據(jù);

繼承是為了重用父類代碼,子類繼承父類就擁有了父類的成員。

方法的重寫、重載與動態(tài)連接構(gòu)成多態(tài)性。Java之所以引入多態(tài)的概念,原因之一是它在類的繼承問題上和C++不同,后者允許多繼承,這確實(shí)給其帶來的非常強(qiáng)大的功能,但是復(fù)雜的繼承關(guān)系也給C++開發(fā)者帶來了更大的麻煩,為了規(guī)避風(fēng)險(xiǎn),Java只允許單繼承,派生類與基類間有IS-A的關(guān)系(即“貓”is a “動物”)。這樣做雖然保證了繼承關(guān)系的簡單明了,但是勢必在功能上有很大的限制,所以,Java引入了多態(tài)性的概念以彌補(bǔ)這點(diǎn)的不足,此外,抽象類和接口也是解決單繼承規(guī)定限制的重要手段。同時(shí),多態(tài)也是面向?qū)ο缶幊痰木杷凇?

理解多態(tài),首先要知道“向上轉(zhuǎn)型”。

我定義了一個(gè)子類Cat,它繼承了Animal類,那么后者就是前者是父類。我可以通過

Cat c = new Cat();
實(shí)例化一個(gè)Cat的對象,這個(gè)不難理解。但當(dāng)我這樣定義時(shí):

Animal a = new Cat();
這代表什么意思呢?

很簡單,它表示我定義了一個(gè)Animal類型的引用,指向新建的Cat類型的對象。由于Cat是繼承自它的父類Animal,所以Animal類型的引用是可以指向Cat類型的對象的。這就是“向上轉(zhuǎn)型”。

那么這樣做有什么意義呢?因?yàn)樽宇愂菍Ω割惖囊粋€(gè)改進(jìn)和擴(kuò)充,所以一般子類在功能上較父類更強(qiáng)大,屬性較父類更獨(dú)特, 定義一個(gè)父類類型的引用指向一個(gè)子類的對象既可以使用子類強(qiáng)大的功能,又可以抽取父類的共性。 所以,父類類型的引用可以調(diào)用父類中定義的所有屬性和方法,而對于子類中定義而父類中沒有的方法,父類引用是無法調(diào)用的;

那什么是動態(tài)鏈接呢?當(dāng)父類中的一個(gè)方法只有在父類中定義而在子類中沒有重寫的情況下,才可以被父類類型的引用調(diào)用; 對于父類中定義的方法,如果子類中重寫了該方法,那么父類類型的引用將會調(diào)用子類中的這個(gè)方法,這就是動態(tài)連接。

下面看一下典型的多態(tài)例子:

  1. class Father{
  2. public void func1(){
  3. func2();
  4. }
  5. //這是父類中的func2()方法,因?yàn)橄旅娴淖宇愔兄貙懥嗽摲椒?
  6. //所以在父類類型的引用中調(diào)用時(shí),這個(gè)方法將不再有效
  7. //取而代之的是將調(diào)用子類中重寫的func2()方法
  8. public void func2(){
  9. System.out.println("AAA");
  10. }
  11. }
  12. class Child extends Father{
  13. //func1(int i)是對func1()方法的一個(gè)重載,主要不是重寫!
  14. //由于在父類中沒有定義這個(gè)方法,所以它不能被父類類型的引用調(diào)用
  15. //所以在下面的main方法中child.func1(68)是不對的
  16. public void func1(int i){
  17. System.out.println("BBB");
  18. }
  19. //func2()重寫了父類Father中的func2()方法
  20. //如果父類類型的引用中調(diào)用了func2()方法,那么必然是子類中重寫的這個(gè)方法
  21. public void func2(){
  22. System.out.println("CCC");
  23. }
  24. }
  25. public class PolymorphismTest {
  26. public static void main(String[] args) {
  27. Father child = new Child();
  28. child.func1();//打印結(jié)果將會是什么?
  29. child.func1(68);
  30. }
  31. }
  1. class Father{   
  2.     public void func1(){   
  3.         func2();   
  4.     }   
  5.     //這是父類中的func2()方法,因?yàn)橄旅娴淖宇愔兄貙懥嗽摲椒?nbsp;  
  6.     //所以在父類類型的引用中調(diào)用時(shí),這個(gè)方法將不再有效   
  7.     //取而代之的是將調(diào)用子類中重寫的func2()方法   
  8.     public void func2(){   
  9.         System.out.println("AAA");   
  10.     }   
  11. }   
  12.     
  13. class Child extends Father{   
  14.     //func1(int i)是對func1()方法的一個(gè)重載,主要不是重寫!  
  15.     //由于在父類中沒有定義這個(gè)方法,所以它不能被父類類型的引用調(diào)用   
  16.     //所以在下面的main方法中child.func1(68)是不對的   
  17.     public void func1(int i){   
  18.         System.out.println("BBB");   
  19.     }   
  20.     //func2()重寫了父類Father中的func2()方法   
  21.     //如果父類類型的引用中調(diào)用了func2()方法,那么必然是子類中重寫的這個(gè)方法   
  22.     public void func2(){   
  23.         System.out.println("CCC");   
  24.     }   
  25. }   
  26.     
  27. public class PolymorphismTest {   
  28.     public static void main(String[] args) {   
  29.         Father child = new Child();   
  30.         child.func1();//打印結(jié)果將會是什么?    
  31.         child.func1(68);  
  32.     }   
  33. }   

上面的程序是個(gè)很典型的多態(tài)的例子。子類Child繼承了父類Father,并重載了父類的func1()方法,重寫了父類的func2()方法。重載后的func1(int i)和func1()不再是同一個(gè)方法,由于父類中沒有func1(int i),那么,父類類型的引用child就不能調(diào)用func1(int i)方法。而子類重寫了func2()方法,那么父類類型的引用child在調(diào)用該方法時(shí)將會調(diào)用子類中重寫的func2()。

那么該程序?qū)蛴〕鍪裁礃拥慕Y(jié)果呢?
很顯然,應(yīng)該是“CCC”。

對于多態(tài),可以總結(jié)以下幾點(diǎn):

一、使用父類類型的引用指向子類的對象;
二、該引用只能調(diào)用父類中定義的方法和變量;
三、如果子類中重寫了父類中的一個(gè)方法,那么在調(diào)用這個(gè)方法的時(shí)候,將會調(diào)用子類中的這個(gè)方法;(動態(tài)連接、動態(tài)調(diào)用)
四、變量不能被重寫(覆蓋),”重寫“的概念只針對方法,如果在子類中”重寫“了父類中的變量,那么在編譯時(shí)會報(bào)錯(cuò)。

另轉(zhuǎn)載:

多態(tài)是通過:
1 接口 和 實(shí)現(xiàn)接口并覆蓋接口中同一方法的幾不同的類體現(xiàn)的
2 父類 和 繼承父類并覆蓋父類中同一方法的幾個(gè)不同子類實(shí)現(xiàn)的.

一、基本概念

多態(tài)性:發(fā)送消息給某個(gè)對象,讓該對象自行決定響應(yīng)何種行為。
通過將子類對象引用賦值給超類對象引用變量來實(shí)現(xiàn)動態(tài)方法調(diào)用。

java 的這種機(jī)制遵循一個(gè)原則:當(dāng)超類對象引用變量引用子類對象時(shí),被引用對象的類型而不是引用變量的類型決定了調(diào)用誰的成員方法,但是這個(gè)被調(diào)用的方法必須是在超類中定義過的,也就是說被子類覆蓋的方法。

1. 如果a是類A的一個(gè)引用,那么,a可以指向類A的一個(gè)實(shí)例,或者說指向類A的一個(gè)子類。
2. 如果a是接口A的一個(gè)引用,那么,a必須指向?qū)崿F(xiàn)了接口A的一個(gè)類的實(shí)例。


二、Java多態(tài)性實(shí)現(xiàn)機(jī)制

SUN目前的JVM實(shí)現(xiàn)機(jī)制,類實(shí)例的引用就是指向一個(gè)句柄(handle)的指針,這個(gè)句柄是一對指針:
一個(gè)指針指向一張表格,實(shí)際上這個(gè)表格也有兩個(gè)指針(一個(gè)指針指向一個(gè)包含了對象的方法表,另外一個(gè)指向類對象,表明該對象所屬的類型);
另一個(gè)指針指向一塊從java堆中為分配出來內(nèi)存空間。

三、總結(jié)

1、通過將子類對象引用賦值給超類對象引用變量來實(shí)現(xiàn)動態(tài)方法調(diào)用。

DerivedC c2=new DerivedC();
BaseClass a1= c2; //BaseClass 基類,DerivedC是繼承自BaseClass的子類
a1.play(); //play()在BaseClass,DerivedC中均有定義,即子類覆寫了該方法

分析:
* 為什么子類的類型的對象實(shí)例可以覆給超類引用?
自動實(shí)現(xiàn)向上轉(zhuǎn)型。通過該語句,編譯器自動將子類實(shí)例向上移動,成為通用類型BaseClass;
* a.play()將執(zhí)行子類還是父類定義的方法?
子類的。在運(yùn)行時(shí)期,將根據(jù)a這個(gè)對象引用實(shí)際的類型來獲取對應(yīng)的方法。所以才有多態(tài)性。一個(gè)基類的對象引用,被賦予不同的子類對象引用,執(zhí)行該方法時(shí),將表現(xiàn)出不同的行為。

在a1=c2的時(shí)候,仍然是存在兩個(gè)句柄,a1和c2,但是a1和c2擁有同一塊數(shù)據(jù)內(nèi)存塊和不同的函數(shù)表。

2、不能把父類對象引用賦給子類對象引用變量

BaseClass a2=new BaseClass();
DerivedC c1=a2;//出錯(cuò)

在java里面,向上轉(zhuǎn)型是自動進(jìn)行的,但是向下轉(zhuǎn)型卻不是,需要我們自己定義強(qiáng)制進(jìn)行。
c1=(DerivedC)a2; 進(jìn)行強(qiáng)制轉(zhuǎn)化,也就是向下轉(zhuǎn)型.

3、記住一個(gè)很簡單又很復(fù)雜的規(guī)則,一個(gè)類型引用只能引用引用類型自身含有的方法和變量。
你可能說這個(gè)規(guī)則不對的,因?yàn)楦割愐弥赶蜃宇悓ο蟮臅r(shí)候,最后執(zhí)行的是子類的方法的。
其實(shí)這并不矛盾,那是因?yàn)椴捎昧撕笃诮壎?,動態(tài)運(yùn)行的時(shí)候又根據(jù)型別去調(diào)用了子類的方法。而假若子類的這個(gè)方法在父類中并沒有定義,則會出錯(cuò)。
例如,DerivedC類在繼承BaseClass中定義的函數(shù)外,還增加了幾個(gè)函數(shù)(例如 myFun())

分析:
當(dāng)你使用父類引用指向子類的時(shí)候,其實(shí)jvm已經(jīng)使用了編譯器產(chǎn)生的類型信息調(diào)整轉(zhuǎn)換了。
這里你可以這樣理解,相當(dāng)于把不是父類中含有的函數(shù)從虛擬函數(shù)表中設(shè)置為不可見的。注意有可能虛擬函數(shù)表中有些函數(shù)地址由于在子類中已經(jīng)被改寫了,所以對象虛擬函數(shù)表中虛擬函數(shù)項(xiàng)目地址已經(jīng)被設(shè)置為子類中完成的方法體的地址了。

4、Java與C++多態(tài)性的比較

jvm關(guān)于多態(tài)性支持解決方法是和c++中幾乎一樣的,
只是c++中編譯器很多是把類型信息和虛擬函數(shù)信息都放在一個(gè)虛擬函數(shù)表中,但是利用某種技術(shù)來區(qū)別。

Java把類型信息和函數(shù)信息分開放。Java中在繼承以后,子類會重新設(shè)置自己的虛擬函數(shù)表,這個(gè)虛擬函數(shù)表中的項(xiàng)目有由兩部分組成。從父類繼承的虛擬函數(shù)和子類自己的虛擬函數(shù)。
虛擬函數(shù)調(diào)用是經(jīng)過虛擬函數(shù)表間接調(diào)用的,所以才得以實(shí)現(xiàn)多態(tài)的。

Java的所有函數(shù),除了被聲明為final的,都是用后期綁定。

四. 示例:1個(gè)行為,不同的對象,他們具體體現(xiàn)出來的方式不一樣,
比如: 方法重載 overloading 以及 方法重寫(覆蓋)override
class Human{
void run(){輸出 人在跑}
}
class Man extends Human{
void run(){輸出 男人在跑}
}
這個(gè)時(shí)候,同是跑,不同的對象,不一樣(這個(gè)是方法覆蓋的例子)
class Test{
void out(String str){輸出 str}
void out(int i){輸出 i}
}
這個(gè)例子是方法重載,方法名相同,參數(shù)表不同

ok,明白了這些還不夠,還用人在跑舉例
Human ahuman=new Man();
這樣我等于實(shí)例化了一個(gè)Man的對象,并聲明了一個(gè)Human的引用,讓它去指向Man這個(gè)對象
意思是說,把 Man這個(gè)對象當(dāng) Human看了.

比如去動物園,你看見了一個(gè)動物,不知道它是什么, "這是什么動物? " "這是大熊貓! "
這2句話,就是最好的證明,因?yàn)椴恢浪谴笮茇?但知道它的父類是動物,所以,
這個(gè)大熊貓對象,你把它當(dāng)成其父類 動物看,這樣子合情合理.

這種方式下要注意 new Man();的確實(shí)例化了Man對象,所以 ahuman.run()這個(gè)方法 輸出的 是 "男人在跑 "

如果在子類 Man下你 寫了一些它獨(dú)有的方法 比如 eat(),而Human沒有這個(gè)方法, 在調(diào)用eat方法時(shí),一定要注意 強(qiáng)制類型轉(zhuǎn)換 ((Man)ahuman).eat(),這樣才可以... 對接口來說,情況是類似的...

  1. 實(shí)例:
  2. package domatic;
  3. //定義超類superA
  4. class superA {
  5. int i = 100;
  6. void fun(int j) {
  7. j = i;
  8. System.out.println("This is superA");
  9. }
  10. }
  11. // 定義superA的子類subB
  12. class subB extends superA {
  13. int m = 1;
  14. void fun(int aa) {
  15. System.out.println("This is subB");
  16. }
  17. }
  18. // 定義superA的子類subC
  19. class subC extends superA {
  20. int n = 1;
  21. void fun(int cc) {
  22. System.out.println("This is subC");
  23. }
  24. }
  25. class Test {
  26. public static void main(String[] args) {
  27. superA a = new superA();
  28. subB b = new subB();
  29. subC c = new subC();
  30. a = b;
  31. a.fun(100);
  32. a = c;
  33. a.fun(200);
  34. }
  35. }
  1. 實(shí)例:   
  2. package domatic;   
  3.   //定義超類superA   
  4.   class superA {   
  5.     int i = 100;   
  6.     void fun(int j) {   
  7.       j = i;   
  8.       System.out.println("This is superA");   
  9.     }   
  10.   }   
  11. // 定義superA的子類subB   
  12. class subB extends superA {   
  13.    int m = 1;   
  14.    void fun(int aa) {   
  15.      System.out.println("This is subB");   
  16.    }   
  17. }   
  18. // 定義superA的子類subC   
  19. class subC extends superA {   
  20.   int n = 1;   
  21.   void fun(int cc) {   
  22.     System.out.println("This is subC");   
  23.   }   
  24. }   
  25. class Test {   
  26.   public static void main(String[] args) {   
  27.     superA a = new superA();   
  28.     subB b = new subB();   
  29.     subC c = new subC();   
  30.     a = b;   
  31.     a.fun(100);   
  32.     a = c;   
  33.     a.fun(200);   
  34.   }   
  35. }   

/*
* 上述代碼中subB和subC是超類superA的子類,我們在類Test中聲明了3個(gè)引用變量a, b,
* c,通過將子類對象引用賦值給超類對象引用變量來實(shí)現(xiàn)動態(tài)方法調(diào)用。也許有人會問:
* "為什么(1)和(2)不輸出:This is superA"。
* java的這種機(jī)制遵循一個(gè)原則:當(dāng)超類對象引用變量引用子類對象時(shí),
* 被引用對象的類型而不是引用變量的類型決定了調(diào)用誰的成員方法,
* 但是這個(gè)被調(diào)用的方法必須是在超類中定義過的,
* 也就是說被子類覆蓋的方法。
* 所以,不要被上例中(1)和(2)所迷惑,雖然寫成a.fun(),但是由于(1)中的a被b賦值,
* 指向了子類subB的一個(gè)實(shí)例,因而(1)所調(diào)用的fun()實(shí)際上是子類subB的成員方法fun(),
* 它覆蓋了超類superA的成員方法fun();同樣(2)調(diào)用的是子類subC的成員方法fun()。
* 另外,如果子類繼承的超類是一個(gè)抽象類,雖然抽象類不能通過new操作符實(shí)例化,
* 但是可以創(chuàng)建抽象類的對象引用指向子類對象,以實(shí)現(xiàn)運(yùn)行時(shí)多態(tài)性。具體的實(shí)現(xiàn)方法同上例。
* 不過,抽象類的子類必須覆蓋實(shí)現(xiàn)超類中的所有的抽象方法,
* 否則子類必須被abstract修飾符修飾,當(dāng)然也就不能被實(shí)例化了
*/



1.JAVA里沒有多繼承,一個(gè)類之能有一個(gè)父類。而繼承的表現(xiàn)就是多態(tài)。一個(gè)父類可以有多個(gè)子類,而在子類里可以重寫父類的方法(例如方法print()),這樣每個(gè)子類里重寫的代碼不一樣,自然表現(xiàn)形式就不一樣。這樣用父類的變量去引用不同的子類,在調(diào)用這個(gè)相同的方法print()的時(shí)候得到的結(jié)果和表現(xiàn)形式就不一樣了,這就是多態(tài),相同的消息(也就是調(diào)用相同的方法)會有不同的結(jié)果。舉例說明:

  1. //父類
  2. public class Father{
  3. //父類有一個(gè)打孩子方法
  4. public void hitChild(){
  5. }
  6. }
  7. //子類1
  8. public class Son1 extends Father{
  9. //重寫父類打孩子方法
  10. public void hitChild(){
  11. System.out.println("為什么打我?我做錯(cuò)什么了!");
  12. }
  13. }
  14. //子類2
  15. public class Son2 extends Father{
  16. //重寫父類打孩子方法
  17. public void hitChild(){
  18. System.out.println("我知道錯(cuò)了,別打了!");
  19. }
  20. }
  21. //子類3
  22. public class Son3 extends Father{
  23. //重寫父類打孩子方法
  24. public void hitChild(){
  25. System.out.println("我跑,你打不著!");
  26. }
  27. }
  28. //測試類
  29. public class Test{
  30. public static void main(String args[]){
  31. Father father;
  32. father = new Son1();
  33. father.hitChild();
  34. father = new Son2();
  35. father.hitChild();
  36. father = new Son3();
  37. father.hitChild();
  38. }
  39. }
  1. //父類   
  2. public class Father{   
  3.     //父類有一個(gè)打孩子方法   
  4.     public void hitChild(){   
  5.     }   
  6. }   
  7. //子類1   
  8. public class Son1 extends Father{   
  9.     //重寫父類打孩子方法   
  10.     public void hitChild(){   
  11.       System.out.println("為什么打我?我做錯(cuò)什么了!");   
  12.     }   
  13. }   
  14. //子類2   
  15. public class Son2 extends Father{   
  16.     //重寫父類打孩子方法   
  17.     public void hitChild(){   
  18.       System.out.println("我知道錯(cuò)了,別打了!");   
  19.     }   
  20. }   
  21. //子類3   
  22. public class Son3 extends Father{   
  23.     //重寫父類打孩子方法   
  24.     public void hitChild(){   
  25.       System.out.println("我跑,你打不著!");   
  26.     }   
  27. }   
  28. //測試類   
  29. public class Test{   
  30.     public static void main(String args[]){   
  31.       Father father;   
  32.       father = new Son1();   
  33.       father.hitChild();   
  34.       father = new Son2();   
  35.       father.hitChild();   
  36.       father = new Son3();   
  37.       father.hitChild();   
  38.     }   
  39. }   

都調(diào)用了相同的方法,出現(xiàn)了不同的結(jié)果!這就是多態(tài)的表現(xiàn)。

上面的示例也就是工廠模式的一個(gè)簡單體現(xiàn)

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多