版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、Thinking in Java 3rd Edition致讀者:我從 2002 年 7 月開始翻譯這本書,當(dāng)時(shí)還是第二版。但是翻完前言和介紹部分后,chinapub 就登出先生的作品。,說要的譯本。于是我中止了翻譯,等著侯我是第一時(shí)間買的 這本書,但是我失望了。比起第一版,我終于能看懂這本書了,但是相比預(yù)期,它還是差一點(diǎn)。所以當(dāng) Bruce Eckel 在他的上公開本書的第三版的時(shí)候,我決定把它翻譯出來。說說容易,做做難。一本 1000 多頁的書不是那么容易翻的。期間我也曾打過退堂鼓,但最終還是全部翻譯出來了。從今年的兩月初起,到 7 月底,我?guī)缀醴艞壛怂械臉I(yè)余時(shí)間,全身心地投入本書的翻譯之
2、中。應(yīng)該說,這項(xiàng)工作的難度超出了想像。首先,讀一本書和翻譯一本書完全是兩碼事。英語與中文是兩種不同的語言,用英語說得很暢的句子,翻成中文之后就完全破了相。有時(shí)我得花好幾分鐘,用中文重述一句我能用幾秒鐘讀懂的句子。更何況作為讀者,一兩句話沒搞 懂,并不影響你理解整本書,但對(duì)譯者來說,這就不一樣了。其次,這是一本講英語的人寫給講英語的人的書,所以同很多要照顧非英語讀者的技術(shù)文檔不同,它在用詞,句式方面非常隨意。英語讀者會(huì)很欣賞這一 點(diǎn),但是對(duì)外國讀者來說,這就是負(fù)擔(dān)了。再有,Bruce Eckel 這樣的大牛人,寫了 1000 多頁,如果都讓你讀懂,他豈不是太沒面子?所以,書里還有一些很有“禪意”
3、的句子。比如那句著名的“Thegenesis of the computer revolution was in a machine. Tesis of ourprogramming languages thus tends to look liket machine.”我就一直沒吃準(zhǔn)該怎么翻譯。大概沒人能吃準(zhǔn),說不定 Bruce 要的就是這個(gè)效果。這是一本公認(rèn)的名著,作者在技術(shù)上的造詣無可挑剔。而作為譯者,編程能力差了很多。再加上上面講的這些原因,使得我不得不格外的謹(jǐn)慎。當(dāng)我重讀初稿的時(shí)候,我發(fā)現(xiàn)需要修改的地方實(shí)在太多了。因此,我不能現(xiàn)在就公開全部譯稿,我只能公開已經(jīng)修改過的部分。不過這不是
4、最終的版本,我還會(huì)繼續(xù)修訂的。本來,我準(zhǔn)備到 10 月份,等我修改完前 7 章之后再公開。但是,我發(fā)現(xiàn)我又有點(diǎn)要放棄了,因此我決定給自己一點(diǎn)壓力,現(xiàn)在就公開。以后,我將修改完一章就公開一章,請(qǐng)關(guān)注 。如果你覺得好,請(qǐng)給告訴我,你的鼓勵(lì)是我工作的動(dòng)力;如果你覺得不好,那就更應(yīng)該告訴我了,我會(huì)參考你的意見作修改的。我希望能通過這種方法,譯出一本配得上原著的書。shhgs 2003 年 9 月 8 日第 1 頁 共 30 頁Chapter 6: Reusing Classes6: 復(fù)用類Java 最令人心動(dòng)的特性就是它的代碼復(fù)用了。但是僅僅拷貝源代碼再作修改是不能被稱為“”的。那是 C 之類的過程語
5、言所采用的辦法,而且也不怎么成功。就像 Java 里的一切,要解決這個(gè)問題還要靠類。你可以利用別人寫好的、已經(jīng)測試通過的類來創(chuàng)建新的類,不必一切都從零開始。這么做的訣竅就是,要在不改動(dòng)原有代碼的前提下使用類。本章會(huì)介紹兩種做法。第一種非常簡單:在新的類里直接創(chuàng)建舊的類的對(duì)象。這被稱為(comtion),因?yàn)樾碌念愂怯膳f的類是代碼的功能,而不是它的形式。而來的。你所復(fù)用的只第二種方法更為精妙。它會(huì)創(chuàng)建一個(gè)新的,與原來那個(gè)類同屬一種類型的類。你全盤接受了舊類的形式,在沒有對(duì)它做修改的情況下往里面添加了新的代碼。這種神奇的做法就被稱為繼承(inheritance)。編譯器會(huì)承擔(dān)絕大部分的工作。繼承是
6、面象編程的基石,它還有一些額外的含義,對(duì)此會(huì)在第 7 章再做探討。與繼承在語法和行為上有許多相似之處(這很好理解,因?yàn)樗鼈兌际窃谠蓄惖幕A(chǔ)上創(chuàng)建新類)。你會(huì)在本章學(xué)到這些代碼復(fù)用的機(jī)制。所使用的語法實(shí)際上已經(jīng)看到很多的案例了。只要把對(duì)象的 reference 直接放到新的類里面就行了。假設(shè),你要?jiǎng)?chuàng)建一個(gè)新的類,其中有幾個(gè) String 對(duì)象,幾個(gè) primitive 數(shù)據(jù),以及一個(gè)別的什么類型的對(duì)象。對(duì)于非 primitive 的對(duì)象,你只要把它的 reference 放到類里就行了,但是對(duì)于 primitive,你就只能直接定義了:/: c06:SprinklerSystem.java/
7、Comition for code reuse. import com.bruceeckel.simpletest.*;class WaterSource private String s; WaterSource() System.out.prln(WaterSource();s = new String(Constructed);public String toString() return s; public class SprinklerSystem private sic Test monitor = new Test(); private String valve1, valve2
8、, valve3, valve4;第 2 頁 共 30 頁Thinking in Java 3rd Editionprivate WaterSourource;privatei;private floatf; toString()public String returnvalve1 = valve2 = valve3 = valve4 = + valve1 + valve2 + valve3 + valve4+ n + n + n + n +i = + i + n +f = + f + n +source = + source;public sicvoid main(String args)
9、SprinklerSystem sprinklers = newSprinklerSystem(); System.out.prln(sprinklers);monitor.expect(new String valve1 = null,valve2 = null, valve3 = null, valve4 = null, i = 0,f = 0.0,source = null); /:這兩個(gè)類都定義了一個(gè)特殊的方法:toString( )。以后你就會(huì)知道,所有非 primitive 對(duì)象都有一個(gè) toString( )方法,當(dāng)編譯器需要一個(gè) String 而它卻是一個(gè)對(duì)象的時(shí)候,編譯器就會(huì)
10、自動(dòng)調(diào)用這個(gè)方法。所以當(dāng)編譯器從 SprinklerSystem.toString( )的:source = + source;中看到,你想把 String 同 WaterSouce 相加的時(shí)候,它就會(huì)說“由于 String 只能同 String 相加,因此我要調(diào)用 source 的toString( ),因?yàn)椴拍馨阉D(zhuǎn)換成 String!”。 于是它就把這兩個(gè) String 連起來,然后再 String 的形式把結(jié)果返還給 System.out.prln( )。如果你想讓你寫的類也具備這個(gè)功能,只要寫一個(gè) toString(方法就行了。已經(jīng)在第 2 章講過,當(dāng) primitive 數(shù)據(jù)作為類
11、的成員的時(shí)候,會(huì)被自動(dòng)地初始化為零。而對(duì)象的 reference 則會(huì)被初始化為 null,如果這時(shí),你去調(diào)用這個(gè)對(duì)象的方法,就會(huì)得到異常。能把它打印出來而不拋出異常,這真是太好了(而且也很實(shí)用)。第 3 頁 共 30 頁Chapter 6: Reusing Classes“編譯器不為 reference 準(zhǔn)備默認(rèn)對(duì)象”的這種做法,實(shí)際上也是很合乎邏輯的。因?yàn)樵诤芏嗲闆r下,這么做會(huì)不必要的性能開銷。如果你想對(duì) reference 進(jìn)行初始化,那么可以在以下幾個(gè)時(shí)間進(jìn)行:1.在定義對(duì)象的時(shí)候。這就意味著在構(gòu)造函數(shù)調(diào)用之前,它們已經(jīng)初始化完畢了。在這個(gè)類的構(gòu)造函數(shù)里。在即將使用那個(gè)對(duì)象之前。這種做
12、法通常被稱為“偷懶初始化(lazy initialization)”。如果碰到創(chuàng)建對(duì)象的代價(jià)很高,或者不是每次都需要?jiǎng)?chuàng)建對(duì)象的時(shí)候,這種做法就能降低程序的開銷了。2.3.下面這把這三種辦法都演示一遍:/: c06:Bath.java/ Constructor initialization with comition. import com.bruceeckel.simpletest.*;class Soap private String s; Soap() System.out.prln(Soap();s = new String(Constructed);public String toSt
13、ring() return s; public class Bath private sic Test monitor = new Test();private String / Initializing at po definition:s1 = new String(Happy), s2 = Happy,s3, s4;private Soap castille;ofprivatei;private float toy; public Bath() System.out.prln(Inside Bath();s3 = new String(Joy);i = 47;toy = 3.14f;ca
14、stille = new Soap();public String toString() if(s4 = null) / Delayed initialization: s4 = new String(Joy);returns1 = + s1 s2 = + s2 s3 = + s3 s4 = + s4 i = + i + n + n + n + n +n +toy = + toy + n + castille = + castille;public sic void main(String args) Bath b = new Bath();第 4 頁 共 30 頁Thinking in Ja
15、va 3rd EditionSystem.out.prln(b);monitor.expect(new String Inside Bath(),Soap(),s1 = Happy, s2 = Happy, s3 = Joy,s4 = Joy,i = 47,toy = 3.14,castille = Constructed); /:注意,Bath 的構(gòu)造函數(shù)會(huì)先打印一條消息再進(jìn)行初始化。如果你不在定義對(duì)象的時(shí)候進(jìn)行初始化,那么沒人可以擔(dān)保,在向這個(gè)對(duì)象的 reference 發(fā)送消息的時(shí)候,它已經(jīng)被初始化了反倒是會(huì)有異常來告訴你,它還沒有初始化,。調(diào)用 toString( )的時(shí)候它會(huì)先為 s
16、4 賦一個(gè)值,這樣它就不會(huì)化而被使用了。初始繼承所使用的語法繼承是 Java(也是所有 OOP 語言)不可分割的一部分。實(shí)際上當(dāng)你創(chuàng)建類的時(shí)候,你就是在繼承,要么是顯式地繼承別的什么類,要么是隱含地繼承了標(biāo)準(zhǔn) Java 根類,Object。的語法很平淡,但繼承就有所不同了。繼承的時(shí)候,你得先“新類和舊類是一樣的。”跟平常一樣,你得先在程序里寫上類的名字,但是在開始定義類之前,你還得加上 extends和基類(base class)的名字。做完這些之后,新類就會(huì)自動(dòng)獲得基類的全部成員和方法。下面就是一個(gè)例子:/: c06:Detergent.java/ Inheritanyntax & prop
17、erties.import com.bruceeckel.simpletest.*;class Cleanser protected sic Test monitor = new Test(); private String s = new String(Cleanser); public void append(String a) s += a; public void dilute() append( dilute(); public void apply() append( apply(); public void scrub() append( scrub(); public Stri
18、ng toString() return s; public sic void main(String args) Cleanser x = new Cleanser(); x.dilute(); x.apply(); x.scrub();第 5 頁 共 30 頁Chapter 6: Reusing ClassesSystem.out.prln(x);monitor.expect(new String Cleanser dilute() apply() scrub();public class Detergent extends Cleanser / Change a method:publi
19、c void scrub() append( Detergent.scrub(); super.scrub(); / Call base-class ver/ Add methods to theerface:public void foam() append( foam(); / Test the new class:public sic void main(String args) Detergent x = new Detergent(); x.dilute();x.apply();x.scrub();x.foam();System.out.pr System.out.prln(x);l
20、n(Testing base class:);monitor.expect(new String Cleanser dilute() apply() +Detergent.scrub() scrub() foam(),Testing base class:,);Cleanser.main(args);/:這能告訴很多東西。首先 Cleanser 的 append( )方法用+= 運(yùn)算符將 Sting 同 s 聯(lián)接起來。Java 的設(shè)計(jì)者們“重載”了這個(gè)操作符,使之能作用于 String。第二,Cleanser 和 Detergent 都有一個(gè) main( )方法。你可以為每個(gè)類都創(chuàng)建一個(gè) ma
21、in( ),而且這也是一種值得提倡的編程方法,因?yàn)檫@樣一來,測試代碼就能都放進(jìn)類里了。即使程序包括了很多類,它也只會(huì)調(diào)用你在命令行下給出的那個(gè)類的 main( )方法。(只要 main( )是 public 的就行了,至于類是不是 public 的,并不重要。)于是,當(dāng)你輸入 java Detergent 的時(shí)候,它就會(huì)調(diào)用 Detergent.main( )。雖然 Cleanser 不是 public 的,但是你也可以用 java Cleanser 來調(diào)用 Cleanser.main( )。這種往每個(gè)類里都放一個(gè) main( )的做法,能讓類的單元測試變得更容易一些。做完測試之后,你也不必移
22、除main( );留下它可以供以后的測試用。這里,Detergent.main( )直接調(diào)用了 Cleanser.main( ),并且把命令行參數(shù)原封不動(dòng)地傳給了它(實(shí)際上可以使用任何 String 數(shù)組)。第 6 頁 共 30 頁Thinking in Java 3rd Edition Cleanser public package package Detergent ? package ? ? Cleanser ? ? Cleanser public (? ? ? protected )? ? ? ? private ? public ? ? ? ? ? ? ? ? ? Cleanser
23、? ? ?append() dilute() apply() scrub() ? toString() ? Detergent ?Cleanser ? ( ? extends) ? ? ? ? ? ? ? ? ? ? ? ? scrub( )? ? ? ? ? ? ? ? ? ? ? ? ?scrub( )? scrub( ) ? ? ? ? ? Java ? ? ? ? (superclass)? super scrub( ) super.scrub( ) ? ? ? ? ? ? ? ? ? ? ? ? foam( ) ? ? Degergent.main( )? Detergent ? C
24、leanser ? ? ( foam() ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(subobject) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Java ? ? ? ? ? ? ? ? ? ? ? 730Chapter 6: Reusing Classes/: c06:Cartoon.java/ Constructor calls during inheritance. import com.bruceeckel.simpletes
25、t.*;class Art Art() System.out.prln(Art constructor);class Drawing extends Art Drawing() System.out.prln(Drawing constructor);public class Cartoon extends Drawing private sic Test monitor = new Test(); public Cartoon() System.out.prln(Cartoon constructor);public sic void main(String args) Cartoon x
26、= new Cartoon(); monitor.expect(new String Art constructor, Drawing constructor, Cartoon constructor); /:可以看到,構(gòu)造行為是從基類“向外”發(fā)展的,所以基類會(huì)在派生類的構(gòu)造函數(shù)它之前先進(jìn)行初始化。即便你不創(chuàng)建 Cartoon( )的構(gòu)造函數(shù),編譯器也會(huì)為你造一個(gè)默認(rèn)的構(gòu)造函數(shù),然后再由它去調(diào)用基類的構(gòu)造函數(shù)。帶參數(shù)的構(gòu)造函數(shù)在上述例程中,構(gòu)造函數(shù)都是默認(rèn)的;也就是不帶參數(shù)的。對(duì)編譯器來說,調(diào)用這種構(gòu)造函數(shù)會(huì)非常簡單,因?yàn)楦揪蜎]有要傳哪些參數(shù)的問題。但是如果類沒有默認(rèn)的構(gòu)造函數(shù)(也就是無參數(shù)
27、的構(gòu)造函數(shù)),或者你要調(diào)用的基類構(gòu)造函數(shù)是帶參數(shù)的,你就必須用 super的參數(shù)明確地調(diào)用基類的構(gòu)造函數(shù):以及合適/: c06:Chess.java/ Inheritance, constructors and arguments. import com.bruceeckel.simpletest.*;class Game Game(i) System.out.prln(Game constructor);第 8 頁 共 30 頁Thinking in Java 3rd Editionclass BoardGame extends Game BoardGame(i) super(i);Syst
28、em.out.prln(BoardGame constructor);public class Chess extends BoardGame private sic Test monitor = new Test(); Chess() super(11);System.out.prln(Chess constructor);public sic void main(String args) Chess x= new Chess();monitor.expect(new String Game constructor, BoardGame constructor, Chess construc
29、tor); /:如果你不在 BoardGame( )里面調(diào)用基類的構(gòu)造函數(shù),編譯器就會(huì)報(bào)錯(cuò)說它找不到 Game( ) 形式(譯者注:即默認(rèn))的構(gòu)造函數(shù)。此外,對(duì)派生類構(gòu)造函數(shù)而言,調(diào)用基類的構(gòu)造函數(shù)應(yīng)該是它做的第一件事。(如果你做錯(cuò)了,編譯器就會(huì)提醒你。)捕獲基類構(gòu)造函數(shù)拋出的異常剛說了,編譯器會(huì)強(qiáng)制你將基類構(gòu)造函數(shù)的調(diào)用放在派生類的構(gòu)造函數(shù)的最前面。也就是說,在它之前不能有任何東西。等到第 9 章你就會(huì)知道,這么做會(huì)妨礙派生類的構(gòu)造函數(shù)捕獲基類拋出的異常。這一點(diǎn)有時(shí)會(huì)很不方便。把和繼承結(jié)合起來同時(shí)使用和繼承的現(xiàn)象是很普遍的。下面這演示了,怎樣使用和繼承,以及利用構(gòu)造函數(shù)來進(jìn)行初始化這一必不可
30、少的步驟,來創(chuàng)建一個(gè)較為復(fù)雜的類:/: c06:Plaetting.java/ Combining comition & inheritance.import com.bruceeckel.simpletest.*;class Plate Plate(i) System.out.prln(Plate constructor);第 9 頁 共 30 頁Chapter 6: Reusing Classesclass Dinnlate extends Plate Dinnlate(i) super(i); System.out.prln(Dinnlate constructor);clatensil
31、 Utensil(i) System.out.prln(Utensil constructor);class Spoon extends Spoon(i) super(i); System.out.prUtensil ln(Spoon constructor);class Fork extends Fork(i) super(i); System.out.prUtensil ln(Fork constructor);class Knife extends Knife(i) super(i); System.out.prUtensil ln(Knife constructor);/ A cult
32、ural way class Custom Custom(i) Sfng something:ln(Custom constructor);public class Plaetting extends Custom private sic Test monitor = new Test(); private Spoon sp;private Fork frk;private Knife kn; private Dinnlate pl;public Pla super(i +etting(i) 1);sp = new Spoon(i + 2); frk = new Fo
33、rk(i + 3); kn = new Knife(i + 4);pl = new Dinn System.out.prlate(i + 5);ln(Plaetting constructor);public sic void main(String args) Plaetting x = new Plaetting(9);monitor.expect(new String Custom constructor, Utensil constructor, Spoon constructor,第 10 頁 共 30 頁Thinking in Java 3rd EditionUtensil con
34、structor, Fork constructor, Utensil constructor, Knife constructor, Plate constructor,Dinnlate constructor,Pla);etting constructor /:雖然編譯器會(huì)強(qiáng)制你對(duì)基類進(jìn)行初始化,并且會(huì)要求你在構(gòu)造函數(shù)的開始部分完成初始化,但是它不會(huì)檢查你是不是進(jìn)行了成員對(duì)象的初始化,因此你只能自己留神了。確保進(jìn)行妥善地拆構(gòu)函數(shù)(destructor)是 C+里面的概念,它是一種能在對(duì)象的時(shí)候自動(dòng)調(diào)用的方法,Java 里面沒有這種概念。原因可能是 Java 處理這類問題的時(shí)候,只是簡單地把
35、對(duì)象放到一邊,然后留給回收器去處理,它不會(huì)去主動(dòng)地進(jìn)行。在大多數(shù)情況下,這種做法也很不錯(cuò),但是有時(shí)候,會(huì)遇到一些特殊的類,在它們的對(duì)象的時(shí)候會(huì)需要進(jìn)行一些額外的操作。正如第 4 章所說的,你既不知道回收器什么時(shí)候啟動(dòng),也不知道它會(huì)不會(huì)啟動(dòng)。所以如果要進(jìn)行,你就必須明確地寫一個(gè)專門干這件事的方法,然后告訴客戶程序員們?nèi)フ{(diào)用這個(gè)方法。做了這些還不夠到第 9 章(“用異常處理錯(cuò)誤”)還要講為了應(yīng)付異常,你還要把它放到 finally 子句里面。就拿計(jì)算機(jī)輔助設(shè)計(jì)系統(tǒng)舉例,要在屏幕上畫一點(diǎn)東西:/: c06:CADSystem.java/ Ensuring proper cleanup. packag
36、e c06;import com.bruceeckel.simpletest.*; import java.util.*;class Sh Sh(i) System.out.prln(Shconstructor);void dise() System.out.prln(Shdise);class Circle extends Sh Circle(i) super(i);第 11 頁 共 30 頁Chapter 6: Reusing ClassesSystem.out.prln(DrawingCircle);void dise() System.out.prln(ErasingCircle);s
37、uper.dise();class Triangle extends Sh Triangle(i) super(i);System.out.prln(DrawingTriangle);void dise() System.out.prln(ErasingTriangle);super.dise();class Line extends Shprivate Line(start, end;start,end) super(start);this.start = start; this.end = end; System.out.prln(DrawingLine: + start+,+ end);
38、void dise() System.out.prln(ErasingLine: + start+,+ end);super.dise();public class CADSystem extends Sh private sic Test monitor = new Test(); private Circle c;private Triangle t;privaine lines = new Line5; public CADSystem(i) super(i + 1);for(j = 0; j = 0; i-)linesi.dise();super.dise();public sic v
39、oid main(String args) CADSystem x = new CADSystem(47);try 第 12 頁 共 30 頁Thinking in Java 3rd Edition/ Code and exception handling. finally x.dise();monitor.expect(new String Shconstructor,Shconstructor, Drawing Line: 0, 0, Shconstructor, Drawing Line: 1, 1, Shconstructor, Drawing Line: 2, 4,Shconstru
40、ctor,Drawing Line: 3, 9,Shconstructor,Drawing Line: 4, 16, Shconstructor, Drawing Circle,Shconstructor, Drawing Triangle, Combined constructor, CADSystem.dise(), Erasing Triangle,Shdise, Erasing Circle, Shdise,Erasing Line: 4,16,Shdise,Erasing Line: 3,9,Shdise,Erasing Line: 2, Shdise, Erasing Line:
41、1, Shdise, Erasing Line: 0, Shdise,Shdise4,1,0,); /:這個(gè)系統(tǒng)里的所有東西都是 Sh(而 Sh本身又是 Object,因?yàn)樗请[含地繼承自根類) 。各個(gè)類在覆寫 Sh的 dise( )方法的時(shí)候,除了用 super 調(diào)用基類的 dis之外,還要完成它自己的e(活動(dòng)。具體的 Sh類Cir,Triangle 以及 Line都有會(huì)把自己“畫出來”的構(gòu)造函數(shù),但實(shí)際上,對(duì)象的生命周期內(nèi)調(diào)用的任何方法,都可能會(huì)造成一些需要進(jìn)行的,用來恢復(fù)內(nèi)存以外的資源狀態(tài)的 dis的。每個(gè)類都有它自己e( )方法。main( )里面有兩個(gè)要到第 9會(huì)正式介紹的新:try
42、 和finally。try 表示下面這(由花括號(hào)限定)是一個(gè)需要給予特殊關(guān)注的受保護(hù)的區(qū)域(guarded region)。所謂的特殊關(guān)注就是,無論以何種方式退出 try 區(qū)塊,都必須執(zhí)行跟在這個(gè)受保護(hù)區(qū)域后面的 finally 子第 13 頁 共 30 頁Chapter 6: Reusing Classes句。(對(duì)于異常處理來說,會(huì)有很多退出 try 區(qū)塊的情況。)這里finally 的意思是“不論發(fā)生什么事情,你都必須調(diào)用 x 的e( )”。會(huì)在第 9 章再詳細(xì)解釋這兩個(gè)。dis注意,在方法中,如果子對(duì)象之間有依賴關(guān)系,那么你還要留意其基類和成員對(duì)象的方法的調(diào)用順序。總之, 這個(gè)順序同 C
43、+的編譯器要求的拆構(gòu)函數(shù)的執(zhí)行順序是一樣的:先按照創(chuàng)建對(duì)象的相反順序進(jìn)行類的。(一般來說,這要求留著基類對(duì)象以供。)然后調(diào)用基類的清理方法,就像這里所做的。在很多情況下,并不是什么問題;把它留給回收器就行了。但是如果你要自己做的話,那就只能辛苦一點(diǎn)了,而且還要格外,因?yàn)樵诨厥辗矫?,誰都幫不上你。回收器可能永遠(yuǎn)也不會(huì)啟動(dòng)。即便它啟動(dòng)了,你也沒法控制它的回收順序。最好不要依賴回收器去做任何與內(nèi)存回收無關(guān)的事情。如果你要進(jìn)行去用 finalize( )。,一定要自己寫方法,別名字的遮蓋如果 Java 的基類里有一個(gè)被重載了好幾次的方法,那么在派生類里重新定義那個(gè)方法,是不會(huì)把基類里定義的任何一個(gè)給遮
44、蓋掉的(這點(diǎn)同 C+不同)。因此,無論方法是在這一層還是在其基類定義的,重載都能起作用:/: c06:Hide.java/ Overloading a base-class method name in classaderived/ does not hide the base-class ver import com.bruceeckel.simpletest.*;s.class Homer char doh(char c) System.out.prln(doh(char); return d;float doh(float f) System.out.prln(doh(float); r
45、eturn 1.0f;class Milhouse class Bart extends Homer void doh(Milhouse m) System.out.prln(doh(Milhouse);public class private s public sBart b =Hide ic Test monitor = new Test(); ic void main(String args) new Bart();第 14 頁 共 30 頁Thinking in Java 3rd Editionb.doh(1); b.doh(x);b.doh(1.0f);b.doh(new Milho
46、use(); monitor.expect(new String doh(float),doh(char),doh(float), doh(Milhouse); /:可以看到,盡管 Bart 又重載了一遍,但 Homer 所重載的方法,在Bart 里依然有效(在 C+里這么做的話,就會(huì)把基類方法全都隱了)。到下一章你就會(huì)知道,在派生類里用相同的參數(shù)列表,相同的返回類型來覆寫方法的這種做法,實(shí)在是太普通了。否則就太亂了(這也是為什么 C+不允許你這么做的原因要防止你去做可能會(huì)是錯(cuò)的事)。用還是繼承與繼承都能讓你將子對(duì)象植入新的類(是顯式的,繼承是隱含的)。也許你想了解一下這兩者區(qū)別,以及該如何進(jìn)
47、行選擇。一般來說,用于新類要使用舊類的功能,而不是其接口的場合。也就是說,把對(duì)象嵌進(jìn)去,用它來實(shí)現(xiàn)新類的功能,但是用戶看到的是新類的接口,而不是嵌進(jìn)去的對(duì)象的接口。因此,你得在新類里嵌入 private的舊類對(duì)象。有時(shí),讓用戶直接新類的各個(gè)組成部分也是合乎情理的;這就是說,將成員對(duì)象定義成 public。成員對(duì)象各自都有“隱藏實(shí)現(xiàn)”的機(jī)制,因此這么做也是安全的。如果用戶知道你用了哪些零件,那么接口對(duì)他們來說就變得更簡單了。car 對(duì)象就是一個(gè)好例子:/: c06:Car.java/ Comition with public objects.class Engine public void st
48、art() public void rev() public void stop() class Wheel public void inflate(psi) class Window public void rollup() public void rolldown() 第 15 頁 共 30 頁Chapter 6: Reusing Classesclass Door public Window window = public void open() public void close() newWindow();public class Car public Engine engine =
49、 public Wheel wheel = public Doornew newEngine();Wheel4;left = new Door(),right = new Door(); / 2-door public Car() for(i = 0; i wheeli = new 4; i+)Wheel();public sic voidmain(String args)Car car = new Car();car.left.window.rollup(); car.wheel0.inflate(72); /:由于在這個(gè)例子里,car 的各個(gè)組成部分(不僅僅是其底層實(shí)現(xiàn)的一部分)還是一個(gè)分
50、析問題的過程,因而將成員定義成 public 的,有助于客戶程序員理解該如何使用這個(gè)類,由此也降低了這個(gè)類自身的開發(fā)難度。但是要記住這只是一個(gè)特列,通常情況下,你都應(yīng)該將成員數(shù)據(jù)定義成 private 的。繼承則是要對(duì)已有的類做一番改造,以此獲得一個(gè)特殊版本。簡而言之,你要將一個(gè)較為抽象的類改造成能適用于某些特定需求的類。稍微想一下就會(huì)知道,用 vehicle(車輛)對(duì)象來一個(gè) car(轎車)是毫無意義的car 不包含 vehicle,它本來就是vehicle。繼承要表達(dá)的是一種“是(is-a)”關(guān)系,而表達(dá)要表達(dá)的是“有(has-a)”關(guān)系。protected現(xiàn)在你已經(jīng)知道繼承了,因此pro
51、tected 也有意義了。在理想情況下 private已經(jīng)夠用了。但是在實(shí)際的項(xiàng)目中,你有時(shí)會(huì)碰到,要讓一些東西對(duì)外部世界隱藏,但是卻要對(duì)它的繼承類開放。就是這種實(shí)用主義的體現(xiàn)。它的意思是“對(duì)用戶而protected言,它是 private 的,但是如果你想繼承這個(gè)類,或者開發(fā)一個(gè)也屬于這個(gè) package 的類的話,就可以提供 package 的權(quán)限。)它了。”(Java 的 protected 也最好的做法是,將數(shù)據(jù)成員設(shè)成 private 的;你應(yīng)該永遠(yuǎn)保留修改底層實(shí)現(xiàn)的權(quán)利。然后用 protected 權(quán)限的方法來控制繼承類的權(quán)限:第 16 頁 共 30 頁Thinking in Ja
52、va 3rd Edition/: c06:Orc.java/ The protected keyword.import com.bruceeckel.simpletest.*; import java.util.*;class Villain private String name;protected void set(String nm) name = nm; public Villain(String name) = name; public String toString() return Im a Villain and my name is + name;publ
53、ic class Orc extends Villain private sic Test monitor = new Test();privateumber;public Orc(String name, super(name);umber =umber)umber;public void change(String name, set(name); / Availabecause itsumber) protectedumber =umber;public String toString() return Orc + super.toString();umber + : +public s
54、ic void main(String Orc orc = new Orc(Limburger, System.out.prln(orc); orc.change(Bob, 19); System.out.prln(orc); monitor.expect(new String args) 12);Orc 12: Im a Villain and my nameisLimburger,Orc 19: Im a Villain and my nameis Bob); /:可以看到 change( )調(diào)用了set( ),因?yàn)樗?protected 的。 此外還要注意一下 Orc 的 toStri
55、ng( )方法,它用到了基類的 toString( )方法。漸進(jìn)式的開發(fā)繼承的優(yōu)點(diǎn)之一就是,它支持漸進(jìn)式的開發(fā)(incremental develop)。添加新的代碼的時(shí)候,不會(huì)給老代碼帶來 bug;實(shí)際上新的 bug 全都被圈在新代碼里。通過繼承已有的,已經(jīng)能正常工作的類,然后再添加一些數(shù)據(jù)成員和方法(以及重新定義一些原有的方法),你可以不去修改那些可能還有人在用的老代碼,因而也就不會(huì)造成 bug 了。一旦發(fā)現(xiàn)了 bug,你第 17 頁 共 30 頁Chapter 6: Reusing Classes就知道它肯定是在新代碼里。相比要去修改老代碼,新代碼會(huì)短很多,讀起來也更簡單。類的竟會(huì)如此徹
56、底,這真是太令人驚訝了。你甚至不需要源代碼就能進(jìn)行復(fù)用。最多就是 import 一個(gè) package。(對(duì)于繼承和這樣。)而言都是你得明白,程序開發(fā)就像人的學(xué)樣,是一個(gè)漸進(jìn)的過程。不論你作過多少分析,不實(shí)際做項(xiàng)目的話,還是得不到。如果你能摒棄像建玻璃摩天樓那樣畢其功于一役的開發(fā)方式,而采用類似生物進(jìn)化的,讓那個(gè)項(xiàng)目逐步的“增長”的開發(fā)方式,那么你就會(huì)獲得更大的成功以及的及時(shí)反饋。盡管在試驗(yàn)階段,繼承是一種非常有用的技術(shù),但是當(dāng)項(xiàng)目進(jìn)入穩(wěn)定階段之后,你就得用一種新的眼光來審視類的繼承體系了,你要把它壓縮成一個(gè)合乎邏輯的結(jié)構(gòu)。記住,在這些錯(cuò)綜復(fù)雜的關(guān)系后面,繼承實(shí)質(zhì)上是在表達(dá)這樣一種關(guān)系:“新的類
57、是一種舊的類”。程序不應(yīng)該圍著 bit 轉(zhuǎn),它應(yīng)該從問題空間出發(fā),通過創(chuàng)建和操控形形問題的方法。的對(duì)象來表達(dá)一種解決上傳繼承最重要的特征不在于它為新類提供了方法,而是它表達(dá)了新類同基類之間的關(guān)系。這種關(guān)系可以被歸納為一句話“新類就是一種原有的類。”這并不是在空口說白話語言直接給了支持。比方說,表示樂器的基類叫 Instrument,然后有一個(gè)叫Wind 的派生類。繼承的意思就是基類有的方法派生類都有,因此送給基類的消息也可以送給派生類。如果 Instrument 有一個(gè) play( )方法,那么 Wind 也有。也就是說,你可以很有把握地說,Wind 對(duì)象也是一種 Instrument。下面這
58、示了編譯器是怎樣支持這種觀念的:演/: c06:Wind.java/ Inheritance & upcasting. import java.util.*;class Instrument public void play() sic void tune(Instrument i) / .play();/ Wind objects are instruments/ because they have the sameerface:public class Wind extends Instrument public sic void main(String args) Wind flute
59、= new Wind();第 18 頁 共 30 頁Thinking in Java 3rd EditionInstrument.tune(flute); / Upcasting /:這個(gè)例子里的 tune( )方法很有趣。它需要 Instrument 的 reference作參數(shù),但是 Wind.main( )給了它一個(gè)Wind 的 reference。知道 Java 的類型檢查是很挑剔的,因此如果你不知道 Wind 對(duì)象就是一種 Instrument 對(duì)象,而且除了 Wind 之外 tune( )沒有別的 Instrument 可調(diào),你就會(huì)覺得很困惑,為什么接受 Instrument 的方
60、法也可以接受 Wind。tune( )的代碼可以作用于 Instrument,以及 Instrument 的派生類,而將 Wind 的 reference 轉(zhuǎn)換成 Instrument 的 reference 的這種做法就被稱為“上傳 (upcasting)”。為什么叫“上傳”?這個(gè)術(shù)語是有的,它緣于類的繼承關(guān)系圖的傳統(tǒng)畫法:將根置于頂端,然后向下發(fā)展(當(dāng)然,你也可以按照你的繼承關(guān)系圖就是:來畫。)Wind.java 的把派生類傳給基類就是沿著繼承圖往上送,因此被稱為“上傳 (upcasting)”。上傳總是安全的,因?yàn)槟闶前岩粋€(gè)較具體的類型轉(zhuǎn)換成較為一般的類型。也就是說派生類是基類的超集(s
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 小學(xué)五年級(jí)《分?jǐn)?shù)加減混合運(yùn)算》教學(xué)設(shè)計(jì)
- 小學(xué)數(shù)學(xué)一年級(jí)上冊:10以內(nèi)口算過關(guān)練習(xí)題
- 《瞳孔大小的臨床見》課件
- 湖南省株洲市2025屆高三上學(xué)期教學(xué)質(zhì)量統(tǒng)一檢測物理答案
- 高考新課標(biāo)語文模擬試卷系列之66
- 《病房監(jiān)護(hù)系統(tǒng)》課件
- 《研究性學(xué)習(xí)的評(píng)價(jià)》課件
- 《汽車行業(yè)發(fā)展》課件
- 營養(yǎng)科護(hù)士年終總結(jié)
- 建材行業(yè)人事工作總結(jié)
- 云南省昆明市官渡區(qū)2023-2024學(xué)年五年級(jí)上學(xué)期期末學(xué)業(yè)水平檢測科學(xué)試題
- 初中語文部編版八年級(jí)上冊期末文學(xué)文化常識(shí)專項(xiàng)練習(xí)(2022秋)(附參考答案)
- 2023-2024學(xué)年廣東省中山市高二上冊期末英語試題(附答案)
- 支氣管鏡檢查并發(fā)癥預(yù)防及處理
- 山西省晉中市2022-2023學(xué)年四年級(jí)下學(xué)期期末學(xué)業(yè)水平監(jiān)測英語試題
- 2023年樁基項(xiàng)目經(jīng)理年度總結(jié)及年后展望
- 企業(yè)社會(huì)責(zé)任與數(shù)字時(shí)代的適應(yīng)性
- 巴以沖突完整
- Unit5PartALetsspellPartBCLetscheck-Storytime教學(xué)設(shè)計(jì)四年級(jí)英語上冊(人教PEP版)
- 苗木采購?fù)稑?biāo)方案(技術(shù)標(biāo))
- 垃圾分類督導(dǎo)服務(wù)投標(biāo)方案(技術(shù)方案)
評(píng)論
0/150
提交評(píng)論