第五章隱藏實施過程_第1頁
第五章隱藏實施過程_第2頁
第五章隱藏實施過程_第3頁
第五章隱藏實施過程_第4頁
第五章隱藏實施過程_第5頁
已閱讀5頁,還剩10頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

1、第5章隱藏實施過程“進(jìn)行面向?qū)ο蟮脑O(shè)計時,一項基本的考慮是:如何將發(fā)生變化的東西與保持不變的東西分隔開?!边@一點對于庫來說是特別重要的。那個庫的用戶(客戶程序員)必須能依賴自己使用的那一部分,并知道一 旦新版本的庫岀臺,自己不需要改寫代碼。而與此相反,庫的創(chuàng)建者必須能自由地進(jìn)行修改與改進(jìn),同時保 證客戶程序員代碼不會受到那些變動的影響。為達(dá)到這個目的,需遵守一定的約定或規(guī)則。例如,庫程序員在修改庫內(nèi)的一個類時,必須保證不刪除已有 的方法,因為那樣做會造成客戶程序員代碼岀現(xiàn)斷點。然而,相反的情況卻是令人痛苦的。對于一個數(shù)據(jù)成 員,庫的創(chuàng)建者怎樣才能知道哪些數(shù)據(jù)成員已受到客戶程序員的訪問呢?若方法

2、屬于某個類唯一的一部分, 而且并不一定由客戶程序員直接使用,那么這種痛苦的情況同樣是真實的。如果庫的創(chuàng)建者想刪除一種舊有 的實施方案,并置入新代碼,此時又該怎么辦呢?對那些成員進(jìn)行的任何改動都可能中斷客戶程序員的代 碼。所以庫創(chuàng)建者處在一個尷尬的境地,似乎根本動彈不得。為解決這個問題,Jav推岀了“訪問指示符”的概念,允許庫創(chuàng)建者聲明哪些東西是客戶程序員可以使用 的,哪些是不可使用的。這種訪問控制的級別在“最大訪問”和“最小訪問”的范圍之間,分別包括: public, “友好的"(無關(guān)鍵字),prot以及tperdivate。根據(jù)前一段的描述,大家或許已總結(jié)岀作為- 名庫設(shè)計者,應(yīng)將

3、所有東西都盡可能保持為“private” (私有),并只展示岀那些想讓客戶程序員使用的方 法。這種思路是完全正確的,盡管它有點兒違背那些用其他語言(特別是C)編程的人的直覺,那些人習(xí)慣于在沒有任何限制的情況下訪問所有東西。到這一章結(jié)束時,大家應(yīng)該可以深刻體會到Jav訪問控制的價值。然而,組件庫以及控制誰能訪問那個庫的組件的概念現(xiàn)在仍不是完整的。仍存在這樣一個問題:如何將組件 綁定到單獨一個統(tǒng)一的庫單元里。這是通過JaVMpackage(打包)關(guān)鍵字來實現(xiàn)的,而且訪問指示符要受到類在相同的包還是在不同的包里的影響。所以在本章的開頭,大家首先要學(xué)習(xí)庫組件如何置入包里。這樣 才能理解訪問指示符的完整

4、含義。5. 1包:庫單元我們用impo關(guān)鍵字導(dǎo)入一個完整的庫時,就會獲得“包” (Package)。例如: import java.util.*;它的作用是導(dǎo)入完整的實用工具(Utility )庫,該庫屬于標(biāo)準(zhǔn)av開發(fā)工具包的一部分。由于 Vect位于 java.Ut,所以現(xiàn)在要么指定完整名稱“ java.util.Vector” i m可省語句),要么簡單地指定 一個“ Vector” (因為npo是默認(rèn)的)。若想導(dǎo)入單獨一個類,可在 impo語句:里指定那個類的名字:import java.util.Vector;現(xiàn)在,我們可以自由地使用 Vector。然而,jav中.的其他任何類仍是不可

5、使用的。之所以要進(jìn)行這樣的導(dǎo)入,是為了提供一種特殊的機(jī)制,以便管理“命名空間” (Name Space)。我們所有 類成員的名字相互間都會隔離起來。位于類A內(nèi)的一個方法f()不會與位于類3內(nèi)的、擁有相同“簽名”(自變量列表)的f()發(fā)生沖突。但類名會不會沖突呢?假設(shè)創(chuàng)建一個sta類k將它安裝到已有一個 stack 類(由其他人編寫)的機(jī)器上,這時會岀現(xiàn)什么情況呢?對于因特網(wǎng)中的Jav應(yīng)用,這種情況會在用戶毫不知曉的時候發(fā)生,因為類會在運(yùn)行一個 Jav程序的時候自動下載。正是由于存在名字潛在的沖突,所以特別有必要對Jav書的命名空間進(jìn)行完整的控制,而且需要創(chuàng)建一個完全獨一無二的名字,無論因特網(wǎng)存

6、在什么樣的限制。迄今為止,本書的大多數(shù)例子都僅存在于單個文件中,而且設(shè)計成局部(本地)使用,沒有同包名發(fā)生沖突(在這種情況下,類名置于“默認(rèn)包”內(nèi))。這是一種有效的做法,而且考慮到問題的簡化,本書剩下的部 分也將盡可能地采用它。然而,若計劃創(chuàng)建一個“對因特網(wǎng)友好”或者說“適合在因特網(wǎng)使用”的程序,必 須考慮如何防止類名的重復(fù)。為Ja詭a建一個源碼文件的時候,它通常叫作一個“編輯單元”(有時也叫作“翻譯單元”)。每個編譯單 元都必須有一個以.jav結(jié)尾的名字。而且在編譯單元的內(nèi)部,可以有一個公共(public )類,它必須擁有 與文件相同的名字(包括大小寫形式,但排除.java文件擴(kuò)展名)。如果

7、不這樣做,編譯器就會報告岀錯。每個編譯單元內(nèi)都只能有一個 publ類c同樣地,否則編譯器會報告岀錯)。那個編譯單元剩下的類(如果 有的話)可在那個包外面的世界面前隱藏起來,因為它們并非“公共”的(非public),而且它們由用于主 publ類的"支撐"類組成。編譯一個.ja文牛時,我們會獲得一個名字完全相同的輸岀文件;但對于.java文件中的每個類,它們都有 一個.cla擴(kuò)展名。因此,我們最終從少量的.jav文件里有可能獲得數(shù)量眾多的.clas文件。如以前用一 種匯編語言寫過程序,那么可能已習(xí)慣編譯器先分割岀一種過渡形式(通常是一個.obj 文件),再用一個鏈 接器將其與其

8、他東西封裝到一起(生成一個可執(zhí)行文件),或者與一個庫封裝到一起(生成一個庫)。但那 并不是Jav的工作方式。一個有效的程序就是一系列.clas文件,它們可以圭寸裝和壓縮到一個 JA文件里(使用Java 提供的ja王具)。Jav解釋器負(fù)責(zé)對這些文件的尋找、裝載和解釋(注釋)。:Jav并沒有強(qiáng)制一定要使用解釋器。一些固有代碼的Jav譯器可生成單獨的可執(zhí)行文件。"庫"也由一系列類文件構(gòu)成。每個文件都有一個 publ類C并沒強(qiáng)迫使用一個 publ類,c但這種情況最 很典型的),所以每個文件都有一個組件。如果想將所有這些組件(它們在各自獨立的.java 和.cla文件 里)都?xì)w納到一

9、起,那么 pack關(guān)鍵字就可以發(fā)揮作用)。若在一個文件的開頭使用下述代碼:package mypackage;那么packa吾句必須作為文件的第一個非注釋語句岀現(xiàn)。該語句的作用是指岀這個編譯單元屬于名為 mypacka一個庫的一部分?;蛘邠Q句話說,它表明這個編譯單元內(nèi)的publ類名位于mypack這個名e字的下面。如果其他人想使用這個名字,要么指岀完整的名字,要么與mypacka詒g使用impo關(guān)鍵字(使用前面給岀的選項)。注意根據(jù) Jav包(封裝)的約定,名字內(nèi)的所有字母都應(yīng)小寫,甚至那些中間單 詞亦要如此。例如,假定文件名是 MyClass.java。它意味著在那個文件有一個、而且只能有一

10、bl類。而且那個類 的名字必須是 MyClass(包括大小寫形式):package mypackage;public class MyClass / .現(xiàn)在,如果有人想使用 MyClass,或者想使用pack內(nèi)的其他任何publ類,c他們必須用impcO關(guān)鍵 字激活mypack內(nèi)的g名字,使它們能夠使用。另一個辦法則是指定完整的名稱:mypackage.MyClass m = new mypackage.MyClass();i mpo關(guān)鍵字則可將其變得簡潔得多: import mypackage.*;/ . . .MyClass m = new MyClass();作為一名庫設(shè)計者,一定要記住

11、pack和giempo關(guān)鍵字允許我們做的事情就是分割單個全局命名空間,保證我們不會遇到名字的沖突無論有多少人使用因特網(wǎng),也無論多少人用Jav編寫自己的類。5. 1. 1創(chuàng)建獨一無二的包名大家或許已注意到這樣一個事實:由于一個包永遠(yuǎn)不會真的“封裝”到單獨一個文件里面,它可由多個.cla文件構(gòu)成,所以局面可能稍微有些混亂。為避免這個問題,最合理的一種做法就是將某個特定包使 用的所有.cla文件都置入單個目錄里。也就是說,我們要利用操作系統(tǒng)的分級文件結(jié)構(gòu)避免岀現(xiàn)混亂局 面。這正是Jav所采取的方法。它同時也解決了另兩個問題:創(chuàng)建獨一無二的包名以及找岀那些可能深藏于目錄結(jié)構(gòu)某處的類。正如我們在 第2章

12、講述的那樣,為達(dá)到這個目的,需要將.clas文件的位置路徑編碼到 packa勺名字里。但根據(jù)約 定,編譯器強(qiáng)迫pack名的第一部分是類創(chuàng)建者的因特網(wǎng)域名。由于因特網(wǎng)域名肯定是獨一無二的(由 Inter保證一注釋,它控制著域名的分配),所以假如按這一約定行事package的名稱就肯定不會 重復(fù),所以永遠(yuǎn)不會遇到名稱沖突的問題。換句話說,除非將自己的域名轉(zhuǎn)讓給其他人,而且對方也按照相 同的路徑名編寫Jav代碼,否則名字的沖突是永遠(yuǎn)不會岀現(xiàn)的。當(dāng)然,如果你沒有自己的域名,那么必須創(chuàng) 造一個非常生僻的包名(例如自己的英文姓名),以便盡最大可能創(chuàng)建一個獨一無二的包名。如決定發(fā)行自 己的Jav代碼,那么強(qiáng)

13、烈推薦去申請自己的域名,它所需的費(fèi)用是非常低廉的。ernic .net這個技巧的另一部分是將 pack名解折成自己機(jī)器上的一個目錄。這樣一來,Java程序運(yùn)行并需要裝 載.cla文件的時候(這是動態(tài)進(jìn)行的,在程序需要創(chuàng)建屬于那個類的一個對象,或者首次訪問那個類的一 個stat成員時),它就可以找到.clas文件駐留的那個目錄。JavM釋器的工作程序如下:首先,它找到環(huán)境變量CLASSPATHa(將者具有JaVI釋能力的工具一如瀏覽器一一安裝到機(jī)器中時,通過操作系統(tǒng)進(jìn)行設(shè)定)°CLASSPAT包含了一個或多個目錄,它們作為 一種特殊的“根”使用,從這里展開對.clas文件的搜索。從那個

14、根開始,解釋器會尋找包名,并將每個點 號(句點)替換成一個斜杠,從而生成從 CLASSPA開始的一個路徑名(所以 package foo.會變ir.baz 成foobar或者fzo/bar/baz ;具體是正斜杠還是反斜杠由操作系統(tǒng)決定)。隨后將它們連接到一起, 成為CLASSP內(nèi)T各個條目(入口)。以后搜索.clasS件時,就可從這些地方開始查找與準(zhǔn)備創(chuàng)建的類 名對應(yīng)的名字。此外,它也會搜索一些標(biāo)準(zhǔn)目錄一一這些目錄與JaV解釋器駐留的地方有關(guān)。為進(jìn)一步理解這個問題,下面以我自己的域名為例,它是。將其反轉(zhuǎn)過來后,com.bruce就為我的類創(chuàng)建了獨一無二的全局名稱(com,edu,org,等e

15、展名以前在Jav包.中都 是大寫的,但自Java以來,2這種情況已發(fā)生了變化?,F(xiàn)在整個包名都是小寫的)。由于決定創(chuàng)建一個名 為ut啲庫,我可以進(jìn)一步地分割它,所以最后得到的包名如下: package com.bruceeckel.util;現(xiàn)在,可將這個包名作為下述兩個文件的“命名空間”使用:/:Vector.java/ Creating a packagepackage com.bruceeckel.util;public class Vector public Vector() System.out.println( "com.bruceeckel.util.Vector&quo

16、t;); /: 創(chuàng)建自己的包時,要求 packa吾句必須是文件中的第一個“非注釋”代碼。第二個文件表面看起來是類似的:/: List.java/ Creating a packagepackage com.bruceeckel.util;public class List public List() System.out.println( "com.bruceeckel.util.List"); /: 這兩個文件都置于我自己系統(tǒng)的一個子目錄中:C:DOCJavaTcombruceeckelutil若通過它往回走,就會發(fā)現(xiàn)包名 com.bruceeckel.uti l,但路徑

17、的第一部分又是什么呢A這是由ATH 環(huán)境變量決定的。在我的機(jī)器上,它是:CLASSPATH = . D:I J AVDOCJavaT可以看岀,CLASSP里能包含大量備用的搜索路徑。然而,使用JA文件時要注意一個問題:必須將 JAR文件的名字置于類路徑里,而不僅僅是它所在的路徑。所以對一個名為grape.的JAR件來說,我們的類路徑需要包括:CLASSPATH = . IL:I£ J AVXflavorsgrape.jar正確設(shè)置好類路徑后,可將下面這個文件置于任何目錄里(若在執(zhí)行該程序時遇到麻煩,請參見第3章的3.1小節(jié)“賦值”):/:LibTest.java/ Uses the

18、librarypackage cO5;import com.bruceeckel.util.*;public class LibTest public static void main(String args) Vector v = new Vector();List l = new List(); /: 編譯器遇到impo語句后,它會搜索由 CLASSPeATHU錄,查找子目錄 combruceeckeluti l,然后查 找名稱適當(dāng)?shù)囊丫幾g文件(對于 Vect是Vector.clasLsi,對是List.class) Ve注意;和rLis內(nèi)t無論類還是需要的方法都必須設(shè)為 publico1

19、.自動編譯為導(dǎo)入的類首次創(chuàng)建一個對象時(或者訪問一個類的stat成員時),編譯器會在適當(dāng)?shù)哪夸浝飳ふ彝?cla文件(所以如果創(chuàng)建類 X的一個對象,就應(yīng)該是 X.class)。若只發(fā)現(xiàn)class,它就是必須使用 的那一個類。然而,如果它在相同的目錄中還發(fā)現(xiàn)了一個X.java,編譯器就會比較兩個文件的日期標(biāo)記。如果X.ja比X.cl新s就會自動編譯 X.java,生成一個最新的勺class。對于一個特定的類,或在與它同名的.java文件中沒有找到它,就會對那個類采取上述的處理。2. 沖突若通過*導(dǎo)入了兩個庫,而且它們包括相同的名字,這時會岀現(xiàn)什么情況呢?例如,假定一個程序使用了下述 導(dǎo)入語句:

20、import com.bruceeckel.util.*;import java.util.*;由于j ava.util.*也包含了一個類,所以這會造成潛在的沖突。然而,只要沖突并不真的發(fā)生,那么就不會產(chǎn)生任何問題一一這當(dāng)然是最理想的情況,因為否則的話,就需要進(jìn)行大量編程工作,防范那些可能可能永遠(yuǎn)也不會發(fā)生的沖突。如現(xiàn)在試著生成一個 Vector,就肯定會發(fā)生沖突。如下所示: Vector v = new Vector();它引用的到底是哪個 Vect類呢?編譯器對這個問題沒有答案,讀者也不可能知道。所以編譯器會報告一個 錯誤,強(qiáng)迫我們進(jìn)行明確的說明。例如,假設(shè)我想使用標(biāo)準(zhǔn)的Java Vect

21、or,那么必須象下面這樣編程:java.util.Vector v = new java.util.Vector();由于它(與CLASSPATH完整指定了那個 Vect的位置,所以不再需要 import j ava.util.*語句,除 非還想使用來自java.ut他1東西。5. 1. 2自定義工具庫掌握前述的知識后,接下來就可以開始創(chuàng)建自己的工具庫,以便減少或者完全消除重復(fù)的代碼。例如,可為System.out.println()創(chuàng)建一個別名,減少重復(fù)鍵入的代碼量。它Wo®一個包(package)的 一部分:/: P.java/TheP.rint&P.rintlnshor

22、packagecom.bruceeckel.tools;handpublic clas public st System. public st System. public st System. public st System. public st System. public st System. public st System. public st System. public st System. public st System. public st System. public st System.public st System. public st System. publi

23、c st System. public st System.s P aticvoidout.printaticvoidout.printaticvoidout.printaticvoidout.printaticvoidout.printaticvoidout.printaticvoidout.printaticvoidout.printaticvoidout.printaticvoidout.printaticvoidout.printaticvoidout.printaticvoidout.printaticvoidout.printaticvoidout.printaticvoidout

24、.printrint(Object obj)(obj);rint(String s)s);rint(chars)s);rint(char c) c) ;rint(int i) i);rint(long l) l);rint(float f) (f);rint(double d) d) ;rint(boolean b) b);rintln() n();rintln(Object obj)n(obj);rintln(String s)1 n(s);rintln(char s)1 n(s);rintln(char c) 1 n(c);rintln(int i) 1 n(i);rintln(long

25、l) n(l);public static void rintln(float f) System.out.println(f);public static void rintln(double d) System.out.println(d);public static void rintln(boolean b) System.out.println(b);/: 所有不同的數(shù)據(jù)類型現(xiàn)在都可以在一個新行輸岀(P.rintln(),或者不在一個新行輸岀(P.ri)n°t()大家可能會猜想這個文件所在的目錄必須從某個CLASSPM開臺,然后繼續(xù)com/bruceeckel/tools。

26、編譯完畢后,利用一個impo語句,即可在自己系統(tǒng)的任何地方使用P.cl文件s如下所示:/:ToolTest.java/ Uses the tools library import com.bruceeckel.tools.*;public class ToolTest public static void main(String args) P.rintln(HAvailable from now on!"); /: 所以從現(xiàn)在開始,無論什么時候只要做岀了一個有用的新工具,就可將其加入too目錄(或者自己的個人uti或too目錄)。1. CLAS的陷阱THP.ja文件存在一個非常有趣

27、的陷阱。特別是對于早期的Jav現(xiàn)方案來說,類路徑的正確設(shè)定通常都是很困難的一項工作。編寫這本書的時候,我引入了P.ja文件,它最初看起來似乎工作很正常。但在某些情況下,卻開始岀現(xiàn)中斷。在很長的時間里,我都確信這是Jav或其他什么在實現(xiàn)時一個錯誤。但最后,我終于發(fā)現(xiàn)在一個地方引入了一個程序(即第1章要說明的CodePackager.java),它使用了一個不同的類由于它作為一個工具使用,所以有時候會進(jìn)入類路徑里;另一些時候則不會這樣。但只要它進(jìn)入類路徑,那 么假若執(zhí)行的程序需要尋找 com.bruceeck中的類,t J o v首先發(fā)現(xiàn)的就是CodePackager.java中的 P。此時,編譯

28、器會報告一個特定的方法沒有找到。這當(dāng)然是非常令人頭疼的,因為我們在前面的類P里明明看到了這個方法,而且根本沒有更多的診斷報告可為我們提供一條線索,讓我們知道找到的是一個完全不同 的類(那甚至不是 publ的)c。乍一看來,這似乎是編譯器的一個錯誤,但假若考察impo語句,就會發(fā)現(xiàn)它只是說:“在這里可能發(fā)現(xiàn)了P”。然而,我們假定的是編譯器搜索自己類路徑的任何地方,所以一旦它發(fā)現(xiàn)一個P,就會使用它;若在搜索過程中發(fā)現(xiàn)了“錯誤的”一個,它就會停止搜索。這與我們在前面表述的稍微有些區(qū)別,因為存在一些討 厭的類,它們都位于包內(nèi)。而這里有一個不在包內(nèi)的P,但仍可在常規(guī)的類路徑搜索過程中找到。如果您遇到象這

29、樣的情況,請務(wù)必保證對于類路徑的每個地方,每個名字都僅存在一個類。5. 1. 3利用導(dǎo)入改變行為Jav已取消的一種特性是 C的“條件編譯”,它允許我們改變參數(shù),獲得不同的行為,同時不改變其他任何 代碼。Jav之所以拋棄了這一特性,可能是由于該特性經(jīng)常在C里用于解決跨平臺問題:代碼的不同部分根據(jù)具體的平臺進(jìn)行編譯,否則不能在特定的平臺上運(yùn)行。由于Jav的設(shè)計思想是成為一種自動跨平臺的語言,所以這種特性是沒有必要的。然而,條件編譯還有另一些非常有價值的用途。一種很常見的用途就是調(diào)試代碼。調(diào)試特性可在開發(fā)過程中 使用,但在發(fā)行的產(chǎn)品中卻無此功能°Alen Holub()提岀了利用包(pac

30、kage )來模仿條 編譯的概念。根據(jù)這一概念,它創(chuàng)建了 C “斷定機(jī)制”一個非常有用的Jav版本。之所以叫作“斷定機(jī) 制”,是由于我們可以說“它應(yīng)該為真”或者“它應(yīng)該為假”。如果語句不同意你的斷定,就可以發(fā)現(xiàn)相關(guān) 的情況。這種工具在調(diào)試過程中是特別有用的??捎孟旅孢@個類進(jìn)行程序調(diào)試:/:Assert.java/ Assertion tool for debugging package com.bruceeckel.tools.debug;public class Assert private static void perr(String msg) System.err.println(ms

31、g);public final static void is _ true(boolean exp) if(!exp) perr("Assertion failed");public final static void is _ false(boolean exp) if(exp) perr("Assertion failed");public final static voidis _ true(boolean exp, String msg) if(!exp) perr("Assertion failed:"+ msg);publ

32、ic final static voidis _ false(boolean exp, String msg) if(exp) perr("Assertion failed:"+ msg); /: 這個類只是簡單地封裝了布爾測試。如果失敗,就顯示岀岀錯消息。在第9章,大家還會學(xué)習(xí)一個更高級的錯誤控制工具,名為“違例控制”。但在目前這種情況下,perr()方法已經(jīng)可以很好地工作。如果想使用這個類,可在自己的程序中加入下面這一行:import com.bruceeckel.tools.debug.*;如欲清除斷定機(jī)制,以便自己能發(fā)行最終的代碼,我們創(chuàng)建了第二個Asse類,t但卻

33、是在一個不同的包里:/:Assert.java/ Turning off the assertion output / so you can ship the program.package com.bruceeckel.tools;publicclassAssertpublicfinalstaticvoidis_true(boolean exp)publicfinalstaticvoidis_false(boolean exp)publicfinalstaticvoidistrue(booleanexpStringmsg)publicfinalstaticvoidisfalse(boolea

34、nexp,Stringmsg)打 /:現(xiàn)在,假如將前一個impo語句變成下面這個樣子: import com.bruceeckel.tools.程序便不再顯示岀斷言。下面是個例子:1 2 9/:TeStASSertjava/DemonStratingtheaSSertiontoolpackagec055/Commentthefollowing5anduncommentthe/SubSequentlinetochangeaSSertionbehaviorimportcombruceeckeltoolSdebug*5/importcombruceeckeltoolS5publicclaSSTeSt

35、ASSertpublicStaticvoidmain(String 口argS)ASSert.iStrue(2+2)=5);ASSert.iSfalSe(1+1)=2);ASSert.iStrue(2+2)=5II52+2 =5I);ASSert.iSfalSe(1+1)=2,I1+1 !=2I); /:通過改變導(dǎo)入的package,我們可將自己的代碼從調(diào)試版本變成最終的發(fā)行版本。這種技術(shù)可應(yīng)用于任何種 類的條件代碼。5. 1. 4包的停用大家應(yīng)注意這樣一個問題:每次創(chuàng)建一個包后,都在為包取名時間接地指定了一個目錄結(jié)構(gòu)。這個包必須存 在(駐留)于由它的名字規(guī)定的目錄內(nèi)。而且這個目錄必須能從CL

36、ASSI開始搜索并發(fā)現(xiàn)。最開始的時候,packa關(guān)鍵字的運(yùn)用可能會令人迷惑,因為除非堅持遵守根據(jù)目錄路徑指定包名的規(guī)則,否則就會在 運(yùn)行期獲得大量莫名其妙的消息,指岀找不到一個特定的類一一即使那個類明明就在相同的目錄中。若得到 象這樣的一條消息,請試著將 packa吾句作為注釋標(biāo)記岀去。如果這樣做行得通,就可知道問題到底岀在 哪兒。5. 2 Java訪問指示符針對類內(nèi)每個成員的每個定義,Java訪問指示符poublic,pro以及 ptreva都置于它們的最前面一 無論它們是一個數(shù)據(jù)成員,還是一個方法。每個訪問指示符都只控制著對那個特定定義的訪問。這與C + +存在著顯著不同。在 C + +中

37、,訪冋指示符控制著它后面的所有定義,直到又一個訪冋指示符加入為止。通過千絲萬縷的聯(lián)系,程序為所有東西都指定了某種形式的訪問。在后面的小節(jié)里,大家要學(xué)習(xí)與各類訪問 有關(guān)的所有知識。首次從默認(rèn)訪問開始。5. 2. 1 “友好的”如果根本不指定訪問指示符,就象本章之前的所有例子那樣,這時會岀現(xiàn)什么情況呢?默認(rèn)的訪問沒有關(guān)鍵 字,但它通常稱為“友好” (Friend)l訪問。這意味著當(dāng)前包內(nèi)的其他所有類都能訪問“友好的”成員, 但對包外的所有類來說,這些成員卻是“私有” (Private )的,外界不得訪問。由于一個編譯單元(一個文 件)只能從屬于單個包,所以單個編譯單元內(nèi)的所有類相互間都是自動“友好

38、”的。因此,我們也說友好元 素?fù)碛小鞍L問"權(quán)限。友好訪問允許我們將相關(guān)的類都組合到一個包里,使它們相互間方便地進(jìn)行溝通。將類組合到一個包內(nèi)以后 (這樣便允許友好成員的相互訪問,亦即讓它們“交朋友”),我們便“擁有” 了那個包內(nèi)的代碼。只有我 們已經(jīng)擁有的代碼才能友好地訪問自己擁有的其他代碼。我們可認(rèn)為友好訪問使類在一個包內(nèi)的組合顯得有 意義,或者說前者是后者的原因。在許多語言中,我們在文件內(nèi)組織定義的方式往往顯得有些牽強(qiáng)。但在 Jav書,卻強(qiáng)制用一種頗有意義的形式進(jìn)行組織。除此以外,我們有時可能想排除一些類,不想讓它們訪問 當(dāng)前包內(nèi)定義的類。對于任何關(guān)系,一個非常重要的問題是“誰能

39、訪問我們的私有或priva代碼e。類控制著哪些代碼能夠訪問自己的成員。沒有任何秘訣可以“闖入”。另一個包內(nèi)推薦可以聲明一個新類,然后說:“嗨,我是Bo的朋友!",并指望看到 Eo的“ protected"(受到保護(hù)的)、友好的以及“ private” (私有)的成員。為獲得對一個訪問權(quán)限,唯一的方法就是:(1) 使成員成為"public” (公共的)。這樣所有人從任何地方都可以訪問它。(2) 變成一個“友好”成員,方法是舍棄所有訪問指示符,并將其類置于相同的包內(nèi)。這樣一來,其他類就 可以訪問成員。(3) 正如以后引入“繼承”概念后大家會知道的那樣,一個繼承的類既可

40、以訪問一個 proteC成鳧迪可 以訪問一個publ成員(但不可訪問priv成員e。只有在兩個類位于相同的包內(nèi)時,它才可以訪問友好 成員。但現(xiàn)在不必關(guān)心這方面的問題。(4) 提供“訪問器/變化器”方法(亦稱為“獲取/設(shè)置”方法),以便讀取和修改值。這是 OO環(huán)境中最 正規(guī)的一種方法,也是 Java E的基礎(chǔ)一具體情況會在第 13章介紹。5. 2. 2 publ i c:接口訪問使用publ關(guān)鍵字時,它意味著緊隨在 publ后面的成員聲明適用于所有人,特別是適用于使用庫的客戶 程序員。假定我們定義了一個名為 dessd勺包t其中包含下述單元(若執(zhí)行該程序時遇到困難,請參考第 3章3.1小節(jié)“賦值

41、”):/:Cookie.java/ Creates a library package c05.dessert;public class Cookie public Cookie() System.out.println(HCookie constructor");void foo() System.out.println("fooH); /: 請記住,Cookie .必須駐留在名為 dess的一個子目錄內(nèi),而這個子目錄又必須位于由CLASSI指YTH定的C0目錄下面(C05弋表本書的第5章)。不要錯誤地以為 Jav無侖如何都會將當(dāng)前目錄作為搜索的起 點看待。如果不將一個“

42、”作為 CLASSI勺一部分使用,Jav就不會考慮當(dāng)前目錄。現(xiàn)在,假若創(chuàng)建使用了 Cook的一個程序,如下所示:/:Dinner.java/ Uses the libraryc05.desserpublicclassDipublicDinnerSystemout.ppublicstaticCookieX=/!Xfoo()mportnner() /:voidmain(StrnewCookie(); /Canztaccrintln("Dinnerconstructor");ing args) ess就可以創(chuàng)建一個 Cook對象,因為它的構(gòu)建器是 publ的,c而且類也是 pub

43、l的c公共類的概念稍后還會 進(jìn)行更詳細(xì)的講述)。然而,foo()成員不可在Dinner.內(nèi)訪問a因為foo()只有在ess包內(nèi)才是“友好”的。1. 默認(rèn)包大家可能會驚訝地發(fā)現(xiàn)下面這些代碼得以順利編譯一一盡管它看起來似乎已違背了規(guī)則:31/:Cake.java/Accessesa classin a separate/compilation unit.classCakepublic static void main(StringargsPie x = new Pie(); x.f(); /: 在位于相同目錄的第二個文件里:/:Pie.java/The otherclassSystem.out.p

44、rin1 n("Pie.f()");class Pie void f() /: 最初可能會把它們看作完全不相干的文件,然而Cal能邇【J建一個Pi對象,并能調(diào)用它的f()方法!通常的想法會認(rèn)為Pi和 f()是“友好的”,所以不適用于Cake。它們確實是友好的一一這部分結(jié)論非常正確。但 它們之所以仍能在Cake.j電使V用,是由于它們位于相同的目錄中,而且沒有明確的包名。Java把象這樣 的文件看作那個目錄“默認(rèn)包”的一部分,所以它們對于目錄內(nèi)的其他文件來說是“友好”的。5. 2. 3 private:不能接觸!priv關(guān)鍵字意味著除非那個特定的類,而且從那個類的方法里,否則

45、沒有人能訪問那個成員。同一個包 內(nèi)的其他成員不能訪問 priv成員e這使其顯得似乎將類與我們自己都隔離起來。另一方面,也不能由幾 個合作的人創(chuàng)建一個包。所以 priv允許我們自由地改變那個成員,同時毋需關(guān)心它是否會影響同一個包 內(nèi)的另一個類。默認(rèn)的“友好”包訪問通常已經(jīng)是一種適當(dāng)?shù)碾[藏方法;請記住,對于包的用戶來說,是不 能訪問一個“友好”成員的。這種效果往往能令人滿意,因為默認(rèn)訪問是我們通常采用的方法。對于希望變 成public(公共)的成員,我們通常明確地指岀,令其可由客戶程序員自由調(diào)用。而且作為一個結(jié)果,最開 始的時候通常會認(rèn)為自己不必頻繁使用 priv關(guān)鍵字,因為完全可以在不用它的前提

46、下發(fā)布自己的代碼(這與C + +是個鮮明的對比)。然而,隨著學(xué)習(xí)的深入,大家就會發(fā)現(xiàn) priv仍然有非常重要的用途,特 別是在涉及多線程處理的時候(詳情見第14章)。下面是應(yīng)用了 pri v的一個例子:/:IceCream.java/ Demonstrates "private" keywordpublic cla public s /! SuSundaess IceCre tatic voi ndae x =x = Sundam id main(Str new Sundae ae.makeASuing args)();ndae();classSundaeprivateSu

47、ndastaticSundaereturnnewe() 打makeASundae()Sundae(); /:這個例子向我們證明了使用 priv的方便:有時可能想控制對象的創(chuàng)建方式,并防止有人直接訪問一個特 定的構(gòu)建器(或者所有構(gòu)建器)。在上面的例子中,我們不可通過它的構(gòu)建器創(chuàng)建一個Sund對象;相反,必須調(diào)用makeASundae()方法來實現(xiàn)(注釋)。private,所以可防止private,從而保證自己 privaBt可保證自己 :此時還會產(chǎn)生另一個影響:由于默認(rèn)構(gòu)建器是唯一獲得定義的,而且它的屬性是 對這個類的繼承(這是第 6章要重點講述的主題)。若確定一個類只有一個“助手”方法,那么對

48、于任何方法來說,都可以把它們設(shè)為 不會誤在包內(nèi)其他地方使用它,防止自己更改或刪除方法。將一個方法的屬性設(shè)為一直保持這一選項(然而,若一個句柄被設(shè)為private,并不表明其他對象不能擁有指向同一個對象的publ句柄。有關(guān)“別名”的問題將在第 12章詳述)。5. 2. 4 protected:“友好的一種”protected(受到保護(hù)的)訪問指示符要求大家提前有所認(rèn)識。首先應(yīng)注意這樣一個事實:為繼續(xù)學(xué)習(xí)本書一 直到繼承那一章之前的內(nèi)容,并不一定需要先理解本小節(jié)的內(nèi)容。但為了保持內(nèi)容的完整,這兒仍然要對此 進(jìn)行簡要說明,并提供相關(guān)的例子。prote (關(guān)鍵字為我們引入了一種名為“繼承”的概念,它以

49、現(xiàn)有的類為基礎(chǔ),并在其中加入新的成員,同時不會對現(xiàn)有的類產(chǎn)生影響一一我們將這種現(xiàn)有的類稱為“基礎(chǔ)類”或者“基本類” (Ease Class)。亦 可改變那個類現(xiàn)有成員的行為。對于從一個現(xiàn)有類的繼承,我們說自己的新類“擴(kuò)展” (extends) 了那個現(xiàn) 有的類。如下所示:class Foo extends Bar 類定義剩余的部分看起來是完全相同的。若新建一個包,并從另一個包內(nèi)的某個類里繼承,則唯一能夠訪問的成員就是原來那個包的publ成員。當(dāng)然,如果在相同的包里進(jìn)行繼承,那么繼承獲得的包能夠訪問所有“友好”的成員。有些時候,基礎(chǔ)類的創(chuàng) 建者喜歡提供一個特殊的成員,并允許訪問衍生類。這正是protect作d若往回引用5.2小節(jié)“public :接口訪問"的IlCookie.文偉,v貝a下面這個類就不能訪問“友好"的成員:/:ChocolateChipjava/Can/taccessfriendl/inanotherclassimportc05dessert*5member

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論