




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
第3章類與對象3.1面向對象編程概念的介紹3.2類聲明和類體3.3構造方法與對象的創建和使用3.4域/成員變量3.5成員方法3.6this關鍵字3.7訪問權限3.8嵌套類和內部類3.9包
3.1面向對象編程概念的介紹
使用面向過程的編程語言,如C和Fortran,需要選擇數據結構和設計算法,再把算法翻譯成代碼。而Java是一種面向對象的編程語言,它不僅具有面向過程編程語言的特點,而且通過抽象、封裝、繼承和多態增加了靈活性、模塊性、清晰性和可重用性等極其有用的特性。面向對象編程的核心思想就是將數據和對數據的操作封裝在一起,放入稱之為對象的實體中。什么是對象?在現實生活中,我們每時每刻都在與具體的對象打交道,比如我們身邊的老師、同學、電腦、手機等都是對象。
現實生活中的對象都有兩種特征:他們的狀態和他們的行為。比如同一個班上的同學可以由他們的狀態(姓名,學號,性別,年齡)和他們的行為(學習,運動,休息,交談)刻畫;我們常用的電腦也可以由它的狀態(機箱,顯示器,主板,CPU,內存)和它的行為(開機,關機,休眠,重啟)刻畫。知道了一個對象的狀態和行為,就能夠從一堆對象的集合中唯一的揀選出那個對象。抽象出現實生活中對象的狀態和行為,就是進行面向對象編程的第一步。如果我們仔細觀察身邊的對象,我們會注意到,不同的對象,他們的狀態和行為會存在很大的差別,比如你身邊同學的狀態和行為就和你所使用的電腦的狀態和行為截然不同。有些對象,它有可能還包含有其它的對象,比如一輛汽車是一個對象,它的四個輪子也是一種對象。這些現實生活中觀察到的例子都可以用面向對象編程的術語來描述。面向對象編程中的“對象”,和現實生活中的對象在概念上是非常類似的。面向對象編程中的對象也有狀態和行為。一個對象把它的狀態保存在域中,通過方法表現對象所擁有的行為。對象的方法可以操作一個它自己的內部狀態。方法是對象之間通信的主要手段。在面向對象編程中,我們應該隱藏一個對象的內部狀態,對這個對象的內部狀態的任何修改都只能通過對象對外提供的方法來完成,這就叫做封裝。以電腦顯示器為例子。顯示器有一個內部狀態叫做分辨率。我們可以提供一個方法來改變它的分辨率。這樣使用者如何改變顯示器的分辨率,實際上是由顯示器決定的,不能夠隨心所欲。如果一個顯示器的最高分辨率為1024×768,而一個使用者試圖把它的分辨率調整為1600×1200,那么顯示器可以在它的方法里面拒絕使用者的要求。在現實生活中,許多對象實體都具有同樣的類型。打比方說,在大街上你會看到許多和你所駕駛的小汽車擁有同樣的商標、同樣的外形、同樣的顏色,但是由別人駕駛的小汽車。這些小汽車都是由同一個制造商按照同一份圖紙制造,因此具有同樣的部件。在面向對象編程理論中,我們把你的座駕稱之為這種型號的小汽車的一個實例,而所有這種型號的小汽車稱之為你的座駕所屬的類。一個類就是在構建一個單獨的對象時使用的模板,類模板描述一個對象所擁有的狀態和行為,也即定義了一個對象的域和方法。此外,我們還會注意到,不同種類的對象也會存在著共性。比如小汽車、自行車、電動車他們都是車,他們都共享著車所具有的性質,比如都有車輪,都能夠行駛,都能夠剎車等。但是除了這些共有的性質外,每一類車都有它們自己所特有的性質,比如小汽車有四個輪子,電動車以電力推動,自行車靠人力騎行等。在面向對象編程理論中,通過繼承機制,允許一個類從另外一個類中繼承一些共有的性質,然后再另外加入自己所特有的性質。比如“車”是小汽車、自行車、電動車的共性的抽象,我們可以把“車”稱為小汽車、自行車、電動車的超類,而小汽車、自行車、電動車稱為“車”的子類,它們都繼承了車的性質,然后還加入了自己特有的性質。比如電動車會包括一個特有的成員——充電電池。繼承可能是多層的,一類的超類,可能還有它自己的超類,多層的繼承結構,會形成一個繼承樹。從上述討論中我們已經了解到,對象通過它們提供的方法定義了它們和外界交流的方式,換句話說,方法構成了對象和外部世界交互的接口。舉例來說,鍵盤和鼠標就是你和你所使用的電腦的接口,你通過鍵盤和鼠標與隱藏在它們后面的CPU、內存和主板打交道。點擊鼠標右鍵,電腦顯示器就會彈出右鍵菜單。在Java中,接口是一個重要的面向對象的概念,定義一個接口相當于規范了一個類所允諾提供的行為,接口構成了類和外部世界溝通的一個協議。在編譯的時候,編譯器可以通過檢查一個類是否實現了一個接口來確認這個類是否遵守了它和外部世界所達成的交互協議。在Java中,如果一個類聲明它將實現某個接口,那么它必須實現這個接口所定義的所有方法,否則這個類將無法通過編譯。在編程實踐中引入面向對象的理論,有以下的好處:
(1)模塊化:可以獨立的編寫和維護一個對象的代碼而不會和系統中的其它對象產生沖突。可以安全的在系統中傳遞對象而不用擔心對象的內部狀態受到非法的修改。
(2)信息隱藏:只允許外界通過對象對外提供的方法訪問對象,可以保證一個對象的內部實現細節對外是透明的。
(3)代碼重用:如果存在一個預先編寫好的對象,就可以直接使用它,而無需進行二次開發。因為只要一個對象經過詳細的測試,它就是安全的可被重用的。
(4)易于調試:如果一個特定的對象被發現是有問題的,只需把它從系統中隔離開來,用另外一個沒有問題的對象替換就可以了。這會大大降低調試的難度。
3.2類聲明和類體
如上所述,類是組成Java程序的基本要素。類封裝了一個對象類別的狀態和方法,它是用來創建對象的模板。類的基本格式如下:
class類名{
類體的內容
}其中class是用來定義類的關鍵字,“class類名”是類的聲明部分。類體(括號之間的內容)包含從這個類創建的對象在整個生命周期所需要的代碼,其中包括用于初始化新的對象的構造方法,用于提供類和從屬于它的對象內部狀態的域和實現了類及從屬于它的對象的行為方法。上述類的聲明部分是最基本的。類聲明還可以包含以下的信息:
(1)類的訪問限制符,如public、private等。
(2)通過關鍵字extends聲明父類的名字。注意在Java中的類只能有一個直接父類。
(3)通過關鍵字implements聲明所要實現的接口的名字,一個類可以實現多個接口,多個接口的名字之間以逗號分隔。
一個復雜的類聲明的例子如下所示:
publicclassSunextendsFatherimplementsPlayable,Workable{
……
}在這個例子里面,聲明了一個公共類Sun,它的父類是Father,實現了兩個接口Playable和Workable。
習慣上,類名的第一個字母大寫,當類名由幾個單詞復
合而成時,每個單詞的首字母大寫。如“DateTime”、“AmercianCity”、“EuropeAndAsia”都是符合規范的類名。類體中有兩種類型的成員:
(1)類中的成員變量,也稱之為域,它們用來刻畫對象的內部狀態。
(2)類中的方法,用來刻畫對象對外提供的行為,其中有一類方法非常特殊,它們就是構造方法。構造方法給定類所創建的對象的初始狀態,供類創建對象時使用。我們將在后續章節對構造方法進行詳細的討論。其中成員變量,也即域,其聲明依照順序,由以下三部分組成:
(1)零個或多個修飾符。如public修飾符就用來表明一個域是公共的。
(2)域的數據類型。域的數據類型可以是Java中的任何一種數據類型,包括基本數據類型、對象和接口。
(3)域的名稱。域的名稱遵循一般變量的命名規則和慣例。而成員方法,其聲明依照順序,由以下六部分組成:
(1)方法的訪問限制符和其它的修飾符。如public,private,static等。
(2)方法的返回類型,即方法返回值的數據類型。如果方法沒有返回值,則標記為void,這其中有一種例外,就是構造方法。構造方法沒有返回值,即使標記為void也是不允許的。
(3)方法的名稱。方法的名稱也遵循一般變量的命名規則和慣例,一般而言,其第一個單詞應該是動詞,第一個單詞的首字母小寫。而后續單詞的首字母應該大寫。
(4)由圓括號括起來的,以逗號分隔的參數列表。
(5)有可能拋出的異常的列表。
(6)由一對大括號括起來的方法體的內容。在例3-1中,給出了類Car的定義。類Car中兩個私有的雙精度類型域speed和weight,分別用來刻畫一輛小車的速度和重量。在定義域時我們給定了它們的默認值。這輛小車的默認時速為80mph,默認重量為1.3噸。定義了兩個方法setSpeed和setWeight,分別用來修改一個小車對象的速度和重量。另外定義了兩個方法getSpeed和getWeight,分別用來獲得這輛小車的速度和重量。由于speed和weight是私有的,getSpeed和getWeight方法是我們訪問類Car實例的內部特征的唯一方法。main方法是一個靜態的測試方法,也是應用程序運行的入口。在類的定義中,方法是可以重載的。方法的重載是多態性的一種,指的是一個類中可以有多個方法具有相同的名字,但是這些方法的參數必須不同。還記得我們常用的命令行輸出方法System.ou.println嗎?它有以下的重載版本:
(1)?println():直接換行。
(2)?println(booleanx):輸出一個布爾類型的數字然后
換行。
(3)?println(charx):輸出一個字符然后換行。
(4)?println(char[]x):輸出一個字符數組然后換行。
(5)?println(doublex):輸出一個雙精度類型的數字然后
換行。
(6)?println(floatx):輸出一個單精度類型的數字然后換行。
(7)?println(intx):輸出一個整型數字然后換行。
(8)?println(longx):輸出一個長整型數字然后換行。
(9)?println(Objectx):輸出一個對象然后換行。
(10)?println(Stringx):輸出一個字符串然后換行。需要注意的是:所謂的參數不同指的是參數的個數不同或者參數的類型不同,方法的返回類型和參數的名字不參與比較。比如以下兩個方法不是重載,因為它們僅僅是返回類型不同:
doublegetValue(intval);
intgetValue(intval);
以下兩個方法也不是重載,因為它們只有參數的名字不同,而參數的類型和個數都相同:
voidoutputStr(intx,inty);
voidoutputStr(inta,intb);
3.3構造方法與對象的創建和使用
在類的定義中,構造方法是一類特殊的方法,用于從類模板中創建對象。構造方法的聲明和普通方法的聲明類似,只是它們使用類的名稱,而且沒有返回類型。和類中普通方法的重載類似,Java也允許一個類中有若干個構造方法,但是這些構造方法的參數必須不同,即參數的個數不同,或者參數的類型不同。Java編譯器可以根據構造方法的參數數量和類型來使用不同的構造方法,但是不能在一個類中使用兩個參數個數和類型都相同的構造方法,這樣會引起編譯錯誤。
【例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。這個類里面提供了四個構造方法。第一個構造方法沒有參數,將使用默認的高、長和深創建一個立方體;第二個構造方法允許在創建立方體時修改它的高度;第三個構造方法允許在創建立方體時修改它的高度和長度;第四個構造方法允許在創建立方體時同時修改它的高度、長度和深度。需要注意的是,我們可以不為類提供任何的構造方法。如果一個類的代碼沒有提供構造方法,則在編譯的時候,Java編譯器會為這個類提供一個沒有參數的默認構造方法,這個默認構造方法不作任何域的初始化工作。但是如果我們為類提供了一個以上的構造方法,則在編譯的時候,Java編譯器不再為這個類提供沒有參數的默認構造方法。這在有的時候會引起混淆,所以強烈建議為每一個類都提供一個沒有參數的默認構造方法。當使用一個類創建一個對象時,我們也說給出了這個類的一個實例。創建一個對象包括了聲明對象和為對象分配成員變量兩個步驟。
對象聲明的一般格式為:
類名對象名1[,對象名2,對象名3,…];
如下面的兩個例子:
Cuboidcb;
Carcar1,car2,car3;
第一個例子聲明了Cuboid類的對象變量cb,第二個例子連續聲明了三個Car類的對象變量car1、car2和car3。在第一章中我們已經知道,Java取消了在C/C++?中容易產生的內存錯誤訪問等問題的指針機制。但在Java中,對象變量是一種和指針變量很類似的變量。對象變量是一種“引用型”變量,和基本類型的變量完全不同。基本類型的變量中存放的是這個變量的值,但在對象變量中,存放的是引用對象實體的標志,即存放對象實體所在內存區域的首地址的地址號。當然,Java中的引用型變量和C/C++?中的指針有著本質的區別。Java中的引用型變量不能像指針變量那樣隨意的分配內存地址,或通過進行指針加減運算在內存中隨意游走,因此也就不會導致在C/C++?中容易產生的內存錯誤訪問等問題。在聲明了對象變量后,由于它是一種引用型變量,其中還沒有引用任何對象實體,即沒有存放任何對象實體所在內存區域的首地址的地址號。沒有引用任何對象實體的對象變量稱之為空對象變量。空對象變量不能使用,如果程序中使用了空對象變量,則在運行時會出現異常——NullPointerException。關于異常我們將會在第七章詳細介紹。因此我們在聲明對象變量后,要盡快的為對象變量分配對象實體。所謂對象實體就是用類模板創建一個對象時,這個新建的對象所獲得的內存區域。在Java中,使用new運算符和類的構造方法為新建的對象分配內存,為其中的域置初值,并把對這段內存的引用返回給對象變量,從而完成對象的實體化。new操作符后面跟著的是類模板中某一個構造方法。比如:
Cuboidcb=newCuboid(5,10,15);
在這個例子中,使用new操作符通過調用Cuboid類(有三個參數)的構造方法,創建了一個Cuboid類的對象實體。并把這個對象實體的引用返回給對象變量cb。在創建對象之后,就可以通過對象變量操作它所引用的對象實體中的域,以及調用它所引用的對象實體中的方法來改變對象的內部狀態。通過使用對象成員訪問操作符“.”,就可以實現對一個對象的成員變量和方法的訪問。使用對象成員訪問操作符“.”訪問成員變量的一般格式如下:
對象名.域名
例如以下代碼就分別對Cuboid類的域height和depth置了
初值:
cb.height=20;
cb.depth=50;也可以使用對象變量調用一個對象實體的方法。把對象實體的方法名附加在對象變量之后,中間使用對象成員訪問操作符“.”連接。然后在小括號內提供方法執行所需的參數。如果這個方法不需要任何參數,則使用空括號就可以了。
【例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方法中,通過調用Cuboid類中兩個參數的構造方法,新建了一個Cuboid類的實體,并把實體的引用返回給對象變量cb。然后通過對象變量cb把成員變量depth的值從默認值10修改為5。最后通過調用cb對象的CalVolume方法算出了這個長方體的體積。例3-3的輸出如圖3.1所示。圖3.1例3-3的輸出結果
C++要求程序員跟蹤通過new操作符創建的所有對象,并在不再需要的時候顯式的銷毀它們,這是非常容易出錯的。Java虛擬機提供了一個垃圾回收器,它定期地回收已經不再被引用的對象實體占用的內存。借助Java平臺的垃圾回收機制,程序員可以創建任意數量的對象,且不必為銷毀它們操心。但是Java虛擬機的垃圾回收器是自動選擇執行回收任務的時機的,在某些情況下,Java虛擬機的垃圾回收有一定的滯后性,會導致系統性能的下降。因此如果當對象變量已經沒有使用價值的時候,建議通過顯示將對象變量設置為特殊值null來銷毀對象引用。
3.4域/成員變量
從上述章節中,我們已經知道,類體中可以有兩種類型的成員:域(即成員變量)和成員方法。成員變量用來刻畫類所創建的對象的內部特征,成員變量又可以分為兩種:實例變量和類變量。用關鍵字static修飾的成員變量稱之為靜態變量,也即類變量;不使用關鍵字static修飾的成員變量稱之為實例變量。當從同一個類模板創建多個對象時,每個對象都擁有屬于它自己的實例變量的副本,這是因為每一個對象都被分配一個獨一無二的內存空間,而這些對象的實例變量所對應的實體位于這些內存空間內。因此修改一個對象的實例變量,不會影響另外一個對象的實例變量。但是有時候,我們也希望某些變量是從同一個類模板創建的所有對象共享的,這個時候我們就要使用類變量。一個類模板所創建的所有對象的類變量都被分配到同一個內存區域,修改其中一個對象的類變量,會影響其他由這個類模板創建的對象相應的類變量。為什么修改一個對象的類變量,會影響其他由這個類模板創建的對象相應的類變量?這是因為類變量存放在和類模板相關聯的內存空間,且只有一個副本。當Java應用程序在Java虛擬機中執行時,類的字節碼會被讀入到內存中,構建成類模板。通過類模板每創建一個新的對象實例,系統都會為這個新的對象實例分配一個內存空間,這個對象所擁有的實例變量就存放在這個內存空間中。而類變量由于只有一個副本,所以所有的對象共享著這些類變量。而且,類變量的存在與否和這些對象實例的存在與否是完全無關的。一個對象消亡了,它所擁有的所有實例變量也會隨著自動垃圾回收而消失,但是類變量會一直存在。類變量所占據的內存空間直到程序運行結束才會被釋放,因此,類變量是與類相關聯的數據變量,它不僅可以通過某個對象訪問,也可以直接通過類名訪問。與之相反,實例變量是和特定的對象實例相關聯的,只能通過對象變量訪問。
【例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的運行結果
bob_greet.outputGreetingStr();
alice_greet.prefix=“Hey,guys,”;
alice_greet.outputGreetingStr();
bob_greet.outputGreetingStr();
}
}圖3.2例3-4的運行結果
Greet類運行的結果如圖3.2所示。
在Greet類中,用于打招呼的一句話記錄在字符串類型的類變量prefix中,打招呼的人的名字記錄在字符串類型的實例變量name中,name的值在創建對象時由構造方法的參數傳遞進來。Greet類提供了outputGreetingStr方法,用于輸出打招呼的話,這句打招呼的話是由prefix和name兩個字符串合成的。上述程序從Greet類的main方法開始執行。一開始的時候沒有任何Greet類的對象實例,但是卻可以直接通過Greet類訪問類變量prefix,把它的值設置為“Hi,nicetomeetyou!”。然后我們創建了兩個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修飾,它就變成了常量。所謂常量,也就是這個域的值是不能夠被改動的。如果程序試圖為一個常量重新賦值,則會導致編譯錯誤。既然常量的值在編譯時是已知的,那么Java編譯器會在編譯時,在每個常量該出現的地方,用常量的值替換掉該常量,因此常量不占用內存,這也意味著在聲明常量的時候,必須給出它的初值。代碼中常量的名字習慣上用大寫字母表示,如果名稱由一個以上的單詞構成,那么使用下劃線“_”分隔各個單詞。例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成員方法
除了構造方法外,其它的成員方法又可以分類為類方法和實例方法。在方法聲明中使用關鍵字static修飾的成員方法稱之為靜態方法或類方法,而不使用關鍵字static修飾的成員方法稱之為實例方法。實例方法必須通過對象名來調用;而類方法既可以通過類名來調用,也可以通過對象名來調用。一個類中的方法可以相互調用,在方法中可以訪問這個類的成員變量。但需要注意的是,在實例方法中,既可以訪問實例變量和實例方法,也可以訪問類變量和類方法;但是在類方法中,只可以訪問類變量和類方法,不允許訪問實例變量和實例方法。為什么會存在這樣的區別呢?這是因為,每個對象都擁有屬于它們自己的實例變量的副本,而類變量是存放在和類模板相關聯的內存空間中,類變量的存在與否和對象實例的存在與否是完全無關的。在調用類方法的時候,對象實例可能還不存在,因此這個對象實例所擁有的實例變量也不存在。如果允許類方法訪問實例變量的話,那么類方法可能會訪問尚未分配的內存區域,產生越界訪問錯誤。而在調用實例方法的時候,它所隸屬的對象實例已經存在,因此實例方法可以訪問實例變量。正因為實例方法可以訪問實例變量,所以如果允許類方法訪問實例方法,間接地,也可能會導致類方法訪問未分配的內存區域。所以,只有實例方法才允許訪問一個對象的實例變量和其它的實例方法。
【例3-6】使用類方法和實例方法的例子。在這個例子里面,我們創建了一個Student類。Student類里面有一個初值為0的類成員變量number用來記錄學生的總數。這個類成員變量能通過類方法getNumber訪問,而實例成員變量name用來記錄每個學生的名字。Name能通過實例方法getName訪問。在測試代碼中,創建了三個學生Alice、Bob和Eve。然后把學生的總數和每個學生的名字打印出來。Student類的運行結果如圖3.3所示。圖3.3例3-6的運行結果在定義成員方法的時候,給定的參數稱之為“形式參數”,簡稱為“形參”。當調用成員方法的時候,如果成員方法有參數,則必須提供實際的參數。這種實際的參數簡稱為“實參”。實參具有確定的值,在調用方法的時候,實參的值傳遞給形參。在Java中,所有的參數傳遞都是“按值傳遞”,也即是說,方法中形參的值是傳遞進去的實參的值的一個副本。但是在Java中“按值傳遞”基本數據類型和對象數據類型參數,有著細微的區別,必須對其詳細討論。3.5.1“按值傳遞”基本數據類型參數
對于基本數據類型的實參,它是按值傳遞進方法內部的,也即是說,在方法內部對于參數的任何改動都只限于這個方法的作用域內。當從方法返回時,形參消失,它所占據的內存區域被釋放,因此對參數所作的修改全部都會丟失。在方法執行完畢后,實參的值不受方法內部改動的影響。
【例3-7】“按值傳遞”基本數據類型參數的例子。在這個例子里面,main方法定義了一個局部變量param,它的初值為12。param作為實參傳遞進方法changeParam中,在方法changeParam內部,形參param執行了自加操作。但是從程序的輸出結果,我們會發現,形參所執行的自加操作,在方法執行結束后,并沒有影響到main方法中的實參param的值。PassPrimitiveParam類的運行結果如圖3.4所示。圖3.4例3-7的運行結果另外值得注意的是,向基本數據類型的形參所傳遞的實參值,它的級別不可以高于對應的形參的級別。比如可以傳遞一個float類型的實參值給一個double類型的形參,卻不可以傳遞一個float類型的實參值給一個int類型的形參。如果必須要把高級別的實參值傳遞給低級別的形參,必須要在傳值前進行強制類型轉換,把實參值轉換為形參的類型,否則就會發生編譯錯誤。3.5.2“按值傳遞”對象數據類型參數
對象數據類型的實參也是按值傳遞進方法的,也即是說,在方法內部對于對象數據類型參數的任何改動都只限于這個方法的作用域內。在方法執行完畢后,實參的值不受方法內部改動的影響。但需要注意的是,正如我們在前面章節所提到的,對象變量是一種“引用型”的變量,在對象變量中,存放的是對象實體的引用。因此如果通過對象數據類型的形參,在方法內部對形參所引用的實體進行修改,其改動在方法執行完畢后會保留下來。但是,畢竟在Java中只有按值傳遞,沒有按引用傳遞,因此如果直接修改對象變量類型的形參,比如說把一個新的對象實體的引用復制給對象數據類型的形參,則這種修改在方法執行完畢后不會被保留下來。
【例3-8】“按值傳遞”對象數據類型參數的例子。在這個例子里面,main方法定義了一個MyObject對象類型的局部變量mo,其初始的內部狀態innerStatus為12。mo作為實參傳遞給方法changeParam1的對應形參param。在方法changeParam1內部,通過形參param對對象實體的內部狀態innerStatus執行了自加操作。從程序的輸出結果,我們會發現,在方法執行結束后,main方法中的實參mo的內部狀態innerStatus確實由12變為了13。但是與之相反的一個例子是,mo作為實參也傳遞給方法changeParam2的對應形參param。在方法changeParam2內部,創建了一個新的MyObject的實體,并把它的引用賦值給了形參param,從程序的輸出結果,我們會發現,在方法執行結束后,param的改變并沒有影響到main方法中的實參mo。PassObjectParam類的運行結果如圖3.5所示。圖3.5例3-8的運行結果3.6this關鍵字
this是Java中一個重要的關鍵字,它代表對當前對象的一個引用,也即被調用的方法或構造器所隸屬的對象。通過使用this關鍵字,可以在實例方法或構造方法中引用當前對象的成員變量或成員方法。必須注意的是,this關鍵字不能出現在類方法中,這是因為在調用類方法的時候,對象實例可能還不存在,this引用可能為空。3.6.1在實例方法中使用this
在前面章節已經討論過,在類的實例方法中可以訪問類的成員變量。實際上,完整的在實例方法中訪問成員變量的格式為:
this.?成員變量名
在不引起混淆的前提下,直接通過實例成員變量名就可以在實例方法中訪問它們。但是在一個實例方法中,可能存在和實例成員變量同名的局部變量和參數,在這個時候,必須顯式的使用this關鍵字訪問實例成員變量,避免二義性。
【例3-9】在實例方法中使用this的例子。在這個例子里面,類ThisTest有兩個私有成員變量x和y,它提供了一個方法setValues為成員變量x和y賦值。但是由于這個方法的兩個參數x和y與對應的成員變量同名,在這個方法的作用域里面,成員變量x和y被對應的參數所覆蓋。因此必須顯式使用this關鍵字來訪問它們。
ThisTest類的運行結果如圖3.6所示。圖3.6例3-9的運行結果3.6.2在構造方法中使用this
在構造方法中,和在類的實例方法中一樣,可以通過this關鍵字顯式的訪問成員變量,以避免二義性。但是在構造方法中,this關鍵字還有另外一個用途,就是可以使用this關鍵字調用同一個類中的另一個構造方法。但是必須注意的是,如果在構造方法中使用this關鍵字調用其它的構造方法,則這個語句必須放在構造方法實現語句中的第一行。
【例3-10】在構造方法中使用this的例子。在這個例子里面,Rectangle類刻畫了一個矩形。成員變量x和y代表這個矩形的左上角坐標,width和height代表矩形的寬和高。它有兩個構造方法。第一個構造方法接受四個參數,分別給x、y、width和height賦初值。由于這個方法的四個參數和對應的成員變量同名,因此在里面必須顯式的使用this關鍵字訪問成員變量。第二個構造方法接受兩個參數,分別為width和height賦初值。左上角坐標取默認值(0,0)。因此在這個構造方法里面,通過this(0,0,width,height)直接調用第一個構造方法完成初始化任務。
Rectangle類的運行結果如圖3.7所示。圖3.7例3-10的運行結果
3.7訪問權限
對于一個類而言,它的實例方法總是可以訪問該類中的實例變量和類變量,調用該類中的實例方法和類方法;它的類方法總是可以訪問該類中的類變量,調用該類中的其它類方法。但是一個類,是否可以使用另一個類的某一個成員變量或某一個成員方法呢?這是由訪問權限修飾符決定的。如果一個類的訪問權限修飾符為public,則這個類是公共的,在這種情況下,任何位置的任何類都可以訪問這個類。在Java中,源文件也即Java文件的名字,必須與這個源文件中的公共類名一致。如果一個類沒有訪問權限修飾符,也是允許的。沒有訪問權限修飾符代表著這個類采用默認的訪問權限,也即包私有訪問權限。只有和這個類在同一個包中的類,才能夠訪問它。其它包中的類不能夠訪問具有包私有訪問權限的類。(關于包的概念,我們將在3.9節討論)3.7.1public訪問權限修飾符
用關鍵字public修飾的成員變量和方法被稱為公有變量和公有方法。對于公有變量和公有方法,在任何地方,都可以通過使用對象成員訪問操作符“.”訪問它們。也即是,公有變量和公有方法無論在同一個類內部,處于同一個包的其它類里面,或者處于不同包的其它類里面,都可以被訪問但需通過對象成員訪問操作符。3.7.2private訪問權限修飾符
用關鍵字private修飾的成員變量和方法被稱為私有變量和私有方法。對于私有變量和私有方法,只有在本類中創建的該類的對象才能訪問自己的私有變量和私有方法,在另外一個類中創建的對象,是不能夠訪問該類的對象的私有變量和私有方法的。在編寫一個類的代碼的時候,如果不希望將來外部能夠通過這個類生成的對象直接訪問內部的成員變量和成員方法,就應該將其設置為私有的。在面向對象編程實踐中,一個實體只應該對外暴露它希望外部知道的入口,而隱藏內部的屬性,防止非法的訪問。在Java中,類里面希望外部知道的入口應該被標記為public,而需要隱藏的內部屬性標記為private,這是封裝性的一種體現。
【例3-11】公有成員變量和私有成員變量的例子。在這個例子里面,實現了一個賬戶類Account。Account類中的屬性money是內部屬性,不希望出現外部的非法訪問,因此Account類提供了兩個公有方法getMoney和setMoney分別用來取得賬戶中的金額和修改賬戶中的金額。在測試類Transaction中,只能通過公有方法訪問一個賬戶的內部金額,不能直接訪問money屬性。3.7.3protected訪問權限修飾符
用關鍵字protected修飾的成員變量和方法被稱為受保護的變量和受保護的方法。在不牽涉到繼承的時候,有protected修飾符和無修飾符的作用是一樣的。如果一個類繼承了另外一個類,也即是說一個類是另外一個類的子類的話,那么它能夠訪問其父類的成員變量和成員方法,而無論這個類是否和其父類在同一個包中。3.7.4無修飾符
不用關鍵字public、private、protected修飾符修飾的成員變量和成員方法被稱為友好的變量和友好的方法。一個類里面的友好變量和友好方法,能夠被同一個包中的另一個類通過使用對象成員訪問操作符“.”訪問,但是不能夠被不在同一個包中的其他類訪問。
3.8嵌套類和內部類
Java允許在一個類中定義另一個類,這樣的類被稱為嵌套類。而包含嵌套類的類被稱為這個內部類的外部類。嵌套類是包含它的外部類的成員,因此嵌套類可以訪問外部類的其他成員。值得注意的是,嵌套類不僅可以訪問外部類的public、protected和受保護的成員,連private成員也是可以訪問的。嵌套類分為兩種類型:靜態的和非靜態的。聲明為static的嵌套類被稱為靜態嵌套類,而非靜態嵌套類也被稱為內部類。與類方法和類變量一樣,靜態嵌套類只和外部類相關,和由外部類生成的實例對象無關,因此靜態嵌套類不能直接訪問外部類中定義的實例變量和方法。另一方面,與實例方法和實例變量一樣,內部類和包含它的外部類的一個實例相關聯,內部類可以直接訪問這個實例對象的變量和方法。但是值得注意的是,由于內部類是和外部類的實例相關聯的,因此它不能定義任何靜態成員,比如以下的例子:
classOuterClass{
classInnerClass{
…
}
}
我們可以看到內部類InnerClass位于外部類OuterClass之內,因此它可以直接訪問OuterClass的成員方法和成員變量。實例化內部類之前必須先實例化外部類,比如以下的例子:
OuterClassouterObj=newOuterClass(…);
OuterClass.InnerClassinnerObj=OuterObj.newInnerClass(…);
【例3-12】內部類的例子。在這個例子里面,IDCollection類封裝了一個整數ID的集合。在IDCollection的初始化方法中,一個整數ID數組以及它的長度作為參數傳遞進來,保存在成員變量ids中。IDCollection類中有一個內部類IDIterator,用于從頭到尾或從尾到頭遍歷ID集合。程序運行結果如圖3.8所示。圖3.8例3-12的運行結果
3.9包
在編程工作中,我們可能經常會使用其他程序員提供的類和接口。很有可能出現這樣的情況:我們使用的兩個類或接口具有同樣的名字。比如說,程序員A和B分別向我們提供了他們編寫的同名類Car。程序員A和B各自的Car都具有它們不可替代的特點,所以我們必須同時使用這兩個類。但是Java不允許在同一個虛擬環境中使用兩個同名類。由于程序員A和B的Car類都是早就封裝好的,并且已經用于很多場合,所以我們不能要求他們更改他們自己類的名字。那該怎么辦呢?Java已經為我們提供了一種解決方案,那就是包的機制。在Java中,把一組相關的類和接口放在同一個包里面,以便程序員查找使用類和接口、避免命名沖突和實現訪問控制。比如,我們常用的基礎類都放在java.lang包中,而輸入輸出相關的類都放在java.io中。我們也可以把自己創建的類放在某個特定的
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 客戶有效溝通技巧培訓
- 小學科學青島版 (五四制2017)三年級下冊15 不同環境中的植物教學設計
- 人教版一年級上冊數學8.6 5、4、3、2加幾 課時練
- 食堂廚房管理合同
- 小學英語人教版 (PEP)五年級上冊Unit 2 My week Part C教案設計
- 2024年04月福建泉州市直有關學校招聘編外衛生類專業技術工作人員2人筆試歷年專業考點(難、易錯點)附帶答案詳解
- 未列明金屬制品設計原理考核試卷
- 社區衛生服務中的跨專業協作考核試卷
- 森林中藥材種植技術考核試卷
- 氨綸纖維在戶外帳篷材料中的應用考核試卷
- 湖北省2025屆高三(4月)調研模擬考試英語試題及答案
- 血液制品規范輸注
- 2025年征信業務合規培訓
- 2025項目部與供應商安全生產物資供應合同
- 暖通空調面試題及答案
- 統借統還合同協議
- 防造假培訓課件教案模板
- 冷庫項目工程施工組織設計方案
- 2025年上半年浙江金華義烏市勞動人事爭議仲裁院招聘易考易錯模擬試題(共500題)試卷后附參考答案
- 護理文書如何規范書寫
- 2025年上半年中國十五冶金建設集團限公司公開招聘中高端人才易考易錯模擬試題(共500題)試卷后附參考答案
評論
0/150
提交評論