《Java語(yǔ)言程序設(shè)計(jì)》課件第4章_第1頁(yè)
《Java語(yǔ)言程序設(shè)計(jì)》課件第4章_第2頁(yè)
《Java語(yǔ)言程序設(shè)計(jì)》課件第4章_第3頁(yè)
《Java語(yǔ)言程序設(shè)計(jì)》課件第4章_第4頁(yè)
《Java語(yǔ)言程序設(shè)計(jì)》課件第4章_第5頁(yè)
已閱讀5頁(yè),還剩227頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡(jiǎn)介

4.1繼承

4.2多態(tài)

4.3最終類與抽象類

4.4接口4.5程序包

4.6內(nèi)部類

4.7小結(jié)

4.8習(xí)題

4.1.1引例——“學(xué)生與學(xué)位”

假定根據(jù)學(xué)生的三門(mén)學(xué)位課程的分?jǐn)?shù)決定其是否可以拿到學(xué)位,對(duì)于本科生,如果三門(mén)課程的平均分超過(guò)60分即表示通過(guò),而對(duì)于研究生,則需要平均分超過(guò)80分才能通過(guò)。編一程序判定學(xué)生能否拿到學(xué)位。4.1繼承

1.問(wèn)題剖析

按第3章所學(xué)的內(nèi)容單獨(dú)考慮本科生和研究生就必須將各自的所有特征都列出來(lái),這樣就有相當(dāng)多的內(nèi)容是互相重復(fù)的。如果考慮本科生和研究生都是學(xué)生,只要說(shuō)明本科生和研究生的不同特征就可以避免相互重復(fù)。一方面,可以認(rèn)為本科是學(xué)生的一個(gè)特例,研究生也是學(xué)生的一個(gè)特例,他們除了學(xué)生的特征(如學(xué)號(hào)、姓名等)之外,各自還有相互區(qū)別的特征。另一方面,學(xué)生是本科生、研究生共同特征的一種抽象。因此,我們可以設(shè)計(jì)一個(gè)學(xué)生類,將本科生與研究生的共同特征描述出來(lái),再定義本科生類,應(yīng)用一種機(jī)制將學(xué)生類中描述的特征全部包含起來(lái),只聲明本科生與研究生的不同特征(60分通過(guò)而不是80分通過(guò));同理,再定義一個(gè)研究生類,將上述學(xué)生類中的數(shù)據(jù)成員與方法成員全部包含過(guò)來(lái),只聲明研究生與本科生的不同特征(80分通過(guò)而不是60分通過(guò))。

2.繼承機(jī)制

1)繼承

面向?qū)ο蟪绦蛟O(shè)計(jì)方法中利用現(xiàn)有的類來(lái)定義新的類的過(guò)程,稱為繼承(inheritance)。繼承是面向?qū)ο蟪绦蛟O(shè)計(jì)的一大特色。

面向?qū)ο蟪绦蛟O(shè)計(jì)的一個(gè)原則是,不必每次都從頭開(kāi)始定義一個(gè)新的類,而是將這個(gè)新的類作為一個(gè)或若干個(gè)現(xiàn)有類的擴(kuò)充或特殊化。如果不使用繼承,每個(gè)類都必須顯式地定義它所有的特征。然而使用繼承后,定義一個(gè)新的類時(shí)只需定義其與其他類不同的特征,那些通用特征則可以從其他類繼承下來(lái),而不必逐一顯式地定義。類B繼承類A,即在類B中除了自己定義的成員之外,還自動(dòng)包括了類A中定義的數(shù)據(jù)成員與成員方法,這些自動(dòng)繼承下來(lái)的成員稱為類B的繼承成員,類A稱為類B的父類(parent)、超類(super-class)或基類(base),而類B則稱為類A的子類(child)或派生類(derived-class)。一個(gè)類的祖先類(ancestor)包括了其父類以及父類的祖先類,一個(gè)類的后代類(descendant)包括了其子類以及子類的后代類。類B繼承類A也可以說(shuō)成是類A派生出類B。

2)繼承的語(yǔ)法

在Java語(yǔ)言中,一個(gè)名為Class1的類繼承另一個(gè)已有類Class2的語(yǔ)法形式為

publicclassClass1extendsClass2{

成員列表;

}

其中,類Class2稱為類Class1的基類,新定義的類Class1稱為類Class2的派生類,extends是Java語(yǔ)言的保留字,指明類之間的繼承關(guān)系?;ɡㄌ?hào)中定義的是Class1中新增加的數(shù)據(jù)成員與方法成員。第3章已經(jīng)討論過(guò)由保留字public與private定義的類成員訪問(wèn)控制方式,在Java語(yǔ)言中還有一種類成員訪問(wèn)控制方式,即用保留字protected定義,在protected后定義的數(shù)據(jù)成員或方法成員稱為受保護(hù)成員。受保護(hù)成員具有公有成員與私有成員的雙重角色:它可以被后代類的方法成員訪問(wèn),但不可以被其他不相關(guān)的類的方法成員所訪問(wèn)。

3.問(wèn)題的解決

下面用Java語(yǔ)言編寫(xiě)程序,解決“學(xué)生與學(xué)位”問(wèn)題,我們可以先定義一個(gè)基類——學(xué)生類。

【例4-1】Student.java程序。

//學(xué)生類

publicclassStudent

{

publicfinalstaticintNUM_OF_SCORE=3;

//每個(gè)學(xué)生的分?jǐn)?shù)個(gè)數(shù)

privateStringname; //姓名,私有成員

protectedint[]scores;

//分?jǐn)?shù),受保護(hù)成員,能被后代類的方法成員訪問(wèn)publicStudent()

{

name="";

scores=newint[NUM_OF_SCORE];

}

publicvoidsetName(StringnewName)

{

name=newName;

}publicStringgetName()

{

returnname;

}

publicvoidsetScore(intscoreNum,intscore)

{

//檢查參數(shù)的正確性

if(scoreNum<0||scoreNum>=NUM_OF_SCORE)return;

if(score<0||score>100)return;

scores[scoreNum]=score;

}publicintgetScore(intscoreNum)

{

if(scoreNum<0||scoreNum>=NUM_OF_SCORE)return-1;

returnscores[scoreNum];

}

//計(jì)算學(xué)生平均成績(jī)publicintgetAverageScore()

{

inttotal=0;

for(inti=0;i<NUM_OF_SCORE;i++)

total=total+scores[i];

return(int)(total/NUM_OF_SCORE);

}

}例4-2和例4-3根據(jù)本科生與研究生的不同分別繼承并擴(kuò)展上述基類,以計(jì)算是否通過(guò)學(xué)位考試。

【例4-2】Undergraduate.java程序。

//本科生類Undergraduate,繼承學(xué)生類

publicclassUndergraduateextendsStudent

{

publicUndergraduate()

{}publicStringcomputeGrade()

{

inttotal=0;

for(inti=0;i<NUM_OF_SCORE;i++)

total=total+scores[i];

if(total/NUM_OF_SCORE>60)return"PASS";

elsereturn"NO_PASS";

}

}【例4-3】Graduate.java程序。

//研究生類,繼承學(xué)生類

publicclassGraduateextendsStudent

{

publicStringcomputeGrade()

{

inttotal=0;

for(inti=0;i<NUM_OF_SCORE;i++)

total=total+scores[i];if(total/NUM_OF_SCORE>80)

return“PASS”;

else

return“NO_PASS”;

}

}

上述兩個(gè)程序的成員方法computeGrade()都訪問(wèn)了超類的受保護(hù)成員scores,這是合法的訪問(wèn),因?yàn)樽宇惖某蓡T方法可以訪問(wèn)超類的受保護(hù)成員?!纠?-4】StudentDemo.java程序。

publicclassStudentDemo

{

publicstaticvoidmain(String[]args)

{

Graduatestudent1=newGraduate();

student1.setName("郭敬");

student1.setScore(0,78);

student1.setScore(1,92);

student1.setScore(2,72);System.out.println("Thegradeofgraduatestudent"+student1.getName()+

"is"+puteGrade());

Undergraduatestudent2=newUndergraduate();

student2.setName("黃蓉");

student2.setScore(0,80);

student2.setScore(1,78);

student2.setScore(2,75);

System.out.println("Thegradeofgraduatestudent"+student2.getName()+

"is"+puteGrade());

}

}例4-4程序分別通過(guò)對(duì)象引用student1和student2訪問(wèn)了超類的公有成員方法setName()和setScore(),這是合法的訪問(wèn),因?yàn)樽宇惱^承了超類的成員,而且超類的公有成員被子類繼承后仍然是公有成員。

從上面程序可以看出,Java語(yǔ)言繼承機(jī)制的語(yǔ)法十分簡(jiǎn)單,只要在聲明一個(gè)類時(shí)使用保留字exdents指名其超類即可,上面三個(gè)類之間的繼承關(guān)系如圖4-1所示。圖4-1三個(gè)類之間的繼承關(guān)系4.1.2繼承與成員訪問(wèn)控制

繼承機(jī)制引入了受保護(hù)成員,提供了一種新的成員訪問(wèn)控制級(jí)別,因此需要進(jìn)一步了解Java語(yǔ)言的類成員訪問(wèn)控制規(guī)則。通過(guò)第3章的學(xué)習(xí)我們知道,一個(gè)類自身的方法成員可以訪問(wèn)它所有成員,而其他類的方法成員則只能訪問(wèn)這個(gè)類的公有成員,不能訪問(wèn)這個(gè)類的私有和受保護(hù)成員,這是Java語(yǔ)言最基本的成員訪問(wèn)控制規(guī)則。在引入繼承機(jī)制后,子類繼承了超類除構(gòu)造方法以外的所有成員,這些成員稱為子類的繼承成員。注意,繼承成員不僅包括在超類中定義的公有、受保護(hù)及私有成員,還包括超類的繼承成員,即超類繼承它的祖先類得到的成員。在子類的內(nèi)部,不僅能夠訪問(wèn)子類自己定義的所有成員,也能夠訪問(wèn)超類中公有和受保護(hù)的成員,但不能訪問(wèn)超類中私有的成員。例如,在例4-2中子類Undergraduate繼承了超類Student,它從超類Student得到的繼承成員包括超類的私有成員name,受保護(hù)成員scores,公有成員NUM_OF_SCORE、setName()、getName()、setScore()和getScore(),而方法computeGrade()則是它自己定義的成員。在子類Undergraduate的內(nèi)部既能訪問(wèn)它自己定義的成員computeGrade(),也能訪問(wèn)繼承成員scores、NUM_OF_SCORE、setName()、getName()、setScore()和getScore(),但不能直接訪問(wèn)繼承成員name。

Java的類繼承層次是一種單根層次結(jié)構(gòu),Java程序中的每個(gè)類都是類Object的后代類。如果一個(gè)類沒(méi)有指名其超類,則它的超類暗指類Object。因此例4-1的類Student是類Object的子類,它繼承了類Object的成員,這樣類Undergraduate是類Student的繼承成員,也是類Object的繼承成員。子類的某些繼承成員也能被使用子類的程序訪問(wèn),而哪些成員能被訪問(wèn)則由繼承成員在子類中的訪問(wèn)控制決定。繼承成員在子類的訪問(wèn)控制與它們?cè)诔愔械脑L問(wèn)控制相同,即原先在超類中是公有的成員,被子類繼承后仍然是子類的公有(繼承)成員,原先在超類中是受保護(hù)的成員,被子類繼承后仍然是子類的受保護(hù)(繼承)成員,原先在超類中是私有成員,被子類繼承后仍然是子類的私有(繼承)成員(注意私有繼承成員即使是子類內(nèi)部也不能被直接訪問(wèn))。因此,使用子類的程序能夠訪問(wèn)子類的公有(繼承)成員,但不能訪問(wèn)子類的受保護(hù)(繼承)成員,更不能訪問(wèn)子類的私有(繼承)成員。例4-4的類StudentDemo就是使用子類Undergraduate的程序,它能通過(guò)子類Undergraduate的對(duì)象引用student2訪問(wèn)它的公有繼承成員NUM_OF_SCORE、setName()、getName()、setScore()和getScore(),但不能直接訪問(wèn)受保護(hù)的繼承成員scores,更不能直接訪問(wèn)私有繼承成員name。

總之,子類可繼承超類的所有成員,這些成員是子類的繼承成員,在子類內(nèi)部能訪問(wèn)公有和受保護(hù)的繼承成員,但不能直接訪問(wèn)私有的繼承成員。使用子類的程序能訪問(wèn)子類的公有繼承成員,但不能直接訪問(wèn)子類的受保護(hù)和私有的繼承成員。子類自己定義的成員的訪問(wèn)控制沒(méi)有改變,即子類內(nèi)部能訪問(wèn)它自己定義的所有成員,使用子類的程序只能訪問(wèn)子類自己定義的公有成員,而不能訪問(wèn)子類自己定義的受保護(hù)的成員和私有成員。4.2.1程序的多態(tài)性

早在介紹Java語(yǔ)言的基本數(shù)據(jù)類型與運(yùn)算符時(shí),我們已經(jīng)知道二元運(yùn)算符“/”對(duì)于不同類型的數(shù)據(jù)具有不同含義的運(yùn)算:如果兩個(gè)操作數(shù)都是整數(shù)類型,那么“/”進(jìn)行整除運(yùn)算;如果其中一個(gè)操作數(shù)是浮點(diǎn)或雙精度類型,那么“/”進(jìn)行除法運(yùn)算。有些程序設(shè)計(jì)語(yǔ)言(如Pascal)使用不同的運(yùn)算符表示整除與除法,如用div表示整除、用“/”表示除法,從而將這兩者明顯區(qū)別開(kāi)來(lái)。但Java語(yǔ)言采用同一符號(hào)“/”表示兩種不同含義的運(yùn)算。在程序中同一符號(hào)或名字在不同情況下具有不同解釋的現(xiàn)象稱為多態(tài)性(polymorphism)。4.2多態(tài)在面向?qū)ο蟪绦蛟O(shè)計(jì)語(yǔ)言中,由程序員設(shè)計(jì)的多態(tài)性有兩種最基本的形式:編譯時(shí)多態(tài)性和運(yùn)行時(shí)多態(tài)性。許多程序設(shè)計(jì)語(yǔ)言都或多或少地支持編譯時(shí)多態(tài)性,但運(yùn)行時(shí)多態(tài)性是面向?qū)ο蟪绦蛟O(shè)計(jì)語(yǔ)言的一大特點(diǎn)。

編譯時(shí)多態(tài)性是指在程序編譯階段即可確定下來(lái)的多態(tài)性,主要通過(guò)使用重載機(jī)制獲得。Java語(yǔ)言允許重載方法,即允許程序員用相同的名字表示兩個(gè)或更多的方法,使得語(yǔ)義非常相似的方法可以用同樣的標(biāo)識(shí)符命名。運(yùn)行時(shí)多態(tài)性是指必須等到程序運(yùn)行時(shí)才可確定的多態(tài)性,主要通過(guò)繼承結(jié)合動(dòng)態(tài)綁定獲得。要產(chǎn)生運(yùn)行時(shí)多態(tài)性必須先設(shè)計(jì)一個(gè)類層次,子類重定義超類的某些方法,然后使用超類類型的對(duì)象引用調(diào)用這些方法,則JVM會(huì)根據(jù)這些對(duì)象引用的動(dòng)態(tài)類型選擇所調(diào)用方法的版本,即如果該對(duì)象引用的是超類的對(duì)象實(shí)例,則調(diào)用超類定義的方法;如果引用的是子類的對(duì)象實(shí)例,則調(diào)用子類定義的方法,這就是所謂的動(dòng)態(tài)綁定。簡(jiǎn)單地說(shuō),綁定(binding)就是將一個(gè)名字與它的特性相關(guān)聯(lián)的過(guò)程。靜態(tài)綁定就是綁定過(guò)程在編譯時(shí)進(jìn)行,而動(dòng)態(tài)綁定就是綁定過(guò)程在運(yùn)行時(shí)進(jìn)行。程序中最重要的名字是變量名和方法名,變量最重要的特性是它的類型,方法名最重要的特性是它的實(shí)現(xiàn)體,因此程序中最常見(jiàn)的綁定是確定變量的類型和確定方法調(diào)用所調(diào)用的實(shí)現(xiàn)體。在Java語(yǔ)言中,任何數(shù)據(jù)都在編譯時(shí)確定其靜態(tài)類型,基本數(shù)據(jù)類型的變量只有靜態(tài)類型,對(duì)象引用有靜態(tài)類型和動(dòng)態(tài)類型,對(duì)象引用的動(dòng)態(tài)類型在運(yùn)行時(shí)確定,而且肯定是其靜態(tài)類型的子類型。在Java語(yǔ)言中,確定一個(gè)方法調(diào)用表達(dá)式到底調(diào)用哪個(gè)實(shí)現(xiàn)體比較復(fù)雜。簡(jiǎn)單地說(shuō),互為重載的方法中哪個(gè)被調(diào)用是編譯時(shí)確定的,而被重定義的方法到底調(diào)用哪個(gè)方法則是運(yùn)行時(shí)確定的。4.2.2方法重載

在Java語(yǔ)言中,方法重載是指在一個(gè)類的方法成員(包括其繼承成員)中有多個(gè)名字相同但參數(shù)列表不同的方法。方法重載的好處是程序員可以為語(yǔ)義相似的方法起相同的名字,從而一方面凸現(xiàn)這些方法之間的關(guān)聯(lián)關(guān)系,另一方面減少使用者的記憶量。方法重載的代價(jià)是要明確一個(gè)方法調(diào)用表達(dá)式到底調(diào)用哪個(gè)方法的實(shí)現(xiàn)體時(shí)比較困難,也就是說(shuō),如果設(shè)計(jì)不當(dāng)?shù)脑?,可能使得方法調(diào)用產(chǎn)生二義性。因此,初學(xué)者要謹(jǐn)慎使用方法重載,真正語(yǔ)義相似的方法才能進(jìn)行重載,而且應(yīng)盡量避免由此帶來(lái)的方法調(diào)用方面的困擾。

1.方法重載的途徑

一個(gè)類的方法成員中如果有多個(gè)名字相同但參數(shù)列表不同的方法,就稱這些方法為互為重載的方法。下面通過(guò)例子來(lái)說(shuō)明方法重載,例4-5使用方法重載機(jī)制提供了幾個(gè)互為重載的find()方法從一個(gè)學(xué)生數(shù)組中查找滿足某種條件的學(xué)生。

【例4-5】Finder.java程序。//演示方法重載,使用幾種不同的方式從一組學(xué)生中查找滿足條件的學(xué)生

publicclassFinder

{

privatestaticfinalintNUM_OF_STUDENT=10;

privatestaticStudent[]data=newStudent[NUM_OF_STUDENT];

//使用隨機(jī)的形式初始化學(xué)生數(shù)組data,這樣可省略輸入測(cè)試數(shù)據(jù)的麻煩

publicstaticvoidinitialize()

{for(inti=0;i<NUM_OF_STUDENT;i++)

{

data[i]=newStudent();

//為方便起見(jiàn),學(xué)生的名字設(shè)置為“Student1”等形式

data[i].setName("Student"+i);

for(intj=0;j<Student.NUM_OF_SCORE;j++)

{//使用1~100的隨機(jī)數(shù)來(lái)初始化該學(xué)生的每一個(gè)分?jǐn)?shù)

intscore=1+(int)(Math.random()*100);

//為了更逼真一點(diǎn),如果得到的隨機(jī)數(shù)小于60,我們?cè)俳o他加40

if(score<60)score=score+40;

data[i].setScore(j,score);

}

}

}

//根據(jù)學(xué)生的名字查找學(xué)生,返回學(xué)生在數(shù)組中的下標(biāo),如果沒(méi)有該學(xué)生則返回-1

//在學(xué)生數(shù)組中查找的起點(diǎn)為start(包含start),設(shè)置起點(diǎn)使得使用者可查找所有滿足條件的學(xué)生

publicstaticintfind(intstart,Stringname)

{

//檢查參數(shù)的正確性

if(start<0||start>=NUM_OF_STUDENT)return-1;

for(inti=start;i<NUM_OF_STUDENT;i++)

{//注意要使用equals來(lái)比較兩個(gè)字符串是否相等

if(name.equals(data[i].getName()))returni;

}

return-1;

}

//根據(jù)學(xué)生的第scoreIndex門(mén)課程的分?jǐn)?shù)是否處在scoreMin和scoreMax(包括這兩個(gè)數(shù))之間查找學(xué)生,

//從學(xué)生數(shù)組的start下標(biāo)開(kāi)始查找,返回第一個(gè)滿足條件的學(xué)生的下標(biāo),如果沒(méi)有滿足條件的學(xué)生,

//返回-1publicstaticintfind(intstart,intscoreIndex,intscoreMin,intscoreMax)

{

//檢查參數(shù)的正確性

if(start<0||start>=NUM_OF_STUDENT)return-1;

if(scoreIndex<0||scoreIndex>=Student.NUM_OF_SCORE)return-1;

for(inti=start;i<NUM_OF_STUDENT;i++)

{intscore=data[i].getScore(scoreIndex);

if(score>=scoreMin&&score<=scoreMax)returni;

}

return-1;

}

//根據(jù)學(xué)生三門(mén)課程的平均分?jǐn)?shù)是否處在scoreMin和scoreMax(包括這兩個(gè)數(shù))之間查找學(xué)生,

//從學(xué)生數(shù)組的start下標(biāo)開(kāi)始查找,返回第一個(gè)滿足條件的學(xué)生的下標(biāo),如果沒(méi)有滿足條件的學(xué)生,

//返回-1

publicstaticintfind(intstart,intscoreMin,intscoreMax){

//檢查參數(shù)的正確性

if(start<0||start>=NUM_OF_STUDENT)return-1;

for(inti=start;i<NUM_OF_STUDENT;i++)

{

intscore=data[i].getAverageScore();

if(score>=scoreMin&&score<=scoreMax)returni;

}

return-1;

}//演示的主程序

publicstaticvoidmain(String[]args)

{

intindex=-1;

initialize(); //初始化用于測(cè)試的學(xué)生數(shù)組

//查找名字為“Student5”的學(xué)生

index=find(0,"Student3");

if(index>=0)System.out.println("Student3是數(shù)組的第"+index+"個(gè)元素!");

else

System.out.println("Student3不在學(xué)生數(shù)組中!");

//查找第一門(mén)課程的成績(jī)分?jǐn)?shù)在60~80之間的所有學(xué)生

intcount=1;

index=-1;

while(index<NUM_OF_STUDENT)

{

index=find(index+1,1,60,80);

if(index>=0)

{System.out.println("第1門(mén)課程的成績(jī)分?jǐn)?shù)在60和80之間的第"+count+"個(gè)學(xué)生是:

"+data[index].getName()+",分?jǐn)?shù)是"+data[index].getScore(1));

}elsebreak; //終止查找過(guò)程

count=count+1;

}

//查找平均分?jǐn)?shù)在60~80之間的第一個(gè)學(xué)生index=find(0,60,80);

if(index>=0)

{

System.out.println("平均分?jǐn)?shù)在60和80之間的第一個(gè)學(xué)生是:"+data[index].getName());

}

else

System.out.println("沒(méi)有學(xué)生的平均分?jǐn)?shù)在60和80之間!");

}

}注意例4-5中查找所有第一門(mén)課程的分?jǐn)?shù)在60~80之間的學(xué)生的方法,讀者可模仿這段程序,將后面查找平均分?jǐn)?shù)在60~80之間的第一個(gè)學(xué)生改為查找所有滿足該條件的學(xué)生。另外也請(qǐng)讀者注意程序中對(duì)于學(xué)生數(shù)組的初始化,為了減輕輸入的負(fù)擔(dān),上述程序使用了隨機(jī)數(shù)進(jìn)行初始化,這是使用大批量數(shù)據(jù)進(jìn)行測(cè)試的一種常用手段。有興趣的讀者可以修改方法initialize(),讓用戶輸入有關(guān)數(shù)據(jù)來(lái)初始化學(xué)生數(shù)組。從例4-5可以看出,Java語(yǔ)言的方法重載十分簡(jiǎn)單,只要給出幾個(gè)同名但參數(shù)列表不同的方法就是方法重載。這里要強(qiáng)調(diào)的是,只有參數(shù)列表,即參數(shù)個(gè)數(shù)及參數(shù)類型是區(qū)分重載方法的因素,方法的返回類型及訪問(wèn)控制以及其他修飾(如是否是靜態(tài)成員,是否有final修飾等)等都不能區(qū)分重載方法,例如:

publicintfunction(intx){…}

publicintfunction(Stringx){…}是互為重載的兩個(gè)方法,但:

publicintfunction(intx){…}

publicStringfunction(intx){…}

就不是互為重載的兩個(gè)方法,因?yàn)樗鼈兊膮?shù)列表是完全一樣的,如果這兩個(gè)方法出現(xiàn)在同一個(gè)類中,則編譯器會(huì)報(bào)告編譯錯(cuò)誤,同樣:

publicstaticfinalintfunction(intx){…}

privatefunction(intx){…}

也不是互為重載的兩個(gè)方法。

2.構(gòu)造方法的重載法

從某種意義上說(shuō),構(gòu)造方法可能是引入方法重載機(jī)制的最主要因素,因?yàn)槌绦騿T不能為構(gòu)造方法選擇名字,它必須具有與類相同的名字,所以當(dāng)程序員想為一個(gè)類的對(duì)象提供幾種不同的初始化方式時(shí),必須重載構(gòu)造方法。

重載構(gòu)造方法的方式與重載一般方法相同,只是要遵循編寫(xiě)構(gòu)造方法的規(guī)定,即必須以類名作為訪問(wèn)名,不能有返回值等。下面為例4-5中的Student類編寫(xiě)幾個(gè)互為重載的構(gòu)造方法。publicclassStudent

{

publicStudent(Stringname)

{

=name;//使用this區(qū)分成員數(shù)據(jù)與方法的參數(shù),this的一種用法

scores=newint[NUM_OF_SCORE];

}

publicStudent()

{this(“NoName”);

//使用this調(diào)用上一個(gè)構(gòu)造方法,this的另一種用法

}

publicStudent(Stringname,int[]initScore)

{

=name;

scores=newint[NUM_OF_SCORE];

for(inti=0;i<NUM_OF_SCORE&&i<initScore.length;i++)

score[i]=initScore[i];

}

…… //其他省略

}在第三個(gè)構(gòu)造方法中,一般不直接使用語(yǔ)句“scores=initScore;”來(lái)初始化scores,這樣只是使得scores與initScore引用相同的數(shù)組元素,那么當(dāng)在其他地方對(duì)引用的數(shù)組元素進(jìn)行修改時(shí),也會(huì)影響類Student內(nèi)部的scores,從而破壞了封裝性。上述第二個(gè)構(gòu)造方法中使用語(yǔ)句“this(”NoName“);”傳入缺省的字符串“NoName”調(diào)用第一個(gè)構(gòu)造方法,這是this除用來(lái)指示當(dāng)前類的成員(如)之外的另一種典型用法。

細(xì)心的讀者可以發(fā)現(xiàn),除了在使用new創(chuàng)建對(duì)象實(shí)例時(shí)隱含調(diào)用構(gòu)造方法之外,還有兩種方式對(duì)構(gòu)造方法進(jìn)行顯式的調(diào)用:一是在子類的構(gòu)造方法中使用super調(diào)用直接基類的構(gòu)造方法;二是在某個(gè)類的構(gòu)造方法中使用this調(diào)用這個(gè)類的其他構(gòu)造方法(準(zhǔn)確地說(shuō)也可遞歸地調(diào)用這個(gè)構(gòu)造方法本身)。4.2.3數(shù)據(jù)成員的隱藏

數(shù)據(jù)成員的隱藏是指在子類中重新定義一個(gè)與父類中已定義的數(shù)據(jù)成員名完全相同的數(shù)據(jù)成員,即子類擁有了兩個(gè)相同名字的數(shù)據(jù)成員,一個(gè)是繼承父類的,另一個(gè)是自己定義的。當(dāng)子類引用這個(gè)同名的數(shù)據(jù)成員時(shí),默認(rèn)操作是它自己定義的數(shù)據(jù)成員,而把從父類那里繼承來(lái)的數(shù)據(jù)成員“隱藏”起來(lái)。當(dāng)子類要引用繼承自父類的同名數(shù)據(jù)成員時(shí),可以使用關(guān)鍵字super.數(shù)據(jù)成員名來(lái)引用。4.2.4成員方法的覆蓋

子類可以重新定義與父類同名的成員方法,實(shí)現(xiàn)對(duì)父類方法的覆蓋。方法覆蓋與數(shù)據(jù)成員隱藏的不同之處在于:子類隱藏父類的數(shù)據(jù)成員只是使之不可見(jiàn),父類同名的數(shù)據(jù)成員在子類對(duì)象中仍然占有自己的獨(dú)立的內(nèi)存空間;子類方法對(duì)父類同名方法的覆蓋將清除父類方法占用的內(nèi)存,從而使父類方法在子類對(duì)象中不復(fù)存在。

【例4-6】SubPrintme.java程序。方法的覆蓋中需要注意的是,子類在重新定義父類已有的方法時(shí),應(yīng)保持與父類完全相同的方法名、返回值類型和參數(shù)列表,否則就不是方法的覆蓋,而是子類定義自己特有的方法,與父類的方法無(wú)關(guān)。4.2.5this與super

1.this的使用場(chǎng)合

在一些容易混淆的場(chǎng)合,例如,當(dāng)成員方法的形參名與數(shù)據(jù)成員名相同,或者成員方法的局部變量名與數(shù)據(jù)成員名相同時(shí),可以在方法內(nèi)借助this來(lái)明確表示引用的是類的數(shù)據(jù)成員,而不是形參或局部變量,從而提高程序的可讀性。簡(jiǎn)單地說(shuō),this代表了當(dāng)前對(duì)象的一個(gè)引用,可將其理解為對(duì)象的另一個(gè)名字,通過(guò)這個(gè)名字可以順利地訪問(wèn)、修改對(duì)象的數(shù)據(jù)成員、調(diào)用對(duì)象的方法。歸納起來(lái),this的使用場(chǎng)合有下述三種:

(1)用來(lái)訪問(wèn)當(dāng)前對(duì)象的數(shù)據(jù)成員,其使用形式如下:

this.數(shù)據(jù)成員

(2)用來(lái)訪問(wèn)當(dāng)前對(duì)象的成員方法,其使用形式如下:

this.成員方法(參數(shù))

(3)用來(lái)調(diào)用另一個(gè)構(gòu)造方法,其使用形式如下:

this(參數(shù))

【例4-7】用this訪問(wèn)當(dāng)前對(duì)象的成員方法,計(jì)算圓的面積和周長(zhǎng)。

//演示this的使用方法

classCircle

{

doubler; //定義半徑

finaldoublePI=3 //定義圓周率

publicCircle(doubler) //類的構(gòu)造方法

{this.r=r;

//用來(lái)訪問(wèn)當(dāng)前對(duì)象的數(shù)據(jù)成員

}

//計(jì)算圓面積的方法

doublearea()

{

returnPI*r*r;

//通過(guò)構(gòu)造方法給r賦值

}

//計(jì)算圓周長(zhǎng)的方法

doubleperimeter()

{return2*(this.area()/r);

//使用this變量獲取圓的面積

}

}

//主程序

publicclassCircleDemo

{

publicstaticvoidmain(String[]args)

{

doublex;Circlecircle=newCircle(5.0);

x=circle.area();

System.out.println(“圓的面積=”+x);

x=circle.perimeter();

System.out.println(“圓的周長(zhǎng)=”+x);

}

}

程序運(yùn)行結(jié)果:

圓的面積=78.53981633974999

圓的周長(zhǎng)=31.415926535899995

2.super的使用場(chǎng)合

super表示的是當(dāng)前對(duì)象的直接父類對(duì)象。所謂直接父類,是相對(duì)于當(dāng)前對(duì)象的其他“祖先”類而言。例如,假設(shè)類A派生出子類B,類B又派生出自己的子類C,則B是C的直接父類,而A是C的祖先類。super代表的就是直接父類。若子類的數(shù)據(jù)成員或成員方法名與父類的數(shù)據(jù)成員或成員方法名相同,當(dāng)要調(diào)用父類的同名方法或使用父類的同名數(shù)據(jù)成員時(shí),則可用關(guān)鍵字super來(lái)指明父類的數(shù)據(jù)成員或方法。

super的使用方法有三種:

(1)用來(lái)訪問(wèn)直接父類隱藏的數(shù)據(jù)成員,其使用形式如下:

super.數(shù)據(jù)成員

(2)用來(lái)調(diào)用直接父類中被覆蓋的成員方法,其使用形式如下:

super.成員方法(參數(shù))

(3)用來(lái)調(diào)用直接父類中的構(gòu)造方法,其使用形式如下:

super(參數(shù))

【例4-8】訪問(wèn)直接父類隱藏的數(shù)據(jù)成員和被覆蓋的成員方法。

//演示super的用法

classSuperPrint

{

intx=4;inty=1;

publicvoidprintme()

{System.out.println("x="+x+"y="+y);

System.out.println("classname:"+this.getClass().getName());

}

}

//定義子類

publicclassSubPrintextendsSuperPrint

{

intx;

publicvoidprintme()

{intz=super.x+6; //引用父類(即SuperPrint類)的數(shù)據(jù)成員

super.printme();

//調(diào)用父類(即SuperPrint類)的成員方法

System.out.println(“Iaman”+this.getClass().getName());

x=5;

System.out.println(“z=”+z+“x=”+x);

//打印子類的數(shù)據(jù)成員

}

//主程序

publicstaticvoidmain(String[]args)

{intk;

SuperPrintp1=newSuperPrint();

SubPrintp2=newSubPrint();

p1.printme();

p2.printme();

//super.Printme();

//錯(cuò),在static方法中不能引用非static成員方法

//k=super.x+23;

//錯(cuò),在static方法中不能引用非static數(shù)據(jù)成員

}

}程序運(yùn)行結(jié)果如下:

x=4y=1

classname:SuperPrint

x=4y=1

classname:SubPrint

IamanSubPrint

z=10x=54.3.1最終類與final

繼承機(jī)制允許對(duì)現(xiàn)有的類進(jìn)行擴(kuò)充,但有些時(shí)候,需要把一個(gè)類的功能固定下來(lái),不再允許定義它的子類,Java語(yǔ)言稱這樣的類為最終類(finalclass),例如基本數(shù)據(jù)類型的包裝類(Byte、Double等)都是最終類。4.3最終類與抽象類把一個(gè)類定義為最終類,只要在聲明時(shí)用保留字final修飾即可,例如:

publicfinalclassMyFinalClass

{

...

}

如果把最終類聲明為某個(gè)類的父類,編譯程序?qū)?bào)告錯(cuò)誤。程序員也可以把一個(gè)(普通)類的某些成員方法聲明為最終方法,這使得該方法不可以被后代類重定義,例如:

publicclassMyClass

{

publicfinalvoidmyFinalMethod()

{...}

}

子類中可定義與父類中常量(即用final修飾的成員數(shù)據(jù))同名的成員數(shù)據(jù),因?yàn)閷?shí)質(zhì)上,成員數(shù)據(jù)的重定義不具有成員方法重定義的功能,它只是用來(lái)屏蔽父類的成員數(shù)據(jù)。4.3.2抽象類與abstract

最終類是類層次結(jié)構(gòu)的葉子節(jié)點(diǎn),它們不能再被繼承和擴(kuò)充。Java語(yǔ)言還有一種特殊的類,它們只能被繼承和擴(kuò)充,而不能用于創(chuàng)建自己的對(duì)象實(shí)例,這種類稱為抽象類(abstractclass)。抽象類用于建模現(xiàn)實(shí)世界中一些沒(méi)有具體對(duì)象的純粹抽象的概念。例如,“鳥(niǎo)”可以認(rèn)為是一個(gè)純粹抽象的概念,它本身沒(méi)有任何具體的對(duì)象實(shí)例,任何具體的鳥(niǎo)兒都是由“鳥(niǎo)”經(jīng)過(guò)特殊化形成的某個(gè)具體的種類的對(duì)象,也就是說(shuō),“鳥(niǎo)”是具體的鳥(niǎo)兒經(jīng)過(guò)抽象形成的一個(gè)概念。因此,抽象類可以從現(xiàn)有的一些類中抽取共同特性而得到。什么時(shí)候進(jìn)行這種抽象呢?Eiffel語(yǔ)言的設(shè)計(jì)者BertrandMeyer提供了所謂的單選原則(singlechoiceprinciple):當(dāng)軟件系統(tǒng)要解決的問(wèn)題有一個(gè)可供選擇的集合時(shí),只在一個(gè)軟件構(gòu)建中進(jìn)行這種選擇。例如,一個(gè)圖形處理軟件,要處理各種各樣的圖形,三角形、矩形、圓等,那么應(yīng)該設(shè)計(jì)一個(gè)抽象類——“圖形”作為進(jìn)行選擇的軟件構(gòu)件,也就是說(shuō),其他的軟件構(gòu)件可以通過(guò)這個(gè)構(gòu)件訪問(wèn)所有圖形的共同特性。再例如,一個(gè)文本編輯軟件,要支持文本編輯的許多命令,插入、刪除、移動(dòng)等,那么應(yīng)該設(shè)計(jì)一個(gè)抽象類——“命令”描述這些命令的共同特性。

1.抽象類

Java語(yǔ)言在聲明一個(gè)類時(shí)使用保留字abstract修飾使得它成為抽象類,例如:

publicabstractclassMyAbstractClass

{

...

}

使用抽象類創(chuàng)建對(duì)象實(shí)例,編譯器會(huì)報(bào)告錯(cuò)誤,例如下述語(yǔ)句不能通過(guò)編譯:

MyAbstractClassobj=newMath();//編譯錯(cuò)誤

2.抽象方法

Java語(yǔ)言也可以定義某些成員方法為抽象方法,例如:

publicabstractclassMyAbstractClass

{

publicabstractvoidmyAbstractMethod();

//沒(méi)有方法體

...

}抽象方法沒(méi)有方法體,直接跟分號(hào)表示結(jié)束。注意,只能將抽象類的成員方法定義為抽象方法,不能將非抽象類的成員方法定義為抽象方法。從某種意義上,正是因?yàn)槌橄箢愑幸恍┏蓡T方法是抽象方法,沒(méi)有方法體,不能直接調(diào)用,所以抽象類才不能創(chuàng)建對(duì)象實(shí)例。當(dāng)然抽象類中也可以沒(méi)有抽象方法,全是非抽象的方法,但這多少有些不自然。顯然不能將一個(gè)類或方法同時(shí)使用final和abstract修飾,因?yàn)樽罱K類禁止類被派生,同時(shí)禁止方法被重定義,而抽象類只能被派生才有可能創(chuàng)建對(duì)象實(shí)例,抽象方法只有被重定義才能給出其方法體。

抽象類不能創(chuàng)建對(duì)象實(shí)例,因此它通常作為某些類的父類。抽象類的子類應(yīng)該重定義抽象類的抽象方法,給出它們的具體實(shí)現(xiàn),這時(shí)這個(gè)子類方法不再用abstract修飾。如果抽象類的某個(gè)抽象方法沒(méi)有被它的子類重定義給出具體實(shí)現(xiàn),則這個(gè)子類也是抽象類,在聲明這個(gè)子類時(shí)必須使用abstract修飾。

3.應(yīng)用舉例

這里舉一個(gè)比較完整的例子來(lái)說(shuō)明抽象類的使用。假定要為某個(gè)公司編寫(xiě)雇員工資支付程序。這個(gè)公司有各種類型的雇員,不同類型的雇員按不同的方式支付工資:經(jīng)理(Manager)每月獲得一份固定的工資;銷售人員(Salesman)在基本工資的基礎(chǔ)上每月還有銷售提成;一般工人(Worker)則按每月工作的天數(shù)計(jì)算工資。按照單選原則,應(yīng)該設(shè)計(jì)一個(gè)類——雇員(Employee)來(lái)描述所有雇員的共同特性,例如姓名(name)等。這個(gè)類還應(yīng)提供一個(gè)計(jì)算工資的抽象方法computeSalary()使得可以通過(guò)這個(gè)類計(jì)算所有雇員的工資,這個(gè)方法是抽象方法,因?yàn)槌绦驘o(wú)法為一個(gè)沒(méi)有明確類型的雇員計(jì)算工資。經(jīng)理、銷售人員、一般工人對(duì)應(yīng)的類都繼承這個(gè)父類,并重定義計(jì)算工資的方法computeSalary(),給出它的具體實(shí)現(xiàn)。例4-9定義了父類Employee,例4-9、例4-10、例4-11分別給出了子類Manager、Salesman和Worker的定義。為簡(jiǎn)單起見(jiàn),這些類都采用了最簡(jiǎn)單的設(shè)計(jì)?!纠?-9】Employee.java程序。

//抽象類——雇員

publicabstractclassEmployee

{

privateStringname;

publicEmployee(Stringname)

{

=name;

}

publicStringgetName(){

returnname;

}

//計(jì)算雇員月工資的抽象方法

publicabstractdoublecomputeSalary();

}【例4-10】Manager.java程序。

//經(jīng)理是雇員中的一類人群

publicclassManagerextendsEmployee

{

privatedoublemonthSalary;//月工資額

publicManager(Stringname,doublemonthSalary)

{

super(name);//調(diào)用父類Employee的構(gòu)造方法this.monthSalary=monthSalary;

}

//重定義父類的抽象方法computeSalary,此處不能再使用abstract修飾,下面幾個(gè)類的方法類似。注意,方法重定義只要求方法的返回類型與參數(shù)列表完全一致

publicdoublecomputeSalary()

{

returnmonthSalary;

}

}【例4-11】Salesman.java程序。

//銷售人員也是雇員中的一類人群

publicclassSalesmanextendsEmployee

{

privatedoublebaseSalary; //基本工資額

privatedoublecommision; //每件產(chǎn)品的提成額

privateintquantity; //銷售的產(chǎn)品數(shù)量

publicSalesman(Stringname,doublebaseSalary,doublecommision,intquantity)

{

super(name);

//調(diào)用父類Employee的構(gòu)造方法

this.baseSalary=baseSalary;

mision=commision;

this.quantity=quantity;

}

publicdoublecomputeSalary()

{

returnbaseSalary+commision*quantity;

}

}【例4-12】Worker.java程序。

//工人類同樣是雇員中的一類人群

publicclassWorkerextendsEmployee

{

privatedoubledailySalary;//每天工資額

privateintdays; //每月工作的天數(shù)

publicWorker(Stringname,doubledailySalary,intdays){

super(name); //調(diào)用父類Employee的構(gòu)造方法

this.dailySalary=dailySalary;

this.days=days;

}

publicdoublecomputeSalary()

{

returndailySalary*days;

}

}【例4-13】EmployeeDemo.java程序。

//演示類

publicclassEmployeeDemo

{

publicstaticvoidmain(String[]args)

{

//以共同的父類聲明記錄雇員的數(shù)組Employee[]data=newEmployee[4];

//創(chuàng)建一些雇員對(duì)象,這時(shí)雇員數(shù)組是一個(gè)多態(tài)數(shù)據(jù)結(jié)構(gòu),可以存放各種類型的雇員

data[0]=newManager("Manager",10000);

data[1]=newSalesman("Salesman",3000,200,12);

data[2]=newWorker("WorkerZhang",200,25);

data[3]=newWorker("WorkerLi",250,26);

displaySalary(data);

}

//顯示所有雇員的工資額,不同種類的雇員都放在數(shù)組data中,因?yàn)镋mployee抽象了所有雇員的

//共同特性,因此這里只要以Employee作為參數(shù)即可,而無(wú)需關(guān)心具體種類的雇員

publicstaticvoiddisplaySalary(Employee[]data)

{

for(inti=0;i<data.length;i++)

{

//下面調(diào)用data[i].computeSalary()將根據(jù)data[i]所引用的對(duì)象實(shí)例調(diào)用

//相應(yīng)的computeSalary()方法來(lái)計(jì)算該雇員的月工資額

System.out.println("雇員"+data[i].getName()+"的月工資是:"+data[i].

computeSalary());

}

}

}程序運(yùn)行的結(jié)果如下:

雇員Manager的月工資是:10000.0

雇員Salesman的月工資是:5400.0

雇員WorkerZhang的月工資是:5000.0

雇員WorkerLi的月工資是:6500.0這里雇員數(shù)組data是一個(gè)典型的多態(tài)數(shù)據(jù)結(jié)構(gòu),里面可存放各種類型的雇員(實(shí)際上是雇員對(duì)象實(shí)例的引用),在displaySalary()中調(diào)用data[i].computeSalary()會(huì)根據(jù)data[i]的動(dòng)態(tài)類型調(diào)用相應(yīng)雇員的computeSalary()方法計(jì)算其月工資額。?這種動(dòng)態(tài)多態(tài)性使得程序的擴(kuò)充十分方便,假設(shè)要將一般雇員再細(xì)分為計(jì)時(shí)雇員(DailyWorker)和計(jì)件雇員(PieceWorker)兩類,計(jì)時(shí)雇員按天計(jì)算工資額,而計(jì)件雇員按其生產(chǎn)的產(chǎn)品件數(shù)計(jì)算工資額,那么可取消類Worker,重新派生兩個(gè)類DailyWorker和PieceWorker,重定義其中的computeSalary()方法,再重新編譯這兩個(gè)類即可,無(wú)需重新編譯調(diào)用computeSalary()的displaySalary()方法,當(dāng)data數(shù)組中存放這兩種類型的雇員時(shí),仍可正確計(jì)算其工資額。4.4.1引例——“郭敬問(wèn)題”

郭敬是一名高校教師,他正讀在職研究生,請(qǐng)用Java語(yǔ)言描述像郭敬同樣情況的人。

1.問(wèn)題剖析

讀在職研究生的教師既可作為學(xué)生的子類又可作為教師的子類,這正是因?yàn)槲覀兗瓤梢詮膶W(xué)生的角度觀察這一類人,也可以從教師的角度觀察這一類人。4.4接口在C++語(yǔ)言中,可以先定義一個(gè)Person類描述學(xué)生與教師共有的特征,再定義一個(gè)Student類和Teacher類,分別描述學(xué)生和教師各自獨(dú)有的特征,然后定義一個(gè)在職研究生類(StudentTeacher),同時(shí)繼承上述三個(gè)類即可描述郭敬既是學(xué)生又是教師的身份。然而Java語(yǔ)言不支持類的多重繼承,無(wú)法用類似C++的辦法解決。

Java出于安全性、簡(jiǎn)化程序結(jié)構(gòu)的考慮,不支持類間的多重繼承而只支持單繼承。然而在解決實(shí)際問(wèn)題的過(guò)程中,在很多情況下僅僅依靠單繼承不能將復(fù)雜的問(wèn)題描述清楚,像要描述4.4.1中的郭敬身份的問(wèn)題,就遇到了困難。為了使Java程序的類間層次結(jié)構(gòu)更加合理,更符合實(shí)際問(wèn)題的本質(zhì),Java語(yǔ)言提供接口來(lái)實(shí)現(xiàn)多重繼承機(jī)制。實(shí)際上,在Java語(yǔ)言中,可以先定義一個(gè)Person類描述學(xué)生與教師共有的特征,如姓名、年齡等,再定義一個(gè)AsStudent接口和一個(gè)AsTeacher接口,表示分別從學(xué)生和教師各自不同的角度觀察人的行為和屬性,然后定義一個(gè)在職研究生類InService,繼承上述Person類,同時(shí)實(shí)現(xiàn)AsStudent接口和AsTeacher接口即可描述郭敬既是學(xué)生又是教師的身份。2.聲明接口

1)聲明接口的語(yǔ)法格式

[修飾符]interface接口名[extends父接口名列表]

{

常量數(shù)據(jù)成員聲明

抽象方法聲明

}

2)定義接口注意事項(xiàng)

定義接口要注意以下幾點(diǎn):

(1)接口定義用關(guān)鍵字interface,而不是用class。

(2)接口名要求符合Java標(biāo)識(shí)符規(guī)定。

(3)修飾符有兩種:public和默認(rèn)。public修飾的接口是公共接口,可以被所有的類和接口使用;默認(rèn)修飾符的接口只能被同一個(gè)程序包中的其他類和接口使用。

(4)父接口列表:接口也具有繼承性。定義一個(gè)接口時(shí)可以通過(guò)extends關(guān)鍵字聲明該接口是某個(gè)已經(jīng)存在的父接口的派生接口,它將繼承父接口的所有屬性和方法。與類的繼承不同的是一個(gè)接口可以有一個(gè)以上的父接口,它們之間用逗號(hào)分隔。

(5)常量數(shù)據(jù)成員聲明:常量數(shù)據(jù)成員前可以有也可以沒(méi)有修飾符。修飾符是publicfinalstatic或finalstatic,接口中的數(shù)據(jù)成員都是用final修飾的常量,且必須初始化。書(shū)寫(xiě)格式如下:

[修飾符]數(shù)據(jù)成員類型數(shù)據(jù)成員名=常量值或

數(shù)據(jù)成員名=常量值

例如:

publicfinalstaticdoublePI=3.14159;

finalstaticinta=9;

intSUM=100;(等價(jià)于finalstaticintSUM=100;)但下面的定義是錯(cuò)誤的:

publicinterfaceControllable

{

privateintOFF=0; //錯(cuò)誤,接口不能聲明私有的成員

protectedintON=1; //錯(cuò)誤,接口不能聲明受保護(hù)的成員

...

}

接口的公有靜態(tài)常量都必須使用常量表達(dá)式進(jìn)行初始化,否則會(huì)出現(xiàn)編譯錯(cuò)誤:

publicinterfaceControllable

{

publicintOFF;

//錯(cuò)誤,接口的數(shù)據(jù)成員必須初始化

...

}

(6)抽象方法聲明:接口中的方法都是用abstract修飾的抽象方法。在接口中只能給出這些抽象方法的方法名、返回值和參數(shù)列表,而不能定義方法體,即這些接口僅僅是規(guī)定了一組信息交換、傳輸和處理的“接口”。格式如下:

返回值類型方法名(參數(shù)列表);其中:接口中的方法默認(rèn)為publicabstract方法。例如,下述聲明是錯(cuò)誤的:

publicinterfaceControllable{

privatevoidmethodOne();

//錯(cuò)誤,接口不能聲明私有的成員

finalvoidmethodTwo();

//錯(cuò)誤,接口不能聲明最終方法成員

staticvoidmethodTwo();

//錯(cuò)誤,接口不能聲明靜態(tài)方法成員

}接口中的方法都是抽象方法,因此不能給出任何實(shí)現(xiàn)體。接口相當(dāng)于純抽象類,可以聲明類型為某個(gè)接口的引用變量,但不能使用該引用變量創(chuàng)建對(duì)象實(shí)例,例如:

Controllablerefer; //正確,定義接口也定義了相應(yīng)的類型

refer=newControllable();

//錯(cuò)誤,不能創(chuàng)建接口的對(duì)象實(shí)例

從上面定義接口的語(yǔ)法格式可以看出,定義接口與定義類非常相似。實(shí)際上完全可以把接口理解成為一種由常量和抽象方法組成的特殊類。一個(gè)類只能有一個(gè)父類,但是它可以同時(shí)實(shí)現(xiàn)若干個(gè)接口。這種情況下,如果把接口理解成特殊的類,那么這個(gè)類利用接口就獲得了多個(gè)父類,即實(shí)現(xiàn)了多重繼承。定義接口僅僅是規(guī)定了一組實(shí)現(xiàn)特定功能的對(duì)外接口和規(guī)范,而不能真正地實(shí)現(xiàn)這個(gè)功能,這個(gè)功能的真正實(shí)現(xiàn)是在“繼承”這個(gè)接口的各個(gè)類中完成的,即要由這些類來(lái)具體定義接口中各抽象方法的方法體。因而在Java中,通常稱接口功能的“繼承”為“實(shí)現(xiàn)(implementation)”。

3.問(wèn)題的解決

有了上述知識(shí)準(zhǔn)備,就可以用例4-14~例4-17的程序模擬多重繼承,描述郭敬既是學(xué)生又是老師的雙重身份。

【例4-14】Person.java程序。

//父類Person描述學(xué)生和教師共有的特征

publicclassPerson

{

privateStringname; //人的姓名屬性定義

privateintage; //定義人的年齡屬性publicPerson(Stringname,intage)

{

=name;

this.age=age;

}

publicStringgetName()

{

returnname;

}

publicintgetAge(){

returnage;

}

publicvoidsetAge(intnewAge)

{

age=newAge;

}

}

例4-15和例4-16給出了接口AsStudent和AsTeacher的定義,分別從學(xué)生和教師的角度觀察某個(gè)類。

【例4-15】AsStudent.java程序。

//接口AsStudent表示從學(xué)生的角度觀察人的行為和屬性

publicinterfaceAsStudent

{

intMAX_COURSE_NUMBER=30;

//學(xué)生學(xué)習(xí)的課程數(shù)目的上限

String[]getStudyCourse(); //查看學(xué)生學(xué)習(xí)的課程

int[]getStudyScore(); //查看學(xué)生學(xué)習(xí)的每門(mén)課

程的成績(jī)

StringgetStudyDepartment(); //查看學(xué)生所在系voidsetStudyCourse(String[]course);

//設(shè)置學(xué)生學(xué)習(xí)的課程

voidsetStudyScore(int[]score);

//設(shè)置學(xué)生學(xué)習(xí)的每門(mén)課程的成績(jī)

voidsetStudyDepartment(Stringdept);

//設(shè)置學(xué)生所在的系

}

【例4-16】AsTeacher.java程序。

//接口AsTeacher表示從教師的角度觀察人的行為和屬性

publicinterfaceAsTeacher

{

intMAX_COURSE_NUMBER=30;String[]getTeachCourse(); //查看教師講授的課程

doublegetTeachWage(); //查看教師的工資

StringgetTeachDepartment(); //查看教師所在的系

voidsetTeachCourse(String[]course);

//設(shè)置教師教授的課程

voidsetTeachWage(doublewage); //設(shè)置教師的工資

voidsetTeachDepartment(Stringdept);

//設(shè)置教師所在的系

}【例4-17】InService.java程序。

//定義InService類繼承Person類實(shí)現(xiàn)AsStudent和AsTeacher兩個(gè)接口

publicclassInServiceextendsPersonimplementsAsStudent,AsTeacher

{

privateString[]studyCourse=newString[AsStudent.MAX_COURSE_NUMBER];

privateint[]studyScore=newint[AsStudent.MAX_COURSE_NUMBER];

privateStringstudyDepartment;privateString[]teachCourse=newString[AsTeacher.MAX_COURSE_NUMBER];

privatedoubleteachWage;

privateStringteachDepartment;

publicInService(Stringname,intage)

{

super(name,age);

}//查看學(xué)生學(xué)習(xí)的課程

publicString[]getStudyCourse()

{

//由于數(shù)組本身不是不變對(duì)象,所以為了安全,拷貝一份課程數(shù)組返回給使用者,而不是將引用變量studyCourse直接暴露給使用者,以免被使用者改變

String[]course=newString[AsStudent.MAX_COURSE_NUMBER];

for(inti=0;i<course.length&&i<studyCourse.length;i++)

{//因?yàn)镴avaAPI提供的String是不變類,因此這里采用淺復(fù)制策略,

//只復(fù)制數(shù)組元素,并不復(fù)制元素引用的字符串

course[i]=studyCourse[i];

}

returncourse;

}

//查看學(xué)生學(xué)習(xí)的每門(mén)課程的成績(jī)

publicint[]getStudyScore(){

int[]score=newint[AsStudent.MAX_COURSE_NUMBER];

for(inti=0;i<score.length&&i<studyScore.length;i++)

{

score[i]=studyScore[i];

}

returnscore;

}

//查看學(xué)生所在的系

publicStringgetStudyDepartment(){

returnstudyDepartment;

}

//設(shè)置學(xué)生學(xué)習(xí)的課程

publicvoidsetStudyCourse(String[]course)

{

for(inti=0;i<course.length&&i<studyCourse.length;i++)

{

studyCourse[i]=course[i];

}

}//設(shè)置學(xué)生學(xué)習(xí)的每門(mén)課程的成績(jī)

publicvoidsetStudyScore(int[]score)

{

for(inti=0;i<score.length&&i<studyScore.length;i++)

{

studyScore[i]=score[i];

}

}

//設(shè)置學(xué)生所在的系

publicvoidsetStudyDepartment(Stringdept){

studyDepartment=dept;

}

//查看教師講授的課程

publicString[]getTeachCourse()

{

String[]course=newString[AsTeacher.MAX_COURSE_NUMBER];

for(inti=0;i<course.length&&i<teachCourse.length;i++){

course[i]=teachCourse[i];

}

returncourse;

}

//查看教師的工資

publicdoublegetTeachWage()

{

returnteachWage;}

//查看教師所在的系

publicStringgetTeachDepartment()

{

returnteachDepartment;

}

//設(shè)置教師教授的課程

publicvoidsetTeachCourse(String[]course)

{

for(inti=0;i<course.length&&i<teachCourse.length;i++){

teachCourse[i]=course[i];

}

}

//設(shè)置教師的工資

publicvoidsetTeachW

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(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ì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論