




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
第3章類與對象3.1面向?qū)ο缶幊谈拍畹慕榻B3.2類聲明和類體3.3構(gòu)造方法與對象的創(chuàng)建和使用3.4域/成員變量3.5成員方法3.6this關(guān)鍵字3.7訪問權(quán)限3.8嵌套類和內(nèi)部類3.9包
3.1面向?qū)ο缶幊谈拍畹慕榻B
使用面向過程的編程語言,如C和Fortran,需要選擇數(shù)據(jù)結(jié)構(gòu)和設(shè)計算法,再把算法翻譯成代碼。而Java是一種面向?qū)ο蟮木幊陶Z言,它不僅具有面向過程編程語言的特點(diǎn),而且通過抽象、封裝、繼承和多態(tài)增加了靈活性、模塊性、清晰性和可重用性等極其有用的特性。面向?qū)ο缶幊痰暮诵乃枷刖褪菍?shù)據(jù)和對數(shù)據(jù)的操作封裝在一起,放入稱之為對象的實(shí)體中。什么是對象?在現(xiàn)實(shí)生活中,我們每時每刻都在與具體的對象打交道,比如我們身邊的老師、同學(xué)、電腦、手機(jī)等都是對象。
現(xiàn)實(shí)生活中的對象都有兩種特征:他們的狀態(tài)和他們的行為。比如同一個班上的同學(xué)可以由他們的狀態(tài)(姓名,學(xué)號,性別,年齡)和他們的行為(學(xué)習(xí),運(yùn)動,休息,交談)刻畫;我們常用的電腦也可以由它的狀態(tài)(機(jī)箱,顯示器,主板,CPU,內(nèi)存)和它的行為(開機(jī),關(guān)機(jī),休眠,重啟)刻畫。知道了一個對象的狀態(tài)和行為,就能夠從一堆對象的集合中唯一的揀選出那個對象。抽象出現(xiàn)實(shí)生活中對象的狀態(tài)和行為,就是進(jìn)行面向?qū)ο缶幊痰牡谝徊?。如果我們仔?xì)觀察身邊的對象,我們會注意到,不同的對象,他們的狀態(tài)和行為會存在很大的差別,比如你身邊同學(xué)的狀態(tài)和行為就和你所使用的電腦的狀態(tài)和行為截然不同。有些對象,它有可能還包含有其它的對象,比如一輛汽車是一個對象,它的四個輪子也是一種對象。這些現(xiàn)實(shí)生活中觀察到的例子都可以用面向?qū)ο缶幊痰男g(shù)語來描述。面向?qū)ο缶幊讨械摹皩ο蟆保同F(xiàn)實(shí)生活中的對象在概念上是非常類似的。面向?qū)ο缶幊讨械膶ο笠灿袪顟B(tài)和行為。一個對象把它的狀態(tài)保存在域中,通過方法表現(xiàn)對象所擁有的行為。對象的方法可以操作一個它自己的內(nèi)部狀態(tài)。方法是對象之間通信的主要手段。在面向?qū)ο缶幊讨?,我們?yīng)該隱藏一個對象的內(nèi)部狀態(tài),對這個對象的內(nèi)部狀態(tài)的任何修改都只能通過對象對外提供的方法來完成,這就叫做封裝。以電腦顯示器為例子。顯示器有一個內(nèi)部狀態(tài)叫做分辨率。我們可以提供一個方法來改變它的分辨率。這樣使用者如何改變顯示器的分辨率,實(shí)際上是由顯示器決定的,不能夠隨心所欲。如果一個顯示器的最高分辨率為1024×768,而一個使用者試圖把它的分辨率調(diào)整為1600×1200,那么顯示器可以在它的方法里面拒絕使用者的要求。在現(xiàn)實(shí)生活中,許多對象實(shí)體都具有同樣的類型。打比方說,在大街上你會看到許多和你所駕駛的小汽車擁有同樣的商標(biāo)、同樣的外形、同樣的顏色,但是由別人駕駛的小汽車。這些小汽車都是由同一個制造商按照同一份圖紙制造,因此具有同樣的部件。在面向?qū)ο缶幊汤碚撝校覀儼涯愕淖{稱之為這種型號的小汽車的一個實(shí)例,而所有這種型號的小汽車稱之為你的座駕所屬的類。一個類就是在構(gòu)建一個單獨(dú)的對象時使用的模板,類模板描述一個對象所擁有的狀態(tài)和行為,也即定義了一個對象的域和方法。此外,我們還會注意到,不同種類的對象也會存在著共性。比如小汽車、自行車、電動車他們都是車,他們都共享著車所具有的性質(zhì),比如都有車輪,都能夠行駛,都能夠剎車等。但是除了這些共有的性質(zhì)外,每一類車都有它們自己所特有的性質(zhì),比如小汽車有四個輪子,電動車以電力推動,自行車靠人力騎行等。在面向?qū)ο缶幊汤碚撝?,通過繼承機(jī)制,允許一個類從另外一個類中繼承一些共有的性質(zhì),然后再另外加入自己所特有的性質(zhì)。比如“車”是小汽車、自行車、電動車的共性的抽象,我們可以把“車”稱為小汽車、自行車、電動車的超類,而小汽車、自行車、電動車稱為“車”的子類,它們都繼承了車的性質(zhì),然后還加入了自己特有的性質(zhì)。比如電動車會包括一個特有的成員——充電電池。繼承可能是多層的,一類的超類,可能還有它自己的超類,多層的繼承結(jié)構(gòu),會形成一個繼承樹。從上述討論中我們已經(jīng)了解到,對象通過它們提供的方法定義了它們和外界交流的方式,換句話說,方法構(gòu)成了對象和外部世界交互的接口。舉例來說,鍵盤和鼠標(biāo)就是你和你所使用的電腦的接口,你通過鍵盤和鼠標(biāo)與隱藏在它們后面的CPU、內(nèi)存和主板打交道。點(diǎn)擊鼠標(biāo)右鍵,電腦顯示器就會彈出右鍵菜單。在Java中,接口是一個重要的面向?qū)ο蟮母拍?,定義一個接口相當(dāng)于規(guī)范了一個類所允諾提供的行為,接口構(gòu)成了類和外部世界溝通的一個協(xié)議。在編譯的時候,編譯器可以通過檢查一個類是否實(shí)現(xiàn)了一個接口來確認(rèn)這個類是否遵守了它和外部世界所達(dá)成的交互協(xié)議。在Java中,如果一個類聲明它將實(shí)現(xiàn)某個接口,那么它必須實(shí)現(xiàn)這個接口所定義的所有方法,否則這個類將無法通過編譯。在編程實(shí)踐中引入面向?qū)ο蟮睦碚?,有以下的好處?/p>
(1)模塊化:可以獨(dú)立的編寫和維護(hù)一個對象的代碼而不會和系統(tǒng)中的其它對象產(chǎn)生沖突??梢园踩脑谙到y(tǒng)中傳遞對象而不用擔(dān)心對象的內(nèi)部狀態(tài)受到非法的修改。
(2)信息隱藏:只允許外界通過對象對外提供的方法訪問對象,可以保證一個對象的內(nèi)部實(shí)現(xiàn)細(xì)節(jié)對外是透明的。
(3)代碼重用:如果存在一個預(yù)先編寫好的對象,就可以直接使用它,而無需進(jìn)行二次開發(fā)。因?yàn)橹灰粋€對象經(jīng)過詳細(xì)的測試,它就是安全的可被重用的。
(4)易于調(diào)試:如果一個特定的對象被發(fā)現(xiàn)是有問題的,只需把它從系統(tǒng)中隔離開來,用另外一個沒有問題的對象替換就可以了。這會大大降低調(diào)試的難度。
3.2類聲明和類體
如上所述,類是組成Java程序的基本要素。類封裝了一個對象類別的狀態(tài)和方法,它是用來創(chuàng)建對象的模板。類的基本格式如下:
class類名{
類體的內(nèi)容
}其中class是用來定義類的關(guān)鍵字,“class類名”是類的聲明部分。類體(括號之間的內(nèi)容)包含從這個類創(chuàng)建的對象在整個生命周期所需要的代碼,其中包括用于初始化新的對象的構(gòu)造方法,用于提供類和從屬于它的對象內(nèi)部狀態(tài)的域和實(shí)現(xiàn)了類及從屬于它的對象的行為方法。上述類的聲明部分是最基本的。類聲明還可以包含以下的信息:
(1)類的訪問限制符,如public、private等。
(2)通過關(guān)鍵字extends聲明父類的名字。注意在Java中的類只能有一個直接父類。
(3)通過關(guān)鍵字implements聲明所要實(shí)現(xiàn)的接口的名字,一個類可以實(shí)現(xiàn)多個接口,多個接口的名字之間以逗號分隔。
一個復(fù)雜的類聲明的例子如下所示:
publicclassSunextendsFatherimplementsPlayable,Workable{
……
}在這個例子里面,聲明了一個公共類Sun,它的父類是Father,實(shí)現(xiàn)了兩個接口Playable和Workable。
習(xí)慣上,類名的第一個字母大寫,當(dāng)類名由幾個單詞復(fù)
合而成時,每個單詞的首字母大寫。如“DateTime”、“AmercianCity”、“EuropeAndAsia”都是符合規(guī)范的類名。類體中有兩種類型的成員:
(1)類中的成員變量,也稱之為域,它們用來刻畫對象的內(nèi)部狀態(tài)。
(2)類中的方法,用來刻畫對象對外提供的行為,其中有一類方法非常特殊,它們就是構(gòu)造方法。構(gòu)造方法給定類所創(chuàng)建的對象的初始狀態(tài),供類創(chuàng)建對象時使用。我們將在后續(xù)章節(jié)對構(gòu)造方法進(jìn)行詳細(xì)的討論。其中成員變量,也即域,其聲明依照順序,由以下三部分組成:
(1)零個或多個修飾符。如public修飾符就用來表明一個域是公共的。
(2)域的數(shù)據(jù)類型。域的數(shù)據(jù)類型可以是Java中的任何一種數(shù)據(jù)類型,包括基本數(shù)據(jù)類型、對象和接口。
(3)域的名稱。域的名稱遵循一般變量的命名規(guī)則和慣例。而成員方法,其聲明依照順序,由以下六部分組成:
(1)方法的訪問限制符和其它的修飾符。如public,private,static等。
(2)方法的返回類型,即方法返回值的數(shù)據(jù)類型。如果方法沒有返回值,則標(biāo)記為void,這其中有一種例外,就是構(gòu)造方法。構(gòu)造方法沒有返回值,即使標(biāo)記為void也是不允許的。
(3)方法的名稱。方法的名稱也遵循一般變量的命名規(guī)則和慣例,一般而言,其第一個單詞應(yīng)該是動詞,第一個單詞的首字母小寫。而后續(xù)單詞的首字母應(yīng)該大寫。
(4)由圓括號括起來的,以逗號分隔的參數(shù)列表。
(5)有可能拋出的異常的列表。
(6)由一對大括號括起來的方法體的內(nèi)容。在例3-1中,給出了類Car的定義。類Car中兩個私有的雙精度類型域speed和weight,分別用來刻畫一輛小車的速度和重量。在定義域時我們給定了它們的默認(rèn)值。這輛小車的默認(rèn)時速為80mph,默認(rèn)重量為1.3噸。定義了兩個方法setSpeed和setWeight,分別用來修改一個小車對象的速度和重量。另外定義了兩個方法getSpeed和getWeight,分別用來獲得這輛小車的速度和重量。由于speed和weight是私有的,getSpeed和getWeight方法是我們訪問類Car實(shí)例的內(nèi)部特征的唯一方法。main方法是一個靜態(tài)的測試方法,也是應(yīng)用程序運(yùn)行的入口。在類的定義中,方法是可以重載的。方法的重載是多態(tài)性的一種,指的是一個類中可以有多個方法具有相同的名字,但是這些方法的參數(shù)必須不同。還記得我們常用的命令行輸出方法System.ou.println嗎?它有以下的重載版本:
(1)?println():直接換行。
(2)?println(booleanx):輸出一個布爾類型的數(shù)字然后
換行。
(3)?println(charx):輸出一個字符然后換行。
(4)?println(char[]x):輸出一個字符數(shù)組然后換行。
(5)?println(doublex):輸出一個雙精度類型的數(shù)字然后
換行。
(6)?println(floatx):輸出一個單精度類型的數(shù)字然后換行。
(7)?println(intx):輸出一個整型數(shù)字然后換行。
(8)?println(longx):輸出一個長整型數(shù)字然后換行。
(9)?println(Objectx):輸出一個對象然后換行。
(10)?println(Stringx):輸出一個字符串然后換行。需要注意的是:所謂的參數(shù)不同指的是參數(shù)的個數(shù)不同或者參數(shù)的類型不同,方法的返回類型和參數(shù)的名字不參與比較。比如以下兩個方法不是重載,因?yàn)樗鼈儍H僅是返回類型不同:
doublegetValue(intval);
intgetValue(intval);
以下兩個方法也不是重載,因?yàn)樗鼈冎挥袇?shù)的名字不同,而參數(shù)的類型和個數(shù)都相同:
voidoutputStr(intx,inty);
voidoutputStr(inta,intb);
3.3構(gòu)造方法與對象的創(chuàng)建和使用
在類的定義中,構(gòu)造方法是一類特殊的方法,用于從類模板中創(chuàng)建對象。構(gòu)造方法的聲明和普通方法的聲明類似,只是它們使用類的名稱,而且沒有返回類型。和類中普通方法的重載類似,Java也允許一個類中有若干個構(gòu)造方法,但是這些構(gòu)造方法的參數(shù)必須不同,即參數(shù)的個數(shù)不同,或者參數(shù)的類型不同。Java編譯器可以根據(jù)構(gòu)造方法的參數(shù)數(shù)量和類型來使用不同的構(gòu)造方法,但是不能在一個類中使用兩個參數(shù)個數(shù)和類型都相同的構(gòu)造方法,這樣會引起編譯錯誤。
【例3-2】長方體類Cuboid的定義。
publicclassCuboid{
intheight=20;
intlength=30;
intdepth=10;
publicCuboid(){};
publicCuboid(inth){
height=h;
}
publicCuboid(inth,intl){
height=h;
length=l;
}
publicCuboid(inth,intl,intd){
height=h;
length=l;
depth=d;
}
}在例3-2中,給出了類Cuboid的定義。長方體類Cuboid有三個域height、length和depth,分別代表一個長方體的高、長和深,長方體的高、長和深的初值分別為20、30和10。這個類里面提供了四個構(gòu)造方法。第一個構(gòu)造方法沒有參數(shù),將使用默認(rèn)的高、長和深創(chuàng)建一個立方體;第二個構(gòu)造方法允許在創(chuàng)建立方體時修改它的高度;第三個構(gòu)造方法允許在創(chuàng)建立方體時修改它的高度和長度;第四個構(gòu)造方法允許在創(chuàng)建立方體時同時修改它的高度、長度和深度。需要注意的是,我們可以不為類提供任何的構(gòu)造方法。如果一個類的代碼沒有提供構(gòu)造方法,則在編譯的時候,Java編譯器會為這個類提供一個沒有參數(shù)的默認(rèn)構(gòu)造方法,這個默認(rèn)構(gòu)造方法不作任何域的初始化工作。但是如果我們?yōu)轭愄峁┝艘粋€以上的構(gòu)造方法,則在編譯的時候,Java編譯器不再為這個類提供沒有參數(shù)的默認(rèn)構(gòu)造方法。這在有的時候會引起混淆,所以強(qiáng)烈建議為每一個類都提供一個沒有參數(shù)的默認(rèn)構(gòu)造方法。當(dāng)使用一個類創(chuàng)建一個對象時,我們也說給出了這個類的一個實(shí)例。創(chuàng)建一個對象包括了聲明對象和為對象分配成員變量兩個步驟。
對象聲明的一般格式為:
類名對象名1[,對象名2,對象名3,…];
如下面的兩個例子:
Cuboidcb;
Carcar1,car2,car3;
第一個例子聲明了Cuboid類的對象變量cb,第二個例子連續(xù)聲明了三個Car類的對象變量car1、car2和car3。在第一章中我們已經(jīng)知道,Java取消了在C/C++?中容易產(chǎn)生的內(nèi)存錯誤訪問等問題的指針機(jī)制。但在Java中,對象變量是一種和指針變量很類似的變量。對象變量是一種“引用型”變量,和基本類型的變量完全不同?;绢愋偷淖兞恐写娣诺氖沁@個變量的值,但在對象變量中,存放的是引用對象實(shí)體的標(biāo)志,即存放對象實(shí)體所在內(nèi)存區(qū)域的首地址的地址號。當(dāng)然,Java中的引用型變量和C/C++?中的指針有著本質(zhì)的區(qū)別。Java中的引用型變量不能像指針變量那樣隨意的分配內(nèi)存地址,或通過進(jìn)行指針加減運(yùn)算在內(nèi)存中隨意游走,因此也就不會導(dǎo)致在C/C++?中容易產(chǎn)生的內(nèi)存錯誤訪問等問題。在聲明了對象變量后,由于它是一種引用型變量,其中還沒有引用任何對象實(shí)體,即沒有存放任何對象實(shí)體所在內(nèi)存區(qū)域的首地址的地址號。沒有引用任何對象實(shí)體的對象變量稱之為空對象變量??諏ο笞兞坎荒苁褂?,如果程序中使用了空對象變量,則在運(yùn)行時會出現(xiàn)異常——NullPointerException。關(guān)于異常我們將會在第七章詳細(xì)介紹。因此我們在聲明對象變量后,要盡快的為對象變量分配對象實(shí)體。所謂對象實(shí)體就是用類模板創(chuàng)建一個對象時,這個新建的對象所獲得的內(nèi)存區(qū)域。在Java中,使用new運(yùn)算符和類的構(gòu)造方法為新建的對象分配內(nèi)存,為其中的域置初值,并把對這段內(nèi)存的引用返回給對象變量,從而完成對象的實(shí)體化。new操作符后面跟著的是類模板中某一個構(gòu)造方法。比如:
Cuboidcb=newCuboid(5,10,15);
在這個例子中,使用new操作符通過調(diào)用Cuboid類(有三個參數(shù))的構(gòu)造方法,創(chuàng)建了一個Cuboid類的對象實(shí)體。并把這個對象實(shí)體的引用返回給對象變量cb。在創(chuàng)建對象之后,就可以通過對象變量操作它所引用的對象實(shí)體中的域,以及調(diào)用它所引用的對象實(shí)體中的方法來改變對象的內(nèi)部狀態(tài)。通過使用對象成員訪問操作符“.”,就可以實(shí)現(xiàn)對一個對象的成員變量和方法的訪問。使用對象成員訪問操作符“.”訪問成員變量的一般格式如下:
對象名.域名
例如以下代碼就分別對Cuboid類的域height和depth置了
初值:
cb.height=20;
cb.depth=50;也可以使用對象變量調(diào)用一個對象實(shí)體的方法。把對象實(shí)體的方法名附加在對象變量之后,中間使用對象成員訪問操作符“.”連接。然后在小括號內(nèi)提供方法執(zhí)行所需的參數(shù)。如果這個方法不需要任何參數(shù),則使用空括號就可以了。
【例3-3】新添加了成員方法和測試入口main方法的長方體類Cuboid。
publicclassCuboid{
intheight=20;
intlength=30;
intdepth=10;
publicCuboid(){};
publicCuboid(inth){
height=h;
}
publicCuboid(inth,intl){
height=h;
length=l;
}
publicCuboid(inth,intl,intd){
height=h;
length=l;
depth=d;
}
publicintcalVolume(){
returnheight*length*depth;
}
publicstaticvoidmain(String[]args){
Cuboidcb=newCuboid(10,15);
cb.depth=5;
System.out.printf(“VolumeoftheCuboid:%d\n”,
cb.calVolume());
}
}在例3-3中,類Cuboid新添加了一個返回類型為整型的方法calVolume用來計算一個長方體的體積。在main方法中,通過調(diào)用Cuboid類中兩個參數(shù)的構(gòu)造方法,新建了一個Cuboid類的實(shí)體,并把實(shí)體的引用返回給對象變量cb。然后通過對象變量cb把成員變量depth的值從默認(rèn)值10修改為5。最后通過調(diào)用cb對象的CalVolume方法算出了這個長方體的體積。例3-3的輸出如圖3.1所示。圖3.1例3-3的輸出結(jié)果
C++要求程序員跟蹤通過new操作符創(chuàng)建的所有對象,并在不再需要的時候顯式的銷毀它們,這是非常容易出錯的。Java虛擬機(jī)提供了一個垃圾回收器,它定期地回收已經(jīng)不再被引用的對象實(shí)體占用的內(nèi)存。借助Java平臺的垃圾回收機(jī)制,程序員可以創(chuàng)建任意數(shù)量的對象,且不必為銷毀它們操心。但是Java虛擬機(jī)的垃圾回收器是自動選擇執(zhí)行回收任務(wù)的時機(jī)的,在某些情況下,Java虛擬機(jī)的垃圾回收有一定的滯后性,會導(dǎo)致系統(tǒng)性能的下降。因此如果當(dāng)對象變量已經(jīng)沒有使用價值的時候,建議通過顯示將對象變量設(shè)置為特殊值null來銷毀對象引用。
3.4域/成員變量
從上述章節(jié)中,我們已經(jīng)知道,類體中可以有兩種類型的成員:域(即成員變量)和成員方法。成員變量用來刻畫類所創(chuàng)建的對象的內(nèi)部特征,成員變量又可以分為兩種:實(shí)例變量和類變量。用關(guān)鍵字static修飾的成員變量稱之為靜態(tài)變量,也即類變量;不使用關(guān)鍵字static修飾的成員變量稱之為實(shí)例變量。當(dāng)從同一個類模板創(chuàng)建多個對象時,每個對象都擁有屬于它自己的實(shí)例變量的副本,這是因?yàn)槊恳粋€對象都被分配一個獨(dú)一無二的內(nèi)存空間,而這些對象的實(shí)例變量所對應(yīng)的實(shí)體位于這些內(nèi)存空間內(nèi)。因此修改一個對象的實(shí)例變量,不會影響另外一個對象的實(shí)例變量。但是有時候,我們也希望某些變量是從同一個類模板創(chuàng)建的所有對象共享的,這個時候我們就要使用類變量。一個類模板所創(chuàng)建的所有對象的類變量都被分配到同一個內(nèi)存區(qū)域,修改其中一個對象的類變量,會影響其他由這個類模板創(chuàng)建的對象相應(yīng)的類變量。為什么修改一個對象的類變量,會影響其他由這個類模板創(chuàng)建的對象相應(yīng)的類變量?這是因?yàn)轭愖兞看娣旁诤皖惸0逑嚓P(guān)聯(lián)的內(nèi)存空間,且只有一個副本。當(dāng)Java應(yīng)用程序在Java虛擬機(jī)中執(zhí)行時,類的字節(jié)碼會被讀入到內(nèi)存中,構(gòu)建成類模板。通過類模板每創(chuàng)建一個新的對象實(shí)例,系統(tǒng)都會為這個新的對象實(shí)例分配一個內(nèi)存空間,這個對象所擁有的實(shí)例變量就存放在這個內(nèi)存空間中。而類變量由于只有一個副本,所以所有的對象共享著這些類變量。而且,類變量的存在與否和這些對象實(shí)例的存在與否是完全無關(guān)的。一個對象消亡了,它所擁有的所有實(shí)例變量也會隨著自動垃圾回收而消失,但是類變量會一直存在。類變量所占據(jù)的內(nèi)存空間直到程序運(yùn)行結(jié)束才會被釋放,因此,類變量是與類相關(guān)聯(lián)的數(shù)據(jù)變量,它不僅可以通過某個對象訪問,也可以直接通過類名訪問。與之相反,實(shí)例變量是和特定的對象實(shí)例相關(guān)聯(lián)的,只能通過對象變量訪問。
【例3-4】Greet類,用于生成一句話,這句話的開頭是一句招呼語,然后告訴對方自己的名字。
publicclassGreet{
publicstaticStringprefix;
publicStringname;
publicGreet(Stringn){
name=n;
}
publicvoidoutputGreetingStr(){
System.out.println(prefix+“mynameis”+name);
}
publicstaticvoidmain(String[]args){
Greet.prefix=“Hi,nicetomeetyou!”;
Greetalice_greet=newGreet(“Alice”);
Greetbob_greet=newGreet(“Bob”);
alice_greet.outputGreetingStr();圖3.2例3-4的運(yùn)行結(jié)果
bob_greet.outputGreetingStr();
alice_greet.prefix=“Hey,guys,”;
alice_greet.outputGreetingStr();
bob_greet.outputGreetingStr();
}
}圖3.2例3-4的運(yùn)行結(jié)果
Greet類運(yùn)行的結(jié)果如圖3.2所示。
在Greet類中,用于打招呼的一句話記錄在字符串類型的類變量prefix中,打招呼的人的名字記錄在字符串類型的實(shí)例變量name中,name的值在創(chuàng)建對象時由構(gòu)造方法的參數(shù)傳遞進(jìn)來。Greet類提供了outputGreetingStr方法,用于輸出打招呼的話,這句打招呼的話是由prefix和name兩個字符串合成的。上述程序從Greet類的main方法開始執(zhí)行。一開始的時候沒有任何Greet類的對象實(shí)例,但是卻可以直接通過Greet類訪問類變量prefix,把它的值設(shè)置為“Hi,nicetomeetyou!”。然后我們創(chuàng)建了兩個Greet類的對象alice_greet和bob_greet,分別代表Alice和Bob的招呼語。在輸出兩個人的招呼語字符串后,通過以下語句:
alice_greet.prefix=“Hey,guys,”;
我們把類變量prefix改為了“Hey,guys,”,然后再輸出兩個人的招呼語字符串。從圖3.2中可以看出,盡管我們是通過alice_greet修改類變量prefix的,但是bob_greet中的類變量prefix也被更改為了“Hey,guys,”。如果一個域被修飾符final修飾,它就變成了常量。所謂常量,也就是這個域的值是不能夠被改動的。如果程序試圖為一個常量重新賦值,則會導(dǎo)致編譯錯誤。既然常量的值在編譯時是已知的,那么Java編譯器會在編譯時,在每個常量該出現(xiàn)的地方,用常量的值替換掉該常量,因此常量不占用內(nèi)存,這也意味著在聲明常量的時候,必須給出它的初值。代碼中常量的名字習(xí)慣上用大寫字母表示,如果名稱由一個以上的單詞構(gòu)成,那么使用下劃線“_”分隔各個單詞。例3-5給出了一個使用常量的類。
【例3-5】使用常量的類的例子。在這個例子里面,使用類常量PI記錄了圓周率,這個圓周率被用于計算一個圓形的周長和面積。
publicclassCircle{
privatestaticfinaldoublePI=3.1415926;
privatedoubleradius;
publicCircle(doubler){
radius=r;
}
publicdoublecalPerimeter(){
return2*PI*radius;
}
publicdoublecalArea(){
returnPI*radius*radius;
}
publicstaticvoidmain(String[]args){
Circlec=newCircle(10);
System.out.printf(“Perimeter:%f,Area:%f\n”,c.calPerimeter(),c.calArea());
}
}
3.5成員方法
除了構(gòu)造方法外,其它的成員方法又可以分類為類方法和實(shí)例方法。在方法聲明中使用關(guān)鍵字static修飾的成員方法稱之為靜態(tài)方法或類方法,而不使用關(guān)鍵字static修飾的成員方法稱之為實(shí)例方法。實(shí)例方法必須通過對象名來調(diào)用;而類方法既可以通過類名來調(diào)用,也可以通過對象名來調(diào)用。一個類中的方法可以相互調(diào)用,在方法中可以訪問這個類的成員變量。但需要注意的是,在實(shí)例方法中,既可以訪問實(shí)例變量和實(shí)例方法,也可以訪問類變量和類方法;但是在類方法中,只可以訪問類變量和類方法,不允許訪問實(shí)例變量和實(shí)例方法。為什么會存在這樣的區(qū)別呢?這是因?yàn)?,每個對象都擁有屬于它們自己的實(shí)例變量的副本,而類變量是存放在和類模板相關(guān)聯(lián)的內(nèi)存空間中,類變量的存在與否和對象實(shí)例的存在與否是完全無關(guān)的。在調(diào)用類方法的時候,對象實(shí)例可能還不存在,因此這個對象實(shí)例所擁有的實(shí)例變量也不存在。如果允許類方法訪問實(shí)例變量的話,那么類方法可能會訪問尚未分配的內(nèi)存區(qū)域,產(chǎn)生越界訪問錯誤。而在調(diào)用實(shí)例方法的時候,它所隸屬的對象實(shí)例已經(jīng)存在,因此實(shí)例方法可以訪問實(shí)例變量。正因?yàn)閷?shí)例方法可以訪問實(shí)例變量,所以如果允許類方法訪問實(shí)例方法,間接地,也可能會導(dǎo)致類方法訪問未分配的內(nèi)存區(qū)域。所以,只有實(shí)例方法才允許訪問一個對象的實(shí)例變量和其它的實(shí)例方法。
【例3-6】使用類方法和實(shí)例方法的例子。在這個例子里面,我們創(chuàng)建了一個Student類。Student類里面有一個初值為0的類成員變量number用來記錄學(xué)生的總數(shù)。這個類成員變量能通過類方法getNumber訪問,而實(shí)例成員變量name用來記錄每個學(xué)生的名字。Name能通過實(shí)例方法getName訪問。在測試代碼中,創(chuàng)建了三個學(xué)生Alice、Bob和Eve。然后把學(xué)生的總數(shù)和每個學(xué)生的名字打印出來。Student類的運(yùn)行結(jié)果如圖3.3所示。圖3.3例3-6的運(yùn)行結(jié)果在定義成員方法的時候,給定的參數(shù)稱之為“形式參數(shù)”,簡稱為“形參”。當(dāng)調(diào)用成員方法的時候,如果成員方法有參數(shù),則必須提供實(shí)際的參數(shù)。這種實(shí)際的參數(shù)簡稱為“實(shí)參”。實(shí)參具有確定的值,在調(diào)用方法的時候,實(shí)參的值傳遞給形參。在Java中,所有的參數(shù)傳遞都是“按值傳遞”,也即是說,方法中形參的值是傳遞進(jìn)去的實(shí)參的值的一個副本。但是在Java中“按值傳遞”基本數(shù)據(jù)類型和對象數(shù)據(jù)類型參數(shù),有著細(xì)微的區(qū)別,必須對其詳細(xì)討論。3.5.1“按值傳遞”基本數(shù)據(jù)類型參數(shù)
對于基本數(shù)據(jù)類型的實(shí)參,它是按值傳遞進(jìn)方法內(nèi)部的,也即是說,在方法內(nèi)部對于參數(shù)的任何改動都只限于這個方法的作用域內(nèi)。當(dāng)從方法返回時,形參消失,它所占據(jù)的內(nèi)存區(qū)域被釋放,因此對參數(shù)所作的修改全部都會丟失。在方法執(zhí)行完畢后,實(shí)參的值不受方法內(nèi)部改動的影響。
【例3-7】“按值傳遞”基本數(shù)據(jù)類型參數(shù)的例子。在這個例子里面,main方法定義了一個局部變量param,它的初值為12。param作為實(shí)參傳遞進(jìn)方法changeParam中,在方法changeParam內(nèi)部,形參param執(zhí)行了自加操作。但是從程序的輸出結(jié)果,我們會發(fā)現(xiàn),形參所執(zhí)行的自加操作,在方法執(zhí)行結(jié)束后,并沒有影響到main方法中的實(shí)參param的值。PassPrimitiveParam類的運(yùn)行結(jié)果如圖3.4所示。圖3.4例3-7的運(yùn)行結(jié)果另外值得注意的是,向基本數(shù)據(jù)類型的形參所傳遞的實(shí)參值,它的級別不可以高于對應(yīng)的形參的級別。比如可以傳遞一個float類型的實(shí)參值給一個double類型的形參,卻不可以傳遞一個float類型的實(shí)參值給一個int類型的形參。如果必須要把高級別的實(shí)參值傳遞給低級別的形參,必須要在傳值前進(jìn)行強(qiáng)制類型轉(zhuǎn)換,把實(shí)參值轉(zhuǎn)換為形參的類型,否則就會發(fā)生編譯錯誤。3.5.2“按值傳遞”對象數(shù)據(jù)類型參數(shù)
對象數(shù)據(jù)類型的實(shí)參也是按值傳遞進(jìn)方法的,也即是說,在方法內(nèi)部對于對象數(shù)據(jù)類型參數(shù)的任何改動都只限于這個方法的作用域內(nèi)。在方法執(zhí)行完畢后,實(shí)參的值不受方法內(nèi)部改動的影響。但需要注意的是,正如我們在前面章節(jié)所提到的,對象變量是一種“引用型”的變量,在對象變量中,存放的是對象實(shí)體的引用。因此如果通過對象數(shù)據(jù)類型的形參,在方法內(nèi)部對形參所引用的實(shí)體進(jìn)行修改,其改動在方法執(zhí)行完畢后會保留下來。但是,畢竟在Java中只有按值傳遞,沒有按引用傳遞,因此如果直接修改對象變量類型的形參,比如說把一個新的對象實(shí)體的引用復(fù)制給對象數(shù)據(jù)類型的形參,則這種修改在方法執(zhí)行完畢后不會被保留下來。
【例3-8】“按值傳遞”對象數(shù)據(jù)類型參數(shù)的例子。在這個例子里面,main方法定義了一個MyObject對象類型的局部變量mo,其初始的內(nèi)部狀態(tài)innerStatus為12。mo作為實(shí)參傳遞給方法changeParam1的對應(yīng)形參param。在方法changeParam1內(nèi)部,通過形參param對對象實(shí)體的內(nèi)部狀態(tài)innerStatus執(zhí)行了自加操作。從程序的輸出結(jié)果,我們會發(fā)現(xiàn),在方法執(zhí)行結(jié)束后,main方法中的實(shí)參mo的內(nèi)部狀態(tài)innerStatus確實(shí)由12變?yōu)榱?3。但是與之相反的一個例子是,mo作為實(shí)參也傳遞給方法changeParam2的對應(yīng)形參param。在方法changeParam2內(nèi)部,創(chuàng)建了一個新的MyObject的實(shí)體,并把它的引用賦值給了形參param,從程序的輸出結(jié)果,我們會發(fā)現(xiàn),在方法執(zhí)行結(jié)束后,param的改變并沒有影響到main方法中的實(shí)參mo。PassObjectParam類的運(yùn)行結(jié)果如圖3.5所示。圖3.5例3-8的運(yùn)行結(jié)果3.6this關(guān)鍵字
this是Java中一個重要的關(guān)鍵字,它代表對當(dāng)前對象的一個引用,也即被調(diào)用的方法或構(gòu)造器所隸屬的對象。通過使用this關(guān)鍵字,可以在實(shí)例方法或構(gòu)造方法中引用當(dāng)前對象的成員變量或成員方法。必須注意的是,this關(guān)鍵字不能出現(xiàn)在類方法中,這是因?yàn)樵谡{(diào)用類方法的時候,對象實(shí)例可能還不存在,this引用可能為空。3.6.1在實(shí)例方法中使用this
在前面章節(jié)已經(jīng)討論過,在類的實(shí)例方法中可以訪問類的成員變量。實(shí)際上,完整的在實(shí)例方法中訪問成員變量的格式為:
this.?成員變量名
在不引起混淆的前提下,直接通過實(shí)例成員變量名就可以在實(shí)例方法中訪問它們。但是在一個實(shí)例方法中,可能存在和實(shí)例成員變量同名的局部變量和參數(shù),在這個時候,必須顯式的使用this關(guān)鍵字訪問實(shí)例成員變量,避免二義性。
【例3-9】在實(shí)例方法中使用this的例子。在這個例子里面,類ThisTest有兩個私有成員變量x和y,它提供了一個方法setValues為成員變量x和y賦值。但是由于這個方法的兩個參數(shù)x和y與對應(yīng)的成員變量同名,在這個方法的作用域里面,成員變量x和y被對應(yīng)的參數(shù)所覆蓋。因此必須顯式使用this關(guān)鍵字來訪問它們。
ThisTest類的運(yùn)行結(jié)果如圖3.6所示。圖3.6例3-9的運(yùn)行結(jié)果3.6.2在構(gòu)造方法中使用this
在構(gòu)造方法中,和在類的實(shí)例方法中一樣,可以通過this關(guān)鍵字顯式的訪問成員變量,以避免二義性。但是在構(gòu)造方法中,this關(guān)鍵字還有另外一個用途,就是可以使用this關(guān)鍵字調(diào)用同一個類中的另一個構(gòu)造方法。但是必須注意的是,如果在構(gòu)造方法中使用this關(guān)鍵字調(diào)用其它的構(gòu)造方法,則這個語句必須放在構(gòu)造方法實(shí)現(xiàn)語句中的第一行。
【例3-10】在構(gòu)造方法中使用this的例子。在這個例子里面,Rectangle類刻畫了一個矩形。成員變量x和y代表這個矩形的左上角坐標(biāo),width和height代表矩形的寬和高。它有兩個構(gòu)造方法。第一個構(gòu)造方法接受四個參數(shù),分別給x、y、width和height賦初值。由于這個方法的四個參數(shù)和對應(yīng)的成員變量同名,因此在里面必須顯式的使用this關(guān)鍵字訪問成員變量。第二個構(gòu)造方法接受兩個參數(shù),分別為width和height賦初值。左上角坐標(biāo)取默認(rèn)值(0,0)。因此在這個構(gòu)造方法里面,通過this(0,0,width,height)直接調(diào)用第一個構(gòu)造方法完成初始化任務(wù)。
Rectangle類的運(yùn)行結(jié)果如圖3.7所示。圖3.7例3-10的運(yùn)行結(jié)果
3.7訪問權(quán)限
對于一個類而言,它的實(shí)例方法總是可以訪問該類中的實(shí)例變量和類變量,調(diào)用該類中的實(shí)例方法和類方法;它的類方法總是可以訪問該類中的類變量,調(diào)用該類中的其它類方法。但是一個類,是否可以使用另一個類的某一個成員變量或某一個成員方法呢?這是由訪問權(quán)限修飾符決定的。如果一個類的訪問權(quán)限修飾符為public,則這個類是公共的,在這種情況下,任何位置的任何類都可以訪問這個類。在Java中,源文件也即Java文件的名字,必須與這個源文件中的公共類名一致。如果一個類沒有訪問權(quán)限修飾符,也是允許的。沒有訪問權(quán)限修飾符代表著這個類采用默認(rèn)的訪問權(quán)限,也即包私有訪問權(quán)限。只有和這個類在同一個包中的類,才能夠訪問它。其它包中的類不能夠訪問具有包私有訪問權(quán)限的類。(關(guān)于包的概念,我們將在3.9節(jié)討論)3.7.1public訪問權(quán)限修飾符
用關(guān)鍵字public修飾的成員變量和方法被稱為公有變量和公有方法。對于公有變量和公有方法,在任何地方,都可以通過使用對象成員訪問操作符“.”訪問它們。也即是,公有變量和公有方法無論在同一個類內(nèi)部,處于同一個包的其它類里面,或者處于不同包的其它類里面,都可以被訪問但需通過對象成員訪問操作符。3.7.2private訪問權(quán)限修飾符
用關(guān)鍵字private修飾的成員變量和方法被稱為私有變量和私有方法。對于私有變量和私有方法,只有在本類中創(chuàng)建的該類的對象才能訪問自己的私有變量和私有方法,在另外一個類中創(chuàng)建的對象,是不能夠訪問該類的對象的私有變量和私有方法的。在編寫一個類的代碼的時候,如果不希望將來外部能夠通過這個類生成的對象直接訪問內(nèi)部的成員變量和成員方法,就應(yīng)該將其設(shè)置為私有的。在面向?qū)ο缶幊虒?shí)踐中,一個實(shí)體只應(yīng)該對外暴露它希望外部知道的入口,而隱藏內(nèi)部的屬性,防止非法的訪問。在Java中,類里面希望外部知道的入口應(yīng)該被標(biāo)記為public,而需要隱藏的內(nèi)部屬性標(biāo)記為private,這是封裝性的一種體現(xiàn)。
【例3-11】公有成員變量和私有成員變量的例子。在這個例子里面,實(shí)現(xiàn)了一個賬戶類Account。Account類中的屬性money是內(nèi)部屬性,不希望出現(xiàn)外部的非法訪問,因此Account類提供了兩個公有方法getMoney和setMoney分別用來取得賬戶中的金額和修改賬戶中的金額。在測試類Transaction中,只能通過公有方法訪問一個賬戶的內(nèi)部金額,不能直接訪問money屬性。3.7.3protected訪問權(quán)限修飾符
用關(guān)鍵字protected修飾的成員變量和方法被稱為受保護(hù)的變量和受保護(hù)的方法。在不牽涉到繼承的時候,有protected修飾符和無修飾符的作用是一樣的。如果一個類繼承了另外一個類,也即是說一個類是另外一個類的子類的話,那么它能夠訪問其父類的成員變量和成員方法,而無論這個類是否和其父類在同一個包中。3.7.4無修飾符
不用關(guān)鍵字public、private、protected修飾符修飾的成員變量和成員方法被稱為友好的變量和友好的方法。一個類里面的友好變量和友好方法,能夠被同一個包中的另一個類通過使用對象成員訪問操作符“.”訪問,但是不能夠被不在同一個包中的其他類訪問。
3.8嵌套類和內(nèi)部類
Java允許在一個類中定義另一個類,這樣的類被稱為嵌套類。而包含嵌套類的類被稱為這個內(nèi)部類的外部類。嵌套類是包含它的外部類的成員,因此嵌套類可以訪問外部類的其他成員。值得注意的是,嵌套類不僅可以訪問外部類的public、protected和受保護(hù)的成員,連private成員也是可以訪問的。嵌套類分為兩種類型:靜態(tài)的和非靜態(tài)的。聲明為static的嵌套類被稱為靜態(tài)嵌套類,而非靜態(tài)嵌套類也被稱為內(nèi)部類。與類方法和類變量一樣,靜態(tài)嵌套類只和外部類相關(guān),和由外部類生成的實(shí)例對象無關(guān),因此靜態(tài)嵌套類不能直接訪問外部類中定義的實(shí)例變量和方法。另一方面,與實(shí)例方法和實(shí)例變量一樣,內(nèi)部類和包含它的外部類的一個實(shí)例相關(guān)聯(lián),內(nèi)部類可以直接訪問這個實(shí)例對象的變量和方法。但是值得注意的是,由于內(nèi)部類是和外部類的實(shí)例相關(guān)聯(lián)的,因此它不能定義任何靜態(tài)成員,比如以下的例子:
classOuterClass{
classInnerClass{
…
}
}
我們可以看到內(nèi)部類InnerClass位于外部類OuterClass之內(nèi),因此它可以直接訪問OuterClass的成員方法和成員變量。實(shí)例化內(nèi)部類之前必須先實(shí)例化外部類,比如以下的例子:
OuterClassouterObj=newOuterClass(…);
OuterClass.InnerClassinnerObj=OuterObj.newInnerClass(…);
【例3-12】內(nèi)部類的例子。在這個例子里面,IDCollection類封裝了一個整數(shù)ID的集合。在IDCollection的初始化方法中,一個整數(shù)ID數(shù)組以及它的長度作為參數(shù)傳遞進(jìn)來,保存在成員變量ids中。IDCollection類中有一個內(nèi)部類IDIterator,用于從頭到尾或從尾到頭遍歷ID集合。程序運(yùn)行結(jié)果如圖3.8所示。圖3.8例3-12的運(yùn)行結(jié)果
3.9包
在編程工作中,我們可能經(jīng)常會使用其他程序員提供的類和接口。很有可能出現(xiàn)這樣的情況:我們使用的兩個類或接口具有同樣的名字。比如說,程序員A和B分別向我們提供了他們編寫的同名類Car。程序員A和B各自的Car都具有它們不可替代的特點(diǎn),所以我們必須同時使用這兩個類。但是Java不允許在同一個虛擬環(huán)境中使用兩個同名類。由于程序員A和B的Car類都是早就封裝好的,并且已經(jīng)用于很多場合,所以我們不能要求他們更改他們自己類的名字。那該怎么辦呢?Java已經(jīng)為我們提供了一種解決方案,那就是包的機(jī)制。在Java中,把一組相關(guān)的類和接口放在同一個包里面,以便程序員查找使用類和接口、避免命名沖突和實(shí)現(xiàn)訪問控制。比如,我們常用的基礎(chǔ)類都放在java.lang包中,而輸入輸出相關(guān)的類都放在java.io中。我們也可以把自己創(chuàng)建的類放在某個特定的
溫馨提示
- 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)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 線上線下彩票業(yè)務(wù)合作框架協(xié)議
- 草牧場承包權(quán)流轉(zhuǎn)與農(nóng)業(yè)可持續(xù)發(fā)展合作協(xié)議
- 食品流通市場承包權(quán)轉(zhuǎn)讓合同范本
- 外債融資擔(dān)保機(jī)構(gòu)合作協(xié)議范本
- 樁基露筋防腐處理技術(shù)專題
- 預(yù)應(yīng)力孔道智能壓漿監(jiān)控
- 中職學(xué)校教師培訓(xùn)
- 濱水帶施工合同定交底
- 智慧用電服務(wù)體系建設(shè)方案智慧電能服務(wù)體系建設(shè)方案
- 智慧醫(yī)院節(jié)能監(jiān)管平臺建設(shè)方案節(jié)約型醫(yī)院實(shí)施方案
- 2022年浙江農(nóng)業(yè)博覽會參展單位匯總表
- 貨物簽收單確認(rèn)單
- 《走進(jìn)民間音樂》資料
- 螺桿冷水機(jī)組使用說明書
- 非固化橡膠瀝青防水涂料技術(shù)交底
- 海姆立克急救(生命的擁抱)課件
- 講稿董關(guān)鵬:如何面對媒體與公眾
- 酒店治安保衛(wèi)管理制度
- Q∕SY 06521-2016 煉油化工建設(shè)項(xiàng)目EPC總承包管理規(guī)范
- 課件心肺復(fù)蘇(CPR)
- 劉三姐歌詞大全
評論
0/150
提交評論