Java 多态的简单介绍.
生活随笔
收集整理的這篇文章主要介紹了
Java 多态的简单介绍.
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
?????? 作為面向對象三大知識點之一,? 多態可以講是相對最難理解那個. 本人在這里也是簡單分析一下.
一, 多態的定義.
? ? ? ? 面向對象編程中, 1個超類的引用可以指向屬于超類的對象, 也可以根據需要指向超類的派生類族對象. ??
? ? ? ? 這個過程中根據引用指向不同類型對象, 可以調用不同方法, ?這就是多態.
1.1 ?什么是類的引用.
Java 中, 1個實例化后的對象的內存是分配在內存的Heap(堆)區中的, ?但是如果使用這個對象, 必須在Stuck(棧) 區定義1個 變量來存放那個堆區類型的地址. 例如: Human A;
上面的代碼定義了1個Human類型 的變量A, ?這個A只是存放在Stuck區, ?我們就說這個A是1個引用, ?它可以指向堆區的Human類型的內存.
但是上面這個A類型并沒有指向任何內存, 就是所謂的"對象A沒有實例化"
接下來: A = new Human();
? ? ? ? ?new Human() 這個1句是在Heap區動態分配1塊Human類型的內存. ? ? ?A = new Human(); 就是把該內存的頭部地址賦給A. 這是我們就說引用A已經有了指向, ?也就是所謂的"對象A 被實例化"
1.2 ?類的引用的指向可以改變
? ? ? ?既然應用類型的指向是通過賦值來實現的, 也就代表這個指向可以修改, 意思就是上面的引用A可以指向另一塊堆區內存的頭部地址.看個例子:
Human A = new Human(); Human B = new Human();
上面的兩句代碼就在Heap區分配了兩塊Human類型的內存, ?并把它們的頭部地址分別賦予兩個不同的引用A 和 B
A = B;
? ? ?再執行上面的語句, 意思就是把A指向了B保存的地址, 也就是說A 和 B 現在是指向同一塊內存地址了.
但是, ?A原來指向的Heap區地址就消失了, 如果在C/C++ 中, 這就是明顯的內存泄露. ?但是Java中會自動把A原來的地址釋放的.
1.3 ?超類的引用可以指向其子孫派生類的內存地址.
這個就是多態關鍵了.看下面的例子:
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個業務類, 其中學生類繼承 Human_1類, ? 而課代表類繼承學生類.
在f() 函數中, ?分別對上述3個類各實例化1個對象, ?也就是3個引用 hm, st, sm分別具有了自己的指向.
接下來一句一句講: //st = hm; //error //st = (Student_1)hm; //error (can pass the compilation, but will fail in excution)
? ? ? 看上面兩句, ? 首先 st = hm; ?這句意識是把引用hm保存的頭部地址賦給 引用st. ? ? ? 注意, 這時st 和 hm屬于不同的類. 但是hm所屬的類Student_1 繼承自 st所屬的類 Human_1. ?? 這一句會編譯失敗, ?是因為多態中, 不允許派生類的引用? 指向 超類的對象.
? ? ? 原因也不難理解:
首先可以看看Human_1 類, 有兩個私有成員, ? Student_1 貌似只有1個私有成員, 但是因為繼承關系, ?Student_1 會隱藏繼承 Human_1的所有成員的. 只不過這里沒有對Human_1的成員進行封裝, 所以不能直接使用隱藏的成員.
因為隱藏 也就是說超類對象內存肯定是小于等于 其派生類的對象的.
可就是說應用類型st ?(Student_1) 類本身是可以直接或間接使用3個成員的(id, name, school), ?而 hm本身指向的對象只有兩個成員(id, name).
如果st指向h的對象地址, ?那么當st使用school成員時, 就會在h對象內存里找不到school這個成員.
這個就是java里不允許 ?派生類應用類型 指向 超類對象的原因.
如果按現實意義理解也可以:
st = hm 的意思就是把人類 作為學生來處理, ?這個肯定是不和常理的. ?因為不是所有人類都是學生. 反過來就可以了.
st = (Student_1)hm; 這一句是把hm這個對象強制轉換成Student_1類的新對象(注意hm對象本身并沒有改變), 然后賦予st.
這一句是能通過編譯的, 但是當執行時就會拋出異常: [java] java.lang.ClassCastException: Object_kng.Poly_kng.Human_1 cannot be cast to Object_kng.Poly_kng.Student_1
? ? ? ? ?java里不允許把超類對象轉成 派生類對象.... ?也就是不是任意1個普通人類都能作為學生來看待了.
hm = st; 上面這句就是正確的, ? 意思是把引用hm 指向 引用st 指向的對象.
也就是Human_1 的引用類型 指向了他的子派生類 的對象. ?這是可以的. ? ?
因為派生類Student 隱藏繼承了Human_1 的所有成員. ? 現實意義就是任意1個學生都可以作為人來看待.
hm = sm; //st = hm; //error st = (Student_1)hm; //ok
? ? ? ? 這段就稍稍復雜, 首先把 hm 指向它的 孫派生類的對象, 這個也沒問題. 然后再執行?st = (Student_1)hm;
這一句在上面是錯誤的, 為何在這里是正確的呢.
因為上面的 hm當時是指向 Human_1 類的對象 ?. ? 而在這里hm 已經指向了 Section_monitor_1類的對象. ??
而把 Section_monitor_1對象轉化成 它的父超類 Student_1 ?是沒問題的..
也就說任何1個科代表都可以被看作學生來看待嘛.
1.4 據引用指向不同類型對象, 可以調用不同方法
因為面向對象中, 派生類繼承的方法是可以重寫的. ?兩個繼承關系的類里面分別有兩個相同名字相同參數相同返回值的 函數.?但是函數體可以是不同的.
下面的看下面的例子:
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個類... ?他們是繼承關系
這3個類都有1個print 方法. ?其中 Studnet_2類重寫了 Human_2 的print方法.. ?Section_monitor_2 類重寫了Student_2類的print 方法
在接下來的f() 函數中.
根據引用hm指向的不同, ?
hm.print() 分別執行的是3個不同類的方法:
輸出: [java] it's Human[java] it's Student[java] it's Section_monitor
這個就是多態的最重要的特性之一了.
二, 多態的一個簡單應用.
接下來就利用上面的特征作1個簡單的應用.
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));} }
上面一樣定義了那個3個類, 而且具有兩個成員, 進行了簡單的封裝...
每個類都有1個get_mem()函數, ?返回不同的字符串.
現在需要1個函數, 為每1個類的get_mem()返回值前增加1個日期字符串:
利用多態技術:
可以簡單地定義1個 ?extend_mem() 函數, ?參數是超類的對象. 那么這個函數就可以根據參數的不同類型而調用不同對象的 get_mem()函數.
如果沒有多態技術, ?就必須根據參數的類型不同 ?而寫上3個函數了.
這個還不是最重要的, ?假如以后第三個類 以后還再派生出第4個類, ?那么這個函數一樣適用不必改寫.
也就是說多態可以實現面向對象程序的擴展性, 這個就是多態最重要的意義.
三, 多態的一些簡單要點:
這里也是算是1個簡單總結吧:
1. 派生類對象更可以直接賦給父類引用, 但父類對象任何情況下都不能直接賦給超類引用.
2. 如果1個超類引用指向了1個派生類對象, 只能訪問派生類對象繼承自己超類的成員or方法. 而不能訪問派生類獨有的成員or方法.
3. 超類引用永遠不可能直接賦給派生類引用.
?? 例如用 B extends A.??? a 是 A的一個引用, b 是B的一個引用
?? b = a; 肯定是錯的.
4.? 只有在超類引用本身指向的就是1個派生類或其子孫類(派生類的派生類)對象時, 才可以把超類引用強制轉化為派生類引用.
? 上面的例子, 假如a 指向的實際是B的一個對象.
? 那么
? b = (B)a;?? 是正確的. 這里必須強制轉換.
5. 但是其他情況下不允許把超類引用強制轉化為子類引用, 否則會拋出異常, 上面說過了.
總結
以上是生活随笔為你收集整理的Java 多态的简单介绍.的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 从内存分配角度分析c和java里的sta
- 下一篇: Java里的接口的interface 简