Java的多態(tài)性,抽象類與接口_第1頁
Java的多態(tài)性,抽象類與接口_第2頁
Java的多態(tài)性,抽象類與接口_第3頁
Java的多態(tài)性,抽象類與接口_第4頁
Java的多態(tài)性,抽象類與接口_第5頁
已閱讀5頁,還剩59頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

繼承是指從一個父類(父類)派生出派生類(子類)的過程;繼承使用關(guān)鍵字extends;對于類成員,可以使用public、protected、缺省和private這4種訪問權(quán)限修飾符;對于類,可以使用public和缺省這2個訪問權(quán)限;創(chuàng)建子類實例時,必須先執(zhí)行父類的構(gòu)造方法,然后再執(zhí)行子類的構(gòu)造方法;super關(guān)鍵字有兩種主要用途,都與父類有關(guān)?;仡櫮壳耙豁揬總數(shù)六十四頁\編于十四點為什么使用多態(tài)?寵物生病了,需要主人給寵物看病不同寵物看病過程不一樣不同寵物恢復(fù)后體力值不一樣

打針

吃藥

吃藥

療養(yǎng)

狗狗

Q仔

目前二頁\總數(shù)六十四頁\編于十四點為什么使用多態(tài)?編碼實現(xiàn)publicclassMaster{publicvoidCure(Dogdog){if(dog.getHealth()<50){dog.setHealth(60);System.out.println("打針、吃藥");}}publicvoidCure(Penguinpenguin){if(penguin.getHealth()<50)penguin.setHealth(70);System.out.println("吃藥、療養(yǎng)");}}

主人類……Mastermaster=newMaster();master.Cure(dog);master.Cure(penguin);……

測試方法目前三頁\總數(shù)六十四頁\編于十四點為什么使用多態(tài)?如果又需要給XXX看病,怎么辦?添加XXX類,繼承Pet類修改Master類,添加給XXX看病的方法使用多態(tài)優(yōu)化設(shè)計頻繁修改代碼,代碼可擴展性、可維護(hù)性差目前四頁\總數(shù)六十四頁\編于十四點為什么使用多態(tài)?使用多態(tài)優(yōu)化后的代碼……Petpet=newDog();Mastermaster=newMaster();master.Cure(pet);……

測試方法publicclassDogextendsPet{publicvoidtoHospital(){this.setHealth(60);System.out.println("打針、吃藥");}}publicclassPenguinextendsPet{publicvoidtoHospital(){

this.setHealth(70);System.out.println("吃藥、療養(yǎng)");}}publicclassMaster{publicvoidCure(Petpet){if(pet.getHealth()<50)pet.toHospital();}}

主人類Dog類Penguin類1234又要給XXX看病時,只需:1.編寫XXX類繼承Pet類(舊方案也需要)2.創(chuàng)建XXX類對象(舊方案也需要)3.其他代碼不變(不用修改Master類)目前五頁\總數(shù)六十四頁\編于十四點什么是多態(tài)?生活中的多態(tài)你能列舉出一個多態(tài)的生活示例嗎?程序中的多態(tài)同一個引用類型,使用不同的實例而執(zhí)行不同操作父類引用,子類對象同一種事物,由于條件不同,產(chǎn)生的結(jié)果也不同目前六頁\總數(shù)六十四頁\編于十四點多態(tài)性(polymorphism)概念:是面向?qū)ο蟪绦蛟O(shè)計的另一個重要特征,其基本含義是“擁有多種形態(tài)”,具體指在程序中用相同的名稱來表示不同的含義。例如:用同一方法名來表示不同的操作。類型:有兩種靜態(tài)多態(tài)性:包括變量的隱藏、方法的重載動態(tài)多態(tài)性:在編譯時不能確定調(diào)用方法,只有在運行時才能確定調(diào)用方法,又稱為運行時的多態(tài)性。7目前七頁\總數(shù)六十四頁\編于十四點靜態(tài)多態(tài)靜態(tài)多態(tài)也稱為編譯時多態(tài),即在編譯時決定調(diào)用哪個方法;靜態(tài)多態(tài)一般是指方法重載;只要構(gòu)成了方法重載,就可以認(rèn)為形成了靜態(tài)多態(tài)的條件;靜態(tài)多態(tài)與是否發(fā)生繼承沒有必然聯(lián)系。目前八頁\總數(shù)六十四頁\編于十四點動態(tài)多態(tài)動態(tài)多態(tài)也稱為運行時多態(tài),即在運行時才能確定調(diào)用哪個方法;形成動態(tài)多態(tài)必須具體以下條件:必須要有繼承的情況存在;在繼承中必須要有方法覆蓋;必須由父類的引用指向派生類的實例,并且通過父類的引用調(diào)用被覆蓋的方法;由上述條件可以看出,繼承是實現(xiàn)動態(tài)多態(tài)的首要前提。目前九頁\總數(shù)六十四頁\編于十四點下面主要介紹動態(tài)多態(tài)性//多態(tài)性的例子classAnimal{

publicvoidroar(){ System.out.println("動物:..."); }}classCatextendsAnimal{

publicvoidroar(){ System.out.println("貓:喵,喵,喵,..."); }}classDogextendsAnimal{

publicvoidroar(){ System.out.println("狗:汪,汪,汪,..."); }}10目前十頁\總數(shù)六十四頁\編于十四點//多態(tài)性的例子(續(xù))publicclassAnimalTest{ publicstaticvoidmain(Stringargs[]){ Animalam=newAnimal();

am.roar(); am=newDog();

am.roar(); am=newCat();

am.roar(); }}程序運行結(jié)果:動物:...狗:汪,汪,汪,...貓:喵,喵,喵,... 根據(jù)對象的賦值規(guī)則,可以把子類對象賦給父類對象名,這是允許。當(dāng)用同一形式:

父類對象名.roar()

去調(diào)用時,能夠根據(jù)子類對象的不同,得到不同的結(jié)果,這就是多態(tài)性。11目前十一頁\總數(shù)六十四頁\編于十四點實現(xiàn)多態(tài)的流程用多態(tài)實現(xiàn)打印機分為黑白打印機和彩色打印機不同類型的打印機打印效果不同黑白打印機彩色打印機打印目前十二頁\總數(shù)六十四頁\編于十四點實現(xiàn)多態(tài)的設(shè)計思路使用多態(tài)實現(xiàn)思路編寫父類編寫子類,子類重寫(覆蓋)父類方法運行時,使用父類的類型,子類的對象計算機可以連接各種打印機無論連接何種打印機打印方法都相同根據(jù)連接打印機不同,效果也不同目前十三頁\總數(shù)六十四頁\編于十四點實現(xiàn)多態(tài)

編碼實現(xiàn)實現(xiàn)多態(tài)的兩個要素:1.方法重寫2.使用父類類型classPrinter{

print(Stringstr);}classColorPrinterextendsPrinter{

print(Stringstr)

{System.out.println("輸出彩色的"+str);}}classBlackPrinterextendsPrinter{

print(Stringstr)

{System.out.println("輸出黑白的"+str);}}publicstaticvoidmain(String[]args){

Printerp=newColorPrinter();p.print();p=newBlackPrinter();p.print();}父類子類運行同一種操作方式,不同的操作對象只能調(diào)用父類已經(jīng)定義的方法繼承是子類使用父類的方法,而多態(tài)則是父類使用子類的方法。(把父類當(dāng)做子類來用)目前十四頁\總數(shù)六十四頁\編于十四點方法重寫(覆蓋)方法重寫的規(guī)則在繼承關(guān)系的子類中重寫的方法名、參數(shù)、返回值類型必須與父類相同私有方法不能繼承因而也無法重寫

位置方法名參數(shù)表返回值訪問修飾符方法重寫子類相同相同相同不能比父類更嚴(yán)格方法重載同類相同不相同無關(guān)無關(guān)方法重寫override方法重載overload

VS目前十五頁\總數(shù)六十四頁\編于十四點實現(xiàn)運行時多態(tài)技術(shù)的條件:有一個繼承層次關(guān)系;在子類中重寫父類的方法;通過父類的引用對子類對象進(jìn)行調(diào)用。采用多態(tài)技術(shù)的優(yōu)點: 引進(jìn)多態(tài)技術(shù)之后,盡管子類的對象千差萬別,但都可以采用父類引用.方法名([參數(shù)])統(tǒng)一方式來調(diào)用,在程序運行時能根據(jù)子對象的不同得到不同的結(jié)果。這種“以不變應(yīng)萬變”的形式可以規(guī)范、簡化程序設(shè)計,符合軟件工程的“一個接口,多種方法”思想。

16目前十六頁\總數(shù)六十四頁\編于十四點多態(tài)的運行機制多態(tài)的運行機制

Java多態(tài)機制是基于“方法綁定(binding)”,就是建立methodcall(方法調(diào)用)和methodbody(方法本體)的關(guān)聯(lián)。如果綁定動作發(fā)生于程序執(zhí)行前(由編譯器和連接器完成),稱為“先期綁定”。對于面向過程的語言它們沒有其他選擇,一定是先期綁定。比如C編譯器只有一種methodcall,就是先期綁定。(C++有先期聯(lián)編和后期聯(lián)編)

當(dāng)有多態(tài)的情況時,解決方案便是所謂的后期綁定(latebinding):綁定動作將在執(zhí)行期才根據(jù)對象型別而進(jìn)行。后期綁定也被稱為執(zhí)行期綁定(run-timebinding)或動態(tài)綁定(dynamicbinding)。Java的所有方法,除了被聲明為final者,都使用后期綁定。將方法聲明為final型可以有效防止他人覆寫該函數(shù)。但是或許更重要的是,這么做可以“關(guān)閉”動態(tài)綁定。或者說,這么做便是告訴編譯器:動態(tài)綁定是不需要的。于是編譯器可以產(chǎn)生效率較佳的程序代碼。目前十七頁\總數(shù)六十四頁\編于十四點引用轉(zhuǎn)型父類的引用可以指向子類的對象,如:

BaseClassobj=newDerivedClass();

這樣的語句是合法的;但是子類的引用則不可以指向父類的對象,如:

DerivedClassobj=newBaseClass();

這樣的語句將引發(fā)錯誤。目前十八頁\總數(shù)六十四頁\編于十四點引用轉(zhuǎn)型示例classPerson{//定義人類

……}classStudentextendsPerson{//學(xué)生類繼承于人類

……}public

classOverriddenDemo{

public

static

voidmain(String[]args){

//正確,所有的學(xué)生一定是人

Personper=newStudent();

//錯誤,并不是所有的人都是學(xué)生

Studentstd=newPerson();}}目前十九頁\總數(shù)六十四頁\編于十四點多態(tài)性實例分析Example例如動物園的飼養(yǎng)員能夠給各種各樣的動物喂食。

圖1顯示了飼養(yǎng)員Feeder、食物Food和動物Animal以及它的子類的類框圖。目前二十頁\總數(shù)六十四頁\編于十四點多態(tài)性實例分析

圖1飼養(yǎng)員Feeder、食物Food和動物Animal以及它的子類的類框圖目前二十一頁\總數(shù)六十四頁\編于十四點多態(tài)性實例分析可以把Feeder、Animal和Food都看成獨立的子系統(tǒng)。Feeder類的定義如下:

publicclassFeeder{

publicvoidfeed(Animalanimal,Foodfood){

animal.eat(food);

}

}以下程序演示一個飼養(yǎng)員分別給一只狗喂肉骨頭,給一只貓喂魚。

Feederfeeder=newFeeder();

Animalanimal=newDog();

Foodfood=newBone();

feeder.feed(animal,food);

animal=newCat();

food=newFish();

feeder.feed(animal,food);

//給狗喂肉骨頭//給貓喂魚目前二十二頁\總數(shù)六十四頁\編于十四點多態(tài)性實例分析Java語言允許某個類型的引用變量引用子類的實例,而且可以對這個引用變量進(jìn)行類型轉(zhuǎn)換:Animalanimal=newDog();

Dogdog=(Dog)animal;

Creaturecreature=animal;

如圖2所示,如果把引用變量轉(zhuǎn)換為子類類型,稱為向下轉(zhuǎn)型,如果把引用變量轉(zhuǎn)換為父類類型,稱為向上轉(zhuǎn)型。在進(jìn)行引用變量的類型轉(zhuǎn)換時,會受到各種限制。而且在通過引用變量訪問它所引用的實例的靜態(tài)屬性、靜態(tài)方法、實例屬性、實例方法,以及從父類中繼承的方法和屬性時,Java虛擬機會采用不同的綁定機制。

//向下轉(zhuǎn)型,把Animal類型轉(zhuǎn)換為Dog類型

//向上轉(zhuǎn)型,把Animal類型轉(zhuǎn)換為

Creature類型目前二十三頁\總數(shù)六十四頁\編于十四點多態(tài)

圖2類型轉(zhuǎn)換目前二十四頁\總數(shù)六十四頁\編于十四點多態(tài)性實例分析下面通過具體的例子來演示多態(tài)的各種特性。在下面的例程中,Base父類和Sub子類中都定義了實例變量var、實例方法method()、靜態(tài)變量staticVar和靜態(tài)方法staticMethod(),此外,在Sub類中還定義了實例變量subVar和subMethod()。Sub.java

packagepoly;

classBase{

Stringvar="BaseVar";

staticStringstaticVar="StaticBaseVar";

voidmethod(){

System.out.println("Basemethod");

}

staticvoidstaticMethod(){System.out.println("StaticBasemethod");

}

}//成員(實例)變量//靜態(tài)變量//成員(實例)方法//靜態(tài)方法

目前二十五頁\總數(shù)六十四頁\編于十四點publicclassSubextendsBase{

Stringvar=“SubVar”;

//成員(實例)變量

staticStringstaticVar="StaticSubVar";

//靜態(tài)變量voidmethod(){

//覆蓋父類的method()方法

System.out.println("Submethod");

}

staticvoidstaticMethod(){

//隱藏父類的staticMethod()方法

System.out.println("StaticSubmethod");

}StringsubVar="VaronlybelongingtoSub";

voidsubMethod(){

System.out.println("MethodonlybelongingtoSub");

}

publicstaticvoidmain(Stringargs[]){

Basewho=newSub();

//who被聲明為Base類型,引用Sub實例

System.out.println("who.var="+who.var);

System.out.println("who.staticVar="+who.staticVar);

who.method();

who.staticMethod();

}

}//打印Base類的var變量//打印Base類的staticVar變量//打印Sub實例的method()方法//打印Base類的staticMethod()方法目前二十六頁\總數(shù)六十四頁\編于十四點多態(tài)的一些細(xì)節(jié)(1)對于一個引用類型的變量,Java編譯器按照它聲明的類型來處理。例如在以下代碼中,編譯器認(rèn)為who是Base類型的引用變量,不存在subVar成員變量和subMethod()方法,所以編譯出錯:Basewho=newSub();

//who是Base類型

who.subVar=“123”;

//編譯出錯,提示在Base類中沒有subVar屬性

who.subMethod();

//編譯出錯,提示在Base類中沒有subMethod()方法如果要訪問Sub類的成員,必須通過強制類型的轉(zhuǎn)換:

Basewho=newSub();

//who是Base類型

((Sub)who).subVar="123";

//編譯成功,把Base引用類型強制轉(zhuǎn)換為Sub引用類型

((Sub)who).subMethod();

//編譯成功,把Base引用類型強制轉(zhuǎn)換為Sub引用類型目前二十七頁\總數(shù)六十四頁\編于十四點多態(tài)的一些細(xì)節(jié)Java編譯器允許具有直接或間接繼承關(guān)系的類之間進(jìn)行類型轉(zhuǎn)換.對于向上轉(zhuǎn)型,不必使用強制類型轉(zhuǎn)換,因為子類的對象肯定也可看作父類的對象。例如一個Dog對象是一個Animal對象,也是一個Creature對象,也是一個Object對象:Dogdog=newDog();

Creaturecreature=dog;

//編譯成功,把Dog引用類型直接轉(zhuǎn)換為Creature引用類型

Objectobject=dog;

//編譯成功,把Dog引用類型直接轉(zhuǎn)換為Object引用類型對于向下轉(zhuǎn)型,必須進(jìn)行強制類型轉(zhuǎn)換:Creaturecreature=newCat();

Animalanimal=(Animal)creature;

//編譯成功,把Creature引用類型強制轉(zhuǎn)換為Animal引用類型

Catcat=(Cat)creature;

//編譯成功,把Creature引用類型強制轉(zhuǎn)換為Cat引用類型

Dogdog=(Dog)creature;

//編譯成功,把Creature引用類型強制轉(zhuǎn)換為Dog引用類型目前二十八頁\總數(shù)六十四頁\編于十四點多態(tài)的一些細(xì)節(jié)假如兩種類型之間沒有繼承關(guān)系,即不在繼承樹的同一個繼承分支上,那么Java編譯器不允許進(jìn)行類型轉(zhuǎn)換,例如:

Dogdog=newDog();

Catcat=(Cat)dog;

//編譯出錯,不允許把Dog引用類型轉(zhuǎn)換為Cat引用類型(2)對于一個引用類型變量,運行時Java虛擬機按照它實際引用的對象來處理。例如以下代碼雖然編譯可以通過,但運行時會拋出ClassCastException運行時異常:Basewho=newBase();

//who引用Base類的實例

Subs=(Sub)who;

//運行時拋出ClassCastException

在運行時,子類的對象可以轉(zhuǎn)換為父類類型,而父類的對象實際上無法轉(zhuǎn)換為子類類型。因為通俗的講,父類擁有的成員子類肯定也有,而子類擁有的成員父類不一定有。目前二十九頁\總數(shù)六十四頁\編于十四點多態(tài)的一些細(xì)節(jié)假定Java虛擬機能夠把子類對象轉(zhuǎn)換為父類類型,那么以下代碼中的sub.subMethod()方法無法執(zhí)行:

Basewho=newBase();

//who引用Base類的實例

Subsub=(Sub)who;

//假定運行時未出錯

sub.subMethod();

//sub引用變量實際上引用Base實例,而Base實例沒有subMethod()方法

由此可見,在運行時,Java虛擬機無法把子類對象轉(zhuǎn)變?yōu)楦割愵愋汀R韵麓a盡管能夠編譯成功,但在運行時,creature變量引用的Cat對象無法轉(zhuǎn)變?yōu)镈og類型,因此會拋出ClassCastException:

Creaturecreature=newCat();

Animalanimal=(Animal)creature;

//運行正常,Cat對象可轉(zhuǎn)換為Animal類型

Catcat=(Cat)creature;

//運行正常,Cat對象可以被Cat類型的引用變量引用

Dogdog=(Dog)creature;

//運行時拋出ClassCastException,Cat對象不可轉(zhuǎn)換為Dog類型(但是會編譯成功)目前三十頁\總數(shù)六十四頁\編于十四點(3)在運行時環(huán)境中,通過引用類型變量來訪問所引用對象的方法和屬性時,Java虛擬機采用以下綁定規(guī)則:

1)成員(實例)方法與引用變量實際引用的對象的方法綁定,這種綁定屬于動態(tài)綁定,因為是在運行時由Java虛擬機動態(tài)決定的。

2)靜態(tài)方法與引用變量所聲明的類型的方法綁定,這種綁定屬于靜態(tài)綁定,因為實際上是在編譯階段就已經(jīng)作了綁定。

3)成員變量(包括靜態(tài)變量和實例變量)與引用變量所聲明的類型的成員變量綁定,這種綁定屬于靜態(tài)綁定,因為實際上是在編譯階段就已經(jīng)作了綁定。多態(tài)的一些細(xì)節(jié)目前三十一頁\總數(shù)六十四頁\編于十四點例如,對于以下這段代碼:

Basewho=newSub();

//who被聲明為Base類型,引用Sub實例對象

System.out.println("who.var="+who.var);

//打印Base類的var變量

System.out.println("who.staticVar="+who.staticVar);

//打印Base類的staticVar變量

who.method();

//打印Sub實例的method()方法

who.staticMethod();

//打印Base類的staticMethod()方法

運行時將會輸出如下結(jié)果:

who.var=BaseVar

who.staticVar=StaticBaseVar

Submethod

StaticBasemethod多態(tài)的一些細(xì)節(jié)目前三十二頁\總數(shù)六十四頁\編于十四點再看一個例子:

publicabstractclassA{

abstractvoidmethod();

voidtest(){

method();

//到底調(diào)用哪個類的mehtod()方法?

}

}

publicclassBextendsA{

voidmethod(){

//覆蓋父類的method()方法

System.out.println("Sub");

}

publicstaticvoidmain(Stringargs[]){

newB().test();

}

}多態(tài)的一些細(xì)節(jié)目前三十三頁\總數(shù)六十四頁\編于十四點運行類B的main()方法將打印"Sub"。方法test()在父類A中定義,它調(diào)用了方法method()。雖然方法method()在類A中被定義為是抽象的,它仍然可以被調(diào)用,因為在運行時環(huán)境中,Java虛擬機會執(zhí)行類B的實例的method()方法。一個實例所屬的類肯定實現(xiàn)了父類中所有的抽象方法(否則這個類不能被實例化)。再看一個例子:

publicclassA{

voidmethod(){System.out.println("Base");}

voidtest(){method();}

}

publicclassBextendsA{

voidmethod(){System.out.println("Sub");}publicstaticvoidmain(Stringargs[]){

newA().test();//調(diào)用類A的method()方法

newB().test();

//調(diào)用類B的method()方法

}

多態(tài)的一些細(xì)節(jié)目前三十四頁\總數(shù)六十四頁\編于十四點運行這段代碼將打?。?/p>

Base

Subtest()方法在父類A中定義,它調(diào)用了method()方法,和上面一個例子的區(qū)別是父類A的method()方法不是抽象的。但是通過newB().test()調(diào)用method()方法,執(zhí)行的仍然是子類B的method()方法。由此可以更深入地體會動態(tài)綁定的思想:在運行環(huán)境中,當(dāng)通過B類的實例去調(diào)用一系列的實例方法(包括一個方法調(diào)用的另一個方法),將優(yōu)先和B類本身包含的實例方法動態(tài)綁定,如果B類沒有定義這個實例方法,才會和從父類A中繼承來的實例方法動態(tài)綁定。多態(tài)的一些細(xì)節(jié)目前三十五頁\總數(shù)六十四頁\編于十四點instanceof運算符:功能:用于判斷一個類是否實現(xiàn)接口,也可用來判斷一個對象是否屬于一個類。格式:對象instanceof類或接口結(jié)果:true或false//程序片斷Personp=newPerson();Students=newStudent();System.out.println("人:p");System.out.println("學(xué)生:s");System.out.println("sinstanceofStudent:"+(sinstanceofStudent));System.out.println("sinstanceofPerson:"+(sinstanceofPerson));System.out.println("pinstanceofPerson:"+(pinstanceofPerson)); System.out.println("pinstanceofStudent:"+(pinstanceofStudent));程序運行結(jié)果:人:p學(xué)生:ssinstanceofStudent:truesinstanceofPerson:truepinstanceofPerson:truepinstanceofStudent:false36目前三十六頁\總數(shù)六十四頁\編于十四點instanceof運算符在強制類型轉(zhuǎn)換之前通過instanceof運算符檢查對象的真實類型,可以避免類型轉(zhuǎn)換異常,從而提高代碼健壯性對象instanceof

類或接口/***測試instanceof運算符的使用。*/publicclassTestPoly2{publicstaticvoidmain(String[]args){Petpet=newPenguin(“Q仔”,“QQ企鵝");//Petpet=newDog(“旺財”,“土狗");pet.eat();if(petinstanceofDog){Dogdog=(Dog)pet;dog.catchingFlyDisc();}elseif(petinstanceofPenguin){Penguinpgn=(Penguin)pet;pgn.swimming();}}}/***測試instanceof運算符的使用。*/publicclassTestPoly2{publicstaticvoidmain(String[]args){//Petpet=newPenguin(“Q仔”,“QQ企鵝");Petpet=newDog(“旺財”,“土狗");pet.eat();if(petinstanceofDog){Dogdog=(Dog)pet;dog.catchingFlyDisc();}elseif(petinstanceofPenguin){Penguinpgn=(Penguin)pet;pgn.swimming();}}}注釋創(chuàng)建Penguin對象語句,取消創(chuàng)建Dog對象語句的注釋目前三十七頁\總數(shù)六十四頁\編于十四點回顧繼承:繼承目前三十八頁\總數(shù)六十四頁\編于十四點導(dǎo)入:為什么使用抽象類?如果我們想訪問調(diào)用子類Dog的print()方法。以下代碼有什么問題?Java中也使用抽象類,限制實例化Petpet=newPet();pet.print();實例化Pet沒有意義publicabstractclassPet{

}

通過抽象類來改進(jìn)實現(xiàn)目前三十九頁\總數(shù)六十四頁\編于十四點導(dǎo)入:為什么使用抽象方法?如果父類的方法沒機會被訪問調(diào)用,以下代碼有什么問題?◎可以使用抽象方法來優(yōu)化抽象方法沒有方法體抽象方法必須在抽象類里抽象方法必須在子類中被實現(xiàn),除非子類是抽象類publicabstractvoidprint();沒有方法體publicabstractclassPet{

publicvoidprint(){//…}}每個子類的print()方法實現(xiàn)不同,父類print()方法的實現(xiàn)是多余的。目前四十頁\總數(shù)六十四頁\編于十四點抽象類(abstract)引例

抽象是面向?qū)ο蟮囊环N重要方法,通過抽象我們能夠設(shè)計一個更普通、更通用的類,例:從許許多多學(xué)生中,抽象出Student類,再從學(xué)生、工人、農(nóng)民、…抽象出Person類。

下面,我們來分析一個例子:Shape類(周長、面積)Circle類(周長、面積)Rect類(周長、面積)

Shape類是假想出的共同父類,現(xiàn)實中不存在叫“形狀”的東西,若去實例化這樣的對象很勉強。為阻止生成Shape類對象,可以將該類聲明為抽象類。

41目前四十一頁\總數(shù)六十四頁\編于十四點抽象方法與抽象類

關(guān)鍵字abstract可用來修飾方法和類,表示“尚未實現(xiàn)”的含義:如果方法的聲明中使用了abstract修飾符,那么該方法就稱為抽象方法。這表示該類中不提供方法的實現(xiàn),即不定義方法體。格式:訪問權(quán)限abstract返回類型方法名([參數(shù)表]);//無方法體例如:將前面Shape類的兩個方法聲明為抽象方法:

publicabstractdoublegetArea();

publicabstractdoublegetPerimeter();注意:◎無方法體與方法體為空是兩個不同的概念。42目前四十二頁\總數(shù)六十四頁\編于十四點如果一個類的聲明中有abstract修飾符,那么該類就成為抽象類。格式:[訪問權(quán)限]abstractclass類名{……}例如:將前面Shape類聲明為抽象類: abstractclassShape{……}◎說明:抽象類不能進(jìn)行實例化,否則出現(xiàn)編譯錯誤;通常,一個抽象類至少定義一個抽象方法,但并不是說非要定義一個抽象方法,即使類體沒有一個抽象方法也是允許;當(dāng)一個類中包含有抽象方法時,該類一定要聲明為抽象類;43目前四十三頁\總數(shù)六十四頁\編于十四點◎說明:(續(xù))當(dāng)子類繼承了一個抽象類時,必須實現(xiàn)該抽象類中定義的全部抽象方法,否則必須聲明為抽象類;當(dāng)類實現(xiàn)了一個接口,但并沒有實現(xiàn)該接口的所有方法時,該類必須聲明為抽象類,否則出錯;抽象方法不能被private、final或static修飾。為什么? (抽象方法必須被子類所覆蓋,如果說明為private,則外部無法訪問,覆蓋也無從談起。若說明為Static,即是不創(chuàng)建對象也能訪問:類名.方法名()

,這要求給出方法體,但與抽象方法的定義相矛盾。)問題:以下哪個是抽象方法的正確形式?(1)abstarctvoidexample()(2)abstarctvoidexample(){}(3)staticabstarctvoidexample()(4)finalabstarctvoidexample()(1)44目前四十四頁\總數(shù)六十四頁\編于十四點抽象類與具體類的比較

45目前四十五頁\總數(shù)六十四頁\編于十四點抽象類引用

◎雖然不能實例化抽象類,但可以創(chuàng)建它的引用。Java支持多態(tài)性,允許通過父類引用來引用類的對象。//使用抽象類引用的例子abstractclassA{ //抽象類 abstractvoidm1(); //抽象方法}classBextendsA{

voidm1(){

System.out.println("Inm1.");} //實現(xiàn)了m1()方法 voidm2(){

System.out.println("Inm2.");

}} publicclassAbstractRef{ publicstaticvoidmain(Stringargs[]){ Bb=newB();

Aa=b; //父類引用 a.m1(); }}程序運行結(jié)果:Inm1.46目前四十六頁\總數(shù)六十四頁\編于十四點接口(interface) 如果一個抽象類中的所有方法都是抽象的,可以采用另一種方式——“接口”來定義。接口的概念接口是抽象方法和常量值的定義的集合。從本質(zhì)上講,接口是一種特殊的抽象類,這種抽象類中只包含常量和方法的定義,而沒有方法的實現(xiàn)。從語法上看,接口是一種與“類”很相似的結(jié)構(gòu),只是接口中的所有方法都是抽象的,只有聲明、沒有方法體。接口聲明的關(guān)鍵字是interface。47目前四十七頁\總數(shù)六十四頁\編于十四點什么是接口?認(rèn)識一下接口◎接口特性:接口不可以被實例化實現(xiàn)類必須實現(xiàn)接口的所有方法實現(xiàn)類可以實現(xiàn)多個接口接口中的變量都是靜態(tài)常量publicinterfaceMyInterface

{publicvoidfoo();//其他方法}所有方法都是:publicabstract抽象類除外Java中的多繼承常作為類型使用目前四十八頁\總數(shù)六十四頁\編于十四點接口的聲明格式:[權(quán)限修飾符]interface接口名

[extends父接口列表]{ //抽象方法和靜態(tài)常量}例如:publicinterfaceRunner{ intid=1; publicvoidstart(); publicvoidrun(); publicvoidstop(); }49目前四十九頁\總數(shù)六十四頁\編于十四點說明:接口的修飾符可以是public或缺??;接口中的方法都是抽象的和公有(public)的,僅有方法說明,沒有方法體;接口中的常量默認(rèn)是公共的、靜態(tài)的和最終的,在聲明時一般不需要用public、static、final。接口關(guān)心的是“做什么”,不關(guān)心“怎樣做”,即是聲明一種規(guī)范,而不是實現(xiàn);生活中類似的例子:電腦主板中的各種插槽等;通過接口可以間接實現(xiàn)多重繼承。50目前五十頁\總數(shù)六十四頁\編于十四點接口的實現(xiàn) 接口規(guī)定了類的“原型”,具體實現(xiàn)由實現(xiàn)該接口的類來完成,格式如下:[修飾符]class類名[extends父類][implements接口1,接口2,…]{ …… //包含對接口的所有方法的實現(xiàn)}//例子publicclassPersonimplementsRunner{

publicvoidstart(){

//準(zhǔn)備工作:彎腰、蹬腿、咬牙、瞪眼

//開跑

}

publicvoidrun(){ //擺動手臂 //維持直線方向

} publicvoidstop(){ //減速直至停止、喝水。

}}51目前五十一頁\總數(shù)六十四頁\編于十四點說明:一個類可以實現(xiàn)多個接口,從而達(dá)到多重繼承的目的;多個無關(guān)的類可以實現(xiàn)同一個接口;通過接口可以實現(xiàn)不相關(guān)類的相同行為,而不需要考慮這些類之間的層次關(guān)系;一個具體類實現(xiàn)接口時,必須實現(xiàn)接口中的所有抽象方法;當(dāng)一個類未實現(xiàn)接口中的所有抽象方法時,應(yīng)聲明為抽象類;類在實現(xiàn)某個接口的抽象方法時,必須使用完全相同的方法頭,例如:接口定義中通常省略public修飾符,但在實現(xiàn)抽象方法時必須顯式使用public修飾符;通過接口可以指明多個類需要實現(xiàn)的方法;與繼承關(guān)系類似,接口與實現(xiàn)類之間存在多態(tài)性。???52目前五十二頁\總數(shù)六十四頁\編于十四點下面類A的定義形式中,哪一個是正確的?(1)classAextendsB,C{ ……}(2)classAimplementsB,C{ ……}(3)classAextendsBimplementsC{ ……}(4)classAextendsBimplementsC,D{ ……}答案:(2)、(3)、(4)53目前五十三頁\總數(shù)六十四頁\編于十四點通過接口可以實現(xiàn)不相關(guān)類的相同行為<<interface>>Runner+start()+run()+stop()Person+start()+run()+stop()+dance()Car+start()+run()+stop()+fillFuel()+crack()Bird+start()+run()+stop()+fly()54目前五十四頁\總數(shù)六十四頁\編于十四點//接口應(yīng)用例子interfaceRunner{ //接口1 publicvoidrun();}interfaceSwimmer{ //接口2 publicvoidswim();}abstractclassAnimal{ //抽象類,去掉關(guān)鍵字abstract是否可行? publicabstractvoideat();}classPersonextendsAnimalimplementsRunner,Swimmer{//繼承類,實現(xiàn)接口 publicvoidrun(){ System.out.println("我是飛毛腿,跑步速度極快!"); } publicvoidswim(){ System.out.println("我游泳技術(shù)很好,會蛙泳、自由泳、仰泳、蝶泳..."); } publicvoideat(){ System.out.println("我牙好胃好,吃啥都香!"); }}55目前五十五頁\總數(shù)六十四頁\編于十四點publicclassInterfaceTest{ publicstaticvoidmain(Stringargs[]){ InterfaceTestt=newInterfaceTest();

Personp=newPerson(); t.m1(p);

//接口回調(diào),下同

t.m2(p); t.m3(p); } publicvoidm1(Runnerr){r.run();} //接口作參數(shù),下同 public

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論