Java面向程序的三大特性之封裝、繼承、多態(tài)詳解(最新整理)
1. 封裝
1.1 什么是封裝呢?
我們生活中使用的很多電子產(chǎn)品都是封裝好的,有的部分讓我們看到,有的部分不讓我們看到。比如機(jī)械手表,它的指針指向的時(shí)間讓我們看到,而它的工作原理用表盤遮擋起來(lái)不讓我們看,這也是一種合理的設(shè)計(jì),我們只需要看時(shí)機(jī)就行了。
其Java的實(shí)封裝也是這樣設(shè)計(jì)的,把類封裝起來(lái),有些成員變量和成員方法讓我們看到和使用,有的成員變量和成員方法不讓我們使用。
面向?qū)ο蟪绦蛉筇匦?/strong>:封裝、繼承、多態(tài)。而類和對(duì)象階段,主要研究的就是封裝特性。何為封裝呢?簡(jiǎn)單來(lái)說(shuō)就是套殼屏蔽細(xì)節(jié)。
封裝:將成員變量和成員方法進(jìn)行有機(jī)結(jié)合,隱藏對(duì)象的屬性和實(shí)現(xiàn)細(xì)節(jié),僅對(duì)外公開(kāi)接口來(lái)和對(duì)象進(jìn)行、交互。
實(shí)現(xiàn)這些功能就需要訪問(wèn)限定符
1.2 訪問(wèn)限定符
范圍 | private | default | protected | public | |
---|---|---|---|---|---|
1 | 同一包中的同一類 | ? | ? | ? | ? |
2 | 同一包中的不同類 | ? | ? | ? | |
3 | 不同包中的子類 | ? | ? | ||
4 | 不同包中的非子類 | ? |
說(shuō)明:public
:只看單詞的意思就可以明白,是公共的,誰(shuí)都可以看到和使用。protected
:主要是用在繼承中,子類可以使用。default
: 對(duì)于自己家族中(同一個(gè)包中)不是什么秘密,對(duì)于其他人來(lái)說(shuō)就是隱私了。什么都不寫(xiě)時(shí)的默認(rèn)權(quán)限private
:?jiǎn)卧~的意思是私密的,只有自己使用,其他人不能使用
訪問(wèn)權(quán)限除了可以限定類中成員的可見(jiàn)性,也可以控制類的可見(jiàn)性
1.3 使用封裝
在cn.nyist.watch
包中創(chuàng)建一個(gè)手表類Watch
:
package cn.nyist.watch; public class Watch { private String machine; // 機(jī)械手表的動(dòng)力來(lái)源 public String time; // 時(shí)間 String brand; // 品牌,default // 構(gòu)造器 public Watch(String machine,String time, String brand){ this.machine = machine; this.time = time; this.brand = brand; } // 手表的功能 看時(shí)間 public void See_time(){ System.out.println("時(shí)間"); } //調(diào)試零件 private void debugging(){ System.out.println("調(diào)試零件"); } }
在Watch
類中使用main
函數(shù),相當(dāng)于是創(chuàng)造表的人:
可以任意查看和使用
// 程序的入口,在Watch類中 public static void main(String[] args){ // 在類中創(chuàng)建一個(gè)手表 // 此場(chǎng)景相當(dāng)于創(chuàng)造表的人 Watch w = new Watch("機(jī)械","12:00","勞力士"); //開(kāi)始使用手表 //查看表的機(jī)械部件 System.out.println(w.machine); // 查看時(shí)間 System.out.println(w.time); //查看品牌 System.out.println(w.brand); w.See_time(); w.debugging(); }
在cn.nyist.watch
包中創(chuàng)建一個(gè)Main類,作為相當(dāng)于買手表的人:
不可以訪問(wèn)Watch
類定義的私有成員和私有方法
package cn.nyist.watch; public class Buy { // 程序的入口,在類中 public static void main(String[] args){ // 此場(chǎng)景相當(dāng)于手表的買者即擁有者 Watch w = new Watch("機(jī)械","12:00","勞力士"); //開(kāi)始使用手表 //查看表的機(jī)械部件 //System.out.println(w.machine); 不可以查看private屬性 // 查看時(shí)間 System.out.println(w.time); // time是public屬性 //查看品牌 System.out.println(w.brand); // brand 是default屬性 w.See_time(); // See_time是public屬性 //w.debugging(); // 不能調(diào)用private方法 } }
在其他包中的非子類,相當(dāng)于一個(gè)陌生人去看你的表:
只能訪問(wèn)public屬性的成員變量和成員方法
public class People { // 程序的入口 public static void main(String[] args){ // 此場(chǎng)景相當(dāng)于一個(gè)陌生人去看你的表 Watch w = new Watch("機(jī)械","12:00","勞力士"); //查看表的機(jī)械部件 //System.out.println(w.machine); 不可以查看private屬性 // 查看時(shí)間 System.out.println(w.time); // time是public屬性 //查看品牌 //System.out.println(w.brand); // 不可以看default屬性 w.See_time(); // See_time是public屬性 //w.debugging(); // 不能調(diào)用private方法 } }
2. 繼承
2.1 為什么要有繼承?
Java中的程序都是類與類進(jìn)行交互,如果多個(gè)類中有很多一樣的成員方法或成員變量就會(huì)出現(xiàn)代碼冗余的現(xiàn)象,比如:
在cn.nyist.animal
包中創(chuàng)建一個(gè)Dog
類:
package cn.nyist.animal; public class Dog{ public String name; public String color; protected int age; // 構(gòu)造器 public Dog(String name,String color, int age){ this.name = name; this.color = color; this.age = age; } public void eat(){ System.out.println("吃飯~"); } public void sleep(){ System.out.println("睡覺(jué)~"); } // 叫 public void bark(){ System.out.println("汪汪~(yú)"); } }
在cn.nyist.animal
包中創(chuàng)建一個(gè)Cat
類:
package cn.nyist.animal; public class Cat{ public String name; public String color; protected int age; //構(gòu)造器 public Cat(String name,String color, int age){ this.name = name; this.color = color; this.age = age; } public void eat(){ System.out.println("吃飯~"); } public void sleep(){ System.out.println("睡覺(jué)~"); } //叫 public void mm() { System.out.println("喵喵~"); } }
這兩個(gè)類是不是除了bar()
函數(shù),其余的內(nèi)容都是一樣,這就出現(xiàn)了代碼冗余,我們?cè)趺唇鉀Q呢?
面向?qū)ο笏枷胫刑岢隽死^承的概念,專門用來(lái)進(jìn)行共性抽取,實(shí)現(xiàn)代碼復(fù)用。
2.2 繼承的概念
繼承(inheritance)機(jī)制:是面向?qū)ο蟪绦蛟O(shè)計(jì)使代碼可以復(fù)用的最重要的手段,它允許程序員在保持原有類特性的基礎(chǔ)上進(jìn)行擴(kuò)展,增加新功能,這樣產(chǎn)生新的類,稱派生類(子類)。繼承呈現(xiàn)了面向?qū)ο蟪绦蛟O(shè)計(jì)的層次結(jié)構(gòu), 體現(xiàn)了由簡(jiǎn)單到復(fù)雜的認(rèn)知過(guò)程。繼承主要解決的問(wèn)題是:共性的抽取,實(shí)現(xiàn)代碼復(fù)用。
例如:狗和貓都是動(dòng)物,那么我們就可以將共性的內(nèi)容進(jìn)行抽取,然后采用繼承的思想來(lái)達(dá)到共用。
上述圖示中,Dog
和Cat
都繼承了Animal
類,其中:Animal
類稱為父類/基類或超類,Dog
和Cat
可以稱為Animal
的子類/派生類,繼承之后,子類可以復(fù)用父類中成員,子類在實(shí)現(xiàn)時(shí)只需關(guān)心自己新增加的成員即可。從繼承概念中可以看出繼承最大的作用就是:實(shí)現(xiàn)代碼復(fù)用,還有就是來(lái)實(shí)現(xiàn)多態(tài)。
2.3 繼承的語(yǔ)法
在Java中如果要表示類之間的繼承關(guān)系,需要借助extends
關(guān)鍵字,語(yǔ)法如下:
修飾符 class 子類名 extends 父類名 { // ... }
對(duì)Dog
和Cat
函數(shù)的重新實(shí)現(xiàn):
錯(cuò)誤示例:
在cn.nyist.animal
包中創(chuàng)建一個(gè)Dog
和Cat
的父類Animal
類:
package cn.nyist.animal; // 基類(父類) public class Animal{ public String name; public String color; protected int age; //構(gòu)造器 public Animal(String name,String color, int age){ this.name = name; this.color = color; this.age = age; } public void eat(){ System.out.println(name + "吃飯~"); } public void sleep(){ System.out.println(name + "睡覺(jué)~"); } }
在cn.nyist.animal
包中創(chuàng)建一個(gè)Dog
,Dog
繼承Animal
package cn.nyist.animal; public class Dog extends Animal{ // 只需要再添加一個(gè)bark()方法 public void bark(){ System.out.println("汪汪~(yú)"); } }
當(dāng)寫(xiě)完上述兩步的時(shí)候就運(yùn)行會(huì)發(fā)生報(bào)錯(cuò),這是為什么呢?不是已經(jīng)按要求寫(xiě)了嗎?
因?yàn)?Dog 類的沒(méi)有顯示的創(chuàng)建構(gòu)造器,當(dāng)創(chuàng)建Dog類對(duì)象的時(shí)候會(huì)調(diào)用默認(rèn)構(gòu)造器,也會(huì)調(diào)用默認(rèn)構(gòu)造器中的super()(super()是用來(lái)調(diào)用父類的構(gòu)造器,并可以傳入?yún)?shù)),調(diào)用的super()是無(wú)參的,但是父類Animal沒(méi)有創(chuàng)建無(wú)參的構(gòu)造器,這會(huì)導(dǎo)致發(fā)生報(bào)錯(cuò)。
正確示例:
在cn.nyist.animal
包中創(chuàng)建一個(gè)Dog
和Cat
的父類Animal
類:
package cn.nyist.animal; public class Animal{ public String name; public String color; protected int age; // 子類可以使用protected的變量 //構(gòu)造器 public Animal(String name,String color, int age){ this.name = name; this.color = color; this.age = age; } public void eat(){ System.out.println(name + "吃飯~"); } public void sleep(){ System.out.println(name + "睡覺(jué)~"); } }
在cn.nyist.animal
包中創(chuàng)建一個(gè)Dog
,Dog
繼承Animal
package cn.nyist.animal; public class Dog extends Animal{ public Dog(String name, String color, int age){ super(name,color,age); } // 只需要再添加一個(gè)bark()方法 public void bark(){ System.out.println("汪汪~(yú)"); } }
在cn.nyist.animal
包中創(chuàng)建一個(gè)Cat
,Dog
繼承Cat
package cn.nyist.animal; public class Cat extends Animal{ // 構(gòu)造器 public Cat(String name, String color, int age){ super(name,color,age); } public void mm() { System.out.println("喵喵~"); } }
測(cè)試
package cn.nyist.animal; public class Cat extends Animal{ // 構(gòu)造器 public Cat(String name, String color, int age){ super(name,color,age); } public void mm() { System.out.println("喵喵~"); } }
運(yùn)行結(jié)果:
java文件的結(jié)構(gòu)
2.4 訪問(wèn)父類成員
2.4.1 子類中訪問(wèn)父類成員的變量
在繼承體系中,子類將父類中的方法和字段繼承下來(lái)了,那在子類中能否直接訪問(wèn)父類中繼承下來(lái)的成員呢?
在cn.nyist.study
包中創(chuàng)建基類Base
package cn.nyist.study; public class Base { protected int a; protected int b; }
子類和父類不存在同名成員變量
package cn.nyist.study; public class Son extends Base { int c; public void Fun(){ a = 10; //訪問(wèn)父類的成員 b = 20; // 訪問(wèn)父類的成員 c = 30; // 訪問(wèn)自己的成員 } }
子類和父類存在同名成員變量
package cn.nyist.study; public class Son extends Base { int b; int c; public void Fun(){ a = 10; //訪問(wèn)父類的成員 b = 20; // 訪問(wèn)自己的成員 c = 30; // 訪問(wèn)自己的成員 } }
在子類方法中 或者 通過(guò)子類對(duì)象訪問(wèn)成員時(shí):
- 如果訪問(wèn)的成員變量子類中有,優(yōu)先訪問(wèn)自己的成員變量。
- 如果訪問(wèn)的成員變量子類中無(wú),則訪問(wèn)父類繼承下來(lái)的,如果父類也沒(méi)有定義,則編譯報(bào)錯(cuò)。
- 如果訪問(wèn)的成員變量與父類中成員變量同名,則優(yōu)先訪問(wèn)自己的。
成員變量訪問(wèn)遵循就近原則,自己有優(yōu)先自己的,如果沒(méi)有則向父類中找。
2.4.2 訪問(wèn)父類的成員方法
在包cn.nyist.study
中創(chuàng)建一個(gè)類Base
package cn.nyist.study; public class Base { public void methodA(){ System.out.println("Base中的methodA()"); } public void methodB(){ System.out.println("Base中的methodB()"); } }
成員方法不同名
package cn.nyist.study; public class Son extends Base { public void methodC() { System.out.println("Son中的method(int)方法"); } public void methodD(){ methodA(); // 訪問(wèn)父類中的methodA() methodB(); // 訪問(wèn)父類中的methodA() methodC(); // 訪問(wèn)子類中的methodC() } }
總結(jié):成員方法沒(méi)有同名時(shí),在子類方法中或者通過(guò)子類對(duì)象訪問(wèn)方法時(shí),則優(yōu)先訪問(wèn)自己的,自己沒(méi)有時(shí)再到父類中找,如果父類中也沒(méi)有則報(bào)錯(cuò)。
成員方法同名
package cn.nyist.study; public class Son extends Base { public void methodA(int a) { System.out.println("Son中的method(int)方法"); } public void methodB(){ System.out.println("Son中的methodB()方法"); } public void methodD(){ methodA(); // 沒(méi)有傳參,訪問(wèn)父類中的methodA() methodA(20); // 傳遞int參數(shù),訪問(wèn)子類中的methodA(int) methodB(); // 直接訪問(wèn),則永遠(yuǎn)訪問(wèn)到的都是子類中的methodB(),基類的無(wú)法訪問(wèn)到 } }
【說(shuō)明】
- 通過(guò)子類對(duì)象訪問(wèn)父類與子類中不同名方法時(shí),優(yōu)先在子類中找,找到則訪問(wèn),否則在父類中找,找到則訪問(wèn),否則編譯報(bào)錯(cuò)。
- 通過(guò)派生類對(duì)象訪問(wèn)父類與子類同名方法時(shí),如果父類和子類同名方法的參數(shù)列表不同(重載),根據(jù)調(diào)用方法適傳遞的參數(shù)選擇合適的方法訪問(wèn),如果沒(méi)有則報(bào)錯(cuò);
2.5 super關(guān)鍵字
在有些場(chǎng)景中,子類和父類中可能會(huì)存在相同名稱的成員,如果要在子類方法中訪問(wèn)父類同名成員時(shí),該如何操作?
直接訪問(wèn)是無(wú)法做到的,Java提供了super
關(guān)鍵字,該關(guān)鍵字主要作用:在子類方法中訪問(wèn)父類的成員。
在包cn.nyist.study
中創(chuàng)建一個(gè)類Base
package cn.nyist.study; public class Base { int a; int b; public void methodA(){ System.out.println("Base中的methodA()"); } public void methodB(){ System.out.println("Base中的methodB()"); } }
在包cn.nyist.study
中創(chuàng)建類Base
的子類Son
package cn.nyist.study; public class Son extends Base { int a; // 與父類中成員變量同名且類型相同 char b; // 與父類中成員變量同名但類型不同 // 與父類中methodA()構(gòu)成重載 public void methodA(int a) { System.out.println("Son中的method()方法"); } // 與基類中methodB()構(gòu)成重寫(xiě)(即原型一致,重寫(xiě)后序詳細(xì)介紹) public void methodB() { System.out.println("Son中的methodB()方法"); } public void methodC() { // 對(duì)于同名的成員變量,直接訪問(wèn)時(shí),訪問(wèn)的都是子類的 a = 100; // 等價(jià)于: this.a = 100; b = 101; // 等價(jià)于: this.b = 101; // 注意:this是當(dāng)前對(duì)象的引用 // 訪問(wèn)父類的成員變量時(shí),需要借助super關(guān)鍵字 // super是獲取到子類對(duì)象中從基類繼承下來(lái)的部分 super.a = 200; super.b = 201; // 父類和子類中構(gòu)成重載的方法,直接可以通過(guò)參數(shù)列表區(qū)分清訪問(wèn)父類還是子類方法 methodA(); // 沒(méi)有傳參,訪問(wèn)父類中的methodA() methodA(20); // 傳遞int參數(shù),訪問(wèn)子類中的methodA(int) // 如果在子類中要訪問(wèn)重寫(xiě)的基類方法,則需要借助super關(guān)鍵字 methodB(); // 直接訪問(wèn),則永遠(yuǎn)訪問(wèn)到的都是子類中的methodA(),基類的無(wú)法訪問(wèn)到 super.methodB(); // 訪問(wèn)基類的methodB() } }
【注意事項(xiàng)】
- 只能在非靜態(tài)方法中使用
- 在子類方法中,訪問(wèn)父類的成員變量和方法。
2.6 super 和 this
super
和this
都可以在成員方法中用來(lái)訪問(wèn):成員變量和調(diào)用其他的成員函數(shù),都可以作為構(gòu)造方法的第一條語(yǔ)句,那他們之間有什么區(qū)別呢?
【相同點(diǎn)】
- 都是Java中的關(guān)鍵字
- 只能在類的非靜態(tài)方法中使用,用來(lái)訪問(wèn)非靜態(tài)成員方法和字段
- 在構(gòu)造方法中調(diào)用時(shí),必須是構(gòu)造方法中的第一條語(yǔ)句,并且不能同時(shí)存在
【不同點(diǎn)】
this是當(dāng)前對(duì)象的引用,當(dāng)前對(duì)象即調(diào)用實(shí)例方法的對(duì)象,super相當(dāng)于是子類對(duì)象中從父類繼承下來(lái)部分成員的引用
在非靜態(tài)成員方法中,this用來(lái)訪問(wèn)本類的方法和屬性,super用來(lái)訪問(wèn)父類繼承下來(lái)的方法和屬性在構(gòu)造方法中:this(…)用于調(diào)用本類構(gòu)造方法,super(…)用于調(diào)用父類構(gòu)造方法,兩種調(diào)用不能同時(shí)在構(gòu)造
方法中出現(xiàn)構(gòu)造方法中一定會(huì)存在super(…)的調(diào)用,用戶沒(méi)有寫(xiě)編譯器也會(huì)增加,但是this(…)用戶不寫(xiě)則沒(méi)有
2.7 子類的構(gòu)造方法使用規(guī)則
父子父子,先有父再有子,即:創(chuàng)建子類對(duì)象時(shí),需要先調(diào)用基類構(gòu)造方法,然后執(zhí)行子類的構(gòu)造方法。
在包cn.nyist.study
中創(chuàng)建基類Base
package cn.nyist.study; public class Base { public Base() { System.out.println("Base()"); } }
在包cn.nyist.study
中創(chuàng)建類Base
的子類
package cn.nyist.study; public class Son extends Base { public Son(){ // super(); // 注意子類構(gòu)造方法中默認(rèn)會(huì)調(diào)用基類的無(wú)參構(gòu)造方法:super(), // 用戶沒(méi)有寫(xiě)時(shí),編譯器會(huì)自動(dòng)添加,而且super()必須是子類構(gòu)造方法中第一條語(yǔ)句, // 并且只能出現(xiàn)一次 System.out.println("Son()構(gòu)造方法"); } }
在包cn.nyist.study
中創(chuàng)建類Main
,運(yùn)行程序
package cn.nyist.study; public class Main { public static void main(String[] args) { Son d = new Son(); } }
在子類構(gòu)造方法中,并沒(méi)有寫(xiě)任何關(guān)于基類構(gòu)造的代碼,但是在構(gòu)造子類對(duì)象時(shí),先執(zhí)行基類的構(gòu)造方法,然后執(zhí)行子類的構(gòu)造方法,因?yàn)椋鹤宇悓?duì)象中成員是有兩部分組成的,基類繼承下來(lái)的以及子類新增加的部分 。父子父子
肯定是先有父再有子,所以在構(gòu)造子類對(duì)象時(shí)候 ,先要調(diào)用基類的構(gòu)造方法,將從基類繼承下來(lái)的成員構(gòu)造完整,然后再調(diào)用子類自己的構(gòu)造方法,將子類自己新增加的成員初始化完整。
注意:
若父類顯式定義無(wú)參或者默認(rèn)的構(gòu)造方法,在子類構(gòu)造方法第一行默認(rèn)有隱含的super()調(diào)用,即調(diào)用基類構(gòu)造方法
class Father{ public String name; public int age; //不寫(xiě)構(gòu)造方法時(shí),會(huì)有一個(gè)默認(rèn)的無(wú)參構(gòu)造方法 //public Father(){} } public class Son extends Father { public String color; // // 不寫(xiě)構(gòu)造方法時(shí),會(huì)有一個(gè)默認(rèn)的無(wú)參構(gòu)造方法 // public Son(){ // // 默認(rèn)會(huì)調(diào)用父類的無(wú)參構(gòu)造方法 // super(); // } }
如果父類構(gòu)造方法是帶有參數(shù)的,此時(shí)需要用戶為子類顯式定義構(gòu)造方法,并在子類構(gòu)造方法中選擇合適的父類構(gòu)造方法調(diào)用,否則編譯失敗。
class Father{ public String name; public int age; //定義有參構(gòu)造方法 public Father(String name, int age){ this.name = name; this.age = age; } } public class Son extends Father { public String color; // 父類只有有參構(gòu)造方法,子類必須顯示的調(diào)用父類的構(gòu)造方法 public Son(String name, int age, String color){ // 子類必須顯示的調(diào)用父類的構(gòu)造方法,否則會(huì)報(bào)錯(cuò) super(name, age); this.color = color; } }
在子類構(gòu)造方法中,super(…)調(diào)用父類構(gòu)造時(shí),必須是子類構(gòu)造函數(shù)中第一條語(yǔ)句。
class Father{ public String name; public int age; //定義有參構(gòu)造方法 public Father(String name, int age){ this.name = name; this.age = age; } } public class Son extends Father { public String color; // 父類只有有參構(gòu)造方法,子類必須顯示的調(diào)用父類的構(gòu)造方法 public Son(String name, int age, String color){ this.color = color; // super必須子在第一行,這種寫(xiě)法是錯(cuò)誤的 super(name, age); } }
super(...)
只能在子類構(gòu)造方法中出現(xiàn)一次,并且不能和this(…)同時(shí)出現(xiàn)。
class Father{ public String name; public int age; //定義有參構(gòu)造方法 public Father(String name, int age){ this.name = name; this.age = age; } } public class Son extends Father { public String color; public Son(String color){ super("張三",18); this.color = color; } // 父類只有有參構(gòu)造方法,子類必須顯示的調(diào)用父類的構(gòu)造方法 public Son(String name, int age, String color){ // 子類必須顯示的調(diào)用父類的構(gòu)造方法,否則會(huì)報(bào)錯(cuò) super(name, age); // super()和this()不能同時(shí)使用 this(color); } }
3. 多態(tài)
3.1 多態(tài)的概念
多態(tài)允許你使用一個(gè)接口來(lái)表示不同的類型,從而實(shí)現(xiàn)更靈活和可擴(kuò)展的代碼設(shè)計(jì)。
多態(tài)的概念:通俗來(lái)說(shuō),就是多種形態(tài),具體點(diǎn)就是去完成某個(gè)行為,當(dāng)不同的對(duì)象去完成時(shí)會(huì)產(chǎn)生出不同 的狀態(tài)。
比如,有一個(gè)基類Animal
,有一個(gè)吃飯的方法;子類Dog
,重寫(xiě)方法吃飯變?yōu)槌怨穷^;子類Cat
重寫(xiě)方法變?yōu)槌贼~(yú)。使用這兩個(gè)子類的時(shí)候,都調(diào)用吃飯的方法,但是打印出不同的內(nèi)容。
3.2 多態(tài)的實(shí)現(xiàn)
在java中要實(shí)現(xiàn)多態(tài),必須要滿足如下幾個(gè)條件,缺一不可:
- 必須在繼承體系下。
- 子類必須要對(duì)父類中方法進(jìn)行重寫(xiě)。
- 通過(guò)父類的引用調(diào)用重寫(xiě)的方法。
多態(tài)體現(xiàn):在代碼運(yùn)行時(shí),當(dāng)傳遞不同類對(duì)象時(shí),會(huì)調(diào)用對(duì)應(yīng)類中的方法
示例:
在cn.nyist.study
包中創(chuàng)建一個(gè)基類Animal
package cn.nyist.animal; public class Animal{ public String name; public String color; protected int age; //構(gòu)造器 public Animal(String name,String color, int age){ this.name = name; this.color = color; this.age = age; } public void eat(){ System.out.println(name + "吃飯~"); } public void sleep(){ System.out.println(name + "睡覺(jué)~"); } }
在cn.nyist.study
包中創(chuàng)建子類Dog
package cn.nyist.animal; public class Dog extends Animal{ public Dog(String name, String color, int age){ super(name,color,age); } // 重寫(xiě)父類方法 public void eat(){ System.out.println(name+"吃骨頭"); } }
在cn.nyist.study
包中創(chuàng)建子類Cat
package cn.nyist.animal; public class Cat extends Animal{ // 構(gòu)造器 public Cat(String name, String color, int age){ super(name,color,age); } public void eat(){ System.out.println(name + "吃魚(yú)"); } }
在cn.nyist.study
包類Main
,運(yùn)行程序
package cn.nyist.animal; public class Main { public static void main(String[] args) { // 創(chuàng)建狗類的對(duì)象 Dog dog = new Dog("小狗","黑色",1); Fun_eat(dog); // 創(chuàng)建貓類的對(duì)象 Cat cat = new Cat("小貓","花色",3); Fun_eat(cat); } // 編譯器在編譯代碼時(shí),并不知道要調(diào)用Dog 還是 Cat 中eat的方法 // 等程序運(yùn)行起來(lái)后,形參a引用的具體對(duì)象確定后,才知道調(diào)用那個(gè)方法 // 注意:此處的形參類型必須時(shí)父類類型才可以 public static void Fun_eat(Animal a){ a.eat(); a.sleep(); } }
3.3 重寫(xiě)
重寫(xiě)(override):也稱為覆蓋。重寫(xiě)是子類對(duì)父類非靜態(tài)、非private修飾,非final修飾,非構(gòu)造方法等的實(shí)現(xiàn)過(guò)程進(jìn)行重新編寫(xiě),返回值和形參都不能改變。即外殼不變,核心重寫(xiě)!重寫(xiě)的好處在于子類可以根據(jù)需要,定義特定于自己的行為。 也就是說(shuō)子類能夠根據(jù)需要實(shí)現(xiàn)父類的方法。
【方法重寫(xiě)的規(guī)則】
- 子類在重寫(xiě)父類的方法時(shí),一般必須與父類方法原型一致: 返回值類型 方法名 (參數(shù)列表) 要完全一致
- 被重寫(xiě)的方法返回值類型可以不同,但是必須是具有父子關(guān)系
- 訪問(wèn)權(quán)限不能比父類中被重寫(xiě)的方法的訪問(wèn)權(quán)限更低。例如:如果父類方法被public修飾,則子類中重寫(xiě)該方法就不能聲明為 protected
- 父類被static、private修飾的方法、構(gòu)造方法都不能被重寫(xiě)。
- 重寫(xiě)的方法, 可以使用 @Override 注解來(lái)顯式指定. 有了這個(gè)注解能幫我們進(jìn)行一些合法性校驗(yàn). 例如不小心將方法名字拼寫(xiě)錯(cuò)了 (比如寫(xiě)成 aet), 那么此時(shí)編譯器就會(huì)發(fā)現(xiàn)父類中沒(méi)有 aet 方法, 就會(huì)編譯報(bào)錯(cuò), 提示無(wú)法構(gòu)成重寫(xiě).
【重寫(xiě)和重載的區(qū)別】
區(qū)別點(diǎn) | 重寫(xiě)(override) | 重載(overload) |
---|---|---|
參數(shù)列表 | 一定不能修改 | 必須修改 |
返回類型 | 一定不能修改【除非可以構(gòu)成父子類關(guān)系】 | 可以修改 |
訪問(wèn)限定符 | 一定不能做更嚴(yán)格的限制(可以降低限制) | 可以修改 |
到此這篇關(guān)于Java面向程序的三大特性之封裝、繼承、多態(tài)的文章就介紹到這了,更多相關(guān)Java封裝、繼承、多態(tài)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringAMQP消息隊(duì)列實(shí)戰(zhàn)教程
這篇文章主要介紹了SpringAMQP消息隊(duì)列的相關(guān)知識(shí),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧2024-02-02關(guān)于Java Spring三級(jí)緩存和循環(huán)依賴的深入理解
對(duì)于循環(huán)依賴,我相信讀者無(wú)論只是聽(tīng)過(guò)也好,還是有過(guò)了解也好,至少都有所接觸。但是我發(fā)現(xiàn)目前許多博客對(duì)于循環(huán)依賴的講解并不清楚,都提到了Spring的循環(huán)依賴解決方案是三級(jí)緩存,但是三級(jí)緩存每一級(jí)的作用是什么,很多博客都沒(méi)有提到,本篇文章帶你深入了解2021-09-09spring schedule配置多任務(wù)動(dòng)態(tài)cron(增刪啟停)
這篇文章主要介紹了spring schedule配置多任務(wù)動(dòng)態(tài)cron(增刪啟停),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03SpringBoot數(shù)據(jù)庫(kù)初始化datasource配置方式
這篇文章主要為大家介紹了SpringBoot數(shù)據(jù)庫(kù)初始化datasource配置方式,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12