目錄
第1關(guān):封裝、繼承和多態(tài)進(jìn)階(一)
相關(guān)知識(shí)
面向?qū)ο笏枷?/p>
封裝
繼承
組合和繼承
構(gòu)造函數(shù)
super()和this()
編程要求
第2關(guān):封裝、繼承和多態(tài)進(jìn)階(二)
相關(guān)知識(shí)
重寫和重載
abstract(抽象類)和interface(接口)
final關(guān)鍵字
static關(guān)鍵字
static關(guān)鍵字的作用
多態(tài)
編程要求
第3關(guān):封裝、繼承和多態(tài)進(jìn)階(三)
相關(guān)知識(shí)
測(cè)試說明
???????
第1關(guān):封裝、繼承和多態(tài)進(jìn)階(一)
相關(guān)知識(shí)
為了完成本關(guān)任務(wù),我們回顧一下前面所學(xué)知識(shí):1.面向?qū)ο笏枷?;2.封裝;3.繼承;4.構(gòu)造函數(shù);5.super()
和this()
。
面向?qū)ο笏枷?/h4>
-
構(gòu)造器:new
就是一個(gè)構(gòu)造器,作用是:①分配空間;②賦初始值(避免錯(cuò)誤,簡(jiǎn)化輸入);
-
new Object(Parameters)
構(gòu)造器調(diào)用構(gòu)造函數(shù),傳參為了賦初始值;
-
對(duì)象的基本元素是:屬性和方法 類成員(屬性和方法)。屬性最為重要,屬性的集合是一個(gè)狀態(tài),方法是一個(gè)狀態(tài)到另一個(gè)狀態(tài)的橋梁;
-
封裝:屬性和處理屬性的方法集合起來。把數(shù)據(jù)及數(shù)據(jù)的操作方法放在一起,作為一個(gè)相互依存的整體,即對(duì)象。
構(gòu)造器:new
就是一個(gè)構(gòu)造器,作用是:①分配空間;②賦初始值(避免錯(cuò)誤,簡(jiǎn)化輸入);
new Object(Parameters)
構(gòu)造器調(diào)用構(gòu)造函數(shù),傳參為了賦初始值;
對(duì)象的基本元素是:屬性和方法 類成員(屬性和方法)。屬性最為重要,屬性的集合是一個(gè)狀態(tài),方法是一個(gè)狀態(tài)到另一個(gè)狀態(tài)的橋梁;
封裝:屬性和處理屬性的方法集合起來。把數(shù)據(jù)及數(shù)據(jù)的操作方法放在一起,作為一個(gè)相互依存的整體,即對(duì)象。
面向?qū)ο笫腔诿嫦蜻^程而言的,面向?qū)ο笫菍⒐δ艿韧ㄟ^對(duì)象來實(shí)現(xiàn),將功能封裝進(jìn)對(duì)象之中,讓對(duì)象去實(shí)現(xiàn)具體的細(xì)節(jié);這種思想是將數(shù)據(jù)作為第一位,而方法或者說是算法作為其次,這是對(duì)數(shù)據(jù)一種優(yōu)化,操作起來更加的方便,簡(jiǎn)化了過程。
封裝
訪問權(quán)限的控制常被稱為是具體實(shí)現(xiàn)的隱藏。把數(shù)據(jù)和方法包裝進(jìn)類中,以及具體實(shí)現(xiàn)的隱藏共同被稱為封裝。
-
public
:可以被所有其他類訪問; -
protected
:自身、子類、及同一個(gè)包中類(接受包外的子類訪問); -
default
:同一包中的類可以訪問,聲明時(shí)沒有加修飾符,認(rèn)為是friendly
(拒絕一切外包訪問); -
private
:只能被自己訪問和修改。
類的訪問控制符只有三種:public
、private
、protected
default
是無訪問控制符。
繼承
在一個(gè)子類被創(chuàng)建的時(shí)候,首先會(huì)在內(nèi)存中創(chuàng)建一個(gè)父類對(duì)象,然后在父類對(duì)象外部放上子類獨(dú)有的屬性,兩者合起來形成一個(gè)子類的對(duì)象。繼承使子類擁有父類所有的屬性和方法,但是父類對(duì)象中的私有屬性和方法,子類是無法訪問到的,只是擁有,但不能使用。子類不能繼承父類的構(gòu)造函數(shù),只是顯式或隱式調(diào)用,可以從子類調(diào)用超類的構(gòu)造函數(shù)。
用new
創(chuàng)建子類的對(duì)象時(shí),若子類沒有帶參構(gòu)造函數(shù),將先執(zhí)行父類的無參構(gòu)造函數(shù),然后再執(zhí)行自己的構(gòu)造函數(shù)。父類定義了有參的構(gòu)造函數(shù)后,可以不定義無參的構(gòu)造函數(shù),系統(tǒng)也不會(huì)提供默認(rèn)的無參構(gòu)造函數(shù)。這時(shí)子類只能調(diào)用父類的有參構(gòu)造函數(shù)。 Java類是單繼承,Java接口可以多繼承。類可以實(shí)現(xiàn)多個(gè)接口,接口可以繼承(擴(kuò)展)多個(gè)接口。先繼承后實(shí)現(xiàn)接口。
組合和繼承
組合是指在新類里面創(chuàng)建原有的類的對(duì)象,重復(fù)利用已有類的功能。(“has - a
”) 組合和繼承都允許在新的類中設(shè)置子對(duì)象,只是組合是顯式的,而繼承是隱式的。組合中的整體類和繼承中的子類對(duì)應(yīng),組合中的局部類和繼承中的父類對(duì)應(yīng)。
組合和繼承的選擇規(guī)則:
① 除非兩個(gè)類之間是“is - a
”的關(guān)系,否則不要輕易地使用繼承。過多的使用繼承會(huì)破壞代碼的可維護(hù)性,當(dāng)父類修改時(shí),會(huì)影響所有繼承他的子類,增加了程序維護(hù)的難度和成本。
②不要僅僅為實(shí)現(xiàn)多態(tài)而使用繼承,如果類之間沒有“is - a
”關(guān)系,可以通過實(shí)現(xiàn)接口與組合的方式來達(dá)到相同的目的。
構(gòu)造函數(shù)
用來在對(duì)象實(shí)例化時(shí)初始化對(duì)象的成員變量。
特點(diǎn):
① 方法名必須和類名相同,不能有返回值(也不能為void
);
② 一個(gè)類可以有多個(gè)構(gòu)造函數(shù),沒有定義的話,編譯器會(huì)在源代碼編譯成字節(jié)碼文件的過程中會(huì)提供一個(gè)沒有參數(shù)的默認(rèn)的構(gòu)造方法。若定義后,不會(huì)再創(chuàng)建默認(rèn)的構(gòu)造方法;
③構(gòu)造函數(shù)的參數(shù)有(0
到多個(gè));
④構(gòu)造函數(shù)在對(duì)象實(shí)例化時(shí)會(huì)被自動(dòng)調(diào)用,且只運(yùn)行一次;普通方法是在程序執(zhí)行到時(shí)才調(diào)用且可以被該對(duì)象調(diào)用多次;
⑤構(gòu)造函數(shù)的作用是完成對(duì)象的初始化;
⑥構(gòu)造函數(shù)不能被繼承,不能被覆蓋,能被重載;
⑦子類可以通過super()
關(guān)鍵字來顯示調(diào)用父類的構(gòu)造函數(shù),父類沒有提供無參構(gòu)造,子類的構(gòu)造函數(shù)中必須顯式得調(diào)用父類的構(gòu)造函數(shù);
⑧父類和子類都沒有定義構(gòu)造函數(shù)時(shí),編譯器都會(huì)為父類生成一個(gè)默認(rèn)的無參構(gòu)造,給子類也生成一個(gè)默認(rèn)的無參的構(gòu)造函數(shù);
⑨構(gòu)造方法會(huì)在成員變量之后初始化; ⑩構(gòu)造方法不能被static
、final
、synchronize
、abstract
、native
修飾,但可以被public
、private
、protect
修飾。
在繼承的時(shí)候,父類當(dāng)然也有構(gòu)造方法,如果你要?jiǎng)?chuàng)建子類的對(duì)象,那么執(zhí)行的過程首先是調(diào)用父類的無參構(gòu)造方法生成父類的對(duì)象,然后再調(diào)用子類的無參構(gòu)造方法來生成子類對(duì)象。繼承的時(shí)候都是先生成父類的對(duì)象,然后再生成子類的對(duì)象。
通過使用this
關(guān)鍵字帶上參數(shù),可以在一個(gè)構(gòu)造函數(shù)中調(diào)用另外一個(gè)構(gòu)造函數(shù)。這是this
除了單純表示“當(dāng)前對(duì)象”(注意是針對(duì)對(duì)象而不是類的概念)之外的第二個(gè)作用。但是注意3
點(diǎn): ① 必須放在第一行;
②只能調(diào)用一個(gè)其它的構(gòu)造函數(shù)。(也許可以這樣理解,正是因?yàn)橛辛说谝稽c(diǎn),如果可以調(diào)用多個(gè)的話,那么就無法放在“第一行”,所以只能允許一次調(diào)用);
③只能是構(gòu)造函數(shù)調(diào)用構(gòu)造函數(shù),普通函數(shù)無法調(diào)用構(gòu)造函數(shù)。
super()
和this()
-
super()
關(guān)鍵字表示超類的意思,當(dāng)前類是從超類繼承而來。this
表示當(dāng)前對(duì)象; -
只有在重寫(
Override
)父類的方法中,子類要調(diào)用繼承自父類的方法,才使用super
關(guān)鍵字。 使用super()
或者this()
方法是必須放在構(gòu)造函數(shù)的第一行; -
由于
this
函數(shù)指向的構(gòu)造函數(shù)默認(rèn)有super()
方法,所以規(guī)定this()
和super()
不能同時(shí)出現(xiàn)在一個(gè)構(gòu)造函數(shù)中。 因?yàn)?code>static方法或者語句塊沒有實(shí)例時(shí)可以使用,而此時(shí)不需要構(gòu)造實(shí)例,所以不能用this()
和super()
。
編程要求
根據(jù)提示,在右側(cè)編輯器Begin-End
處補(bǔ)充代碼:
-
聲明一個(gè)抽象類
Pet
,封裝屬性name
和sex
,聲明一個(gè)帶有兩個(gè)參數(shù)的構(gòu)造函數(shù),聲明抽象方法void talk()
和void eat()
; -
聲明一個(gè)
Dog
類繼承自Pet
,封裝屬性color
,聲明帶有三個(gè)參數(shù)的構(gòu)造函數(shù),復(fù)寫talk()
和eat()
方法; -
聲明一個(gè)
Cat
類繼承自Pet
,封裝屬性weight
,聲明帶有三個(gè)參數(shù)的構(gòu)造函數(shù),復(fù)寫talk()
和eat()
方法; -
編寫測(cè)試類,通過有參構(gòu)造函數(shù)實(shí)例化
Dog
類對(duì)象,調(diào)用talk()
方法和eat()
方法;通過有參構(gòu)造函數(shù)實(shí)例化Cat
類對(duì)象 ,調(diào)用talk()
方法和eat()
方法;package case1; import java.util.Scanner; public class Task1 { public static void main(String[] args) { Scanner sc = new Scanner(System.in); String dogName = sc.next(); String dogSex = sc.next(); String dogColor = sc.next(); String catName = sc.next(); String catSex = sc.next(); double catWeight = sc.nextDouble(); // 通過有參構(gòu)造函數(shù)實(shí)例化Dog類對(duì)象dog // dog調(diào)用talk()方法 // dog調(diào)用eat()方法 /********* begin *********/ Dog dog=new Dog(dogName,dogSex,dogColor); dog.talk(); dog.eat(); /********* end *********/ // 通過有參構(gòu)造函數(shù)實(shí)例化Cat類對(duì)象cat // cat調(diào)用talk()方法 // cat調(diào)用eat()方法 /********* begin *********/ Cat cat = new Cat(catName,catSex,catWeight); cat.talk(); cat.eat(); /********* end *********/ } } // 抽象類Pet 封裝屬性name和sex // 構(gòu)造函數(shù)初始化name和sex // 聲明抽象方法talk() // 聲明抽象方法eat() abstract class Pet { /********* begin *********/ private String name; private String sex; public Pet(String n,String s){ this.name=n; this.sex=s; } public void output(){ System.out.print("名稱:"+this.name+",性別:"+this.sex); } public void putName(){ System.out.print(name); } abstract public void talk(); abstract public void eat(); /********* end *********/ } // Dog類繼承自Pet類 封裝屬性color // 構(gòu)造函數(shù)初始化name、sex和color // 實(shí)現(xiàn)自己的talk()方法和eat()方法 // talk()輸出'名稱:name,性別:sex,顏色:color,汪汪叫' // eat()輸出'name吃骨頭' class Dog extends Pet { /********* begin *********/ private String color; public Dog(String n,String s,String c){ super(n,s); this.color=c; } public void talk(){ //System.out.println("名稱:"+this.name+",性別:"+this.sex+",顏色:"+this.color+",汪汪叫"); super.output(); System.out.println(",顏色:"+this.color+",汪汪叫"); } public void eat(){ putName(); System.out.println("吃骨頭!"); } /********* end *********/ } // Cat類繼承自Pet類 封裝屬性weight // 構(gòu)造函數(shù)初始化name、sex和weight // 實(shí)現(xiàn)自己的talk()方法和eat()方法 // talk()輸出'名稱:name,性別:sex,體重:weight kg,喵喵叫' // eat()輸出'name吃魚' class Cat extends Pet { /********* begin *********/ private double weight; public Cat(String n,String s,double w){ super(n,s); this.weight=w; } public void talk(){ //System.out.println("名稱:"+name+",性別:"+sex+",體重:"+weight+"kg,喵喵叫"); output(); System.out.println(",體重:"+weight+"kg,喵喵叫"); } public void eat(){ putName(); System.out.println("吃魚!"); } /********* end *********/ }
第2關(guān):封裝、繼承和多態(tài)進(jìn)階(二)
相關(guān)知識(shí)
為了完成本關(guān)任務(wù),你需要掌握:1.重寫和重載;2.abstract
(抽象類)和interface
(接口);3.final
關(guān)鍵字;4.static
關(guān)鍵字;5.多態(tài)。
重寫和重載
方法重載(overload
):
-
必須是同一個(gè)類;
-
方法名(也可以叫函數(shù))一樣;
-
參數(shù)類型不一樣或參數(shù)數(shù)量或順序不一樣;
-
不能通過返回值來判斷重載。
方法的重寫(override
)
子類重寫了父類的同名方法,兩同兩小一大原則:
-
方法名相同,參數(shù)類型相同;
-
子類返回類型是父類返回類型的子類;
-
子類拋出異常小于等于父類方法拋出異常;
-
子類訪問權(quán)限大于等于父類方法訪問權(quán)限。
-
-
在重寫中,運(yùn)用的是動(dòng)態(tài)單分配,根據(jù)
new
的類型確定對(duì)象,從而確定調(diào)用的方法; -
在重載中,運(yùn)用的是靜態(tài)多分配,根據(jù)靜態(tài)類型確定對(duì)象,不能根據(jù)
new
的類型確定調(diào)用方法; -
多態(tài)中,
Father f = new Son()
。成員變量:編譯運(yùn)行參考左邊; 成員函數(shù):編譯看左邊,運(yùn)行看右邊; 靜態(tài)函數(shù):編譯運(yùn)行看左邊。
abstract
(抽象類)和interface
(接口)
抽象類
-
用
abstract
修飾的類表示抽象類,抽象類位于繼承樹的抽象層,抽象類不能被實(shí)例化。 -
用
abstract
修飾的方法表示抽象方法,抽象方法沒有方法體。抽象方法用來描述系統(tǒng)具有什么功能,但不提供具體的實(shí)現(xiàn),把具體實(shí)現(xiàn)留給繼承該類的子類。
抽象類特點(diǎn):
-
含有抽象方法的類必須聲明為抽象類(不管其中是否有其他方法);
-
抽象類可以沒有抽象方法,可以有普通方法;
-
抽象類必須被繼承,抽象方法必須被重寫(若子類還是抽象類,不需要重寫);
-
抽象類不能被實(shí)例化(不能直接構(gòu)造一個(gè)該類的對(duì)象)。
抽象方法特點(diǎn):
-
在類中沒有方法體(抽象方法只需聲明,而不需實(shí)現(xiàn)某些功能);
-
抽象類中的抽象方法必須被實(shí)現(xiàn);
-
如果一個(gè)子類沒有實(shí)現(xiàn)父類中的抽象方法,則子類也變成了一個(gè)抽象類。
接口
interface
中的方法默認(rèn)為**public
** abstract
(public
、abstract
可以省略),變量默認(rèn)為public static final
;類中的方法全部都是抽象方法。只有聲明沒有實(shí)現(xiàn),在不同類中有不同的方法實(shí)現(xiàn)。
不同點(diǎn):
-
接口中只能包含抽象方法和默認(rèn)方法,不能為普通方法提供方法實(shí)現(xiàn);抽象類中可以包含普通方法;
-
接口里不能定義靜態(tài)方法(
jdk1.8
下可以定義static
方法),抽象類可以定義靜態(tài)方法; -
接口中只能定義靜態(tài)常量,不能定義普通成員變量;抽象類即可以定義變量又可以定義靜態(tài)常量;
-
接口中不包含構(gòu)造器,抽象類里可以包含構(gòu)造器,抽象類中的構(gòu)造器并不是用于創(chuàng)建對(duì)象,而是讓其他子類調(diào)用這些構(gòu)造器來完成抽象類的初始化操作;
-
接口里不能包含初始化塊,但抽象類可以包含;
-
一個(gè)類最多只能有一個(gè)父類,包括抽象類;但一個(gè)類可以直接實(shí)現(xiàn)多個(gè)接口,通過實(shí)現(xiàn)多個(gè)接口可以彌補(bǔ)Java單繼承的不足。
共同點(diǎn):
-
接口和抽象類都不能被實(shí)例化,都位于繼承樹的頂端,用于被其他類實(shí)現(xiàn)的繼承;
-
接口和抽象類都可以包含抽象方法,實(shí)現(xiàn)接口和繼承抽象類的普通子類都必須實(shí)現(xiàn)這些方法。
final
關(guān)鍵字
-
final
修飾的類,就是最終類,不能被繼承。 -
final
修飾的方法,就是最終方法,最終方法不能被重寫。 -
final
修飾一個(gè)引用變量時(shí),是指引用變量不能變,引用變量所指向的對(duì)象中的內(nèi)容還是可以改變的。修飾基本數(shù)據(jù)類型變量時(shí),內(nèi)容不能變。 -
final
成員變量必須在初始化代碼塊或在構(gòu)造器中初始化。
作用:
-
final
類:如果一個(gè)類不需要有子類,類的實(shí)現(xiàn)細(xì)節(jié)不允許改變,并且確信這個(gè)類不會(huì)再被擴(kuò)展,那么就設(shè)計(jì)成final
類。 -
final
方法:①把方法鎖定,防止任何繼承類修改它的意義和實(shí)現(xiàn)。②高效,編譯器在遇到調(diào)用final
方法時(shí)候會(huì)轉(zhuǎn)入內(nèi)嵌機(jī)制,大大提升執(zhí)行效率。
static
關(guān)鍵字
類方法指被static
修飾的方法,無this
指針。其他的就是實(shí)例方法。類方法可以調(diào)用其他類的static
方法。
-
final
類:如果一個(gè)類不需要有子類,類的實(shí)現(xiàn)細(xì)節(jié)不允許改變,并且確信這個(gè)類不會(huì)再被擴(kuò)展,那么就設(shè)計(jì)成final
類。 -
final
方法:①把方法鎖定,防止任何繼承類修改它的意義和實(shí)現(xiàn)。②高效,編譯器在遇到調(diào)用final
方法時(shí)候會(huì)轉(zhuǎn)入內(nèi)嵌機(jī)制,大大提升執(zhí)行效率。 -
static
修飾的變量稱為靜態(tài)變量,靜態(tài)變量屬于整個(gè)類,而局部變量屬于方法,只在該方法內(nèi)有效。**static
不能修飾局部變量。static
方法內(nèi)部不能調(diào)用非靜態(tài)方法。** -
靜態(tài)變量只能在類主體中定義,不能在方法中定義;
-
static
變量只會(huì)創(chuàng)建一份,不管創(chuàng)建幾個(gè)對(duì)象,都共用一個(gè)變量。
類方法和對(duì)象方法的區(qū)別:
1、 類方法是屬于整個(gè)類的,而實(shí)例方法是屬于類的某個(gè)對(duì)象的。 由于類方法是屬于整個(gè)類的,并不屬于類的哪個(gè)對(duì)象,所以類方法的方法體中不能有與類的對(duì)象有關(guān)的內(nèi)容。即類方法體有如下限制:
-
類方法中不能引用對(duì)象變量;
-
類方法中不能調(diào)用類的對(duì)象方法;
-
在類方法中不能使用
super
、this
關(guān)鍵字。(**this
表示當(dāng)前類的對(duì)象,由static
修飾的方法是類直接調(diào)用,不需要?jiǎng)?chuàng)建對(duì)象,所以不能用this
**); -
類方法不能被覆蓋。?
2、與類方法相比,對(duì)象方法幾乎沒有什么限制:
-
對(duì)象方法中可以引用對(duì)象變量,也可以引用類變量;
-
對(duì)象方法中可以調(diào)用類方法;
-
對(duì)象方法中可以使用
super
、this
關(guān)鍵字。
static
關(guān)鍵字的作用
-
為某特定數(shù)據(jù)類型或?qū)ο蠓峙鋯我坏拇鎯?chǔ)空間,而與創(chuàng)建對(duì)象的個(gè)數(shù)無關(guān);實(shí)現(xiàn)某個(gè)方法或?qū)傩耘c類而不是對(duì)象關(guān)聯(lián)在一起;
-
靜態(tài)變量屬于類,在內(nèi)存中只有一個(gè)復(fù)制,只要靜態(tài)變量所在的類被加載,這個(gè)靜態(tài)變量就會(huì)被分配空間。
多態(tài)
-
定義:不同類的對(duì)象對(duì)同一消息做出響應(yīng)。同一消息可以根據(jù)發(fā)送對(duì)象的不同而采用多種不同的行為方式;
-
多態(tài)存在的三個(gè)必要條件:繼承、重寫、父類引用指向子類對(duì)象;
-
Java中多態(tài)的實(shí)現(xiàn)方式:接口實(shí)現(xiàn),繼承父類進(jìn)行方法重寫,同一個(gè)類中進(jìn)行方法重載;
-
父類引用指向子類對(duì)象,該引用不能再訪問子類新增的成員。
Animal cat = new Cat()
與直接new
一個(gè)父類實(shí)例(Animal a = new Animal()
)的區(qū)別? 答:當(dāng)父類是接口和抽象類時(shí),不能實(shí)例化,只能運(yùn)用多態(tài),向上轉(zhuǎn)型。普通類中,可以在子類中重寫父類中的方法,這樣就可以訪問子類中的重寫方法。
編程要求
按照要求編寫一個(gè)Java
應(yīng)用程序:
-
定義一個(gè)抽象類
Person
,包含抽象方法eat()
,封裝屬性name
、sex
、age
,聲明包含三個(gè)參數(shù)的構(gòu)造方法; -
定義一個(gè)
Chinese
類,繼承自Person
類,重寫父類的eat()
方法,并定義一個(gè)自己特有的方法shadowBoxing()
; -
定義一個(gè)
English
類,繼承自Person
類,重寫父類的eat()
方法,并定義一個(gè)自己特有的方法horseRiding()
; -
編寫測(cè)試類,定義一個(gè)
showEat()
方法,使用父類作為方法的形參,實(shí)現(xiàn)多態(tài),分別調(diào)用showEat()
方法,通過強(qiáng)制類型轉(zhuǎn)換調(diào)用各自類特有的方法。
package case2;
import java.util.Scanner;
public class Task2 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String cName = sc.next();
String cSex = sc.next();
int cAge = sc.nextInt();
String eName = sc.next();
String eSex = sc.next();
int eAge = sc.nextInt();
// 創(chuàng)建測(cè)試類對(duì)象test
// 創(chuàng)建Person類對(duì)象person1,引用指向中國(guó)人,通過有參構(gòu)造函數(shù)實(shí)例化中國(guó)人類對(duì)象
// 通過showEat()方法調(diào)用Chinese的eat()方法
// 創(chuàng)建Person類對(duì)象person2,引用指向英國(guó)人,通過有參構(gòu)造函數(shù)實(shí)例化英國(guó)人類對(duì)象
// 通過showEat()方法調(diào)用English的eat()方法
/********* begin *********/
Person person1=new Chinese(cName,cSex,cAge);
showEat(person1);
Person person2=new English(eName,eSex,eAge);
showEat(person2);
/********* end *********/
// 強(qiáng)制類型轉(zhuǎn)換(向下轉(zhuǎn)型) 調(diào)用Chinese類特有的方法shadowBoxing()
// 強(qiáng)制類型轉(zhuǎn)換(向下轉(zhuǎn)型) 調(diào)用English類特有的方法horseRiding()
/********* begin *********/
Chinese chi=(Chinese) person1;
chi.shadowBoxing();
English eng=(English) person2;
eng.horseRiding();
/********* end *********/
}
// 定義showEat方法,使用父類作為方法的形參,實(shí)現(xiàn)多態(tài),傳入的是哪個(gè)具體對(duì)象就調(diào)用哪個(gè)對(duì)象的eat()方法
/********* begin *********/
public static void showEat(Person p){
if (p instanceof Chinese) {
Chinese c = (Chinese) p;
c.eat();
} else if (p instanceof English) {
English c = (English) p;
c.eat();
}
}
/********* end *********/
}
// 抽象類Person 封裝屬性name、sex和age
// 構(gòu)造函數(shù)初始化name、sex和age
// 聲明抽象方法eat()
abstract class Person {
/********* begin *********/
//private String name;
//private String sex;
//private int age;
public String name;
public String sex;
public int age;
abstract void eat();
public Person(String n,String s,int age){
this.name=n;
this.sex=s;
this.age=age;
}
/********* end *********/
}
// Chinese類繼承自Person類
// 構(gòu)造函數(shù)初始化name、sex和age
// 重寫父類方法eat() 輸出'姓名:name,性別:sex,年齡:age,我是中國(guó)人,我喜歡吃飯!'
// 定義子類特有方法shadowBoxing(),當(dāng)父類引用指向子類對(duì)象時(shí)無法調(diào)用該方法 輸出'name在練習(xí)太極拳!'
class Chinese extends Person {
/********* begin *********/
public Chinese(String n,String s,int age){
super(n,s,age);
}
public void eat(){
System.out.println("姓名:"+this.name+",性別:"+this.sex+",年齡:"+this.age+",我是中國(guó)人,我喜歡吃飯!");
}
public void shadowBoxing(){
System.out.println(name+"在練習(xí)太極拳!");
}
/********* end *********/
}
// English類繼承自Person類
// 構(gòu)造函數(shù)初始化name、sex和age
// 重寫父類方法eat() 輸出'姓名:name,性別:sex,年齡:age,我是英國(guó)人,我喜歡吃三明治!'
// 定義子類特有方法horseRiding(),當(dāng)父類引用指向子類對(duì)象時(shí)無法調(diào)用該方法 輸出'name在練習(xí)騎馬!'
class English extends Person {
/********* begin *********/
public English (String n,String s,int age){
super(n,s,age);
}
void eat(){
System.out.println("姓名:"+name+",性別:"+sex+",年齡:"+age+",我是英國(guó)人,我喜歡吃三明治!");
}
public void horseRiding(){
System.out.println(this.name+"在練習(xí)騎馬!");
}
/********* end *********/
}
第3關(guān):封裝、繼承和多態(tài)進(jìn)階(三)
相關(guān)知識(shí)
為了完成本關(guān)任務(wù),我們通過一個(gè)實(shí)例來一步一步總結(jié)歸納Java面向?qū)ο蟮闹R(shí)。
package test;
/*知識(shí)點(diǎn)目錄
1,Java繼承
1.1 繼承的概念
1.2 繼承的特性
1.3 繼承關(guān)鍵字
1.4 構(gòu)造器
2,Java重寫(Override)與重載(Overload)
2.1 重寫(Override)
2.2 重載(Overload)
2.3 總結(jié)
3,Java多態(tài)
3.1 多態(tài)的實(shí)現(xiàn)方式
4,Java抽象類
5,Java封裝
6,Java接口
*/
/* 1.1繼承的概念
繼承在本職上是特殊——一般的關(guān)系,即常說的is-a關(guān)系。子類繼承父類,表明子類是一種特殊的父類,并且具有父類所不具有的一些屬性或方法。通過 extends 關(guān)鍵字可以聲明一個(gè)類是從另外一個(gè)類繼承而來的。
*/
/* 1.2繼承的特性
1).子類擁有父類非private的屬性,方法;
2).子類可以擁有自己的屬性和方法,即子類可以對(duì)父類進(jìn)行擴(kuò)展;
3).子類可以用自己的方式實(shí)現(xiàn)父類的方法;
4).Java的繼承是單繼承,這是Java繼承區(qū)別于C++繼承的一個(gè)特性;
5).提高了類之間的耦合性(繼承的缺點(diǎn),耦合度高就會(huì)造成代碼之間的聯(lián)系)。
*/
/* 1.3繼承關(guān)鍵字
1).使用 extends 和 implements 來實(shí)現(xiàn)繼承,所有的類都是繼承于 java.lang.Object,當(dāng)一個(gè)類沒有繼承的兩個(gè)關(guān)鍵字,則默認(rèn)繼承Object;
2).一個(gè)子類只能擁有一個(gè)父類,所以 extends 只能繼承一個(gè)類;
3).使用 implements 關(guān)鍵字變相的使Java具有多繼承的特性,為類繼承接口,可以同時(shí)繼承多個(gè)接口;
4).通過super關(guān)鍵字來實(shí)現(xiàn)對(duì)父類成員的訪問,用來引用當(dāng)前對(duì)象的父類;
5).final 關(guān)鍵字
5.1) 聲明類則把類定義為不能繼承的,即最終類;修飾方法,則該方法不能被子類重寫;定義實(shí)例變量,則變量不能被修改;
5.2) final 類的方法自動(dòng)為 final方法,但實(shí)例變量不自動(dòng)是 final變量。
*/
interface A{}
interface B{}
class Animal{
public void move(){
System.out.println("動(dòng)物可以移動(dòng)");
}
Animal(){
System.out.println("Animal()");
}
Animal(int n){
System.out.println("Animal(int)");
}
}
final class Dog extends Animal implements A,B {
final String name = "旺財(cái)";
final public void finalFun(){
}
/* 2.1.1 重寫(Override)
1).重寫是子類對(duì)父類的允許訪問的方法的實(shí)現(xiàn)過程進(jìn)行重新編寫, 返回值和形參都不能改變。即外殼不變,核心重寫;
2).重寫的好處在于子類可以根據(jù)需要,定義特定于自己的行為。也就是說子類能夠根據(jù)需要實(shí)現(xiàn)父類的方法;
3).重寫方法不能拋出新的檢查異?;蛘弑缺恢貙懛椒暶鞲訉挿旱漠惓!?/code>
*/
public void move(){
System.out.println("狗可以跳墻");
}
/*2.2.1重載(Overload)
1).重載是在一個(gè)類里面,方法名字相同,而參數(shù)不同。返回類型可以相同也可以不同;
2).每個(gè)重載的方法(或者構(gòu)造函數(shù))都必須有一個(gè)獨(dú)一無二的參數(shù)類型列表。
*/
public void move(int n){
System.out.println("這只狗可以跳"+n+"米的墻");
}
public String toString() {
//1.3.1 通過super關(guān)鍵字來實(shí)現(xiàn)對(duì)父類成員的訪問,用來引用當(dāng)前對(duì)象的父類。
return "I am a Dog " + super.toString();
}
/* 1.4 構(gòu)造器
1).子類不能繼承父類的構(gòu)造器(構(gòu)造方法或者構(gòu)造函數(shù)),但是父類的構(gòu)造器帶有參數(shù)的,則必須在子類的構(gòu)造器中顯式地通過super關(guān)鍵字調(diào)用父類的構(gòu)造器并配以適當(dāng)?shù)膮?shù)列表;
2).如果父類有無參構(gòu)造器,則在子類的構(gòu)造器中用super調(diào)用父類構(gòu)造器不是必須的,如果沒有使用super關(guān)鍵字,系統(tǒng)會(huì)自動(dòng)調(diào)用父類的無參構(gòu)造器。
*/
Dog(){
super(300);
System.out.println("Dog()");
}
Dog(int n){
System.out.println("Dog(int)");
}
}
class Test{
static void testsuper(){
System.out.println("-----testsuper----");
new Dog();
new Dog(1);
}
static void testoverride(){
System.out.println("-----testoverride-----");
Animal a = new Animal();
Animal b = new Dog();
a.move();
/*2.1.2 重寫(Override)
1).盡管b屬于Animal類型,但是它運(yùn)行的是Dog類的move方法;
2).在編譯階段,只是檢查參數(shù)的引用類型。在運(yùn)行時(shí),Java虛擬機(jī)(JVM)指定對(duì)象的類型并且運(yùn)行該對(duì)象的方法。
*/
b.move();
}
}
public class JavaDemo{
public static void main(String[] args) {
Test.testsuper();
Test.testoverride();
}
}
/* 2.1.3方法的重寫規(guī)則
1).參數(shù)列表必須完全與被重寫方法的相同;
2).返回類型必須完全與被重寫方法的返回類型相同;
3).訪問權(quán)限不能比父類中被重寫的方法的訪問權(quán)限更低;
4).父類的成員方法只能被它的子類重寫;
5).聲明為final的方法不能被重寫;
6).聲明為static的方法不能被重寫,但是能夠被再次聲明;
7).子類和父類在同一個(gè)包中,那么子類可以重寫父類所有方法,除了聲明為private和final的方法;
8).子類和父類不在同一個(gè)包中,那么子類只能夠重寫父類的聲明為public和protected的非final方法;
9).重寫的方法能夠拋出任何非強(qiáng)制異常,無論被重寫的方法是否拋出異常,但是,重寫的方法不能拋出新的強(qiáng)制性異常,或者比被重寫方法聲明的更廣泛的強(qiáng)制性異常,反之則可以;
10).構(gòu)造方法不能被重寫;
11).如果不能繼承一個(gè)方法,則不能重寫這個(gè)方法。
*/
/*2.2.2重載規(guī)則
1).被重載的方法必須改變參數(shù)列表(參數(shù)個(gè)數(shù)或類型或順序不一樣);
2).被重載的方法可以改變返回類型;
3).被重載的方法可以改變?cè)L問修飾符;
4).被重載的方法可以聲明新的或更廣的檢查異常;
5).方法能夠在同一個(gè)類中或者在一個(gè)子類中被重載;
6).無法以返回值類型作為重載函數(shù)的區(qū)分標(biāo)準(zhǔn)。
*/
/*
重寫與重載之間的區(qū)別
區(qū)別點(diǎn)? 重載方法? 重寫方法
參數(shù)列表 必須修改? 一定不能修改
返回類型 可以修改? 一定不能修改
異常? ?可以修改? 可以減少或刪除,一定不能拋出新的或者更廣的異常
訪問? ?可以修改? 一定不能做更嚴(yán)格的限制(可以降低限制)
總結(jié)
方法的重寫和重載是Java多態(tài)性的不同表現(xiàn),重寫是父類與子類之間多態(tài)性的一種表現(xiàn),重載是一類中多態(tài)性的一種表現(xiàn)。
*/
/*訪問控制修飾符
Java中,可以使用訪問控制符來保護(hù)對(duì)類、變量、方法和構(gòu)造方法的訪問。Java支持4種不同的訪問權(quán)限。
默認(rèn)的,也稱為 default,在同一包內(nèi)可見,不使用任何修飾符;
私有的,以 private 修飾符指定,在同一類內(nèi)可見;
共有的,以 public 修飾符指定,對(duì)所有類可見;
受保護(hù)的,以 protected 修飾符指定,對(duì)同一包內(nèi)的類和所有子類可見。
我們可以可以通過以下表來說明訪問權(quán)限:
訪問控制
修飾符 當(dāng)前類 同一包內(nèi) 子孫類 其他包
public Y Y Y Y
protected Y Y Y N
default Y Y N N
private Y N N N
*/
/*3,Java多態(tài)
多態(tài)就是同一個(gè)接口,使用不同的實(shí)例執(zhí)行不同操作。
3.1 多態(tài)的實(shí)現(xiàn)方式 重寫、接口、抽象類和抽象方法。
*/
/*4,Java抽象類
1).使用abstract class來定義抽象類,抽象類不能被實(shí)例化;
2).抽象類中不一定包含抽象方法,但是有抽象方法的類必定是抽象類;
3).抽象類中的抽象方法只是聲明,不包含方法體;
4).構(gòu)造方法,類方法(用static修飾的方法)不能聲明為抽象方法;
5).任何子類必須重寫父類的抽象方法,或者聲明自身為抽象類。
*/
abstract class Employee{
//抽象方法
public abstract double computePay();
}
/*4.2繼承抽象類
*/
class Salary extends Employee{
public double computePay(){
return 0;
}
}
/*
5,Java封裝
封裝(英語:Encapsulation)是指,一種將抽象性函式接口的實(shí)現(xiàn)細(xì)節(jié)部份包裝、隱藏起來的方法。
封裝的優(yōu)點(diǎn):
1).良好的封裝能夠減少耦合;
2).類內(nèi)部的結(jié)構(gòu)可以自由修改;
3). 可以對(duì)成員變量進(jìn)行更精確的控制;
4). 隱藏信息,實(shí)現(xiàn)細(xì)節(jié)。
實(shí)現(xiàn)Java封裝的步驟:
1). 修改屬性的可見性來限制對(duì)屬性的訪問;
2).對(duì)每個(gè)值屬性提供對(duì)外的公共方法訪問。
*/
/*
6,Java接口
1).接口,在Java編程語言中是一個(gè)抽象類型,是抽象方法的集合,接口通常以interface來聲明;
2).一個(gè)實(shí)現(xiàn)接口的類,必須實(shí)現(xiàn)接口內(nèi)所描述的所有方法,否則就必須聲明為抽象類;
3).接口不能包含成員變量,除了 static 和 final 變量;
4).接口中每一個(gè)方法也是隱式抽象的,接口中的方法會(huì)被隱式的指定為 public abstract;
5).接口中可以含有變量,但是接口中的變量會(huì)被隱式的指定為 public static final 變量;
6).接口中的方法是不能在接口中實(shí)現(xiàn)的,只能由實(shí)現(xiàn)接口的類來實(shí)現(xiàn)接口中的方法;
7).類在實(shí)現(xiàn)接口的方法時(shí),不能拋出強(qiáng)制性異常,只能在接口中,或者繼承接口的抽象類中拋出該強(qiáng)制性異常;
8).類在重寫方法時(shí)要保持一致的方法名,并且應(yīng)該保持相同或者相兼容的返回值類型;
9).接口的繼承使用extends關(guān)鍵字,允許多重繼承,可能定義或繼承相同的方法。
*/
interface C extends A,B{}
輸出結(jié)果: -----testsuper----
Animal(int)
Dog()
Animal()
Dog(int)
-----testoverride-----
Animal()
Animal(int)
Dog()
動(dòng)物可以移動(dòng)
狗可以跳墻
編程要求
教練和運(yùn)動(dòng)員案例:
-
乒乓球運(yùn)動(dòng)員和籃球運(yùn)動(dòng)員;
-
乒乓球教練和籃球教練;
-
跟乒乓球相關(guān)的人員都需要學(xué)習(xí)英語;
-
分析,這個(gè)案例中有哪些抽象類,哪些接口,哪些具體類。
分析過程如下:
文章來源:http://www.zghlxwxcb.cn/news/detail-765107.html
測(cè)試說明
??
?
文章來源地址http://www.zghlxwxcb.cn/news/detail-765107.html
package case3;
import java.util.Scanner;
public class Task3 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String pppName = sc.next();
int pppAge = sc.nextInt();
String bpName = sc.next();
int bpAge = sc.nextInt();
String ppcName = sc.next();
int ppcAge = sc.nextInt();
String bcName = sc.next();
int bcAge = sc.nextInt();
// 測(cè)試運(yùn)動(dòng)員(乒乓球運(yùn)動(dòng)員和籃球運(yùn)動(dòng)員)
// 乒乓球運(yùn)動(dòng)員
// 通過帶參構(gòu)造函數(shù)實(shí)例化PingPangPlayer對(duì)象ppp
// 輸出'name---age'
// 分別調(diào)用sleep()、eat()、study()、speak()方法
/********* begin *********/
PingPangPlayer ppp=new PingPangPlayer(pppName,pppAge);
//System.out.println(name+"---"+age);
ppp.outInf();
ppp.sleep();
ppp.eat();
ppp.study();
ppp.speak();
/********* end *********/
System.out.println("----------------");
// 籃球運(yùn)動(dòng)員
// 通過帶參構(gòu)造函數(shù)實(shí)例化BasketballPlayer對(duì)象bp
// 輸出'name---age'
// 分別調(diào)用sleep()、eat()、study()方法
/********* begin *********/
BasketballPlayer bp=new BasketballPlayer(bpName,bpAge);
//System.out.println(name+"---"+age);
bp.outInf();
bp.sleep();
bp.eat();
bp.study();
/********* end *********/
System.out.println("----------------");
// 測(cè)試教練(乒乓球教練和籃球教練)
// 乒乓球教練
// 通過帶參構(gòu)造函數(shù)實(shí)例化PingPangCoach對(duì)象ppc
// 輸出'name---age'
// 分別調(diào)用sleep()、eat()、teach()、speak()方法
/********* begin *********/
PingPangCoach ppc=new PingPangCoach(ppcName,ppcAge);
//System.out.println(name+"---"+age);
ppc.outInf();
ppc.sleep();
ppc.eat();
ppc.teach();
ppc.speak();
/********* end *********/
System.out.println("----------------");
// 籃球教練
// 通過帶參構(gòu)造函數(shù)實(shí)例化BasketballCoach對(duì)象bc
// 輸出'name---age'
// 分別調(diào)用sleep()、eat()、teach()方法
/********* begin *********/
BasketballCoach bc=new BasketballCoach(bcName,bcAge);
//System.out.println(name+"---"+age);
bc.outInf();
bc.sleep();
bc.eat();
bc.teach();
/********* end *********/
System.out.println("----------------");
}
}
// 說英語接口 聲明抽象方法speak()
interface SpeakEnglish {
/********* begin *********/
abstract void speak();
/********* end *********/
}
// 定義人的抽象類Person 封裝name和age
// 無參構(gòu)造函數(shù)
// 有參構(gòu)造函數(shù)初始化name和age
// 定義具體方法sleep() 輸出'人都是要睡覺的'
// 抽象方法eat()(吃的不一樣)
abstract class Person {
/********* begin *********/
String name;
int age;
Person(){
name =" ";
age =0;
}
Person(String n,int a){
this.name=n;
this.age=a;
}
public void sleep(){
System.out.println("人都是要睡覺的");
}
public void outInf(){
System.out.println(name+"---"+age);
}
abstract void eat();
/********* end *********/
}
// 定義運(yùn)動(dòng)員Player(抽象類)繼承自Person類
// 無參構(gòu)造函數(shù)
// 有參構(gòu)造函數(shù)初始化name和age
// 運(yùn)動(dòng)員學(xué)習(xí)內(nèi)容不一樣,抽取為抽象 定義抽象方法study()
abstract class Player extends Person {
/********* begin *********/
Player(){
super();
}
Player(String n,int a){
super(n,a);
}
abstract void study();
/********* end *********/
}
// 定義教練Coach(抽象類)繼承自Person類
// 無參構(gòu)造函數(shù)
// 有參構(gòu)造函數(shù)初始化name和age
// 教練教的不一樣 定義抽象方法teach()
abstract class Coach extends Person {
/********* begin *********/
Coach(){
super();
}
Coach(String n,int a){
super(n,a);
}
abstract void teach();
/********* end *********/
}
// 定義乒乓球運(yùn)動(dòng)員具體類PingPangPlayer 繼承自Player類并實(shí)現(xiàn)SpeakEnglish類(兵乓球運(yùn)動(dòng)員需要說英語)
// 無參構(gòu)造函數(shù)
// 有參構(gòu)造函數(shù)初始化name和age
// 實(shí)現(xiàn)自己的eat()方法 輸出'乒乓球運(yùn)動(dòng)員吃大白菜,喝小米粥'
// 實(shí)現(xiàn)自己的study()方法 輸出'乒乓球運(yùn)動(dòng)員學(xué)習(xí)如何發(fā)球和接球'
// 實(shí)現(xiàn)自己的speak()方法 輸出'乒乓球運(yùn)動(dòng)員說英語'
class PingPangPlayer extends Player implements SpeakEnglish {
/********* begin *********/
PingPangPlayer(){
super();
}
PingPangPlayer(String n,int a){
super(n,a);
}
public void eat(){
System.out.println("乒乓球運(yùn)動(dòng)員吃大白菜,喝小米粥");
}
public void study(){
System.out.println("乒乓球運(yùn)動(dòng)員學(xué)習(xí)如何發(fā)球和接球");
}
public void speak(){
System.out.println("乒乓球運(yùn)動(dòng)員說英語");
}
/********* end *********/
}
// 定義籃球運(yùn)動(dòng)員具體類BasketballPlayer 繼承自Player類 不需要繼承接口,因?yàn)樗恍枰f英語
// 無參構(gòu)造函數(shù)
// 有參構(gòu)造函數(shù)初始化name和age
// 實(shí)現(xiàn)自己的eat()方法 輸出'籃球運(yùn)動(dòng)員吃牛肉,喝牛奶'
// 實(shí)現(xiàn)自己的study()方法 輸出'籃球運(yùn)動(dòng)員學(xué)習(xí)如何運(yùn)球和投籃'
class BasketballPlayer extends Player {
/********* begin *********/
BasketballPlayer(){
super();
}
BasketballPlayer(String n,int a){
super(n,a);
}
public void eat(){
System.out.println("籃球運(yùn)動(dòng)員吃牛肉,喝牛奶");
}
public void study(){
System.out.println("籃球運(yùn)動(dòng)員學(xué)習(xí)如何運(yùn)球和投籃");
}
/********* end *********/
}
// 定義乒乓球教練具體類 PingPangCoach 繼承自Coach類并實(shí)現(xiàn)SpeakEnglish類(兵乓球教練需要說英語)
// 無參構(gòu)造函數(shù)
// 有參構(gòu)造函數(shù)初始化name和age
// 實(shí)現(xiàn)自己的eat()方法 輸出'乒乓球教練吃小白菜,喝大米粥'
// 實(shí)現(xiàn)自己的teach()方法 輸出'乒乓球教練教如何發(fā)球和接球'
// 實(shí)現(xiàn)自己的speak()方法 輸出'乒乓球教練說英語'
class PingPangCoach extends Coach implements SpeakEnglish {
/********* begin *********/
PingPangCoach(){
super();
}
PingPangCoach(String n,int a){
super(n,a);
}
public void eat(){
System.out.println("乒乓球教練吃小白菜,喝大米粥");
}
public void teach(){
System.out.println("乒乓球教練教如何發(fā)球和接球");
}
public void speak(){
System.out.println("乒乓球教練說英語");
}
/********* end *********/
}
// 定義籃球教練具體類BasketballCoach 繼承自Coach類 不需要繼承接口,因?yàn)樗恍枰f英語
// 無參構(gòu)造函數(shù)
// 有參構(gòu)造函數(shù)初始化name和age
// 實(shí)現(xiàn)自己的eat()方法 輸出'籃球教練吃羊肉,喝羊奶'
// 實(shí)現(xiàn)自己的teach()方法 輸出'籃球教練教如何運(yùn)球和投籃'
class BasketballCoach extends Coach {
/********* begin *********/
BasketballCoach(){
super();
}
BasketballCoach(String n,int a){
super(n,a);
}
public void eat(){
System.out.println("籃球教練吃羊肉,喝羊奶");
}
public void teach(){
System.out.println("籃球教練教如何運(yùn)球和投籃");
}
/********* end *********/
}
到了這里,關(guān)于Educoder/頭歌JAVA——JAVA面向?qū)ο螅悍庋b、繼承和多態(tài)的綜合練習(xí)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!