學(xué)習(xí)java的同學(xué)注意了!!! 學(xué)習(xí)過程中遇到什么問題或者想獲取學(xué)習(xí)資源的話,歡迎加入Java學(xué)習(xí)交流群,群號碼:523047986 我們一起學(xué)Java!

1 package javastudy.summary; 2 3 class Animal { 4 /** 5 * 聲明一個私有的成員變量name。 6 */ 7 PRivate String name; 8 9 /** 10 * 在Animal類自定義的構(gòu)造方法 11 * @param name 12 */ 13 Animal(String name) { 14 this.name = name; 15 } 16 17 /** 18 * 在Animal類里面自定義一個方法enjoy 19 */ 20 public void enjoy() { 21 System.out.println("動物的叫聲……"); 22 } 23 } 24 25 /** 26 * 子類Cat從父類Animal繼承下來,Cat類擁有了Animal類所有的屬性和方法。 27 * @author gacl 28 * 29 */ 30 class Cat extends Animal { 31 /** 32 * 在子類Cat里面定義自己的私有成員變量 33 */ 34 private String eyesColor; 35 36 /** 37 * 在子類Cat里面定義Cat類的構(gòu)造方法 38 * @param n 39 * @param c 40 */ 41 Cat(String n, String c) { 42 /** 43 * 在構(gòu)造方法的實現(xiàn)里面首先使用super調(diào)用父類Animal的構(gòu)造方法Animal(String name)。 44 * 把子類對象里面的父類對象先造出來。 45 */ 46 super(n); 47 eyesColor = c; 48 } 49 50 /** 51 * 子類Cat對從父類Animal繼承下來的enjoy方法不滿意,在這里重寫了enjoy方法。 52 */ 53 public void enjoy() { 54 System.out.println("我養(yǎng)的貓高興地叫了一聲……"); 55 } 56 } 57 58 /** 59 * 子類Dog從父類Animal繼承下來,Dog類擁有了Animal類所有的屬性和方法。 60 * @author gacl 61 * 62 */ 63 class Dog extends Animal { 64 /** 65 * 在子類Dog里面定義自己的私有成員變量 66 */ 67 private String furColor; 68 69 /** 70 * 在子類Dog里面定義Dog類的構(gòu)造方法 71 * @param n 72 * @param c 73 */ 74 Dog(String n, String c) { 75 /** 76 * 在構(gòu)造方法的實現(xiàn)里面首先使用super調(diào)用父類Animal的構(gòu)造方法Animal(String name)。 77 * 把子類對象里面的父類對象先造出來。 78 */ 79 super(n); 80 furColor = c; 81 } 82 83 /** 84 * 子類Dog對從父類Animal繼承下來的enjoy方法不滿意,在這里重寫了enjoy方法。 85 */ 86 public void enjoy() { 87 System.out.println("我養(yǎng)的狗高興地叫了一聲……"); 88 } 89 } 90 91 /** 92 * 子類Bird從父類Animal繼承下來,Bird類擁有Animal類所有的屬性和方法 93 * @author gacl 94 * 95 */ 96 class Bird extends Animal { 97 /** 98 * 在子類Bird里面定義Bird類的構(gòu)造方法 99 */100 Bird() {101 /**102 * 在構(gòu)造方法的實現(xiàn)里面首先使用super調(diào)用父類Animal的構(gòu)造方法Animal(String name)。103 * 把子類對象里面的父類對象先造出來。104 */105 super("bird");106 }107 108 /**109 * 子類Bird對從父類Animal繼承下來的enjoy方法不滿意,在這里重寫了enjoy方法。110 */111 public void enjoy() {112 System.out.println("我養(yǎng)的鳥高興地叫了一聲……");113 }114 }115 116 /**117 * 定義一個類Lady(女士)118 * @author gacl119 *120 */121 class Lady {122 /**123 * 定義Lady類的私有成員變量name和pet124 */125 private String name;126 private Animal pet;127 128 /**129 * 在Lady類里面定義自己的構(gòu)造方法Lady(),130 * 這個構(gòu)造方法有兩個參數(shù),分別為String類型的name和Animal類型的pet,131 * 這里的第二個參數(shù)設(shè)置成Animal類型可以給我們的程序帶來最大的靈活性,132 * 因為作為養(yǎng)寵物來說,可以養(yǎng)貓,養(yǎng)狗,養(yǎng)鳥,只要是你喜歡的都可以養(yǎng),133 * 因此把它設(shè)置為父類對象的引用最為靈活。134 * 因為這個Animal類型的參數(shù)是父類對象的引用類型,因此當(dāng)我們傳參數(shù)的時候,135 * 可以把這個父類的子類對象傳過去,即傳Dog、Cat和Bird等都可以。136 * @param name137 * @param pet138 */139 Lady(String name, Animal pet) {140 this.name = name;141 this.pet = pet;142 }143 144 /**145 * 在Lady類里面自定義一個方法myPetEnjoy()146 * 方法體內(nèi)是讓Lady對象養(yǎng)的寵物自己調(diào)用自己的enjoy()方法發(fā)出自己的叫聲。147 */148 public void myPetEnjoy() {149 pet.enjoy();150 }151 }152 153 public class TestPolymoph {154 public static void main(String args[]) {155 /**156 * 在堆內(nèi)存里面new了一只藍(lán)貓對象出來,這個藍(lán)貓對象里面包含有一個父類對象Animal。157 */158 Cat c = new Cat("Catname", "blue");159 /**160 * 在堆內(nèi)存里面new了一只黑狗對象出來,這個黑狗對象里面包含有一個父類對象Animal。161 */162 Dog d = new Dog("Dogname", "black");163 /**164 * 在堆內(nèi)存里面new了一只小鳥對象出來,這個小鳥對象里面包含有一個父類對象Animal。165 */166 Bird b = new Bird();167 168 /**169 * 在堆內(nèi)存里面new出來3個小姑娘,名字分別是l1,l2,l3。170 * l1養(yǎng)了一只寵物是c(Cat),l2養(yǎng)了一只寵物是d(Dog),l3養(yǎng)了一只寵物是b(Bird)。171 * 注意:調(diào)用Lady類的構(gòu)造方法時,傳遞過來的c,d,b是當(dāng)成Animal來傳遞的,172 * 因此使用c,d,b這三個引用對象只能訪問父類Animal里面的enjoy()方法。173 */174 Lady l1 = new Lady("l1", c);175 Lady l2 = new Lady("l2", d);176 Lady l3 = new Lady("l3", b);177 /**178 * 這三個小姑娘都調(diào)用myPetEnjoy()方法使自己養(yǎng)的寵物高興地叫起來。179 */180 l1.myPetEnjoy();181 l2.myPetEnjoy();182 l3.myPetEnjoy();183 }184 }
運(yùn)行結(jié)果:
1.2.畫內(nèi)存圖理解動態(tài)綁定(多態(tài))
首先從main方法的第一句話開始分析:
Cat c = new Cat("Catname","blue");
程序執(zhí)行到這里,棧空間里有一個變量c,c里面裝著一系列的值,通過這些值可以找到位于堆內(nèi)存里面new出來的Cat對象。因此c是Cat對象的一個引用,通過c可以看到這個Cat對象的全部。c指向new出來的Cat對象。在new這個Cat對象的時候,調(diào)用了Cat對象的構(gòu)造方法Cat(String n,String c),定義如下:
Cat(String n,String c){
super(n);
eyesColor=c;
}
因此在構(gòu)造子類對象時首先使用父類對象的引用super調(diào)用父類的構(gòu)造方法Animal(String name),定義如下:
Animal(String name){
this.name=name;
}
因此會把傳過來的字符串“Catname”傳遞給父類對象的name屬性。當(dāng)Cat(String n,String c)構(gòu)造方法調(diào)用結(jié)束后,真真正正在堆內(nèi)存里面new出了一只Cat,這只Cat里面包含有父類對象Animal,這個Animal對象有自己的屬性name,name屬性的值為調(diào)用父類構(gòu)造方法時傳遞過來的字符串Catname。除此之外,這只Cat還有自己的私有成員變量eyesColor,eyesColor屬性的屬性值為調(diào)用子類構(gòu)造方法時傳遞過來的字符串blue。所以執(zhí)行完這句話以后,內(nèi)存中的布局是棧內(nèi)存里面有一個引用c,c指向堆內(nèi)存里面new出來的一只Cat,而這只Cat對象里面又包含有父類對象Animal,Animal對象有自己的屬性name,屬性值為Catname,Cat除了擁有從Animal類繼承下來的name屬性外,還擁有一個自己私有的屬性eyesColor,屬性值為blue。這就是執(zhí)行完第一句話以后整個內(nèi)存布局的情況如下圖所示:
接著看這句話:Lady l1 = new Lady(“l(fā)1”,c);
程序執(zhí)行到這里,首先在棧內(nèi)存里面多了一個引用變量l1,l1里面裝著一個值,通過這個值可以找到在堆內(nèi)存里面new出來的Lady對象。l1就是這個Lady對象的引用,l1指向Lady對象。在創(chuàng)建Lady對象時,調(diào)用Lady類的構(gòu)造方法:Lady(String name,Animal pet),其定義如下:
Lady(String name,Animal pet){
this.name=name;
this.pet=pet;
}
這個構(gòu)造方法有兩個參數(shù),分別是String類型的name和Animal類型的pet,pet參數(shù)是一個父類對象的引用類型,這里把l1和c作為實參傳遞給了構(gòu)造方法,接著在構(gòu)造方法里面執(zhí)行this.name=name,把傳遞過來的l1由傳給Lady對象的name屬性,因此Lady對象的name屬性值為l1,這里也把前面new出來的那只Cat的引用c傳遞給了構(gòu)造方法里面的參數(shù)pet,接著在構(gòu)造方法里面執(zhí)行this.pet=pet,pet參數(shù)又把c傳過來的內(nèi)容傳遞給Lady對象的pet屬性,因此pet屬性的屬性值就是可以找到Cat對象的地址,因此Lady對象的pet屬性也成為了Cat對象的引用對象了,通過pet里面裝著的值是可以找到Cat對象的,因此pet也指向了Cat,但并不是全部指向Cat,pet指向的只是位于Cat對象內(nèi)部的Animal對象,這是因為在調(diào)用構(gòu)造方法時,是把c當(dāng)成一個Animal對象的引用傳過來的,把c作為一個Animal對象傳遞給了pet,所以得到的pet也是一個Animal對象的引用,因此這個pet引用指向的只能是位于Cat對象里面的Animal對象。在我pet引用對象眼里,你Cat對象就是一只普通的Animal,訪問你的時候只能訪問得到你里面的name屬性,而你的eyesColor屬性我是訪問不到的,我能訪問到你的name屬性,訪問的是位于你內(nèi)部里面的父對象的name屬性,因為我pet引用本身就是一個父類對象的引用,因此我可以訪問父類對象的全部屬性,而你子類對象Cat自己新增加的成員我pet引用是訪問不了的。不過現(xiàn)在我pet引用不去訪問你父類對象的成員變量name了,而是去訪問你的成員方法enjoy了。首先是使用Lady對象的引用l1去調(diào)用Lady對象的myPetEnjoy()方法,myPetEnjoy()方法定義如下:
public void myPetEnjoy(){
pet.enjoy();
}
然后在myPetEnjoy()方法體里面又使用pet引用對象去調(diào)用父類對象里面的enjoy方法。
方法是放在代碼區(qū)(code seg)里面的,里面的方法就是一句句代碼。因此當(dāng)使用pet引用去訪問父類對象的方法時,首先是找到這個父類對象,然后看看它里面的方法到底在哪里存著,找到那個方法再去執(zhí)行。這里頭就比較有意思了,code seg里面有很多個enjoy方法,有父類的enjoy()方法,也有子類重寫了從父類繼續(xù)下來的enjoy()方法,那么調(diào)用的時候到底調(diào)用的是哪一個呢?是根據(jù)誰來確定呢?注意:這是根據(jù)你實際當(dāng)中的對象來確定的,你實際當(dāng)中new出來的是誰,就調(diào)用誰的enjoy方法,當(dāng)你找這個方法的時候,通過pet引用能找得到這個方法,但調(diào)用代碼區(qū)里面的哪一個enjoy方法不是通過引用類型來確定的,如果是通過引用類型pet來確定,那么調(diào)用的肯定是Animal的enjoy()方法,可是現(xiàn)在是根據(jù)實際的類型來確定,我們的程序運(yùn)行以后才在堆內(nèi)存里面創(chuàng)建出一只Cat,然后根據(jù)你實際當(dāng)中new出來的類型來判斷我到底應(yīng)該調(diào)用哪一個enjoy()方法。如果是根據(jù)實際類型,那么調(diào)用的就應(yīng)該是Cat的enjoy()方法。如果是根據(jù)引用類型,那么調(diào)用的就應(yīng)該是Animal的enjoy()方法。現(xiàn)在動態(tài)綁定這種機(jī)制指的是實際當(dāng)中new的是什么類型,就調(diào)用誰的enjoy方法。所以說雖然你是根據(jù)我父類里面的enjoy方法來調(diào)用,可是實際當(dāng)中卻是你new的是誰調(diào)用的就是誰的enjoy()方法。即實際當(dāng)中調(diào)用的卻是子類里面重寫后的那個enjoy方法。當(dāng)然,講一點(diǎn)更深的機(jī)制,你實際當(dāng)中找這個enjoy方法的時候,在父類對象的內(nèi)部有一個enjoy方法的指針,指針指向代碼區(qū)里面父類的Animal的enjoy方法,只不過當(dāng)你new這個對象的時候,這個指針隨之改變,你new的是什么對象,這個指針就指向這個對象重寫后的那個enjoy方法,所以這就叫做動態(tài)綁定。只有在動起來的時候,也就是在程序運(yùn)行期間,new出了這個對象了以后你才能確定到底要調(diào)用哪一個方法。我實際當(dāng)中的地址才會綁定到相應(yīng)的方法的地址上面,所以叫動態(tài)綁定。調(diào)這個方法的時候,只要你這個方法重寫了,實際當(dāng)中調(diào)哪一個,要看你實際當(dāng)中new的是哪個對象,這就叫多態(tài),也叫動態(tài)綁定。動態(tài)綁定帶來莫大的好處是使程序的可擴(kuò)展性達(dá)到了最好,我們原來做這個可擴(kuò)展性的時候,首先都是要在方法里面判斷一下這只動物是哪一類里面的動物,通過if (object instanceof class)這樣的條件來判斷這個new出來的對象到底是屬于哪一個類里面的,如果是一只貓,就調(diào)用貓的enjoy方法,如果是一條狗,就調(diào)用狗的enjoy方法。如果我現(xiàn)在增加了一個Bird類,那么擴(kuò)展的時候,你又得在方法里面寫判斷這只鳥屬于哪一個類然后才能調(diào)用這只鳥的enjoy方法。每增加一個對象,你都要在方法里面增加一段判斷這個對象到底屬于哪個類里面的代碼然后才能執(zhí)行這個對象相應(yīng)的方法。即每增加一個新的對象,都要改變方法里面的處理代碼,而現(xiàn)在,你不需要再改變方法里面的處理代碼了,因為有了動態(tài)綁定。你要增加哪一個對象,你實際當(dāng)中把這個對象new出來就完了,不再用去修改對象的處理方法里面的代碼了。也就是當(dāng)你實際當(dāng)中要增加別的東西的時候,很簡單,你直接加上去就成了,不用去改原來的結(jié)構(gòu),你要在你們家大樓的旁邊蓋一個廚房,很簡單,直接在旁邊一蓋就行了,大樓的主要支柱什么的你都不用動,這就可以讓可擴(kuò)展性達(dá)到了極致,這就為將來的可擴(kuò)展打下了基礎(chǔ),也只有動態(tài)綁定(多態(tài))這種機(jī)制能幫助我們做到這一點(diǎn)——讓程序的可擴(kuò)展性達(dá)到極致。因此動態(tài)綁定是面向?qū)ο蟮暮诵模绻麤]有動態(tài)綁定,那么面向?qū)ο蠼^對不可能發(fā)展得像現(xiàn)在這么流行,所以動態(tài)綁定是面向?qū)ο蠛诵闹械暮诵摹?/strong>
總結(jié)動態(tài)綁定(多態(tài)):動態(tài)綁定是指在“執(zhí)行期間”(而非編譯期間)判斷所引用的實際對象類型,根據(jù)其實際的類型調(diào)用其相應(yīng)的方法。所以實際當(dāng)中找要調(diào)用的方法時是動態(tài)的去找的,new的是誰就找誰的方法,這就叫動態(tài)綁定。動態(tài)綁定幫助我們的程序的可擴(kuò)展性達(dá)到了極致。
多態(tài)的存在有三個必要的條件:
要有繼承(兩個類之間存在繼承關(guān)系,子類繼承父類)要有重寫(在子類里面重寫從父類繼承下來的方法)父類引用指向子類對象 這三個條件一旦滿足,當(dāng)你調(diào)用父類里面被重寫的方法的時候,實際當(dāng)中new的是哪個子類對象,就調(diào)用子類對象的方法(這個方法是從父類繼承下來后重寫后的方法)。
面向?qū)ο蟊容^強(qiáng)調(diào)類和類之間,對象和對象之間的一種組織關(guān)系,如果能把這種組織關(guān)系組織得比較好的話,你的程序想擴(kuò)展性比較好,比較健壯,維護(hù)性比較好這些都可以達(dá)到,關(guān)鍵看你的設(shè)計到底好還是不好。
學(xué)習(xí)Java的同學(xué)注意了!!! 學(xué)習(xí)過程中遇到什么問題或者想獲取學(xué)習(xí)資源的話,歡迎加入Java學(xué)習(xí)交流群,群號碼:523047986 我們一起學(xué)Java!
新聞熱點(diǎn)
疑難解答
圖片精選