版權(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025-2030年中國(guó)智慧酒店行業(yè)開(kāi)拓第二增長(zhǎng)曲線戰(zhàn)略制定與實(shí)施研究報(bào)告
- 2025-2030年中國(guó)小家電行業(yè)資本規(guī)劃與股權(quán)融資戰(zhàn)略制定與實(shí)施研究報(bào)告
- 2023年-2024年員工三級(jí)安全培訓(xùn)考試題【滿分必刷】
- 2023-2024年企業(yè)主要負(fù)責(zé)人安全培訓(xùn)考試題帶答案(精練)
- 鐵路運(yùn)輸PC吊裝工程合同
- 三人合伙文創(chuàng)產(chǎn)品店合同
- 關(guān)于學(xué)習(xí)勞動(dòng)合同的心得
- 2025年企業(yè)用工人才共享合同
- 2025年施工吊車(chē)租賃合同
- 2025年公益親子互動(dòng)中心贈(zèng)與合同
- 2025年湖南出版中南傳媒招聘筆試參考題庫(kù)含答案解析
- 2025年度商用廚房油煙機(jī)安裝與維護(hù)服務(wù)合同范本3篇
- 2024年03月恒豐銀行2024年春季招考畢業(yè)生筆試歷年參考題庫(kù)附帶答案詳解
- 網(wǎng)絡(luò)安全系統(tǒng)運(yùn)維方案
- ISO 56001-2024《創(chuàng)新管理體系-要求》專業(yè)解讀與應(yīng)用實(shí)踐指導(dǎo)材料之14:“6策劃-6.3變更的策劃”(雷澤佳編制-2025B0)
- 【公開(kāi)課】同一直線上二力的合成+課件+2024-2025學(xué)年+人教版(2024)初中物理八年級(jí)下冊(cè)+
- 12G614-1砌體填充墻結(jié)構(gòu)構(gòu)造
- 電鍍產(chǎn)品檢驗(yàn)作業(yè)指導(dǎo)書(shū)
- 湖北省武漢市各縣區(qū)鄉(xiāng)鎮(zhèn)行政村村莊村名居民村民委員會(huì)明細(xì)及行政區(qū)劃代碼
- 路面輪胎模型建立方法swift
- 10KV供配電工程施工組織設(shè)計(jì)
評(píng)論
0/150
提交評(píng)論