java继承和合成的输出结果_Java中合成和繼承的區別

class WaterSource {

private String s;

WaterSource() {

System.out.println("WaterSource()");

s = new String("Constructed");

}

public String toString() {

return s;

}

}

public class SprinklerSystem {

private String valve1, valve2, valve3, valve4;

WaterSource source;

int i;

float f;

void print() {

prt("valve1 = " + valve1);

prt("valve2 = " + valve2);

prt("valve3 = " + valve3);

prt("valve4 = " + valve4);

prt("i = " + i);

prt("f = " + f);

prt("source = " + source);

}

public static void main(String[] args) {

SprinklerSystem x = new SprinklerSystem();

x.print();

}

static void prt(String s) {

System.out.println(s);

}

}

1.2、類合成的典型應用//Composition with public objects

class Engine {

public void start() {

}

public void rev() {

}

public void stop() {

}

}

class Wheel {

public void inflate(int psi) {

}

}

class Window {

public void rollup() {

}

public void rolldown() {

}

}

class Door {

public Window window = new Window();

public void open() {

}

public void close() {

}

}

public class Car {

public Engine engine = new Engine();

public Wheel[] wheel = new Wheel[4];

public Door left = new Door(), right = new Door(); // 2-door

Car() {

for (int i = 0; i < 4; i++)

wheel[i] = new Wheel();

}

public static void main(String[] args) {

Car car = new Car();

car.left.window.rollup();

car.wheel[0].inflate(72);

}

}由於汽車的裝配是故障分析時需要考慮的一項因素(並非只是基礎設計簡單的一部分),所以有助於客戶程序員理解如何使用類,而且類創建者的編程復雜程度也會大幅度降低。

如選擇繼承,就需要取得一個現成的類,並制作它的一個特殊版本。通常,這意味着我們准備使用一個常規用途的類,並根據特定的需求對其進行定制。只需稍加想象,就知道自己不能用一個車輛對象來合成一輛汽車——汽車並不“包含”車輛;相反,它“屬於”車輛的一種類別。“屬於”關系是用繼承來表達的,而“包含”關系是用合成來表達的。

2.1、類繼承的語法需要繼承的時候,我們會說:“這個新類和那個舊類差不多。”為了在代碼里表面這一觀念,需要給出類名。但在類主體的起始花括號之前,需要放置一個關鍵字extends,在后面跟隨“基礎類”的名字。若采取這種做法,就可自動獲得基礎類的所有數據成員以及方法。下面是一個例子:// Inheritance syntax & properties

class Cleanser {

private String s = new String("Cleaner");

public void append(String a) { s += a; }

public void dilute() {

append(" dilute()");

}

public void apply() {

append(" apply()");

}

public void scrub() {

append(" scrub()");

}

public void print() {

System.out.println(s);

}

public static void main(String[] args) {

Cleanser x = new Cleanser();

x.dilute();

x.apply();

x.scrub();

x.print();

}

}

public class Detergent extends Cleanser{

// Change a method :

public void scrub() {

append(" Detergent.scrub()");

super.scrub();// Call base-class version

}

// Add methods to the interface :

public void foam() {

append(" foam()");

}

public static void main(String[] args) {

Detergent x = new Detergent();

x.dilute();

x.apply();

x.scrub();

x.foam();

x.print();

System.out.println("Texting base class : ");

Cleanser.main(args);

}

}

無論Cleanser 還是Detergent 都包含了一個main()方法。我們可為自己的每個類都創建一個main()。通常建議大家象這樣進行編寫代碼,使自己的測試代碼能夠封裝到類內。即便在程序中含有數量眾多的類,但對於在命令行請求的public 類,只有main()才會得到調用。所以在這種情況下,當我們使用“java Detergent”的時候,調用的是Degergent.main()——即使Cleanser 並非一個public 類。采用這種將main()置入每個類的做法,可方便地為每個類都進行單元測試。而且在完成測試以后,毋需將main()刪

去;可把它保留下來,用於以后的測試。

在這里,大家可看到Deteregent.main()對Cleanser.main()的調用是明確進行的。注意Cleanser 在它的接口中含有一系列方法:append(),dilute(),apply(),scrub()以及print()。由於Detergent 是從Cleanser 衍生出來的(通過extends 關鍵字),所以它會自動獲得接口內的所有這些方法——即使我們在Detergent 里並未看到對它們的明確定義。這樣一來,就可將繼承想象成“對接口的重復利用”或者“接口的再生”(以后的實施細節可以自由設置,但那並非我們強調的重點)。

正如在scrub()里看到的那樣,可以獲得在基礎類里定義的一個方法,並對其進行修改。在這種情況下,我們通常想在新版本里調用來自基礎類的方法。但在scrub()里,不可只是簡單地發出對scrub()的調用。那樣便造成了遞歸調用,我們不願看到這一情況。為解決這個問題,Java 提供了一個super 關鍵字,它引用當前類已從中繼承的一個“超類”(Superclass)。所以表達式super.scrub()調用的是方法scrub()的基礎類版本。進行繼承時,我們並不限於只能使用基礎類的方法。亦可在衍生出來的類里加入自己的新方法。這時采取的做法與在普通類里添加其他任何方法是完全一樣的:只需簡單地定義它即可。extends 關鍵字提醒我們准備將新方法加入基礎類的接口里,對其進行“擴展”。foam()便是這種做法的一個產物。

2.2、上塑造型繼承是對新類和基礎類之間的關系的一種表達。可這樣總結該關系:“新類屬於現有類的一種類型”。這種表達並不僅僅是對繼承的一種形象化解釋,繼承是直接由語言提供支持的。作為一個例子,大家可考慮一個名為Instrument 的基礎類,它用於表示樂器;另一個衍生類叫作Wind。由於繼承意味着基礎類的所有方法亦可在衍生出來的類中使用,所以我們發給基礎類的任何消息亦可發給衍生類。若Instrument 類有一個play()方法,則Wind 設備也會有這個方法。這意味着我們能肯定地認為一個Wind 對象也是Instrument 的一種類型。下面這個例子揭示出編譯器如何提供對這一概念的支持:class Instrument {

public void play() {

}

static void tune(Instrument i) {

// ...

i.play();

}

}

// Wind objects are instruments

// because they have the same interface:

class Wind extends Instrument {

public static void main(String[] args) {

Wind flute = new Wind();

Instrument.tune(flute); // Upcasting

}

}

程序正常運行,沒有報錯。這個例子中最有趣的無疑是tune()方法,它能接受一個Instrument 句柄。但在Wind.main()中,tune()方法是通過為其賦予一個Wind 句柄來調用的。由於Java 對類型檢查特別嚴格,所以大家可能會感到很奇怪,為什么接收一種類型的方法也能接收另一種類型呢?但是,我們一定要認識到一個Wind 對象也是一個Instrument 對象。而且對於不在Wind 中的一個Instrument(樂器),沒有方法可以由tune()調用。在tune()中,代碼適用於Instrument 以及從Instrument 衍生出來的任何東西。在這里,我們將從一個Wind 句柄轉換成一個Instrument 句柄的行為叫作“上溯造型”。

2.3、何謂“上溯造型”7e908837303959bbcd6153a401a8e487.png由於造型的方向是從衍生類到基礎類,箭頭朝上,所以通常把它叫作“上溯造型”,即Upcasting。上溯造型肯定是安全的,因為我們是從一個更特殊的類型到一個更常規的類型。換言之,衍生類是基礎類的一個超集。它可以包含比基礎類更多的方法,但它至少包含了基礎類的方法。進行上溯造型的時候,類接口可能出現的唯一一個問題是它可能丟失方法,而不是贏得這些方法。這便是在沒有任何明確的造型或者其他特殊標注的情況下,編譯器為什么允許上溯造型的原因所在。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值