Java 繼承詳解
什麼是繼承?
多個類中存在相同屬性和行為時,將這些內容抽取到單獨一個類中,那麼多個類無需再定義這些屬性和行為,只要繼承那個類即可。
多個類可以稱為子類,單獨這個類稱為父類、超類或者基類。
子類可以直接訪問父類中的非私有的屬性和行為。
通過 extends 關鍵字讓類與類之間產生繼承關係。
class SubDemo extends Demo{} //SubDemo是子類,Demo是父類
繼承有什麼好處?
- 提高程式碼的複用性。
- 讓類與類之間產生了關係,是多型的前提。
繼承的特點
1.Java只支援單繼承,不支援多繼承。
//一個類只能有一個父類,不可以有多個父類。 class SubDemo extends Demo{} //ok class SubDemo extends Demo1,Demo2...//error
2.Java支援多層(重)繼承(繼承體系)。
class A{} class B extends A{} class C extends B{}
使用繼承時的注意事項
- 如果類之間存在著:is a 的關係,就可以考慮使用繼承。
- 不要為了繼承部分功能,而去使用繼承。
super和this有什麼區別?
super是一個關鍵字,代表父類的儲存空間標識。(可以理解為父親的引用)
super和this的用法相似。
this代表物件的引用(誰呼叫就代表誰);
super代表當前子類對父類的引用。
使用場景
- 當子父類出現同名成員時,可以用super進行區分;
- 子類要呼叫父類建構函式時,可以使用super語句。
區別
1.成員變數
this.變數 -- 本類的 super.變數 -- 父類的
2.構造方法
this(...) -- 本類的 super(...) -- 父類的
3.成員方法
this.方法名() -- 本類的 super.方法名() -- 父類的
super();和this();都是在建構函式的第一行,不能同時出現。
方法的重寫(覆蓋)
子類中出現與父類一模一樣的方法時(除了許可權修飾符,許可權修飾符大於等於不包括private,返回值型別,方法名和引數列表相同),會出現覆蓋操作,也稱為重寫或者複寫。
父類私有方法,子類看不到,因此父類私有方法的重寫也就無從談起。
覆蓋注意事項:
- 覆蓋時,子類方法許可權一定要大於等於父類方法許可權;
- 靜態只能覆蓋靜態。
覆蓋的使用場景:
當子類需要父類的功能,而功能主體子類有自己特有內容時,可以複寫父類中的方法,這樣,既沿襲了父類的功能,又定義了子類特有的內容。
方法重寫和過載有什麼區別?
方法的重寫用在子類方法與父類方法一模一樣時,除許可權修飾符,返回值型別,方法名和引數列表都是相同的。
過載用在同一個類中各方法方法名相同,引數列表不同(與返回值型別沒有關係)的情況。
子父類中構造方法的用法:
- 子類的初始化過程中,首先回去執行父類的初始化動作。因為子類的構造方法中預設有一個super()。子類要使用父類的成員變數,這個初始化,必須在子類初始化之前完成。所以,子類的初始化過程中,會先執行父類的初始化。
- 如果父類沒有無參構造方法
- 使用super呼叫父類的帶參構造。推薦方式。
- 使用this呼叫本身的其他構造。
靜態程式碼塊、構造程式碼塊,構造方法的執行順序:
父類靜態程式碼塊→子類靜態程式碼塊→父類構造程式碼塊→父類構造方法→子類構造程式碼塊→子類構造方法
final關鍵字
final是一個關鍵字,可以用於修飾類,成員變數,成員方法。
特點:
- 它修飾的類不能被繼承。
- 它修飾的成員變數是一個常量。
- 它修飾的成員方法是不能被子類重寫的。
final修飾的常量定義一般都有書寫規範,被final修飾的常量名稱,所有字母都大寫。
final修飾成員變數,必須初始化,初始化有兩種
- 顯示初始化;
- 構造方法初始化。
但是不能兩個一起初始化
final和private的區別:
- final修飾的類可以訪問;
private不可以修飾外部類,但可以修飾內部類(其實把外部類私有化是沒有意義的)。 - final修飾的方法不可以被子類重寫;
private修飾的方法表面上看是可以被子類重寫的,其實不可以,子類是看不到父類的私有方法的。 - final修飾的變數只能在顯示初始化或者建構函式初始化的時候賦值一次,以後不允許更改;
private修飾的變數,也不允許直接被子類或一個包中的其它類訪問或修改,但是他可以通過set和get方法對其改值和取值。
多型
概念:
物件在不同時刻表現出來的不同狀態。
多型的前提:
- 要有繼承或者實現關係。
- 要有方法的重寫。
- 要有父類引用指向子類物件。
程式中的體現:
父類或者介面的引用指向或者接收自己的子類物件。
好處和作用:
多型的存在提高了程式的擴充套件性和後期可維護性。
弊端:
父類呼叫的時候只能呼叫父類裡的方法,不能呼叫子類的特有方法,因為你並不清楚將來會有什麼樣的子類繼承你。
多型的成員特點:
- 成員變數:編譯時期:看引用型變數所屬的類中是否有所呼叫的變數;
執行時期:也是看引用型變數所屬的類是否有呼叫的變數。
成員變數無論編譯還是執行都看引用型變數所屬的類,簡單記成員變數,編譯和執行都看等號左邊。 - 成員方法:編譯時期:要檢視引用變數所屬的類中是否有所呼叫的成員;
執行時期:要檢視物件所屬的類中是否有所呼叫的成員。如果父子出現同名的方法,會執行子類中的方法,因為方法有覆蓋的特性。
編譯看左邊執行看右邊。 - 靜態方法:編譯時期:看的引用型變數所屬的類中是否有所呼叫的變數;
執行時期:也是看引用型變數所屬的類是否有呼叫的變數。
編譯和執行都看等號左邊。
一定不能夠將父類的物件轉換成子類型別!
父類的引用指向子類物件,該引用可以被提升,也可以被強制轉換。
多型自始至終都是子類物件在變化!
//多型向下轉型和向上轉型的例子,多型轉型解決了多型中父類引用不能使用子類特有成員的弊端。 class PolymorphicTest2 { public static void main(String[] args) { Phone p1 = new Nokia(); //向上轉型,型別提升 Nokia no = (Nokia)p1; //向下轉型,強制將父類的引用轉換成子類型別,不能將Nokia型別轉成Moto或Nexus型別 no.print(); //輸出結果為Phone---null---0,因為繼承了父類的方法 Phone p2 = new Moto(); Moto m = (Moto)p2; m.print(); //輸出結果為Moto---yellow---1599,方法重寫,子類方法覆蓋父類方法 Phone p3 = new Nexus(); Nexus ne = (Nexus)p3; ne.print(); } } class Phone{ String color; int price; public void print(){ System.out.println("Phone---" + color + "---" + price ); } } class Nokia extends Phone{ String color = "red"; int price = 1009; //public void print(){ // System.out.println("Nokia---" + color + "---" + price); //} } class Moto extends Phone{ String color = "yellow"; int price = 1599; public void print(){ System.out.println("Moto---" + color + "---" + price); } } class Nexus extends Phone{ String color = "black"; int price = 1999; public void print(){ System.out.println("Nexus---" + color + "---" + price); } } }
抽象(abstract)
抽象就是從多個事物中將共性的,本質的內容抽象出來。
抽象類:
Java中可以定義沒有方法體的方法,該方法的具體實現由子類完成,該方法稱為抽象方法,包含抽象方法的類就是抽象類。
由來:
多個物件都具備相同的功能,但是功能具體內容有所不同,那麼在抽取過程中,只抽取了功能定義,並未抽取功能主體,那麼只有功能宣告,沒有功能主體的方法稱為抽象方法。
抽象類特點:
- 抽象方法一定在抽象類中;
- 抽象方法和抽象類都必須被abstract關鍵字修飾;
- 抽象類不可以用new建立物件,因為呼叫抽象方法沒意義;
- 抽象類中的抽象方法要被使用,必須由子類複寫其所有的抽象方法後,建立子類物件呼叫; 如果子類只覆蓋了部分的抽象方法,那麼該子類還是一個抽象類;
- 抽象類中可以有抽象方法,也可以有非抽象方法,抽象方法用於子類例項化;
- 如果一個類是抽象類,那麼,繼承它的子類,要麼是抽象類,要麼重寫所有抽象方法。
特殊:抽象類中可以不定義抽象方法,這樣做僅僅是不讓該類建立物件。
抽象類的成員特點:
- 成員變數:可以是變數,也可以是常量;
- 構造方法:有構造方法;
- 成員方法:可以是抽象方法,也可以是非抽象方法。
abstract class 葵花寶典 { public abstract void 自宮(); } class 嶽不群 extends 葵花寶典 { public void 自宮(){ System.out.println("剪刀"); } } class 林平之 extends 葵花寶典{ public void 自宮(){ System.out.println("指甲刀"); } } class AbstractTest { public static void main(String[] args) { 嶽不群 嶽 = new 嶽不群(); 嶽.自宮(); 林平之 林 = new 林平之(); 林.自宮(); } }
抽象類注意事項:
抽象類不能被例項化,為什麼還有建構函式?
只要是class定義的類裡面就肯定有建構函式。抽象類中的函式是給子類例項化的。
一個類沒有抽象方法,為什麼定義為抽象類?
不想被繼承,還不想被例項化。
抽象關鍵字abstract不可以和哪些關鍵字共存?
- final:如果方法被抽象,就需要被覆蓋,而final是不可以被覆蓋,所以衝突。
- private:如果函式被私有了,子類無法直接訪問,怎麼覆蓋呢?
- static:不需要物件,類名就可以呼叫抽象方法。而呼叫抽象方法沒有意義。
介面(interface)
介面是抽象方法和常量值的集合。從本質上講,介面是一種特殊的抽象類,這種抽象類只包含常量和方法的定義,而沒有變數和方法的實現。
格式:interface 介面名{}
介面的出現將”多繼承“通過另一種形式體現出來,即”多實現“。
實現(implements)
格式:class 類名 implements 介面名 {}
特點:
- 介面不能被例項化。
- 一個類如果實現了介面,要麼是抽象類,要麼實現介面中的所有方法。
介面的成員特點:
介面中的成員修飾符是固定的!
- 成員常量:public static final,介面裡定義的變數是全域性常量,而且修飾符只能是這三個關鍵字,都可以省略,常量名要大寫。
- 成員方法:public abstract,介面裡定義的方法都是抽象的,兩個修飾符關鍵字可省略。
- 推薦:永遠手動給出修飾符。
繼承與實現的區別:
- 類與類之間稱為繼承關係:因為該類無論是抽象的還是非抽象的,它的內部都可以定義非抽象方法,這個方法可以直接被子類使用,子類繼承即可。只能單繼承,可以多層繼承。((class))
- 類與介面之間是實現關係:因為介面中的方法都是抽象的,必須由子類實現才可以例項化。可以單實現,也可以多實現;還可以在繼承一個類的同時實現多個介面。((class) extends (class) implements (interface1,interface2…))
- 介面與介面之間是繼承關係:一個介面可以繼承另一個介面,並新增新的屬性和抽象方法,並且介面可以多繼承。((interface) extends (interface1,interface2…))
抽象類和介面的區別:
成員變數
- 抽象類能有變數也可以有常量
- 介面只能有常量
成員方法
- 抽象類可以有非抽象的方法,也可以有抽象的方法
- 介面只能有抽象的方法
構造方法
-抽象類有構造方法
-介面沒有構造方法
類與抽象類和介面的關係
- 類與抽象類的關係是繼承 extends
- 類與介面的關係是實現 implements
介面的思想特點:
- 介面是對外暴露的規則;
- 介面是程式的功能擴充套件;
- 介面的出現降低耦合性;(實現了模組化開發,定義好規則,每個人實現自己的模組,大大提高了開發效率)
- 介面可以用來多實現;
- 多個無關的類可以實現同一個介面;
- 一個類可以實現多個相互直接沒有關係的介面;
- 與繼承關係類似,介面與實現類之間存在多型性。
//運動員和教練的案例(下圖是思路分析) /* 籃球運動員和教練 乒乓球運動員和教練 現在籃球運動員和教練要出國訪問,需要學習英語 請根據你所學的知識,分析出來哪些是類,哪些是抽象類,哪些是介面 */ interface SpeakEnglish { public abstract void speak(); } interface GoAboard{ public abstract void aboard(); } abstract class Person { private String name; private int age; public Person(){} public Person(String name,int age){ this.name = name; this.age = age; } public void setName(String name){ this.name = name; } public String getName(){ return name; } public void setAge(int age){ this.age = age; } public int getAge(){ return age; } //吃飯 public abstract void eat(); //睡覺 public void sleep(){ System.out.println("Zzz..."); } } //運動員 abstract class Player extends Person { public abstract void study(); } //教練 abstract class Coach extends Person { public abstract void teach(); } //籃球運動員 class BasketballPlayer extends Player implements SpeakEnglish,GoAboard{ public void eat(){ System.out.println(getAge() + "歲的" + getName() + "吃雞腿"); } public void study(){ System.out.println(getAge() + "歲的" + getName() + "學扣籃"); } public void speak(){ System.out.println(getAge() + "歲的" + getName() + " Say Hello World"); } public void aboard(){ System.out.println(getAge() + "歲的" + getName() + " Go Aboard"); } } //乒乓運動員 class PingPangPlayer extends Player{ public void eat(){ System.out.println(getAge() + "歲的" + getName() + "吃雞蛋"); } public void study(){ System.out.println(getAge() + "歲的" + getName() + "學扣球"); } } //籃球教練 class BasketballCoach extends Coach implements SpeakEnglish { public void eat(){ System.out.println(getAge() + "歲的" + getName() + "啃雞爪"); } public void teach(){ System.out.println(getAge() + "歲的" + getName() + "教扣籃"); } public void speak(){ System.out.println(getAge() + "歲的" + getName() + " Say Hello Java"); } public void aboard(){ System.out.println(getAge() + "歲的" + getName() + " Go Aboard"); } } //乒乓球教練 class PingPangCoach extends Coach{ public void eat(){ System.out.println(getAge() + "歲的" + getName() + "吃雞蛋皮"); } public void teach(){ System.out.println(getAge() + "歲的" + getName() + "教扣球"); } } class PlayerAndCoach { public static void main(String[] args) { //籃球運動員 BasketballPlayer bp = new BasketballPlayer(); bp.setName("郭艾倫"); bp.setAge(33); bp.eat(); bp.sleep(); bp.study(); bp.speak(); bp.aboard(); System.out.println("***********************"); //籃球教練 BasketballCoach bc = new BasketballCoach(); bc.setName("波波維奇"); bc.setAge(65); bc.eat(); bc.sleep(); bc.teach(); bc.speak(); bc.aboard(); System.out.println("***********************"); //多型 Person p = new BasketballPlayer(); p.setName("Kobe Bryant"); p.setAge(33); p.eat(); p.sleep(); //p.study(); //p.speak(); BasketballPlayer bp2 = (BasketballPlayer)p; bp2.study(); bp2.speak(); bp2.aboard(); System.out.println("***********************"); } }
內部類
將一個類定義在另一個類裡面,裡面的那個類就稱為內部類。內部類的出現,再次打破了Java單繼承的侷限性。
訪問特點:
- 內部類可以直接訪問外部類的成員,包括私有成員。
- 外部類要訪問內部類的成員,必須要建立內部類的物件。
內部類分類及共性:
共性:
- 內部類仍然是一個獨立的類,在編譯之後會內部類會被編譯成獨立的.class檔案,但是前面冠以外部類的類名和$符號。
- 內部類不能用普通的方式訪問。內部類是外部類的一個成員,因此內部類可以自由地訪問外部類的成員變數,無論是否是private的。
成員內部類
在外部類中有成員變數和成員方法,成員內部類就是把整個一個類作為了外部類的成員;
成員內部類是定義在類中方法外的類;
建立物件的格式為:外部類名.內部類名 物件名 = 外部類物件.內部類物件;
成員內部類之所以可以直接訪問外部類的成員,那是因為內部類中都持有一個外部類物件的引用:外部類名.this;
成員內部類可以用的修飾符有final,abstract,public,private,protected,static.
靜態內部類
靜態內部類就是成員內部類加上靜態修飾符static,定義在類中方法外。
在外部類中訪問靜態內部類有兩種場景:
- 在外部類中訪問靜態內部類中非靜態成員:*外部類名.內部類名 物件名 = 外部類名.內部對象*,需要通過建立物件訪問;
- 在外部類中訪問靜態內部類中的靜態成員:同樣可以使用上面的格式進行訪問,也可以直接使用外部類名.內部類名.成員。
區域性內部類
區域性內部類是定義在方法中的類。
- 方法內部類只能在定義該內部類的方法內例項化,不可以在此方法外對其例項化。
- 方法內部類物件不能使用該內部類所在方法的非final區域性變數。
可以用於方法內部類的修飾符有final,abstract;
靜態方法中的方法內部類只能訪問外部的靜態成員。
匿名內部類
匿名內部類是內部類的簡化寫法,是建立一個帶內容的外部類或者介面的子類匿名物件。
前提:
內部類可以繼承或實現一個外部類或者介面。
格式:
new 外部類名或者介面名(){重寫方法};
通常在方法的形式引數是介面或者抽象類,並且該介面中的方法不超過三個時,可以將匿名內部類作為引數傳遞。
不同修飾符修飾的內容(和內部類無關)
類 | 成員變數 | 成員方法 | 構造方法 | |
---|---|---|---|---|
private | Y | Y | Y | |
預設 | Y | Y | Y | Y |
protected | Y | Y | Y | |
public | Y | Y | Y | Y |
abstract | Y | Y | ||
static | Y | Y | Y | |
final | Y | Y | Y |
注意,常見規則如下:
- 以後,所有的類都用public修飾。並且,在一個java檔案中,只寫一個類。
- 以後,所有的成員變數用private修飾。
- 以後,所有的成員方法用public修飾。
如果是抽象類或者介面:public abstract + … - 以後,所有的構造方法用public修飾。
如果類是工具類或者單例類:構造用private修飾
四種許可權修飾符
本類 | 同包(無關類或子類) | 不同包(子類) | 不同包(無關類) | |
---|---|---|---|---|
private | Y | |||
預設 | Y | Y | ||
protected | Y | Y | Y | |
public | Y | Y | Y | Y |
推薦:
- 成員變數 private
- 構造方法 public
- 成員方法 public
相關文章
- java繼承基礎詳解Java繼承
- JavaScript繼承詳解(二)JavaScript繼承
- js 組合繼承詳解JS繼承
- 「萬字圖文」史上最姨母級Java繼承詳解Java繼承
- Java實現多執行緒詳解一 ( 繼承Thread方式 )Java執行緒繼承thread
- springmvc中Dispatchservlet繼承體系詳解SpringMVCServlet繼承
- 12.16 Java繼承Java繼承
- Java的繼承Java繼承
- java繼承extendsJava繼承
- JAVA中的註解可以繼承嗎?Java繼承
- Kotlin——中級篇(四):繼承類詳解Kotlin繼承
- Java集合繼承圖Java繼承
- java中的繼承Java繼承
- java -繼承 -重寫Java繼承
- Java繼承練習Java繼承
- odoo 繼承(owl繼承、web繼承、view繼承)Odoo繼承WebView
- ES6中的類繼承和ES5中的繼承模式詳解繼承模式
- 詳解C++中繼承的基本內容C++中繼繼承
- js繼承圖解JS繼承圖解
- java繼承與多型Java繼承多型
- 【Java】繼承、抽象、組合Java繼承抽象
- Java 繼承是什麼?Java繼承
- java物件導向繼承Java物件繼承
- JS原型鏈、prototype、__proto__、原型鏈繼承詳解JS原型繼承
- 「MoreThanJava」Day 5:物件導向進階——繼承詳解Java物件繼承
- 詳解JS的繼承(三)-- 圖解Es6的ExtendJS繼承圖解
- 好程式設計師Java培訓之泛型繼承原理與用法詳解程式設計師Java泛型繼承
- 菱形繼承,虛繼承繼承
- 原型,繼承——原型繼承原型繼承
- 初步瞭解 JS 繼承JS繼承
- js繼承方式講解JS繼承
- 類的繼承圖解繼承圖解
- 圖解js的繼承圖解JS繼承
- java——繼承遇到構造方法Java繼承構造方法
- Java之繼承和抽象類Java繼承抽象
- 多繼承 與 多重繼承繼承
- Javascript繼承2:建立即繼承—-建構函式繼承JavaScript繼承函式
- Javascript繼承4:潔淨的繼承者—-原型式繼承JavaScript繼承原型
- 好程式設計師分享JavaScript六種繼承方式詳解程式設計師JavaScript繼承