版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領
文檔簡介
第3章類與對象3.1面向?qū)ο蟮幕舅枷牒突靖拍?.2案例:員工工資計算程序3.3類的聲明與對象的創(chuàng)建3.4封裝性3.5繼承性3.6多態(tài)性3.7靜態(tài)成員3.8字符串3.9數(shù)組3.10包裝類3.11編程實例 3.1面向?qū)ο蟮幕舅枷牒突靖拍?/p>
大部分傳統(tǒng)的高級程序設計語言(如C語言)都是過程化的語言,在軟件開發(fā)的過程中采用自頂向下逐步細化的方法將整個程序描述為一個過程。對于小型的系統(tǒng),這種方法是可行的,但是當系統(tǒng)規(guī)模很大、復雜度很高時,用過程化方法描述變得十分困難,面向?qū)ο蟮能浖_發(fā)方法可以很好地解決這個問題。
目前,面向?qū)ο蟮姆椒ㄔ谲浖_發(fā)工作中得到了廣泛的應用,越來越多的軟件開發(fā)工具開始支持面向?qū)ο蟮拈_發(fā)方法。Java語言就是一種面向?qū)ο蟮某绦蛟O計語言,要充分利用Java語言的特性首先應該理解面向?qū)ο蟮幕舅枷搿?.1.1面向?qū)ο蟮幕舅枷?/p>
面向?qū)ο蟮幕舅枷胝J為,系統(tǒng)是由若干對象構成的,每個對象都有各自的內(nèi)部狀態(tài)和運動規(guī)律,不同對象之間通過消息傳送相互作用和聯(lián)系。
圖3.1是生活中看電視的一個場景,觀眾按下遙控器上的頻道按鈕,遙控器發(fā)出紅外信號,電視機收到紅外信號后切換到相應的頻道,播放該頻道的節(jié)目。這里有三個對象:觀眾、遙控器和電視機,三個對象之間通過特定的方法相互發(fā)送消息。圖3.1看電視場景中的對象及消息采用對象的觀點看待所要解決的問題,并將其抽象為系統(tǒng)是極其自然與簡單的,因為它符合人類的思維習慣,使得應用系統(tǒng)更容易理解。同時,由于應用系統(tǒng)是由相互獨立的對象構成的,使得系統(tǒng)的修改可以局部化,因此系統(tǒng)更易于維護。例如,對于一個企業(yè)的管理信息系統(tǒng),將整個系統(tǒng)描述成一個過程是難以想象的,但可以分別描述各個部門的特性及工作流程,然后描述部門之間的聯(lián)系。這里各個部門就是組成企業(yè)的對象,當然,在描述每個部門特性時可以采用同樣的方法。3.1.2對象與類
使用計算機軟件來模擬現(xiàn)實世界,必須使用適當?shù)姆椒▉砻枋霈F(xiàn)實世界中的事物。面向?qū)ο蠓椒▽⒖陀^世界中的事物用一組數(shù)據(jù)和施加于該組數(shù)據(jù)上的一組操作(行為)來描述,稱為對象。
對象的描述通常由三個部分組成:
(1)私有的數(shù)據(jù)結構。用于描述對象的內(nèi)部狀態(tài)。例如電視機內(nèi)部保存當前的頻道、音量、圖像亮度等信息的數(shù)據(jù)結構。
(2)處理,稱為操作或方法。它是施加于數(shù)據(jù)結構之上的。例如電視機接收到更換頻道的紅外遙控信號后,更換頻道,修改內(nèi)部保存當前頻道的數(shù)據(jù)。
(3)接口。這是對象可被共享的部分,消息通過接口調(diào)用相應的操作。接口規(guī)定哪些操作是允許的,它不提供操作是如何實現(xiàn)的信息。例如電視機通過紅外接口接收指定的操作信號,電視機對外的接口就是其能夠接收的紅外信號。實際上,采用面向?qū)ο蠓椒ㄟM行系統(tǒng)分析與設計時要描述的并不是一個個具體的對象。就像電視機的設計人員設計的并不是某一臺具體的電視機,而是某一個型號的電視機,按照該型號的設計方案可以生產(chǎn)許多臺電視機,這些電視機具有相同的特征。為了描述這種具有相同特征的對象,面向?qū)ο蠓椒ㄒ肓祟惖母拍睢n愂菍σ唤M具有相同特征的對象的抽象描述,所有這些對象都是這個類的實例。對于一個具體的系統(tǒng)而言,可能存在很多具有相同特征的對象,而且通常系統(tǒng)中對象的數(shù)目是不確定的。例如,對于一個學籍管理系統(tǒng),存在許多學生對象,它們具有相同的結構特征和行為特征,只是表示內(nèi)部狀態(tài)的數(shù)據(jù)值不同。對于學籍管理系統(tǒng),學生是一個類,而一個具體的學生則是學生類的一個實例。一個類的不同實例具有相同的操作或行為的集合和相同的信息結構或?qū)傩缘亩x,但屬性值可以不同;不同的實例具有不同的對象標識。對于學生類中的每一個對象,描述它們所使用的數(shù)據(jù)結構相同,但是值不同。一個類的定義至少包含以下兩個方面的描述:
(1)該類所有實例的屬性定義或結構的定義。
(2)該類所有實例的操作(或行為)的定義。
類的概念與人們在認識客觀世界的事物時所采取的分類思想相同。人們在認識事物時總是將具有相同特征的事物歸為一類,屬于某類的一個事物具有該類事物的共同特征。
在程序設計語言中,類是一種數(shù)據(jù)類型,而對象是該類型的變量,變量名即是某個具體對象的標識。Java語言程序的基本單位就是類,一個完整的Java程序是由若干個類構成的,每個類由若干數(shù)據(jù)和方法構成,一個類的定義包含屬性(數(shù)據(jù))和方法(行為)兩部分內(nèi)容。3.1.3封裝性、繼承性與多態(tài)性
1.封裝性
對象的一個基本特性是封裝性。封裝是一種信息隱藏技術,對象內(nèi)部對用戶是隱藏的,不可直接訪問;用戶只能見到對象封裝界面上的信息,通過對象的外部接口訪問對象。用戶向?qū)ο蟀l(fā)送消息,對象根據(jù)收到的消息調(diào)用內(nèi)部方法作出響應。封裝的目的在于將對象的使用者和對象的設計者分開,使用者無需知道對象內(nèi)部實現(xiàn)的細節(jié),只需要知道對象接收的消息即可。觀察一下上面所舉電視機和遙控器的例子可以發(fā)現(xiàn),電視機內(nèi)部保存了當前電視機的狀態(tài),如頻道、音量等,觀眾可以通過電視機屏幕顯示的信息來了解其狀態(tài),而無需知道其內(nèi)部是如何運作的,只需要通過遙控器向電視機發(fā)送操作指令即可。
Java語言通過類來實現(xiàn)封裝,類中定義的屬性和方法分為私有和公有的。私有屬性和方法不能在對象的外部訪問,只能由類內(nèi)的方法訪問。而在對象的外部,只能訪問對象的公有屬性和方法,只需要知道公有屬性的數(shù)據(jù)類型和名字以及公有方法的原型,至于這些方法是如何實現(xiàn)的對象外部并不需要知道。對象的封裝特性可以提高模塊之間的獨立性,使得系統(tǒng)易于調(diào)試和維護。在電視機的例子中,電視機和遙控器是兩個相互獨立的對象,電視機和遙控器的設計人員確定電視機所接收的紅外信號的數(shù)據(jù)格式后,可以分別設計電視機和遙控器,不管電視機的設計方案如何修改,只要其所接收紅外信號的數(shù)據(jù)格式不變,遙控器設計人員便無需修改其設計
方案。
2.繼承性
人們在對客觀世界的事物進行描述時,經(jīng)常采取分類的方法。類是有層次的,即某個大類的事物可能分為若干小類,而這些小類又可能分為若干個更小的類。
面向?qū)ο蠓椒ú杉{了事物分類的層次思想,在描述類的時候,某些類之間具有結構和行為的共性。例如,描述教師與學生時均需描述姓名、年齡、身高、體重等屬性,將這些共性抽取出來,形成一個單獨的類——人,用于描述教師類和學生類的共性。類人的結構特征和行為特征可以被多個相關的類共享,教師類和學生類繼承了類人的結構和行為特征。
Java語言支持類的繼承,可以從一個類中派生出一個新的類,原來的類稱為超類或父類,新類稱為超類的子類或派生類。子類的對象具有超類對象的特征,同時又有其自身特有的特征。子類又可以派生出新的子類,子類的子類也稱為派生類。
利用類之間的繼承關系,可以簡化類的描述,提高軟件代碼的可重用性。在設計一個新類時,不必從頭設計編寫全部的代碼,可以通過從已有的具有類似特性的類中派生出一個類,繼承原有類中的部分特性,再加上所需的新特性。另外,人們在對客觀世界的事物分類時,一個事物可能屬于多個類,具有多個類的特性。例如一個黑人學生,他既屬于學生類,又屬于黑人類。這種情形在面向?qū)ο蠓椒ㄖ蟹Q為多繼承,即一個類同時從多個類中派生出來,此時類的層次結構是網(wǎng)狀的。
Java語言為了不使語法過于復雜,不支持多繼承,只允許子類有一個超類,稱為單繼承。不過Java語言提供了接口機制,可以在一定程度上模擬多繼承。
3.多態(tài)性
多態(tài)性是面向?qū)ο笙到y(tǒng)的又一重要特性。所謂多態(tài),即一個名詞可具有多種語義,如一個方法名有多種功能,或者相同的接口有多種實現(xiàn)方法。就電視機的例子來說,采用同樣的操作來降低音量,不同型號的電視機其內(nèi)部實現(xiàn)的方法各不相同,對不同的電視機對象發(fā)送同樣的消息“降低音量”,不同的電視機對象執(zhí)行不同的操作。
在Java語言中,多態(tài)性通過方法的重載、覆蓋和接口來實現(xiàn)。方法的重載是指多個方法具有相同的名稱,但各個方法的參數(shù)表不同,即參數(shù)的類型和參數(shù)的數(shù)量不同。有關重載的問題本書將在3.6.1節(jié)討論。
覆蓋是指類派生過程中,子類與超類的方法不僅名稱相同,參數(shù)也完全相同,但它們的功能不同,這時子類中的方法覆蓋了超類中同名的方法。
接口實際上是一種特殊的類,只給出方法的名稱、參數(shù)和返回值的類型,方法的具體實現(xiàn)在實現(xiàn)該接口的類中給出。本書3.6.3節(jié)將詳細介紹Java語言中接口的使用方法。
多態(tài)性使得方法的調(diào)用更加容易、靈活和方便。
3.2案例:員工工資計算程序
本章將介紹Java語言中面向?qū)ο蟮幕菊Z法,通過不斷完善員工工資計算案例的功能,逐步介紹Java語言中類的定義、對象的創(chuàng)建及其封裝性、繼承性和多態(tài)性。
員工工資計算程序所需實現(xiàn)的功能描述如下:某公司按周付給員工工資。公司有三種類型的員工:固定工資員工,他們無論每周工作時間長短均付給固定的薪水;鐘點工,按小時付工資和加班費;傭金員工,其工資按銷售額提成。該公司希望通過一個Java程序?qū)崿F(xiàn)不同類型工資的計算,輸入員工的類型和相關數(shù)據(jù)后計算每個員工的工資額并輸出工資列表。
最終完成程序的執(zhí)行情況如圖3.2和圖3.3所示。圖3.2輸入員工人數(shù)及工資信息圖3.3輸出員工工資表
3.3類的聲明與對象的創(chuàng)建
3.3.1類聲明的基本語法
1.類的聲明
正如3.1節(jié)所述,在程序設計階段不可能描述某一個具體的對象,而是使用類來描述一類對象。對3.2節(jié)所描述的案例,我們不可能去描述某一個具體的員工所具有的特征。從該案例的描述中可以看出,所有員工分為三種類型,可以定義三個類分別描述固定工資員工、鐘點工和傭金員工。
Java語言類聲明的完整語法很復雜,下面先介紹最簡單的形式:
class類名{
類體
}
類體部分定義類的變量和方法。變量描述該類對象的屬性,方法描述對象的行為特征,一個方法完成一個相對完整的功能,類似于C語言的函數(shù)或其他語言子程序的概念。Java語言中沒有獨立的函數(shù)和過程,所有的子程序都是作為方法定義的,同樣Java語言也沒有其他語言中的全局變量。類中定義的變量和方法都是類的成員,Java語言對類成員的訪問有一定的權限限制。在定義屬性和方法時,可以指定訪問權限,Java中的訪問權限有private、protected和public。如果不指定訪問權限,則為friendly。有關權限的使用將在3.4.1中介紹。
2.變量成員的定義
Java語言中的變量有如下兩種:
一種是在某個方法中定義的局部變量,如第2章main方法中定義的變量,這些變量只有當其所在的方法被調(diào)用時才存在,其作用范圍為方法或其所在的復合語句。第二種變量就是類的成員,定義的一般形式為
[final][private|protected|public]類型屬性名[=初值];
例如:
classTV{ //電視類
intchannel; //頻道
intvolume; //音量
}
final為可選項,用final修飾的成員變量為常量。在程序中不能改變常量的值,常量必須在定義時初始化。
private、protected、public表示訪問權限,三者最多只能有一個存在。與局部變量一樣,成員變量也可以初始化,例如上面的電視類,頻道初始化為1,音量初始化為5,則可定義如下:
classTV{ //電視類
intchannel=1; //頻道
intvolume=5; //音量
}
成員變量的類型可以是Java語言中的任意數(shù)據(jù)類型,包括簡單類型、數(shù)組、類和接口。在類中,成員變量名應該是唯一的。
3.方法成員的定義
方法的定義形式與C語言的函數(shù)定義類似,基本形式為
[private|protected|public]返回值類型方法名([形式參數(shù)表])
{
方法體
}
這是最基本的形式,在后面的章節(jié)中還會陸續(xù)出現(xiàn)一些變化。下列代碼為電視類增加兩個調(diào)整電視頻道的方法,這里假設頻道的范圍為0~99。classTV{ //電視類
intchannel=1; //頻道
intvolume=5; //音量
voidincChannel() //頻道+1
{
channel=(channel+1)%100;
}
voiddecChannel() //頻道-1
{
channel=channel-1;
if(channel<0)
channel=99;
}
}
類中的方法可以訪問該類的變量成員以及方法內(nèi)定義的局部變量和形式參數(shù)。方法內(nèi)定義的局部變量和形式參數(shù)只能在該方法內(nèi)被訪問。方法在定義時還可能使用其他一些修飾符,例如,static、abstract、final、synchronized分別用于聲明靜態(tài)方法、抽象方法、最終方法以及同步方法,本書將在后面的章節(jié)中分別加以介紹。
另外,有時為了滿足一定的要求,需要使用其他語言實現(xiàn)一個方法,如對硬件直接進行某些操作或訪問本地操作系統(tǒng)的系統(tǒng)功能,可以使用native修飾符聲明一個本地方法。native方法不能移植,只能用于指定的運行平臺。
4.方法的調(diào)用
定義一個類后,我們可以創(chuàng)建對象,調(diào)用方法。方法的調(diào)用相當于給對象發(fā)送消息,因此需要首先指定接收消息的對象。下面的例子使用了上面的電視類:
classTVApp{
publicstaticvoidmain(String[]args)
{
TVaTV=newTV(); //買來一臺電視機
aTV.incChannel(); //按下遙控器上頻道增加的按鈕,向電視機發(fā)送消息
}
}
語句aTV.incChannel()調(diào)用TV類的方法,這里aTV為TV類的實例,方法incChannel調(diào)用時將aTV的Channel成員的值加1。
5.方法的返回值
Java語言中方法每一次調(diào)用可以得到返回一個值,可以用賦值語句將其賦給其他變量或參與運算。方法定義時必須指定返回值類型,如果該方法沒有返回值,必須定義為void。下面的代碼片段為電視類增加一個獲取當前頻道的方法。
classTV{ //電視類
…//此處省略前面已有代碼
intgetChannel()
{
returnchannel;
}
}
正如這個例子中看到的,Java語言使用return語句從被調(diào)用的方法中返回。return語句的基本形式有兩種:
(1)?return表達式;或return(表達式);
(2)?return;第一種形式用于有返回值的情況,第二種形式用于無返回值的情況。對于第一種情況,return后的表達式類型應與聲明的返回值類型一致,否則會產(chǎn)生編譯錯誤。
如果某方法聲明了返回值類型,必須保證在該方法的每一條執(zhí)行路徑都有返回值。例如下面的方法定義:
intfunc(inta,intb)
{
if(a>b)
returna-b;
}該方法的定義是錯誤的,當a<=b時該方法在執(zhí)行時沒有確切的返回值。這種情況在Java語言中是不允許的。下面的方法定義是正確的:
intfunc(inta,intb)
{
if(a>b)
returna-b;
else
returnb-a;
}
6.參數(shù)的傳遞
Java語言中的方法可以有參數(shù)也可以沒有參數(shù),參數(shù)類型可以是簡單數(shù)據(jù)類型,如整型、實型、字符型和布爾型,也可以是復合類型,如數(shù)組和自定義類的對象。
方法被調(diào)用時,必須給定實際參數(shù),實際參數(shù)的類型應與形式參數(shù)的類型一致。對于簡單類型數(shù)據(jù),實際參數(shù)傳遞給形式參數(shù)時采用值傳遞;如果參數(shù)為復合類型,則傳遞引用,此時實參、形參為同一對象。程序3.1的運行結果可以很好地說明這個問題。
【程序3.1】
參數(shù)傳遞的方法。
classB{ privateintb;
intGetb(){returnb;}
voidSetb(intj){b=j;}
}
publicclassMethodParm
{
voidMethod1(intParm1,BParm2)
{
Parm1=Parm1+10;
Parm2.Setb(20);
}publicstaticvoidmain(String[]args)
{
inta=10;
Bb=newB();
b.Setb(10);
MethodParmobj=newMethodParm();
obj.Method1(a,b);
System.out.println(a);
System.out.println(b.Getb());
}
}程序運行結果為
10
20
Method1方法有兩個形式參數(shù),其中Parm1為簡單類型,Parm2為復合類型。Method1被調(diào)用時首先為Parm1分配空間,將實際參數(shù)a的值復制給Parm1,因此Parm1與實際參數(shù)a在內(nèi)存中分別占據(jù)不同的空間,Parm1的值改變不影響實際參數(shù)a。而實際參數(shù)b與形式參數(shù)Parm2代表同一對象,Parm2調(diào)用方法Setb并改變其變量成員的值,它實際上是改變了實參對象。
7.this
如果局部變量或形式參數(shù)與變量成員同名,在該方法體中直接使用變量名則是對局部變量或形式參數(shù)的訪問。如果需訪問變量成員,可通過關鍵字this來訪問。
【程序3.2】this使用舉例。
/**
*this關鍵字使用舉例
*/
publicclassthisDemo{
publicstaticvoidmain(String[]args){
thisDemoaObj=newthisDemo(); //創(chuàng)建thisDemo類型對象aObj
aObj.aMethod(); //調(diào)用aMethod方法
}
privateintaVar=10;
publicvoidaMethod(){
intaVar=20;
System.out.println(“變量成員aVar=”+this.aVar);
System.out.println(“局部變量aVar=”+aVar);
}
}
aMethod方法中this.aVar訪問當前對象的成員aVar,第二個輸出語句中的aVar代表方法內(nèi)部定義的局部變量aVar。Java語言中類方法成員調(diào)用時必須通過該類對象調(diào)用,例如上面程序中main方法中的語句:
aObj.aMethod();這里aObj為thisDemo類對象,aMethod為thisDemo類的方法成員,在方法成員中this指代當前對象。在aMethod方法執(zhí)行時,this指代對象aObj。再看下面的例子:
classA{
inta=3;
publicvoidPrint(){System.out.println(“a=”+a);}
}
Print方法中的a相當于this.a,該方法中無同名的局部變量,不會引起混淆,所以無需使用this。3.3.2類的構造方法與對象的初始化
類定義了一類對象的特性,每一個對象都是相應類的實例。定義一個類后,就可以定義對象,然后訪問對象的方法和變量成員了。
1.new運算符
Java語言使用new運算符創(chuàng)建對象。例如,程序3.2中main方法定義對象的語句:
thisDemoaObj=newthisDemo();
創(chuàng)建了一個thisDemo類的對象,也可以寫為下面的形式:
thisDemoaObj;
aObj=newthisDemo();首先聲明一個thisDemo類對象的引用aObj,與定義簡單數(shù)據(jù)類型變量不同,這里尚未創(chuàng)建thisDemo類的對象。變量名aObj只是程序中訪問對象的一條途徑,并不是對象本身,執(zhí)行下面的語句后bObj與aObj表示同一個對象:
thisDemobObj=aObj;
Java語言將這類數(shù)據(jù)類型稱為引用類型,與C/C++中的指針相似。引用類型變量在未賦值前,其值為null。
定義對象引用后,用new運算符完成對象的創(chuàng)建工作,分配存儲空間,并對其初始化。new運算符創(chuàng)建對象的一般方法為變量名=new類名([構造方法實參表]);
類名可以是系統(tǒng)預定義的類,也可以是自定義的類。括號中是傳遞給構造方法的實參,用于初始化該對象。
2.構造方法
構造方法是一種特殊的方法,創(chuàng)建對象時被自動調(diào)用。構造方法的主要作用是初始化對象,如初始化對象的變量成員。程序3.2中使用賦初值的方法給變量成員aVar賦初值為10,任意一個thisDemo類的對象在創(chuàng)建時其變量成員aVar的值均為10。有時我們并不希望這樣,例如首先定義一個學生類,然后再創(chuàng)建不同的學生對象,每個學生對象的學號等信息是不同的,應在對象創(chuàng)建時即能賦予不同的值,此時采用變量賦初值的方法是無法實現(xiàn)的,需要采用構造方法來實現(xiàn)。
與一般的方法不同,構造方法的方法名與類名相同,構造方法沒有返回值類型的說明,方法體中也不可以用return語句帶回返回值。與其他方法相同,構造方法可以有參數(shù)。如果一個類定義了構造方法,則在創(chuàng)建該類對象時必須按照構造方法的要求給出實參。
【程序3.3】
構造方法舉例。
/**
*構造方法舉例
*/
publicclassStudent{
publicStudent(StringstuNo,StringstuName){Number=stuNo;
Name=stuName;
}
publicstaticvoidmain(String[]args){
Studentstu1=newStudent("05060001","zhang"); //傳遞參數(shù)給構造方法
Studentstu2=newStudent("05060002","li");
stu1.printInfo();
stu2.printInfo();
}privateStringNumber;
privateStringName;
publicvoidprintInfo(){
System.out.println("Number:\t"+Number);
System.out.println("Name:\t"+Name);
}
}
Java語言允許一個類有多個構造方法,只要這些方法的參數(shù)形式不同即可。例如:
classPoint{
intx,y;
Point(intx,inty){this.x=x;this.y=y;}
Point(){x=y=0;}
}
下面的語句在創(chuàng)建Point類對象時分別調(diào)用不同的構造方法:
Pointp1=newPoint(10,10);
Pointp2=newPoint();在構造方法中可以通過this關鍵字調(diào)用該類中其他的構造方法。例如,上面的例子可改寫為下面的形式:
classPoint{
intx,y;
Point(intx,inty){this.x=x;this.y=y;}
Point(){this(0,0);}
}
第二個構造方法調(diào)用第一個構造方法,this后面括號中的內(nèi)容為傳遞給第一個構造方法的實參。如果該構造方法中還有其他的語句,應保證將this語句放在最前面。
3.對象初始化
Java語言提供了三種初始化對象的方法,除了上面介紹的構造方法外,還可以采用下面兩種方法:
(1)定義變量成員時賦初值;
(2)使用類體中的初始化程序塊。
下面的例子中使用了Java語言提供的三種初始化對象的方法。
【程序3.4】
對象的初始化。
//對象的初始化方法演示
classA{
intx=5;{ //初始化程序塊開始
System.out.println(x);
x=6;
System.out.println(x);
} //初始化程序塊結束
A(inti)
{
x=i;
System.out.println(x);
}
}publicclassInitDemo
{
publicstaticvoidmain(String[]args)
{
Aa=newA(7);
}
}
程序運行結果為
5
6
7從程序3.4的運行結果可以看出,Java程序在創(chuàng)建對象時首先分配空間,然后按照類中定義的成員變量的初值初始化相應的成員,接著執(zhí)行類中的初始化程序塊,最后執(zhí)行構造方法。
4.對象的清除
在一些程序設計語言如C++?語言中,動態(tài)創(chuàng)建的對象必須由程序顯式清除,釋放其所占用的內(nèi)存空間;而Java語言中對象的清除是自動進行的,當系統(tǒng)內(nèi)存用盡或用戶在程序中調(diào)用System.gc方法時,Java運行系統(tǒng)啟動垃圾收集器,自動回收不再使用的對象。垃圾收集器可以自動判斷哪些對象不再使用,例如程序片斷:
Aa=newA();
a=newA();
在內(nèi)存中創(chuàng)建了兩個A類對象,執(zhí)行該程序片斷之后,第一次創(chuàng)建的A類對象在程序中無法再訪問,Java垃圾收集器將自動收回該對象占用的空間。對不再使用的對象,程序中可以將對該對象的引用賦值為null,以釋放資源。
Java語言允許用戶為每個類定義一個特殊的方法finalize(),當垃圾收集器清除該類對象時將調(diào)用該方法,如果用戶有一些特殊的清除工作可安排在finalize()方法中。但是用戶無法預測finalize()方法被調(diào)用的時間,即使調(diào)用System.gc強制啟動垃圾收集器,也可能因為其他任務的優(yōu)先級高于垃圾收集任務,而不能立即啟動。因此,一些與時間相關的代碼不應加入到finalize()方法中。3.3.3對象的使用
在對象創(chuàng)建之后,就可以使用該對象了,可以訪問對象的變量成員和方法成員。訪問成員的基本語法形式如下:
對象名.變量成員名
對象名.方法成員名([實際參數(shù)表])
前面的例子中已出現(xiàn)多個方法調(diào)用和訪問變量成員的語句,這里不再舉例。
Java語言引入了一個與對象有關的運算符instanceof,用來測試一個指定的對象是否是指定類的實例,若是,返回true,否則返回false。例如:
if(obj1instanceofClass1){
}
其中,obj1為對象名,Class1為類名。若obj1為Class1類的對象,則執(zhí)行大括號中的語句。
3.3.4案例的初步實現(xiàn)
利用上面介紹的語法知識,可以給出員工工資計算程序的一個簡化實現(xiàn),下面的程序僅輸入和處理一個員工工資的信息。3.3.4案例的初步實現(xiàn)
利用上面介紹的語法知識,可以給出員工工資計算程序的一個簡化實現(xiàn),下面的程序僅輸入和處理一個員工工資的信息。
【程序3.5】
員工工資計算程序的初步實現(xiàn)。
//SalaryEmployee.java
publicclassSalaryEmployee{
privateStringname;
privatedoublesalary;publicSalaryEmployee(Stringname,doublesalary){
this.setName(name);
this.setSalary(salary);
}
publicvoidPrintSalary(){
System.out.println(getName()+"\t"+CalculateSalary());
}
publicdoubleCalculateSalary(){
returngetSalary();
}publicStringgetName(){
returnname;
}
publicvoidsetName(Stringname){
=name;
}
publicdoublegetSalary(){
returnsalary;
}
publicvoidsetSalary(doublesalary){this.salary=salary;
}
}
//HourlyEmployee.java
publicclassHourlyEmployee{
publicHourlyEmployee(Stringname,doublehours,doublewage){
this.setName(name);
this.setHours(hours);
this.setWage(wage);}
privateStringname;
privatedoublehours; //小時數(shù)
privatedoublewage; //每小時工資額
publicvoidPrintSalary(){
System.out.println(getName()+"\t"+CalculateSalary());
}
publicdoubleCalculateSalary(){
returngetHours()*getWage();
}publicStringgetName(){
returnname;
}
publicvoidsetName(Stringname){
=name;
}
publicdoublegetHours(){
returnhours;
}
publicvoidsetHours(doublehours){
this.hours=hours;}
publicdoublegetWage(){
returnwage;
}
publicvoidsetWage(doublewage){
this.wage=wage;
}
}
//CommisionSalary.java
publicclassCommisionEmployee{
publicCommisionEmployee(Stringname,doublerate,doublesales){
this.setName(name);
this.setRate(rate);
this.setSales(sales);
}
privateStringname;
privatedoublerate;
privatedoublesales;
publicvoidPrintSalary(){
System.out.println(getName()+"\t"+CalculateSalary());}
publicdoubleCalculateSalary(){
returngetRate()*getSales();
}
publicStringgetName(){
returnname;
}
publicvoidsetName(Stringname){
=name;
}publicdoublegetRate(){
returnrate;
}
publicvoidsetRate(doublerate){
this.rate=rate;
}
publicdoublegetSales(){
returnsales;
}publicvoidsetSales(doublesales){
this.sales=sales;
}
}
//Main.java
importjava.util.Scanner;
publicclassMain{
publicstaticvoidmain(String[]args){
//TODOcodeapplicationlogichere
Scannerinput=newScanner(System.in);
Stringname;System.out.println("請輸入員工類別:1.固定工資員工2.小時工3.傭金員工");
intchoice=input.nextInt();
input.nextLine();
switch(choice)
{
case1:
System.out.println("輸入員工的姓名,工資額:");
name=input.nextLine();
doublesalary=input.nextDouble();SalaryEmployeeaSEmployee=newSalaryEmployee(name,salary);
aSEmployee.PrintSalary();
break;
case2:
System.out.println("輸入員工的姓名,小時數(shù)和每小時工資額:");
name=input.nextLine();
doublehours=input.nextDouble();
doublewage=input.nextDouble();HourlyEmployeeaHEmployee=newHourlyEmployee(name,hours,wage);
aHEmployee.PrintSalary();
break;
case3:
System.out.println("輸入員工的姓名,傭金比例和銷售額:");
name=input.nextLine();
doublerate=input.nextDouble();
doublesales=input.nextDouble();CommisionEmployeeaCEmployee=newCommisionEmployee(name,rate,sales);
aCEmployee.PrintSalary();
break;
default:
System.out.println("輸入錯誤");
break;
}
}
} 3.4封裝性
3.4.1成員的訪問權限
在訪問對象的方法或成員時,存在一個訪問權限的問題,即對象之間和對象內(nèi)部哪些成員是可以訪問的,哪些是不可以訪問的。我們在看電視時不可能直接操作電視機內(nèi)部的電子元件來改變頻道和音量,而是通過遙控器或電視機面板上的按鈕來要求電視機改變頻道和音量。3.3節(jié)中電視類(TV)用channel和volume兩個變量來描述當前的頻道和音量,不希望在TV類的外部直接改變channel和volume變量的值,而是通過調(diào)用TV類的某些方法來改變這兩個變量的值,這樣可以避免channel和volume被改變?yōu)殄e誤的值。前面例子中通過incChannel和decChannel方法來改變channel的值,可以保證channel的值在0~99范圍之內(nèi)。
Java語言中,限定成員的訪問權限是通過定義變量和方法時加上訪問權限修飾符來實現(xiàn)的。表3.1是Java語言中類的成員的訪問范圍,其中關鍵字friendly并不出現(xiàn)在程序中,當一個變量或方法定義沒有使用訪問權限修飾符時,其訪問權限即為friendly。表3.1類的成員的訪問范圍表3.1中,√表示允許訪問,√*?表示訪問時有一定的限制條件。包及子類的訪問權限本書將在3.4.2節(jié)和3.5節(jié)詳細介紹,這里首先介紹同一包中沒有繼承關系的類之間成員訪問的權限。
同一包中沒有繼承關系的類之間成員訪問的權限比較簡單,類的方法成員可以訪問所在類的其他方法成員和變量成員,而類的外部不可以直接訪問private成員。下面修改一下前面的電視類,將channel和volume變量限定為只能在類內(nèi)訪問,代碼見程序3.6?!境绦?.6】成員訪問權限。
//成員訪問權限TV.java
publicclassTV{
privateintchannel=1;
privateintvolume=5;
publicvoidincChannel(){
setChannel((getChannel()+1)%100);
}publicvoiddecChannel(){
setChannel(getChannel()-1);
if(getChannel()<0)
setChannel(99);
}
publicvoidshowChannel(){
System.out.println("Channel"+getChannel());
}
publicintgetChannel(){
returnchannel;
}publicvoidsetChannel(intchannel){
this.channel=channel;
}
publicintgetVolume(){
returnvolume;
}
publicvoidsetVolume(intvolume){
if(volume>=0&&volume<=20)
this.volume=volume;
}
}
//主程序Main.java
publicclassMain{
publicstaticvoidmain(String[]args){
TVaTV=newTV();
aTV.showChannel();
aTV.incChannel();
}
}如果不將channel定義為private變量,則在main方法中下面的語句是合法的:
aTV.channel=100;
這樣的語句將導致aTV的channel變量值不合法。程序3.6中將channel定義為private變量,則上面這條語句在編譯階段即被視為是錯誤的。3.4.2包的使用
Java程序編譯后,每一個類和接口都生成一個獨立的class文件。對于一個大型程序,由于類和接口的數(shù)量很大,如果將它們?nèi)旁谝黄?,則往往顯得雜亂無章,難于管理。Java語言提供了一種解決該問題的方法:將類和接口放在不同的包中。
1.包的概念
一個包由一組類和接口組成,包內(nèi)還可以有子包,類似于文件系統(tǒng)的目錄(文件夾)。實際上,Java系統(tǒng)就是使用文件夾來存放包的,一個包對應一個文件夾,文件夾下有若干個class文件和子文件夾。當要使用其他包內(nèi)的類時,可以使用import語句引入其他包內(nèi)的類。本書前面例子中多次出現(xiàn)了import語句,例如:
importjava.math.*;
這里,java為包名,math為java包內(nèi)的子包,*?表示該包中的所有類,該語句引入了java.math包中的所有類。java包中的子包lang是自動引入的,無需使用import語句引入該包,前面提到的String、Sytem等類均在該包中定義。
java包是Sun公司使用Java語言開發(fā)的類的集合,是隨Java運行系統(tǒng)提供的,Java語言的很多功能依賴于該包中的類。目前的Java虛擬機通常將java包以一種壓縮文件的形式存儲在特定的目錄中,運行類庫包的根目錄可以由環(huán)境變量CLASSPATH設定。
import也可以引入特定的類,只要用類名取代上面例子里的*即可。例如:
importjava.awt.Graphics;
包的使用可以避免名字沖突,每一個類和接口的名字都包含在某個包中,不同的包中可以有同名的類和接口。在程序中使用同名類時,可以加上包名以免引起歧義,例如java.awt.Button表示java.awt包中的Button類,此時無需使用import語句引入該類。
另外還應注意,使用import語句引入某個包中的所有類時并不包括該包的子包中的類。
2.包的封裝作用
Java語言的包還具有一定的封裝作用,可以限定某些類只能被其所在包中的類訪問,而不能被包外的其他類訪問。正如電視機設計過程中需要用很多集成電路,這些集成電路本身是封裝好的模塊,外部只能通過其外部的引腳訪問,而不能訪問其內(nèi)部,這些集成電路對最終用戶來講也應該是不可見的,它們只能被設計維修人員訪問。
Java語言規(guī)定只能使用其他包中公有(public)的類和接口,即在該類定義時,使用了public修飾符,例如程序3.6的TV類。從表3.1中可以看出:使用其他包中的類時,如果類之間沒有繼承關系,只能訪問該類的public成員;同一個包中的類除了private成員外,其他的成員均可以訪問。
3.包的定義
如果希望將程序中不同的類放在多個不同的包中,可以首先在程序的當前目錄中創(chuàng)建相應的子目錄,然后將相應的源文件放入對應的文件夾,分別編譯,同時應在源文件前面加上相應的包定義語句。
包定義語句的格式為
package包名;
注意:該語句必須放在程序源文件的開始,前面不能有注釋語句之外的任何語句。程序3.7是一個使用包的完整的例子,該程序由兩個文件構成,放在不同的包中。
【程序3.7】
包的定義與使用。
//PackageApplet.java
importjava.awt.*;
importjava.applet.*;
importMyPackage.display.displayclass; //引入displayclass類publicclassPackageAppletextendsApplet
{
publicvoidpaint(Graphicsg)
{
Fontfont=newFont("TimesRoman",Font.PLAIN,24);
g.setFont(font);
displayclassmyclass=newdisplayclass();
Strings=myclass.GetDisplayText();
g.drawString(s,60,80);
}}
//MyPackage\display\displayclass.java
packageMyPackage.display; //包定義語句
publicclassdisplayclass
{
publicStringGetDisplayText(){
return"DisplayText";
}
} 3.5繼承性
3.1.3節(jié)介紹了面向?qū)ο蠓椒ǖ闹匾匦浴^承性,利用繼承性可以提高代碼的可重用性,提高軟件開發(fā)的生產(chǎn)率。例如員工工資計算程序中無論是固定工資員工還是鐘點工和傭金員工,都需要輸出其工資信息,利用繼承性只需在超類中定義如何輸出工資信息即可,而無需針對每種類型的員工分別實現(xiàn)該功能。
3.5.1子類的定義
Java語言支持繼承機制,在定義類時通過關鍵字extends指定超類的名稱即可。子類定義的基本語法形式為
class子類名extends超類名{
}
Java語言不支持多繼承,因此超類名只有一個。子類繼承超類的特性,也可以通過定義新的成員修改超類的特性或增加新的特性。例如:
classPoint{
privateintx=0,y=0;
publicintgetX(){returnx;}
publicvoidsetX(intx){this.x=x;} publicintgetY(){returny;}
publicvoidsetY(inty){this.y=y;}
}
classColorPointextendsPoint{ //從Point派生出ColorPoint
privateintcolor=0; //增加新的變量
publicintgetColor(){returncolor;}
publicvoidsetColor(intcolor){this.color=color;}
}
Java語言在為子類對象分配空間時,不僅為子類中新定義的成員分配空間,同時也為超類中定義的成員(包括public、protected、private以及friendly成員)分配空間,在一定程度上可以認為一個子類對象內(nèi)部包含一個超類對象。
Java語言允許將一個子類對象作為超類對象使用,當需要時可以進行隱含的類型轉(zhuǎn)換。例如一個方法的形式參數(shù)定義為超類對象,在調(diào)用時可以將子類對象作為實際參數(shù)。
publicclassMain{
staticvoidDisplayPoint(PointaPoint){ //形式參數(shù)為Point類型 System.out.println("x="+aPoint.getX()+"\ty="+aPoint.getY());
}
publicstaticvoidmain(Stringarg[])
{
ColorPointaPoint=newColorPoint();
DisplayPoint(aPoint); //實際參數(shù)為ColorPoint類型
}
}
Java語言中的各種類型的對象都可以當作Object類的對象使用,Object類中定義了Java語言對象的基本特性,如果在定義一個類時沒有用extends關鍵字指定超類,則該類的超類為Object。
超類中定義的成員根據(jù)其權限的不同在子類中的訪問權限也不同,從表3.1中可以看出超類中定義的private成員在子類定義的方法成員中是不可訪問的,當超類、子類在同一個包中時,超類的public、protected、friendly成員可以在子類中訪問。如果從其他包內(nèi)的類派生出子類,則在子類中可以訪問超類中的protected和public成員。子類在定義新的成員時,允許新成員與超類成員同名。對于同名變量成員,超類中的相應成員被隱藏。對于同名方法成員,如果參數(shù)形式相同且返回值類型相同,則超類中的該方法成員被隱藏;如果參數(shù)形式不同,則調(diào)用時根據(jù)實參類型決定調(diào)用哪一個方法成員,與類內(nèi)方法重載相同(見3.6.1節(jié))。Java語言不允許子類與超類方法同名且參數(shù)形式相同但返回值類型不同。3.5.2super
如果程序中需要訪問被隱藏的同名超類成員,可以使用關鍵字super,super指代當前對象在超類中定義的那一部分。程序3.8和3.9分別演示了如何訪問超類中的同名變量成員和方法成員。
【程序3.8】
用super訪問超類同名變量成員。
classTest{
inti;
publicTest(){i=10;}
}
publicclassTest1extendsTest{
doublei;
publicTest1(){i=12.345678;}
publicvoidprint()
{
System.out.println("iofsubclass"+i); //訪問子類成員i
System.out.println("iofsuperclass"+super.i); //訪問超類成員i
} publicstaticvoidmain(String[]args)
{
Test1t1=newTest1();
t1.print();
}
}
【程序3.9】用super訪問超類同名方法成員。
classTest{
inti;
publicTest(){i=10;}
publicvoidprint() {
System.out.println("iofsuperclass"+i);
}
}
publicclassTest1extendsTest
{
doublei;
publicTest1(){i=12.345678;}
publicvoidprint()
{
System.out.println("iofsubclass"+i); super.print(); //訪問超類中定義的print方法
}
publicstaticvoidmain(String[]args)
{
Test1t1=newTest1();
t1.print();
}
}3.5.3子類對象的構造
子類對象在創(chuàng)建時需要調(diào)用超類的構造方法來構造超類中定義的那部分成員,如果在子類中不特別聲明,則調(diào)用超類不帶參數(shù)的構造方法。
如果超類沒有不帶參數(shù)的構造方法,必須在子類的構造方法中用super關鍵字指定如何調(diào)用超類的構造方法。先看下面的程序:
classA{
inta;
A(inta){ this.a=a;
}
}
classBextendsA{
intb;
B(intb){
this.b=b;
System.out.println("ClassB");
}
}
publicclassClass1
{
publicstaticvoidmain(String[]args)
{
Bb=newB(10);
}
}
該程序中類A構造方法帶有一個int類型的參數(shù),類B為類A的子類。使用VisualJ++編譯該程序,給出如下的錯誤信息:
Class'A'doesn'thaveaconstructorthatmatches'A()'這個錯誤信息表明,在構造類B對象時試圖調(diào)用類A不帶參數(shù)的構造方法。程序3.10演示了如何使用super關鍵字調(diào)用超類的構造方法。
【程序3.10】super關鍵字的使用。
classPoint{
privateintx=0,y=0;
publicPoint(intx,inty){setX(x);setY(y);}
publicPoint(){this(0,0);
publicintgetX(){returnx;}
publicvoidsetX(intx){this.x=x;} publicintgetY(){returny;}
publicvoidsetY(inty){this.y=y;}
}
classColorPointextendsPoint{ //從Point派生出ColorPoint
privateintcolor=0; //增加新的變量
publicColorPoint(intx,inty,intcolor)
{
super(x,y); //訪問超類構造方法,如無此句,則創(chuàng)建ColorPoint //對象時訪問超類中不帶參數(shù)的構造方法
setColor(color);
}
publicintgetColor(){returncolor;}
publicvoidsetColor(intcolor){this.color=color;}
}3.5.4final方法與final類
通過在子類中定義與超類同名的方法成員,覆蓋超類的方法成員,改變了超類原有的特征。有時可能程序員不希望子類修改超類原有的特性,這時可以將對應的方法定義為最終(final)方法,子類不再可以覆蓋該方法。例如:
classA{
finalvoidMethod1(){System.out.println(“Thisisafinalmethod”);}
}
當從類A派生子類時,子類不可以定義與Method1形式相同的方法。關鍵字final也可以用來修飾類的定義,將一個類定義為最終類,則不再可以從該類派生出子類?;菊Z法形式為
finalclass類名{
… //成員定義
}
3.5.5改進的案例
從程序3.5案例的初步實現(xiàn)可以看出,SalaryEmployee、HourlyEmployee和CommisionEmployee三個類有不同的特性,也有相同的特性,可以將這些相同的特性提取出來定義一個超類Employee。程序3.11給出了改進后的實現(xiàn)?!境绦?.11】本章案例的改進實現(xiàn)。
//Employee.java
publicclassEmployee{
publicEmployee(Stringname){
setName(name);
}
publicdoubleCalculateSalary(){
return0.0;
}
publicvoidPrintSalary(){
System.out.println(getName()+"\t"+CalculateSalary());}
publicStringgetName(){
returnname;
}
publicvoidsetName(Stringname){
=name;
}
Stringname;
}//SalaryEmployee.java
publicclassSalaryEmployeeextendsEmployee{
privatedoublesalary;
publicSalaryEmployee(Stringname,doublesalary){
super(name);
this.setSalary(salary);
}
publicdoubleCalculateSalary(){
returngetSalary();
}
publicdoublegetSalary(){returnsalary;
}
publicvoidsetSalary(doublesalary){
this.salary=salary;
}
}
//HourlyEmployee.java
publicclassHourlyEmployeeextendsEmployee{
publicHourlyEmployee(Stringname,doublehours,doublewage){
super(name);this.setHours(hours);
this.setWage(wage);
}
privatedoublehours; //小時數(shù)
privatedoublewage; //每小時工資額
publicdoubleCalculateSalary(){
returngetHours()*getWage();
}
publicdoublegetHours(){
returnhours;
}publicvoidsetHours(doublehours){
this.hours=hours;
}
publicdoublegetWage(){
returnwage;
}
publicvoidsetWage(doublewage){
this.wage=wage;
}
}
//CommisionEmployee.javapublicclassCommisionEmployeeextendsEmployee{
publicCommisionEmployee(Stringname,doublerate,doublesales){
super(name);
this.setRate(rate);
this.setSales(sales);
}
privatedoublerate;
privatedoublesales;
publicdoubleCalculateSalary(){returngetRate()*getSales();
}
publicdoublegetRate(){
returnrate;
}
publicvoidsetRate(doublerate){
this.rate=rate;
}
publicdoublegetSales(){
returnsales;}
publicvoidsetSales(doublesales){
this.sales=sales;
}
}
//Main.java
importjava.util.Scanner;
publicclassMain{
publicstaticvoidmain(String[]args){
//TODOcodeapplicationlogichereScannerinput=newScanner(System.in);
Stringname;
EmployeeaEmployee;
System.out.println("請輸入員工類別:1.固定工資員工2.小時工3.傭金員工");
intchoice=input.nextInt();
input.nextLine();
switch(choice)
{case1:
System.out.println("輸入員工的姓名,工資額:");
name=input.nextLine();
doublesalary=input.nextDouble();
aEmpl
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025版實習生實習期間實習單位培訓責任協(xié)議3篇
- 寫字樓電梯管理協(xié)議
- 2025個人貨車租賃合同書
- 建筑工程:車庫雨棚施工合同范本
- 家政服務伸縮縫安裝施工協(xié)議
- 2025版勞動合同補充協(xié)議范本匯編3篇
- 2024年教育培訓機構廣告合作合同范本3篇
- 自建房屋建筑設備租賃合同
- 證券投資聯(lián)合體投標協(xié)議模板
- 2025年度爬架租賃及拆除服務合同3篇
- 學生宿舍消防安全制度模版(3篇)
- 四川省成都市2023-2024學年高二上學期期末調(diào)研考試語文試題(解析版)
- 《兩用物項證》課件
- 《電梯維保規(guī)則》課件
- DB54T 0425.1-2024 公共數(shù)據(jù) 數(shù)據(jù)元規(guī)范 第一部分:總則
- 江蘇省泰州市2023-2024學年高一上學期期末語文試題及答案
- 2024年高考政治選必二《法律與生活》重要知識問題梳理總結
- 孕早期nt檢查課件
- 【MOOC】工程制圖解讀-西安交通大學 中國大學慕課MOOC答案
- 期末復習(試題)-2024-2025學年三年級上冊數(shù)學蘇教版
- 檢驗科新進人員崗前培訓
評論
0/150
提交評論