Java 多态的简单介绍.
?????? 作為面向?qū)ο笕笾R(shí)點(diǎn)之一,? 多態(tài)可以講是相對(duì)最難理解那個(gè). 本人在這里也是簡(jiǎn)單分析一下.
一, 多態(tài)的定義.
? ? ? ? 面向?qū)ο缶幊讨? 1個(gè)超類(lèi)的引用可以指向?qū)儆诔?lèi)的對(duì)象, 也可以根據(jù)需要指向超類(lèi)的派生類(lèi)族對(duì)象. ??
? ? ? ? 這個(gè)過(guò)程中根據(jù)引用指向不同類(lèi)型對(duì)象, 可以調(diào)用不同方法, ?這就是多態(tài).
1.1 ?什么是類(lèi)的引用.
Java 中, 1個(gè)實(shí)例化后的對(duì)象的內(nèi)存是分配在內(nèi)存的Heap(堆)區(qū)中的, ?但是如果使用這個(gè)對(duì)象, 必須在Stuck(棧) 區(qū)定義1個(gè) 變量來(lái)存放那個(gè)堆區(qū)類(lèi)型的地址. 例如: Human A;
上面的代碼定義了1個(gè)Human類(lèi)型 的變量A, ?這個(gè)A只是存放在Stuck區(qū), ?我們就說(shuō)這個(gè)A是1個(gè)引用, ?它可以指向堆區(qū)的Human類(lèi)型的內(nèi)存.
但是上面這個(gè)A類(lèi)型并沒(méi)有指向任何內(nèi)存, 就是所謂的"對(duì)象A沒(méi)有實(shí)例化"
接下來(lái): A = new Human();
? ? ? ? ?new Human() 這個(gè)1句是在Heap區(qū)動(dòng)態(tài)分配1塊Human類(lèi)型的內(nèi)存. ? ? ?A = new Human(); 就是把該內(nèi)存的頭部地址賦給A. 這是我們就說(shuō)引用A已經(jīng)有了指向, ?也就是所謂的"對(duì)象A 被實(shí)例化"
1.2 ?類(lèi)的引用的指向可以改變
? ? ? ?既然應(yīng)用類(lèi)型的指向是通過(guò)賦值來(lái)實(shí)現(xiàn)的, 也就代表這個(gè)指向可以修改, 意思就是上面的引用A可以指向另一塊堆區(qū)內(nèi)存的頭部地址.看個(gè)例子:
Human A = new Human(); Human B = new Human();
上面的兩句代碼就在Heap區(qū)分配了兩塊Human類(lèi)型的內(nèi)存, ?并把它們的頭部地址分別賦予兩個(gè)不同的引用A 和 B
A = B;
? ? ?再執(zhí)行上面的語(yǔ)句, 意思就是把A指向了B保存的地址, 也就是說(shuō)A 和 B 現(xiàn)在是指向同一塊內(nèi)存地址了.
但是, ?A原來(lái)指向的Heap區(qū)地址就消失了, 如果在C/C++ 中, 這就是明顯的內(nèi)存泄露. ?但是Java中會(huì)自動(dòng)把A原來(lái)的地址釋放的.
1.3 ?超類(lèi)的引用可以指向其子孫派生類(lèi)的內(nèi)存地址.
這個(gè)就是多態(tài)關(guān)鍵了.看下面的例子:
package Object_kng.Ploy_kng;class Human_1{private int id;private int name; }class Student_1 extends Human_1{private String school; }class Section_monitor_1 extends Student_1{private String subject; }public class Poly_1{public static void f(){Human_1 hm = new Human_1();Student_1 st = new Student_1();Section_monitor_1 sm = new Section_monitor_1();//st = hm; //error//st = (Student_1)hm; //error (can pass the compilation, but will fail in excution)//okhm = st; //okhm = sm;//st = hm; //errorst = (Student_1)hm; //ok} }
??
上面定義了3個(gè)業(yè)務(wù)類(lèi), 其中學(xué)生類(lèi)繼承 Human_1類(lèi), ? 而課代表類(lèi)繼承學(xué)生類(lèi).
在f() 函數(shù)中, ?分別對(duì)上述3個(gè)類(lèi)各實(shí)例化1個(gè)對(duì)象, ?也就是3個(gè)引用 hm, st, sm分別具有了自己的指向.
接下來(lái)一句一句講: //st = hm; //error //st = (Student_1)hm; //error (can pass the compilation, but will fail in excution)
? ? ? 看上面兩句, ? 首先 st = hm; ?這句意識(shí)是把引用hm保存的頭部地址賦給 引用st. ? ? ? 注意, 這時(shí)st 和 hm屬于不同的類(lèi). 但是hm所屬的類(lèi)Student_1 繼承自 st所屬的類(lèi) Human_1. ?? 這一句會(huì)編譯失敗, ?是因?yàn)槎鄳B(tài)中, 不允許派生類(lèi)的引用? 指向 超類(lèi)的對(duì)象.
? ? ? 原因也不難理解:
首先可以看看Human_1 類(lèi), 有兩個(gè)私有成員, ? Student_1 貌似只有1個(gè)私有成員, 但是因?yàn)槔^承關(guān)系, ?Student_1 會(huì)隱藏繼承 Human_1的所有成員的. 只不過(guò)這里沒(méi)有對(duì)Human_1的成員進(jìn)行封裝, 所以不能直接使用隱藏的成員.
因?yàn)殡[藏 也就是說(shuō)超類(lèi)對(duì)象內(nèi)存肯定是小于等于 其派生類(lèi)的對(duì)象的.
可就是說(shuō)應(yīng)用類(lèi)型st ?(Student_1) 類(lèi)本身是可以直接或間接使用3個(gè)成員的(id, name, school), ?而 hm本身指向的對(duì)象只有兩個(gè)成員(id, name).
如果st指向h的對(duì)象地址, ?那么當(dāng)st使用school成員時(shí), 就會(huì)在h對(duì)象內(nèi)存里找不到school這個(gè)成員.
這個(gè)就是java里不允許 ?派生類(lèi)應(yīng)用類(lèi)型 指向 超類(lèi)對(duì)象的原因.
如果按現(xiàn)實(shí)意義理解也可以:
st = hm 的意思就是把人類(lèi) 作為學(xué)生來(lái)處理, ?這個(gè)肯定是不和常理的. ?因?yàn)椴皇撬腥祟?lèi)都是學(xué)生. 反過(guò)來(lái)就可以了.
st = (Student_1)hm; 這一句是把hm這個(gè)對(duì)象強(qiáng)制轉(zhuǎn)換成Student_1類(lèi)的新對(duì)象(注意hm對(duì)象本身并沒(méi)有改變), 然后賦予st.
這一句是能通過(guò)編譯的, 但是當(dāng)執(zhí)行時(shí)就會(huì)拋出異常: [java] java.lang.ClassCastException: Object_kng.Poly_kng.Human_1 cannot be cast to Object_kng.Poly_kng.Student_1
? ? ? ? ?java里不允許把超類(lèi)對(duì)象轉(zhuǎn)成 派生類(lèi)對(duì)象.... ?也就是不是任意1個(gè)普通人類(lèi)都能作為學(xué)生來(lái)看待了.
hm = st; 上面這句就是正確的, ? 意思是把引用hm 指向 引用st 指向的對(duì)象.
也就是Human_1 的引用類(lèi)型 指向了他的子派生類(lèi) 的對(duì)象. ?這是可以的. ? ?
因?yàn)榕缮?lèi)Student 隱藏繼承了Human_1 的所有成員. ? 現(xiàn)實(shí)意義就是任意1個(gè)學(xué)生都可以作為人來(lái)看待.
hm = sm; //st = hm; //error st = (Student_1)hm; //ok
? ? ? ? 這段就稍稍復(fù)雜, 首先把 hm 指向它的 孫派生類(lèi)的對(duì)象, 這個(gè)也沒(méi)問(wèn)題. 然后再執(zhí)行?st = (Student_1)hm;
這一句在上面是錯(cuò)誤的, 為何在這里是正確的呢.
因?yàn)樯厦娴?hm當(dāng)時(shí)是指向 Human_1 類(lèi)的對(duì)象 ?. ? 而在這里hm 已經(jīng)指向了 Section_monitor_1類(lèi)的對(duì)象. ??
而把 Section_monitor_1對(duì)象轉(zhuǎn)化成 它的父超類(lèi) Student_1 ?是沒(méi)問(wèn)題的..
也就說(shuō)任何1個(gè)科代表都可以被看作學(xué)生來(lái)看待嘛.
1.4 據(jù)引用指向不同類(lèi)型對(duì)象, 可以調(diào)用不同方法
因?yàn)槊嫦驅(qū)ο笾? 派生類(lèi)繼承的方法是可以重寫(xiě)的. ?兩個(gè)繼承關(guān)系的類(lèi)里面分別有兩個(gè)相同名字相同參數(shù)相同返回值的 函數(shù).?但是函數(shù)體可以是不同的.
下面的看下面的例子:
package Object_kng.Poly_kng;class Human_2{public void print(){System.out.printf("it's Human\n");} }class Student_2 extends Human_2{public void print(){System.out.printf("it's Student\n");} }class Section_monitor_2 extends Student_2{public void print(){System.out.printf("it's Section_monitor\n");} }public class Poly_2{public static void f(){Human_2 hm = new Human_2();Student_2 st = new Student_2();Section_monitor_2 sm = new Section_monitor_2();hm.print();hm = st;hm.print();hm = sm;hm.print();} }
上面一樣定義了3個(gè)類(lèi)... ?他們是繼承關(guān)系
這3個(gè)類(lèi)都有1個(gè)print 方法. ?其中 Studnet_2類(lèi)重寫(xiě)了 Human_2 的print方法.. ?Section_monitor_2 類(lèi)重寫(xiě)了Student_2類(lèi)的print 方法
在接下來(lái)的f() 函數(shù)中.
根據(jù)引用hm指向的不同, ?
hm.print() 分別執(zhí)行的是3個(gè)不同類(lèi)的方法:
輸出: [java] it's Human[java] it's Student[java] it's Section_monitor
這個(gè)就是多態(tài)的最重要的特性之一了.
二, 多態(tài)的一個(gè)簡(jiǎn)單應(yīng)用.
接下來(lái)就利用上面的特征作1個(gè)簡(jiǎn)單的應(yīng)用.
class Human_3{private int id;private String name; public Human_3(int id,String name){this.id = id;this.name = name; } public String get_mem(){return "Human: " + id + " " + name;} }class Student_3 extends Human_3{public Student_3(int id, String name){super(id,name); }public String get_mem(){return "student: " + super.get_mem();} }class Section_monitor_3 extends Student_3{public Section_monitor_3(int id, String name){super(id,name); }public String get_mem(){return "section_monitor: " + super.get_mem();} }public class Poly_3{public static String extend_mem(Human_3 hm){java.util.Date now = new java.util.Date();java.text.DateFormat d1 = java.text.DateFormat.getDateInstance();return d1.format(now) + ": " + hm.get_mem(); }public static void print(){Human_3 hm = new Human_3(1,"Jack");Student_3 st = new Student_3(2,"Dick");Section_monitor_3 sm = new Section_monitor_3(3,"Cindy"); System.out.printf("%s\n",extend_mem(hm));System.out.printf("%s\n",extend_mem(st));System.out.printf("%s\n",extend_mem(sm));} }
上面一樣定義了那個(gè)3個(gè)類(lèi), 而且具有兩個(gè)成員, 進(jìn)行了簡(jiǎn)單的封裝...
每個(gè)類(lèi)都有1個(gè)get_mem()函數(shù), ?返回不同的字符串.
現(xiàn)在需要1個(gè)函數(shù), 為每1個(gè)類(lèi)的get_mem()返回值前增加1個(gè)日期字符串:
利用多態(tài)技術(shù):
可以簡(jiǎn)單地定義1個(gè) ?extend_mem() 函數(shù), ?參數(shù)是超類(lèi)的對(duì)象. 那么這個(gè)函數(shù)就可以根據(jù)參數(shù)的不同類(lèi)型而調(diào)用不同對(duì)象的 get_mem()函數(shù).
如果沒(méi)有多態(tài)技術(shù), ?就必須根據(jù)參數(shù)的類(lèi)型不同 ?而寫(xiě)上3個(gè)函數(shù)了.
這個(gè)還不是最重要的, ?假如以后第三個(gè)類(lèi) 以后還再派生出第4個(gè)類(lèi), ?那么這個(gè)函數(shù)一樣適用不必改寫(xiě).
也就是說(shuō)多態(tài)可以實(shí)現(xiàn)面向?qū)ο蟪绦虻臄U(kuò)展性, 這個(gè)就是多態(tài)最重要的意義.
三, 多態(tài)的一些簡(jiǎn)單要點(diǎn):
這里也是算是1個(gè)簡(jiǎn)單總結(jié)吧:
1. 派生類(lèi)對(duì)象更可以直接賦給父類(lèi)引用, 但父類(lèi)對(duì)象任何情況下都不能直接賦給超類(lèi)引用.
2. 如果1個(gè)超類(lèi)引用指向了1個(gè)派生類(lèi)對(duì)象, 只能訪問(wèn)派生類(lèi)對(duì)象繼承自己超類(lèi)的成員or方法. 而不能訪問(wèn)派生類(lèi)獨(dú)有的成員or方法.
3. 超類(lèi)引用永遠(yuǎn)不可能直接賦給派生類(lèi)引用.
?? 例如用 B extends A.??? a 是 A的一個(gè)引用, b 是B的一個(gè)引用
?? b = a; 肯定是錯(cuò)的.
4.? 只有在超類(lèi)引用本身指向的就是1個(gè)派生類(lèi)或其子孫類(lèi)(派生類(lèi)的派生類(lèi))對(duì)象時(shí), 才可以把超類(lèi)引用強(qiáng)制轉(zhuǎn)化為派生類(lèi)引用.
? 上面的例子, 假如a 指向的實(shí)際是B的一個(gè)對(duì)象.
? 那么
? b = (B)a;?? 是正確的. 這里必須強(qiáng)制轉(zhuǎn)換.
5. 但是其他情況下不允許把超類(lèi)引用強(qiáng)制轉(zhuǎn)化為子類(lèi)引用, 否則會(huì)拋出異常, 上面說(shuō)過(guò)了.
總結(jié)
以上是生活随笔為你收集整理的Java 多态的简单介绍.的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 从内存分配角度分析c和java里的sta
- 下一篇: Java里的接口的interface 简