項(xiàng)目8 繼承與多態(tài)_第1頁
項(xiàng)目8 繼承與多態(tài)_第2頁
項(xiàng)目8 繼承與多態(tài)_第3頁
項(xiàng)目8 繼承與多態(tài)_第4頁
項(xiàng)目8 繼承與多態(tài)_第5頁
已閱讀5頁,還剩21頁未讀, 繼續(xù)免費(fèi)閱讀

付費(fèi)下載

下載本文檔

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

文檔簡(jiǎn)介

Java程序設(shè)計(jì)專業(yè)核心課程

精品課程1教學(xué)計(jì)劃ORJECT了解Java程序的單繼承。了解Java中的多繼承。了解Java語言中的多態(tài)分類。理解Java語言中的靜態(tài)多態(tài)。理解Java語言中的動(dòng)態(tài)多態(tài)。1精品課程28.1Java中的繼承為了了解繼承性,先看這樣一個(gè)場(chǎng)景:一位面向?qū)ο蟮某绦騿T小張,在編程過程中需要描述和處理個(gè)人信息,于是定義了類Person,代碼如下://Person.java文件packagecom.a51work6;importjava.util.Date;publicclassPerson{//名字privateStringname;//年齡privateintage;//出生日期privateDatebirthDate;3精品課程publicStringgetInfo(){return"Person[name="+name+",age="+age+",birthDate="+birthDate+"]";}}一周以后,程序員小張又遇到了新的需求,需要描述和處理學(xué)生信息,于是他又定義了一個(gè)新的類Student,代碼如下://Student.java文件packagecom.a51work6;importjava.util.Date;publicclassStudent{//所在學(xué)校publicStringschool;//名字privateStringname;//年齡privateintage;//出生日期privateDatebirthDate;publicStringgetInfo(){return"Person[name="+name+",age="+age+",birthDate="+birthDate+"]";}}很多人會(huì)對(duì)程序員小張的做法表示理解并相信這是可行的,但問題在于Student和Person兩個(gè)類的結(jié)構(gòu)太接近了,后者只比前者多了一個(gè)屬性school,卻要重復(fù)定義其所有的內(nèi)容,實(shí)在讓人“不甘心”。Java提供了解決類似問題的機(jī)制,那就是類的繼承,示例代碼如下://Student.java文件packagecom.a51work6;importjava.util.Date;publicclassStudentextendsPerson{//所在學(xué)校privateStringschool;}4精品課程5精品課程Student類繼承了Person類中的所有成員變量和方法,從上述代碼可見繼承使用的關(guān)鍵字是extends,extends后面的Person是父類。如果在類的聲明中沒有使用extends關(guān)鍵字指明其父類,則默認(rèn)父類為Object類,java.lang.Object類是Java的根類,所有Java類(包括數(shù)組)都直接或間接繼承了Object類。在Object類中定義了一些有關(guān)面向?qū)ο髾C(jī)制的基本方法,如equals()、toString()和finalize()等方法。面向?qū)ο蠓治雠c設(shè)計(jì)(ObjetOrientedAnalysisDesign,OOAD)會(huì)用到UML圖,類圖非常重要,用來描述系統(tǒng)靜態(tài)結(jié)構(gòu)。Student繼承Person的類圖如圖8-1所示,類圖中的各個(gè)元素說明如圖8-2所示。類用矩形表示,一般分為上、中、下三個(gè)部分,上部分是類名,中間部分是成員變量,下部分是成員方法。實(shí)線+空心箭頭表示繼承關(guān)系,箭頭指向父類,箭頭末端是子類。UML類圖中還有很多關(guān)系,如圖8-3所示,虛線+空心箭頭表示實(shí)現(xiàn)關(guān)系,箭頭指向接口,箭頭末端是實(shí)線類。圖8-1Student繼承Person的類圖圖8-2類圖中的元素6精品課程圖8-3元素之間關(guān)系說明8.1.1?調(diào)用父類構(gòu)造方法當(dāng)子類實(shí)例化時(shí),不僅需要初始化子類成員變量,也需要初始化父類成員變量,初始化父類成員變量需要調(diào)用父類構(gòu)造方法,子類使用super關(guān)鍵字可調(diào)用父類構(gòu)造方法。下面看一個(gè)示例,現(xiàn)有父類Person和子類Student,它們之間的類圖如圖8-4所示。圖8-4Person和Student之間的類圖父類Person的代碼如下://Person.java文件packagecom.a51work6;importjava.util.Date;publicclassPerson{//名字privateStringname;//年齡privateintage;//出生日期privateDatebirthDate;//三個(gè)參數(shù)構(gòu)造方法publicPerson(Stringname,intage,Dated){=name;this.age=age;birthDate=d;}publicPerson(Stringname,intage){//調(diào)用三個(gè)參數(shù)構(gòu)造方法this(name,age,newDate());}}子類Student的代碼如下://Student.java文件packagecom.a51work6;7精品課程importjava.util.Date;publicclassStudentextendsPerson{//所在學(xué)校privateStringschool;publicStudent(Stringname,intage,Dated,Stringschool){super(name,age,d);①this.school=school;}publicStudent(Stringname,intage,Stringschool){//this.school=school;//編譯錯(cuò)誤super(name,age);②this.school=school;}publicStudent(Stringname,Stringschool){//編譯錯(cuò)誤③//super(name,30);this.school=school;}}在Student子類代碼中第①行和第②行都是調(diào)用父類構(gòu)造方法,代碼第①行super(name,age,d)語句是調(diào)用父類的Person(Stringname,intage,Dated)構(gòu)造方法,代碼第②行super(name,age)語句是調(diào)用父類的Person(Stringname,intage)構(gòu)造方法。8精品課程代碼第③行構(gòu)造方法由于沒有super語句,編譯器會(huì)試圖調(diào)用父類默認(rèn)構(gòu)造方法(無參數(shù)構(gòu)造方法),但是父類Person并沒有默認(rèn)構(gòu)造方法,因此會(huì)產(chǎn)生編譯錯(cuò)誤。解決此編譯錯(cuò)誤有以下三種辦法。第一種:在父類Person中添加默認(rèn)構(gòu)造方法,子類Student會(huì)隱式調(diào)用父類的默認(rèn)構(gòu)造方法。第二種:在子類Studen構(gòu)造方法中添加super語句,顯式調(diào)用父類構(gòu)造方法,但super語句必須是第一條語句。第三種:在子類Studen構(gòu)造方法中添加this語句,顯式調(diào)用當(dāng)前對(duì)象其他構(gòu)造方法,this語句必須也是第一條語句。8.1.2?成員變量隱藏和方法覆蓋子類繼承父類后,有子類中有可能聲明了與父類一樣的成員變量或方法,那么會(huì)出現(xiàn)什么情況呢?子類成員變量與父類一樣,會(huì)屏蔽父類中的成員變量,稱為“成員變量隱藏”。示例代碼如下。//ParentClass.java文件packagecom.a51work6;classParentClass{//x成員變量intx=10;①}classSubClassextendsParentClass{//屏蔽父類x成員變量intx=20;②publicvoidprint(){//訪問子類對(duì)象x成員變量9精品課程10精品課程System.out.println("x="+x);③//訪問父類x成員變量System.out.println("super.x="+super.x);④}}其調(diào)用代碼如下://HelloWorld.java文件packagecom.a51work6;publicclassHelloWorld{publicstaticvoidmain(String[]args){//實(shí)例化子類SubClassSubClasspObj=newSubClass();//調(diào)用子類print方法pObj.print();}}其運(yùn)行結(jié)果如下:x=20super.x=1011精品課程上述代碼第①行是在ParentClass類中聲明x成員變量,那么在它的子類SubClass代碼第②行中也聲明了x成員變量,它會(huì)屏蔽父類中的x成員變量。那么代碼第③行的x是子類中的x成員變量。如果要調(diào)用父類中的x成員變量,則需要使用super關(guān)鍵字,見代碼第④行的super.x。8.1.3?方法的覆蓋如果子類方法與父類方法完全相同,即有相同的方法名、相同的參數(shù)列表和相同的返回值,只是方法體不同,稱為子類覆蓋(Override)父類方法。示例代碼如下://ParentClass.java文件packagecom.a51work6;classParentClass{//x成員變量intx;protectedvoidsetValue(){①x=10;}}classSubClassextendsParentClass{//屏蔽父類x成員變量intx;@OverridepublicvoidsetValue(){//覆蓋父類方法②//訪問子類對(duì)象x成員變量x=20;//調(diào)用父類setValue()方法super.setValue();}publicvoidprint(){//訪問子類對(duì)象x成員變量System.out.println("x="+x);//訪問父類x成員變量System.out.println("super.x="+super.x);}}其調(diào)用代碼如下://HelloWorld.java文件packagecom.a51work6;publicclassHelloWorld{publicstaticvoidmain(String[]args){//實(shí)例化子類SubClassSubClasspObj=newSubClass();//調(diào)用setValue方法pObj.setValue();//調(diào)用子類print方法pObj.print();}}其運(yùn)行結(jié)果如下:x=20super.x=1012精品課程命令說明快捷鍵重命名重命名所選擇的

Java

元素Alt+Shift+R移動(dòng)移動(dòng)所選擇的

Java

元素Alt+Shift+V抽取方法創(chuàng)建一個(gè)包含當(dāng)前所選語句或表達(dá)式的新方法,并相關(guān)的引用Alt+Shift+M抽取局部變量創(chuàng)建為當(dāng)前所選表達(dá)式指定的新變量,并將選擇替換為對(duì)新變量的引用Alt+Shift+L命令說明快捷鍵重命名重命名所選擇的

Java

元素Alt+Shift+R移動(dòng)移動(dòng)所選擇的

Java

元素Alt+Shift+V抽取方法創(chuàng)建一個(gè)包含當(dāng)前所選語句或表達(dá)式的新方法,并相關(guān)的引用Alt+Shift+M抽取局部變量創(chuàng)建為當(dāng)前所選表達(dá)式指定的新變量,并將選擇替換為對(duì)新變量的引用Alt+Shift+L命令說明快捷鍵重命名重命名所選擇的

Java

元素Alt+Shift+R移動(dòng)移動(dòng)所選擇的

Java

元素Alt+Shift+V抽取方法創(chuàng)建一個(gè)包含當(dāng)前所選語句或表達(dá)式的新方法,并相關(guān)的引用Alt+Shift+M抽取局部變量創(chuàng)建為當(dāng)前所選表達(dá)式指定的新變量,并將選擇替換為對(duì)新變量的引用Alt+Shift+L命令說明快捷鍵重命名重命名所選擇的

Java

元素Alt+Shift+R移動(dòng)移動(dòng)所選擇的

Java

元素Alt+Shift+V抽取方法創(chuàng)建一個(gè)包含當(dāng)前所選語句或表達(dá)式的新方法,并相關(guān)的引用Alt+Shift+M抽取局部變量創(chuàng)建為當(dāng)前所選表達(dá)式指定的新變量,并將選擇替換為對(duì)新變量的引用Alt+Shift+L上述代碼第①行是在ParentClass類中聲明setValue方法,那么在它的子類SubClass代碼第②行覆蓋父類中的setValue方法,在聲明方法時(shí)添加@Override注解@Override注解不是方法覆蓋過程中必需的,其只是錦上添花,但添加@Override注解有以下兩個(gè)好處。(1)提高程序的可讀性。(2)編譯器會(huì)檢查@Override注解的方法在父類中是否存在,如果不存在則報(bào)錯(cuò)。注意方法重寫時(shí)應(yīng)遵循以下兩個(gè)原則。(1)覆蓋后的方法不能比原方法有更嚴(yán)格的訪問控制(可以相同)。例如,將代碼第②行訪問控制修飾符public修改為private,將會(huì)發(fā)生編譯錯(cuò)誤,因?yàn)楦割愒椒ㄊ莗rotected的。(2)覆蓋后的方法不能比原方法產(chǎn)生更多的異常。8.2多態(tài)在面向?qū)ο蟪绦蛟O(shè)計(jì)中,多態(tài)是一個(gè)非常重要的特性,理解多態(tài)有利于進(jìn)行面向?qū)ο蟮姆治雠c設(shè)計(jì)。8.2.1?多態(tài)概念發(fā)生多態(tài)要有以下三個(gè)前提條件。(1)繼承。多態(tài)發(fā)生一定要在子類和父類之間。(2)覆蓋。子類覆蓋了父類的方法。(3)聲明的變量類型是父類類型,但實(shí)例則指向子類實(shí)例。下面通過一個(gè)示例理解什么是多態(tài)。如圖8-5所示,父類Figure(幾何圖形)類有一個(gè)onDraw(繪圖)方法,有兩個(gè)子類:Ellipse(橢圓形)和Triangle(三角形),Ellipse和Triangle覆蓋onDraw方法,Ellipse和Triangle都有onDraw方法,但具體實(shí)現(xiàn)的方式不同。14精品課程

圖8-5幾何圖形類圖其具體代碼如下://Figure.java文件packagecom.a51work6;publicclassFigure{//繪制幾何圖形方法publicvoidonDraw(){System.out.println("繪制Figure...");}}//Ellipse.java文件packagecom.a51work6;//幾何圖形橢圓形publicclassEllipseextendsFigure{//繪制幾何圖形方法@OverridepublicvoidonDraw(){System.out.println("繪制橢圓形...");}}//Triangle.java文件packagecom.a51work6;//幾何圖形三角形publicclassTriangleextendsFigure{//繪制幾何圖形方法@OverridepublicvoidonDraw(){System.out.println("繪制三角形...");}}其調(diào)用代碼如下://HelloWorld.java文件packagecom.a51work6;publicclassHelloWorld{publicstaticvoidmain(String[]args){//f1變量是父類類型,指向父類實(shí)例Figuref1=newFigure();①f1.onDraw();//f2變量是父類類型,指向子類實(shí)例,發(fā)生多態(tài)Figuref2=newTriangle();②f2.onDraw();//f3變量是父類類型,指向子類實(shí)例,發(fā)生多態(tài)Figuref3=newEllipse();③f3.onDraw();//f4變量是子類類型,指向子類實(shí)例Trianglef4=newTriangle();④f4.onDraw();}}上述代碼第②行和第③行符合多態(tài)的三個(gè)前提條件,因此會(huì)發(fā)生多態(tài)。而代碼第①行和第④行都不符合,就沒有發(fā)生多態(tài)。其運(yùn)行結(jié)果如下:繪制Figure...繪制三角形...繪制橢圓形...繪制三角形...從運(yùn)行結(jié)果可知,多態(tài)發(fā)生時(shí),Java虛擬機(jī)運(yùn)行時(shí)根據(jù)引用變量指向的實(shí)例調(diào)用它的方法,而不是根據(jù)引用變量的類型調(diào)用。8.2.2?引用類型檢查有時(shí)候需要在運(yùn)行時(shí)判斷一個(gè)對(duì)象是否屬于某個(gè)引用類型,這時(shí)可以使用instanceof運(yùn)算符,其語法格式如下:objinstanceoftype其中,obj是一個(gè)對(duì)象,type是引用類型,如果obj對(duì)象是type引用類型實(shí)例,則返回true,否則返回false。為了介紹引用類型檢查,先看一個(gè)示例,如圖8-6所示的類圖,展示了繼承層次樹,Person類是根類,Student是Person的直接子類,Worker也是Person的直接子類。圖8-6繼承關(guān)系類圖繼承層次樹中的具體實(shí)現(xiàn)代碼如下://Person.java文件packagecom.a51work6;publicclassPerson{Stringname;intage;publicPerson(Stringname,intage){=name;this.age=age;}@OverridepublicStringtoString(){return"Person[name="+name+",age="+age+"]";}}//Worker.java文件packagecom.a51work6;publicclassWorkerextendsPerson{Stringfactory;publicWorker(Stringname,intage,Stringfactory){super(name,age);this.factory=factory;}@OverridepublicStringtoString(){return"Worker[factory="+factory+",name="+name+",age="+age+"]";}}//Student.java文件packagecom.a51work6;publicclassStudentextendsPerson{Stringschool;publicStudent(Stringname,intage,Stringschool){super(name,age);this.school=school;}@OverridepublicStringtoString(){return"Student[school="+school+",name="+name+",age="+age+"]";}}其調(diào)用代碼如下://HelloWorld.java文件packagecom.a51work6;publicclassHelloWorld{publicstaticvoidmain(String[]args){Studentstudent1=newStudent("Tom",18,"清華大學(xué)");①Studentstudent2=newStudent("Ben",28,"北京大學(xué)");②Studentstudent3=newStudent("Tony",38,"香港大學(xué)");③Workerworker1=newWorker("Tom",18,"鋼廠");④Workerworker2=newWorker("Ben",20,"電廠");⑤Person[]people={student1,student2,student3,worker1,worker2};⑥intstudentCount=0;intworkerCount=0;for(Personitem:people){⑦if(iteminstanceofWorker){⑧workerCount++;}elseif(iteminstanceofStudent){⑨studentCount++;}}System.out.printf("工人人數(shù):%d,學(xué)生人數(shù):%d",workerCount,studentCount);}}上述代碼第①行至第③行創(chuàng)建了3個(gè)Student實(shí)例,代碼第④行和第⑤行創(chuàng)建了兩個(gè)Worker實(shí)例,然后程序把這5個(gè)實(shí)例放入people數(shù)組中。代碼第⑦行使用for-each遍歷people數(shù)組元素,當(dāng)從people數(shù)組中取出元素時(shí),元素類型是People類型,但是實(shí)例不知道是哪個(gè)子類(Student和Worker)實(shí)例。代碼第⑧行iteminstanceofWorker表達(dá)式是判斷數(shù)組中的元素是否是Worker實(shí)例;類似地,第⑨行iteminstanceofStudent表達(dá)式是判斷數(shù)組中的元素是否是Student實(shí)例。其運(yùn)行結(jié)果如下。工人人數(shù):2,學(xué)生人數(shù):38.2.3?引用類型轉(zhuǎn)換在3.6節(jié)中介紹過數(shù)值類型相互轉(zhuǎn)換,引用類型也可以進(jìn)行轉(zhuǎn)換,但并不是所有的引用類型都能互相轉(zhuǎn)換,只有屬于同一棵繼承層次樹中的引用類型才可以轉(zhuǎn)換。在上一小節(jié)示例上修改HelloWorld.java代碼。//HelloWorld.java文件packagecom.a51work6;publicclassHelloWorld{publicstaticvoidmain(String[]args){Personp1=newStudent("Tom",18,"清華大學(xué)");Personp2=newWorker("Tom",18,"鋼廠");Personp3=newPerson("Tom",28);Studentp4=newStudent("Ben",40,"清華大學(xué)");Workerp5=newWorker("Tony",28,"鋼廠");…}}上述代碼創(chuàng)建了5個(gè)實(shí)例:p1、p2、p3、p4和p5,它們的類型都是Person繼承層次樹中的引用類型,其中,p1和p4是Student實(shí)例;p2和p5是Worker實(shí)例;p3是Person實(shí)例。首先,對(duì)象類型轉(zhuǎn)換一定發(fā)生在繼承的前提下,p1和p2都聲明為Person類型,而實(shí)例是由Person子類型實(shí)例轉(zhuǎn)化的。表8-1歸納了p1、p2、p3、p4和p5這5個(gè)實(shí)例與Worker、Student和Person這3種類型之間的轉(zhuǎn)換關(guān)系。表8-1類型轉(zhuǎn)換對(duì)象Person類型Worker類型Student類型說明p1支持不支持支持(向下轉(zhuǎn)型)類型:Person實(shí)例:Studentp2支持支持(向下轉(zhuǎn)型)不支持類型:Person實(shí)例:Workerp3支持不支持不支持類型:Person實(shí)例:Personp4支持(向上轉(zhuǎn)型)不支持支持類型:Student實(shí)例:Studentp5支持(向上轉(zhuǎn)型)支持不支持類型:Worker實(shí)例:Worker其實(shí)p1本質(zhì)上是Student實(shí)例,但是表面上看卻是Person類型,編譯器也無法推斷p1的實(shí)例是Person、Student還是Worker,此時(shí)可以使用instanceof操作符來判斷它是哪一類的實(shí)例。引用類型轉(zhuǎn)換也是通過小括號(hào)運(yùn)算符實(shí)現(xiàn)的,類型轉(zhuǎn)換有兩個(gè)方向:將父類引用類型變量轉(zhuǎn)換為子類類型,這種轉(zhuǎn)換稱為向下轉(zhuǎn)型(downcast);將子類引用類型變量轉(zhuǎn)換為父類類型,這種轉(zhuǎn)換稱為向上轉(zhuǎn)型(upcast)。向下轉(zhuǎn)型需要強(qiáng)制轉(zhuǎn)換,而向上轉(zhuǎn)型是自動(dòng)的。下面通過示例詳細(xì)說明向下轉(zhuǎn)型和向上轉(zhuǎn)型,在HelloWorld.java的main方法中添加如下代碼://向上轉(zhuǎn)型Personp=(Person)p4;①//向下轉(zhuǎn)型Studentp11=(Student)p1;②Workerp12=(Worker)p2;③//Studentp111=(Student)p2;//運(yùn)行時(shí)異常④if(p2instanceofStudent){Studentp111=(Student)p2;}//Workerp121=(Worker)p1;//運(yùn)行時(shí)異常⑤if(p1instanceofWorker){Workerp121=(Worker)p1;}//Studentp131=(Student)p3;//運(yùn)行時(shí)異常⑥if(p3instanceofStudent){Studentp131=(Student)p3;}上述代碼第①行將p4對(duì)象轉(zhuǎn)換為Person類型,p4本質(zhì)上是Student實(shí)例,這是向上轉(zhuǎn)型,這種轉(zhuǎn)換是自動(dòng)的,其實(shí)不需要小括號(hào)(Person)進(jìn)行強(qiáng)制類型轉(zhuǎn)換。代碼第②行和第③行是向下類型轉(zhuǎn)換,它們的轉(zhuǎn)型都能成功。而代碼第④、⑤、⑥行都會(huì)發(fā)生運(yùn)行時(shí)異常ClassCastException,如果不能確定實(shí)例是哪一種類型,可以在轉(zhuǎn)型之前使用instanceof運(yùn)算符判斷一下。8.2.4?final關(guān)鍵字在前面的學(xué)習(xí)過程中,為了聲明常量使用過final關(guān)鍵字,在Java中final關(guān)鍵字的作用還有很多,如其能修飾變量、方法和類。接下來將進(jìn)行詳細(xì)說明。8.2.5?final修飾變量final修飾的變量可視為常量,只能賦值一次,但是final在修飾局部變量和成員變量時(shí)有所不同。(1)final修飾的局部變量必須在使用之前被賦值一次才能使用。(2)final修飾的成員變量在聲明時(shí)沒有賦值的稱為“空白final變量”,空白final變量必須在構(gòu)造方法或靜態(tài)代碼塊中初始化。final修飾變量的示例代碼如下://FinalDemo.java文件packagecom.a51work6;classFinalDemo{voiddoSomething(){//沒有在聲明的同時(shí)賦值fin

溫馨提示

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