java學習筆記7--Polymorphism

來源:互聯網
上載者:User
筆記 Polymorphism
多態(polymorphism)也被稱為動態綁定『dynamic binding』、後綁定『late binding』或運行時綁定『run-time bingding』。
它的意思是要在程式啟動並執行時候,根據對象的類型來決定該綁定哪個方法。多態性是繼資料抽象和繼承之後的,物件導向的程式設計語言的第三個基本特性。

綁定(binding): 將方法的調用連到方法本身
前綁定(early binding): 當綁定發生在程式運行之前時(就是由編譯器或連接器負責)
後綁定(late binding): 在程式啟動並執行時候,根據對象的類型來決定該綁定哪個方法。

“封裝”(encapsulation)通過將資料的特徵(characteristics)與行為(behavior)結合在一起,建立了一種新的資料類型。
“隱藏實現”(Implementation hiding)通過將細節設成private,完成了介面與實現的分離。
“多態性”是站在“class”的角度來處理這種邏輯上的分離的。


--------------------------------------------------------------------------------



Shape s = new Circle(); //upcasting: 將Circle對象upcast為Shape類型
s.draw();

Upcast(上傳)就是把對象的reference 當作基類的reference 來用.(註:java內部是知道對象屬於什麼類型的)
因為Derived class is a type of base class, 所以基類的reference (Shape s)能接受衍生類別(circle)的對象

upcast以後,基類reference 調用的都是基類自己的method (late binding),
除非這個method是late-bound的,也就是衍生類別覆寫(override)了這個method, 才會根據物件類型選擇相應的method(多態性).

以上面的代碼為例:s是Shape類型的reference, 除非draw()是一個動態綁定的method(衍生類別circle覆寫了這個draw()),
s.draw()才會調用cicle的draw(), 否則調用的都是基類Shape自己的method

private和final的method都會採用early-binding, 因為他們是不能被override的。(註:private 方法自動就是final 的)

建議別用基類的private method的名字去命名衍生類別的method。因為這樣會讓人誤以為會override這個method,
實際上private自動就是final的,不能被override。


--------------------------------------------------------------------------------

override(覆寫) 表示在衍生類別裡寫一個"同樣"的method。 就是重新寫一遍這個method
(註:1."同樣"表示:同名且參數列表和傳回值也相同。 2.method必須是非final, 非private的(private 方法自動就是final 的) )
overload:除了"同樣"的method(即override) 以外的同名method.


--------------------------------------------------------------------------------


當你想要通過一個公用的介面來操控一組類的時候,就可以使用抽象類別了。通過動態綁定機制,那些符合方法特徵的衍生類別方法將會得到調用。
abstract class {}
抽象類別的方法可以使abstract的,也可以是非abstract。衍生類別繼承了一個abstract類,那他要麼實現所有的abstract的method,要麼把自己也變成abstract的。


--------------------------------------------------------------------------------



//: c07:PolyConstructors.java
// Constructors and polymorphism
// don't produce what you might expect.
import com.bruceeckel.simpletest.*;
abstract class Glyph {
abstract void draw();
Glyph() {
System.out.println("Glyph() before draw()");
draw();//如果你new一個衍生類別對象,並且在基類建構函式裡面調用了動態綁定的方法,
//那麼它會使用那個衍生類別覆寫後的版本。
System.out.println("Glyph() after draw()");
}
}
class RoundGlyph extends Glyph {
private int radius = 1;
RoundGlyph(int r) {
radius = r;
System.out.println(
"RoundGlyph.RoundGlyph(), radius = " + radius);
}
void draw() {
System.out.println(
"RoundGlyph.draw(), radius = " + radius);
}
}
public class PolyConstructors {
private static Test monitor = new Test();
public static void main(String[] args) {
new RoundGlyph(5);
monitor.expect(new String[] {
"Glyph() before draw()",
"RoundGlyph.draw(), radius = 0",
"Glyph() after draw()",
"RoundGlyph.RoundGlyph(), radius = 5"
});
}
} ///:~
如果你new一個衍生類別對象,並且在基類建構函式裡面調用了動態綁定的方法,那麼它會使用那個衍生類別覆寫後的版本。

真正的初始化過程是這樣的:
1. 在進行其它工作之前,分配給這個對象的記憶體會先被初始化為兩進位的零。
2. 正如前面一直在所說的,先調用基類的建構函式。這時會調用被覆寫的draw( )方法(是的,在調用RoundGlyph 的建構函式之前調用),
這時它發現,由於受第一步的影響,radius 的值還是零。
3. 資料成員按照它們聲明的順序進行初始化。
4. 調用衍生類別的建構函式的本文。


一個好的建構函式應該: 用最少的工作量把對象的狀態(fields)設定好,而且要儘可能地避免去調用方法。
建構函式唯一能安全調用的方法,就是基類的final/private 方法。它們不會被覆寫,因此也不會產生這種意外的行為。


--------------------------------------------------------------------------------

一旦理解了多態性,你就會覺得所有東西應該都是繼承下來的,因為多態性實在是太聰明了。但是這樣做會加重設計的負擔。
實際上,如果你一遇到“要用已有的類來建立新類”的情況就想到要用繼承的話,事情就會毫無必要地變得複雜起來了。
較好的辦法還是先考慮合成,特別是當你不知道該繼承哪個類的時候。合成並不強求你把設計搞成一個類系。
此外它還更靈活,因為使用合成的時候,你可以動態地選擇成員的類型(以及它們的行為),而使用繼承的話,
就得在編譯時間指明對象的確切類型。下面這段程式就示範了這一點:

//: c07:Transmogrify.java
// Dynamically changing the behavior of an object
// via composition (the "State" design pattern).
import com.bruceeckel.simpletest.*;
abstract class Actor {
public abstract void act();
}
class HappyActor extends Actor {
public void act() {
System.out.println("HappyActor");
}
}
class SadActor extends Actor {
public void act() {
System.out.println("SadActor");
}
}
class Stage {
private Actor actor = new HappyActor();
public void change() { actor = new SadActor(); }
public void performPlay() { actor.act(); }
}
public class Transmogrify {
private static Test monitor = new Test();
public static void main(String[] args) {
Stage stage = new Stage();
stage.performPlay();
stage.change();
stage.performPlay();
monitor.expect(new String[] {
"HappyActor",
"SadActor"
});
}
} ///:~

有一條一般準則“使用繼承來表示行為的不同,而用成員資料來表示狀態的不同”。上述常式同時體現這兩者;兩個衍生類別用來表示act()方法的不同,
而Stage則使用合成來表示狀態的變化。在這種情況下,狀態的不同會導致行為的不同。


--------------------------------------------------------------------------------

下傳(Downcast):把基類重新轉回具體的衍生類別類型,使得reference可以調用衍生類別的extended介面

Java 類型傳遞都要經過檢查!所以,儘管看上去只是用一對括弧作了些普通的的類型轉換,但是啟動並執行時候,系統會對這些轉換作檢查,
以確保 它確實是你想要轉換的類型。如果不是,你就會得到一個ClassCastException。這種運行時的類型檢查被稱為“運行時的類型
鑒別(run-time type identification 縮寫為RTTI)”。





聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.