Java全面分析面向?qū)ο笾^承
繼承
什么是繼承呢?
繼承(Inheritance)是一種聯(lián)結(jié)類與類的層次模型。指的是一個類(稱為子類、子接口)繼承另外的一個類(稱為父類、父接口)的功能,并可以增加它自己的新功能的能力,繼承是類與類或者接口與接口之間最常見的關(guān)系;繼承是一種is-a關(guān)系。
看了這些概念你可能還是百思不得其解,我來用大白話給你講解一下,我們在現(xiàn)實生活中也聽過繼承,不過是繼承家產(chǎn),繼承傳承文明.......等等,那這些繼承是不就把上一代人傳下來的東西全部交給繼承人了,這些繼承下來的東西,繼承人隨時都可以用,但是繼承人也有屬于自己的東西。沒錯這就是繼承,而在我們寫代碼的時候?qū)⒁恍╊愃哂械墓残匀考性谝黄鸱诺揭粋€類中(這個類就是父類),然后子類想要調(diào)用從父類繼承下來的成員就隨時可以調(diào)用了。這也就將共性全部抽取出來,實現(xiàn)了代碼的復用。
說了這么多不寫代碼理解可能還是理解的不太通透,好接下來我就舉個例子來給大家理解。
實現(xiàn)一個狗類:
//實現(xiàn)一個狗類 class Dog{ public String name;//名字 public int age;//年齡 public String furColor;//毛顏色 public void eat() { System.out.println(this.name+"吃飯!??!"); } public void sleep() { System.out.println(this.name+"睡覺?。?!"); } public void bark() { System.out.println(this.name+"汪汪汪"); } }
實現(xiàn)一個貓類:
//實現(xiàn)一個貓類 class Cat{ public String name;//名字 public int age;//年齡 public String furColor;//毛顏色 public void eat() { System.out.println(this.name+"吃飯?。?!"); } public void sleep() { System.out.println(this.name+"睡覺!?。?); } public void mew() { System.out.println(this.name+"喵喵喵"); } }
我們都知道貓和狗都是動物,他們都有共性的屬性比如名字,年齡,吃飯,睡覺,并且它們還有自己的屬性比如狗會汪汪汪,貓會喵喵喵。既然他們有共同的屬性我們就可以創(chuàng)建一個動物類。
//實現(xiàn)一個動物類 class Animal{ public String name;//名字 public int age;//年齡 public String furColor;//毛顏色 public void eat() { System.out.println(this.name+"吃飯?。?!"); } public void sleep() { System.out.println(this.name+"睡覺!?。?); } } //實現(xiàn)一個狗類 class Dog{ public void bark() { System.out.println("汪汪汪"); } } //實現(xiàn)一個貓類 class Cat{ public void mew() { System.out.println("喵喵喵"); } }
既然把他們共性都抽取出來怎么用呢?
顯然直接用是用不了的。那需要怎么用呢。我們想的是這個狗和貓都要有這些屬性,當我們向用的時候隨時用,號接下來我們講的繼承就會很好地解決這個問題。
繼承的語法:繼承是利用extends關(guān)鍵字將子類與父類建立聯(lián)系。
//實現(xiàn)一個狗類 class Dog extends Animal{ public void bark() { System.out.println(this.name+"汪汪汪"); } } //實現(xiàn)一個貓類 class Cat extends Animal{ public void mew() { System.out.println("喵喵喵"); } }
這回就不報錯了。
我們來分析一下這段代碼:
在這里子類又叫做派生類,父類可以叫做父類,基類還有超類。
這里有一個問題:
子類繼承了父類的什么呢?
答案是:除了構(gòu)造方法所有。
子類訪問父類的成員變量
子類訪問父類非同名成員變量
//實現(xiàn)一個貓類 class Cat extends Animal { public void mew() { System.out.println(this.name + "喵喵喵"); } public void Init() {//訪問父類 this.name = "咪咪"; this.age = 2; this.furColor = "橘黃色"; } public void show() { System.out.println(name); System.out.println(age); System.out.println(furColor); } }
這里就體現(xiàn)了繼承的關(guān)系,子類繼承了父類的屬性(成員變量和成員方法);
這里我們還可以調(diào)用構(gòu)造方法初始化成員,調(diào)用toString方法來打印信息
//實現(xiàn)一個貓類 class Cat extends Animal { public void mew() { System.out.println(this.name + "喵喵喵"); } public Cat(String name,int age,String furColor) { this.name =name; this.age=age; this.furColor =furColor; } @Override public String toString() { return "Cat{" + "name='" + name + '\'' + ", age=" + age + ", furColor='" + furColor + '\'' + '}'; } } public class TestDemo1 { public static void main(String[] args) { //Cat cat = new Cat(); Cat cat = new Cat("咪咪",2,"橘黃色"); cat.eat(); cat.sleep(); cat.mew(); System.out.println(cat.toString()); } }
子類訪問父類同名成員變量
當子類成員變量名與父類成員變量同名了會怎么辦呢???
//實現(xiàn)一個動物類 class Animal{ public String name = "花花";//名字 public int age;//年齡 public String furColor;//毛顏色 public void eat() { System.out.println(this.name+"吃飯!??!"); } public void sleep() { System.out.println(this.name+"睡覺?。?!"); } } //實現(xiàn)一個貓類 class Cat extends Animal { public String name = "咪咪"; public void mew() { System.out.println(this.name + "喵喵喵"); } public void Init() {//訪問父類 name = "咪咪"; this.age = 2; this.furColor = "橘黃色"; } } public class TestDemo1 { public static void main(String[] args) { Cat cat = new Cat(); System.out.println(cat.name); } }
總結(jié):當父類與子類同名時遵循就近原則,如果實例化子類對象就去子類找,找不到就去父類,父類沒有就報錯,如果實例化父類對象就直接去父類找,父類沒有就報錯。
子類訪問父類的成員方法
子類訪問父類的非同名方法
class Cat extends Animal { public String name = "咪咪"; public void mew() { System.out.println(this.name + "喵喵喵"); } public void methodSon() { System.out.println("我是子類的方法?。?); } public void method() { methodSon();//訪問子類 methodFather();//訪問父類 } }
子類訪問父類的同名方法
//實現(xiàn)一個動物類 class Animal{ public String name = "花花";//名字 public int age;//年齡 public String furColor;//毛顏色 public void method(int a) { System.out.println("我是父類的方法??!"+a); } } //實現(xiàn)一個貓類 class Cat extends Animal { public String name = "咪咪"; public void mew() { System.out.println(this.name + "喵喵喵"); } public void method() { System.out.println("我是子類的方法??!"); } } public class TestDemo1 { public static void main(String[] args) { Cat cat = new Cat(); cat.method();//沒有帶參數(shù)的只有子類有,如果兩者都有method同樣的方法,那就涉及到重寫(前提是引用子類對象)優(yōu)先訪問子類 cat.method(2);//帶參數(shù)的只有父類有 //cat.method(); } }
子類訪問父類同名的方法也是一樣的也是采取就近原則,當引用子類對象的時候優(yōu)先子類,然后去父類尋找,找不到報錯。
但是方法這里有兩個特殊情況,一是方法重載,說明的是同一個類可以支持方法重載,不同類但是有繼承關(guān)系的也是支持方法重載的。當出現(xiàn)方法重載,就會根據(jù)參數(shù)列表的不同來訪問。
二是方法重寫,方法重寫指的是方法名相同返回值相同,參數(shù)列表相同,當出現(xiàn)方法重寫的時候(前提是引用子類對象)就優(yōu)先子類,會出現(xiàn)動態(tài)綁定,這個咱后面講解。
上面我們都說同名的時候都會遵循就近原則。就比如我們引用子類對象的時候,我就想優(yōu)先調(diào)用父類,那怎么辦呢????
那就該super關(guān)鍵字出場了;
super關(guān)鍵字
super就是一個普通的關(guān)鍵字,來引用當前對象的父類,當我們看見super的時候我們就要知道它是訪問父類的就可以了。
好我們來實踐一下。
super訪問父類成員變量
//實現(xiàn)一個動物類 class Animal{ public String name = "花花";//名字 public int age;//年齡 public String furColor;//毛顏色 } //實現(xiàn)一個貓類 class Cat extends Animal { public String name = "咪咪"; public void mew() { System.out.println(this.name + "喵喵喵"); } public void method() { System.out.println(super.name); } } public class TestDemo1 { public static void main(String[] args) { Cat cat =new Cat(); cat.method(); } }
super訪問父類成員方法
//實現(xiàn)一個動物類 class Animal{ public String name = "花花";//名字 public int age;//年齡 public String furColor;//毛顏色 public void method() { System.out.println("我是父類的方法?。?!"); } } //實現(xiàn)一個貓類 class Cat extends Animal { public void method() { System.out.println("我是子類的方法"); } public void methodA() { super.method(); } } public class TestDemo1 { public static void main(String[] args) { Cat cat =new Cat(); cat.methodA(); } }
創(chuàng)建構(gòu)造方法
當我們?yōu)楦割悇?chuàng)建了一個構(gòu)造方法并且為子類創(chuàng)建構(gòu)造方法的時候就會報錯,原因是什么呢???
那就是當我們創(chuàng)建一個構(gòu)造方法的時候一定要先為父類創(chuàng)建構(gòu)造方法,原因是每個類都要至少要有一個構(gòu)造方法,以前沒有報錯是因為編譯器自動為我們生成了無參的構(gòu)造方法。子類對象一般都有繼承過來的屬性還有自己獨有的屬性,在創(chuàng)建子類對象的時候,一般先執(zhí)行父類的構(gòu)造方法,將子類對象中繼承父類的屬性初始化完整,然后在調(diào)用自己的構(gòu)造方法,為自己獨有的屬性初始化完整。
所以我們一定要先為父類創(chuàng)建構(gòu)造方法。
怎么創(chuàng)建呢??? 與this一樣只不過這是父類的構(gòu)造方法,所以利用super關(guān)鍵字,利用super().
同時super不能在靜態(tài)方法中使用,并且當調(diào)用構(gòu)造方法的時候this()和super()只能出現(xiàn)一個,并且出現(xiàn)在第一行。
super要點:
- super關(guān)鍵字不能在靜態(tài)方法中使用。
- super關(guān)鍵字只能放在方法中的第一行,并且與this()只能出現(xiàn)一個。
- super關(guān)鍵字訪問父類屬性 super.父類方法 super.父類成員變量 super()為父類提供構(gòu)造方法。
- super關(guān)鍵字是引用父類的對象,當我們看見super的時候,他一定是引用父類的東西。
- 當沒有為父類寫帶有參數(shù)的構(gòu)造方法的時候,編譯器會為子類自動提供構(gòu)造方法,當父類寫了帶有參數(shù)的構(gòu)造方法,編譯器不會為子類提供無參的構(gòu)造方法,需要用戶自己寫,并且在寫子類的構(gòu)造方法的時候一定要先為父類構(gòu)造。
super與this的區(qū)別
相同點:
- this與super關(guān)鍵均不能在靜態(tài)方法中使用,靜態(tài)方法不依賴于對象。
- 并且只能放在方法的第一行。
- 都是java中的關(guān)鍵字
不同點:
- this是引用當前對象,super是用來引用當前對象的父類
- this()調(diào)用本類中的構(gòu)造方法,super用來調(diào)用父類的構(gòu)造方法。
- this調(diào)用非靜態(tài)方法中本類當中的屬性,super調(diào)用非靜態(tài)方法中的父類的屬性
- this是非靜態(tài)方法中的隱藏參數(shù),而super不是
- 構(gòu)造方法一定會有super的調(diào)用,當沒有時候編譯器會自動增加,而this不會
同時增加一個this和super的內(nèi)部圖示:
順序
靜態(tài)代碼塊與實例代碼塊及構(gòu)造方法的初始化順序
我們之前在上一篇文章講解了初始化順序,那時候還沒有講解繼承思想,我們再來回憶一下,應該是靜態(tài)代碼塊>實例代碼塊>構(gòu)造方法,并且代碼塊只執(zhí)行一次,也就是只保存一份,當我們構(gòu)造了兩個對象的時候,在實例化第一個對象會執(zhí)行靜態(tài)代碼塊,當實例化第二個代碼塊的時候,靜態(tài)代碼塊不會執(zhí)行。
接下來我們與繼承結(jié)合也就是有了父類和子類的靜態(tài)代碼塊,構(gòu)造方法,實例代碼塊。
那他們的順序又會是怎樣的呢???
我們來用代碼實踐一下,看結(jié)果到底是什么呢???
class Animal { public String name = "花花";//名字 public int age;//年齡 public String furColor;//毛顏色 static { System.out.println("我是父類的靜態(tài)代碼塊?。?!"); } { System.out.println("我是父類的實例化代碼塊!?。?); } public void eat() { System.out.println(this.name + "吃飯!??!"); } public void sleep() { System.out.println(this.name + "睡覺?。?!"); } public void method() { System.out.println("我是父類的方法?。。?); } public Animal(String name, int age, String furColor) { this.name = name; this.age = age; this.furColor = furColor; System.out.println("我是父類的構(gòu)造方法?。?!"); } } //實現(xiàn)一個貓類 class Cat extends Animal { public String name = "咪咪"; static { System.out.println("我是子類的靜態(tài)代碼塊?。?!"); } { System.out.println("我是子類的實例化代碼塊?。?!"); } public Cat(String name, int age, String furColor) { super(name, age, furColor); System.out.println("我是子類的構(gòu)造方法?。。?); } public void mew() { System.out.println(this.name + "喵喵喵"); } public void method() { System.out.println("我是子類的方法"); } public void methodA() { super.method(); } } public class TestDemo { public static void main(String[] args) { Cat cat = new Cat("咪咪",18,"橘黃色"); } }
這里還是用了上面的代碼,定義了兩個類,Animal類和子類Cat,我們同時定義了靜態(tài)代碼塊實例代碼塊,構(gòu)造方法。接下來運行一下看看順序到底是什么呢???
我們這里就可以總結(jié)一下了,還是靜態(tài)代碼塊優(yōu)先執(zhí)行,只不過這里因為在構(gòu)造子類時候優(yōu)先構(gòu)造父類所以是父類的靜態(tài)的代碼塊優(yōu)先于子類的靜態(tài)代碼塊,然后是父類的實例和構(gòu)造方法,最后是子類的實例和構(gòu)造。
還是一樣的我們在創(chuàng)建一個對象看還會跟上次的結(jié)果一樣么??
總結(jié):
初始化順序:父類靜態(tài)代碼塊>子類靜態(tài)代碼塊>父類實例代碼塊>父類構(gòu)造方法>子類實例代碼塊>子類構(gòu)造方法。如果是第二次實例化對象,那就沒有靜態(tài)代碼塊。
這個應該很好記憶就是靜態(tài)代碼塊優(yōu)先然后實例代碼塊然后構(gòu)造方法,因為構(gòu)造子類之前要先構(gòu)造父類所以父類的實例代碼塊和構(gòu)造方法要大于子類的實例代碼塊和構(gòu)造方法。
詳解訪問修飾限定符
訪問修飾限定符 | public(公共的) | private(私有的) | protected(受保護的) | default(默認權(quán)限) |
同一包的同一類 | yes√ | yes√ | yes√ | yes√ |
同一包的不同類 | yes√ | No× | yes√ | yes√ |
不同包的子類 | yes√ | No× | yes√ | No× |
不同報的非子類 | yes√ | No× | No× | No× |
看這個你可能會有些懵,接下來我會詳細講解。前提我們先要知道這些都是訪問權(quán)限超過它自己的權(quán)限就不可以訪問了。
public:它是公共的意思是無論在哪里同一個包還是不同的包,同一類還是不同的類都可以進行訪問。
pivate :只能訪問本類中的成員。
default:默認權(quán)限就是成員前面啥也不加只能在同一個包中訪問。
這里注意:
protected:可以在同一包中訪問,可以再不同包中的子類訪問,但是不可以在非子類訪問,所以它與繼承有很大的關(guān)系。
總結(jié)訪問修飾限定符:
public 可以再同一包中的同一類不同類都能夠訪問,不同包的子類和非子類也能夠訪問。
private 只能在本類中使用。
default 也就是默認訪問權(quán)限,什么也不加,只能在同一個包中訪問。
protected 可以在同一包中訪問,也可以在不同包的子類訪問。
繼承方式與組合
繼承方式
繼承方式多種多樣,如果能的話就可以一直繼承下去,但在java中繼承方式都有哪些呢???
java中的繼承方式可以進行單繼承,多層繼承。
這里還有一個重要的點:那就是雖然可以繼承很多,但是最少不要超過3成繼承關(guān)系。那如果不小心多繼承了呢?沒關(guān)系我們可以利用final修飾,這樣就可以停止繼承了。
但是java中不支持多繼承也就是一個子類同時繼承兩個父類。
這種是不可以的,如果java中要進行多繼承的話那就出現(xiàn)了接口。
組合
如果要說組合的話,我們舉個例子,組合嘛那就是一個東西由什么組成唄,比如汽車由發(fā)動機引擎,輪胎等等組成,學校由老師,學生,工作人員等等組成,而這些老師,學生,工作人員又有自己的屬性。我們把這個學校就稱為組合。
那用代碼如何表示呢??
class Teacher{//老師類 private String name; private int age; } class Student{//學生類 private String name; private int id; } class School{ //學校由學生和老師組成 private Student student[]; private Teacher teacher[]; }
從這里我們也可以知道繼承與組成本質(zhì)到底是什么樣的關(guān)系;
我們把繼承看做是一種 is a 關(guān)系:比如狗是一個動物,貓是一個動物
我們把組合看做是一種 has a的關(guān)系:比如學校有 學生,老師,工作人員組成。
那我們到底什么時候用組合,什么時候用繼承呢???
兩種方式不一定就要用哪個。我們他們兩的區(qū)別:1.繼承是一種is....a的關(guān)系,我們都知道程序是先編譯后運行,而繼承關(guān)系就是在編譯器編譯截斷下確定好的。組合關(guān)系是在運行時確定的。
所以組合比繼承更加簡單靈活高效。我們?nèi)绻潜匾闆r下優(yōu)先選擇組合。
到此這篇關(guān)于Java全面分析面向?qū)ο笾^承的文章就介紹到這了,更多相關(guān)Java繼承內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
你必須得會的SpringBoot全局統(tǒng)一處理異常詳解
程序在運行的過程中,不可避免會產(chǎn)生各種各樣的錯誤,這個時候就需要進行異常處理,本文主要為大家介紹了SpringBoot實現(xiàn)全局統(tǒng)一處理異常的方法,需要的可以參考一下2023-06-06Java Clone深拷貝與淺拷貝的兩種實現(xiàn)方法
今天小編就為大家分享一篇關(guān)于Java Clone深拷貝與淺拷貝的兩種實現(xiàn)方法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-10-10MyBatis-Plus中使用EntityWrappe進行列表數(shù)據(jù)倒序設(shè)置方式
這篇文章主要介紹了MyBatis-Plus中使用EntityWrappe進行列表數(shù)據(jù)倒序設(shè)置方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03springboot配置http跳轉(zhuǎn)https的過程
SSL是為網(wǎng)絡(luò)通信提供安全以及保證數(shù)據(jù)完整性的的一種安全協(xié)議,SSL在網(wǎng)絡(luò)傳輸層對網(wǎng)絡(luò)連接進行加密,這篇文章主要介紹了springboot配置http跳轉(zhuǎn)https的過程,需要的朋友可以參考下2023-04-04Java8中l(wèi)ambda表達式的應用及一些泛型相關(guān)知識
這篇文章主要介紹了Java8中l(wèi)ambda表達式的應用及一些泛型相關(guān)知識的相關(guān)資料2017-01-01