文章目錄
- 繼承概述
- 共性抽取
- 繼承的特點
- 繼承的好處
- 繼承的格式
- 屬性的訪問特點
- 如何區分三種變數重名時
- 方法的訪問特點
- 方法的重寫
- 重寫與多載的區別
- 覆寫重寫的特點
- 覆寫重寫的應用
- 覆寫重寫注意事項(重要)
- 構造器的訪問特點
- 兩個關鍵字
- super
- this
- Java繼承中的三個特點
- 子類物件實體化的程序
- 從結果上來看(繼承性)
- 從程序上來看
繼承概述
繼承是面向物件的三大特性之一,繼承從字面可以理解為繼承了某種事物或者能力,而在Java中繼承發生在子父類(或子父介面)的關系中,當子類繼承父類后子類具有了父類所有的屬性和方法,
共性抽取
繼承主要解決的問題就是:共性抽取,
我們之前學過類和物件的內容,關于類我們可以看作是對一類具備相關屬性和行為的事物的描述,而當多個類之間具有相同性同時也具有相異性,顯然無法將其歸為一類,此時我們可以抽取這些類之間的相同屬性和行為,得到一個具備了這些相同屬性和行為的類,那么其它的類就無需在本類中定義這些相同的屬性和行為,只需要繼承那一個類即可,
比如下圖:

兔子可以歸為一類,綿羊也可以歸為一類,它們都是吃草,那么通過這個共同的特性可以將它們同時歸為食草動物一類,同樣獅子和豹子也可以同時被歸為食肉動物一類,而食草動物和食肉動物也具備相同的屬性和行為,比如:進食、活動、交配…,那么就可以將食草動物和食肉動物歸為一類,這種一級一級的關系就像是繼承,兔子繼承了食草動物的特性,食草動物繼承了動物的特性,
回到Java中,多個具備相同屬性和行為的類被稱作子類或派生類,而通過抽取這些屬性和行為得到的類被稱為父類、超類或者基類,繼承描述的是事物之間的所屬關系,這種關系是: is-a 的關系,
繼承的特點
子類繼承父類后,就繼承父類了所有的屬性和方法,使得子類可以直接訪問父類中的非私有的屬性和方法,
- 子類可以擁有父類的“內容”,
- 子類還可以擁有自己專有的內容,實作功能的拓展,
- 父類中私有的屬性和方法,子類也會繼承,但是不能直接進行訪問,可以通過繼承父類中公共的方法來訪問父類的私有屬性或方法,
繼承的好處
- 減少了代碼的冗余,提高代碼的復用性,
- 便于功能的擴展,
- 類與類之間產生了關系,是多型的前提,
繼承的格式
在繼承的關系中,子類就是一個父類,也就是說,子類可以被當作父類看待(這很重要,多型前提),
例如:父類是員工,子類是講師,那么講師就是一個員工,
關系:is-a
// 定義父類的格式 (一個普通的類定義)
public class 父類名稱{
// ...
}
// 定義子類的格式
public class 子類名稱 extends 父類名稱{
// ...
}
例如:創建一個員工的父類,和它的一些子類,
// 定義一個父類,員工
public class Employee{
public void method() {
System.out.println("方法執行");
}
}
// 定義一個員工的子類,講師
public class Teacher extends Employee {
// 子類會繼承父類的方法
}
// 定義一個員工的子類,助教
public class Assistant extends Employee {
// 子類會繼承父類的方法
}
public class Demo01Extends {
public static void main(String[] args) {
// 創建子類講師的物件
Teacher teacher = new Teacher();
// 子類講師呼叫父類的方法
teacher.method();
// 創建子類助教的物件
Assistant assistant = new Assistant();
// 子類助教呼叫父類的方法
assistant.method();
}
}
// 通過繼承的方法,可以起到代碼復用的作用

屬性的訪問特點
在父子類的繼承關系當中,如果子類和父類中的成員變數重名,則創建子類物件時,訪問有兩種方式:
-
直接通過子類物件訪問成員變數,
等號左邊是誰(物件參考),就優先用誰,沒有則向上找,
-
間接通過成員方法訪問成員變數,
該方法屬于誰,就用優先用誰的成員變數,沒有則向上找,
// 定義父類
public class Father {
int numF = 10;
// 與子類成員變數名相同
int num = 100;
public void methodF()
{
// 此方法內部需要一個num變數,優先用本類的變數
System.out.println(num);
}
}
// 定義子類
public class Son extends Father{
int numS = 20;
// 與父類成員變數名相同
int num = 200;
public void methodZ()
{
// 此方法內部需要一個num變數,優先用本類的變數
System.out.println(num);
}
}
// 創建物件
public class Demo01ExtendsField {
public static void main(String[] args) {
// 創建父類物件
Father fa = new Father();
// 父類只能使用父類的成員變數和方法
System.out.println(fa.numF); // 10
// 創建子類物件
Son son = new Son();
// 子類可以使用父類和子類的成員變數和方法
System.out.println(son.numF); // 10
System.out.println(son.numS); // 20
// 變數名重名,直接方法
// 【等號左邊是誰,就有用誰】
System.out.println(son.num); // 200
// 變數名重名,間接方法
// 【呼叫方法,方法屬于誰,優先用誰的】
son.methodF(); // 100
son.methodZ(); // 200
}
}
如何區分三種變數重名時
三種變數:父成員變數,子成員變數,區域變數
-
區域變數:直接在作用域的{}中寫變數名,根據就近原則會優先使用該區域變數,
-
子類成員變數:使用this.變數名來呼叫子類成員變數,this可以理解為當前物件的參考,
-
父類成員變數:使用super.變數名來呼叫父類成員變數,super可以理解為當前物件的父類參考,
// 區域變數 直接寫變數名
// 子類成員變數 this.變數名
// 父類成員變數 super.變數名
// 父類
public class Father {
int num = 10;
}
// 子類
public class Son extends Father {
int num = 20;
public void method(){
int num = 30;
System.out.println(num); // 30,訪問區域變數
System.out.println(this.num); // 20,訪問本類成員變數
System.out.println(super.num); // 10,訪問父類成員變數
}
}
// 使用
public class Demo01ExtendsField {
public static void main(String[] args) {
Son son = new Son();
// 呼叫子類的成員方法,查看輸出結果
son.method();
}
}

方法的訪問特點
在父子類的繼承關系當中,創建子類物件,訪問成員方法的規則是:
創建的物件是誰,就優先用誰,如果沒有則向上找,
單以繼承舉例,子類物件在呼叫方法時,會優先執行本類中相應的方法,如果子類中沒有該方法則會執行父類相應的方法,
注意事項:
無論是成員方法還是成員變數,如果沒有都是向上找父類,絕不會向下找子類,
// 父類
public class Fu {
public void methodFu(){
System.out.println("父類方法執行!");
}
public void method()
{
System.out.println("父類重名方法執行了");
}
}
// 子類
public class Zi extends Fu{
public void methodZi(){
System.out.println("子類方法執行!");
}
public void method()
{
System.out.println("子類重名方法執行了");
}
}
// 使用
public class Demo01ExtendsMethod {
public static void main(String[] args) {
Zi zi = new Zi();
zi.methodFu();
zi.methodZi();
// 呼叫重名成員方法,查看輸出結果
zi.method();
}
}

方法的重寫
如果子類父類中出現重名的成員方法,這時的訪問是一種特殊情況,叫做方法重寫 (Override),
方法重寫 :子類中出現與父類一模一樣的方法時(回傳值型別,方法名和引數串列都相同),會出現覆寫效果,也稱為重寫或者復寫,宣告不變,重新實作,
class Fu {
public void show() {
System.out.println("Fu show");
}
} class Zi extends Fu {
//子類重寫了父類的show方法
@Override
public void show() {
System.out.println("Zi show");
}
} public class ExtendsDemo05{
public static void main(String[] args) {
Zi z = new Zi();
// 子類中有show方法,只執行重寫后的show方法
z.show(); // Zi show
}
}
重寫與多載的區別
重寫(Override):在繼承關系當中,方法的名稱一樣,引數串列也一樣,
多載(OverLoad):方法的名稱一樣,引數串列不一樣,
覆寫重寫的特點
創建的是子類物件,則優先用子類方法,
覆寫重寫的應用
子類可以根據需要,定義特定于自己的行為,既沿襲了父類的功能名稱,又根據子類的需要重新實作父類方法,從而進行擴展增強,比如新的手機增加來電顯示頭像的功能,代碼如下:
// 老手機
public class Phone {
public void call() {
System.out.println("打電話");
}
public void send() {
System.out.println("發短信");
}
public void show() {
System.out.println("顯示號碼");
}
}
// 定義一個新手機,使用老手機作為父類
public class NewPhone extends Phone {
@Override
public void show() {
super.show(); // 把父類的show方法拿過來使用
// 自己再增加新功能
System.out.println("顯示姓名");
System.out.println("顯示頭像");
}
}
public class Demo01Phone {
public static void main(String[] args) {
Phone phone = new Phone();
// 父類手機
phone.call();
phone.send();
phone.show();
System.out.println("==========");
NewPhone newPhone = new NewPhone();
newPhone.call();
newPhone.send();
newPhone.show();
}
}

覆寫重寫注意事項(重要)
-
必須保證父子類之間方法的名稱相同,引數串列也相同,
@Override寫在方法前,用來檢測是不是有效的正確覆寫重寫,
@Override // 如果不是有效覆寫會報錯
public void method()
{
// ...
}
-
子類方法的回傳值型別必須小于等于父類方法的回傳值范圍,
例如:
java中java.lang.Object類是所有類的公共最高父類,如果父類的回傳值是String,子類的回傳值是Object,這是錯誤寫法會編譯報錯!


-
父類被重寫方法的回傳值型別是void,則子類重寫的方法回傳值型別也必須是void,

-
子類方法的權限修飾符必須大于等于父類方法的權限修飾符,
權限修飾符:
public>protected>(default)>private備注:
(default)不是關鍵字default,而是什么都不寫,留空,


4. 子類不能重寫父類中宣告為private權限的方法,

-
子類方法拋出的例外不能大于父類被重寫方法的例外 ,
-
子類和父類中的同名同引數的方法,要么宣告為非static的(重寫),要么都宣告為static的(不是重寫),
構造器的訪問特點
- 子類構造器當中,有一個默認隱含的
super()呼叫,子類在創建物件時會默認先執行父類構造器,再執行子類構造器,
// 父類
public class Fu {
public Fu(){
System.out.println("父類構造器!");
}
}
// 子類
public class Zi extends Fu{
public Zi(){
// super(); 默認隱含呼叫無參父類構造,不寫也會有
System.out.println("子類構造器!");
}
}
public class Demo01Constructor {
public static void main(String[] args) {
Zi zi = new Zi();
}
}

2. 可以通過**super**關鍵字,呼叫父類多載的構造器,
// 父類
public class Fu {
public Fu(){
System.out.println("父類無參構造器!");
}
public Fu(int num){
System.out.println("父類有參構造器!");
}
}
// 子類
public class Zi extends Fu{
public Zi(){
super(10); // 呼叫父類有參構造器
System.out.println("子類構造器!");
}
}
public class Demo01Constructor {
public static void main(String[] args) {
Zi zi = new Zi(); // 此時會呼叫父類有參構造
}
}

- 子類的構造器中必須要呼叫父類的構造器,如果父類只定義了一個有參構造器,且子類沒有在構造器中呼叫父類的有參構造器,編譯會報錯,
public class Fu{
public Fu(int param){
System.out.println(param);
}
}
public class Zi{
public Zi(){ // 編譯不通過,父類沒有空參構造器,子類必須呼叫父類的有參構造器
}
}
// 修改方式如下:
// 1. 在子類構造器中呼叫父類的帶參構造器
public class Zi{
public Zi(){
super(123);
}
}
// 2. 給父類提供有參構造器
public class Fu{
public Fu(){
}
public Fu(int param){
System.out.println(param);
}
}
super的父類呼叫,必須是子類構造器的第一個陳述句,
錯誤寫法:
public void method(){
super(); // 錯誤寫法!只有子類構造器,才能呼叫父類構造器
}
public class Zi extends Fu{
public Zi(){
super();
super(10); // 錯誤寫法!只能呼叫一個父類構造
System.out.println("子類構造器!");
}
}
public class Zi extends Fu{
public Zi(){
System.out.println("子類構造器!");
super(10); // 錯誤寫法!super() 必須是第一個陳述句
}
}

兩個關鍵字
super
super關鍵字用來訪問父類內容,代表父類的記憶體空間的標識,
父類的成員變數不會被覆寫重寫,如果父類和子類中有同名的成員變變數,各自歸屬于不同的類,如果想在子類中呼叫父類的成員變數則也需要使用super關鍵字,
super關鍵字的用法有三種:
- 在子類的成員方法中,訪問父類的成員變數,
- 在子類的成員方法中,訪問父類的成員方法,
- 在子類的構造器中,呼叫父類的構造器,
注意:
- 子類中使用
super([形參串列])的方式呼叫父類構造器,必須宣告在子類構造器中的首行, - 子類的構造器中會默認隱含一個
super(),如果再定義了一個super([引數串列]),則不會在默認隱含, - 在類的多個構造器中,至少有一個類的構造器中使用了
super([形參串列]),呼叫父類的構造器(沒有直接父類,那么還有Object接盤), - super的追溯不僅限于直接父類,
// 父類
public class Fu {
int num = 10; // 父類私有成員變數
public Fu(int num){
System.out.println("父類構造");
}
public void method() {
System.out.println("父類方法!");
}
}
// 子類
public class Zi extends Fu{
int num = 20;
public Zi() {
super(10); // 呼叫父類構造器
}
public void methodZi() {
System.out.println(super.num); // 父類的num
}
public void method() {
super.method(); // 訪問父類中的method
System.out.println("子類方法!");
}
}
this
this關鍵字用來訪問本類內容
this關鍵字的三種用法:
-
在本類的成員方法中,訪問本類的成員變數,
-
在本類的成員方法中,訪問本類的另一個成員方法,
-
在本類的構造器中,訪問本類的另一個構造器,
this(...)呼叫也必須是構造器的第一個陳述句,唯一一個,
注意:
super(...)和this(...)兩種構造呼叫,不能同時使用,因為this(…) 和 super(…) 都必須是構造器中的第一個陳述句
// 父類
public class Fu {
int num = 30;
}
// 子類
public class Zi extends Fu{
int num = 20;
// 無參構造
public Zi() {
this(10); // 本類無參構造呼叫本類有參構造
// 必須是構造器的第一條(唯一)陳述句
}
// 有參構造
public Zi(int n) {
}
public void showNum(){
int num = 10;
System.out.println(num); // 區域變數
System.out.println(this.num); // 本類成員變數
System.out.println(super.num); // 父類中的成員變數
}
public void methodA() {
System.out.println("AAA");
}
public void methodB(){
methodA();
this.methodA(); // 兩種效果系相同,使用this關鍵字強調本類方法
System.out.println("BBB");
}
}
Java繼承中的三個特點
-
Java語言是單繼承的,一個類的直接父類只有唯一一個,
-
Java語言可以多級繼承,即可以有父親,爺爺,祖宗…,
-
子類的直接父類是唯一的,但是父類可以擁有多個子類,

子類物件實體化的程序
從結果上來看(繼承性)
子類繼承父類以后,就獲取了父類中宣告的屬性或方法,
創建子類的物件,在堆空間中,就會加載所有父類中宣告的屬性(不是創造父類物件),

從程序上來看
通過子類的構造器創建子類物件時,一定會直接或間接的呼叫父的構造器,進而呼叫父類的父類的構造器,直到呼叫了java.lang.Object類中的空參構造器,這也就是為什么子類會繼承所有父類的屬性或方法,

注意:雖然創建子類物件的程序中,呼叫了父類的構造器,但是自始至終只創建了一個物件,即為new的子類物件,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/301943.html
標籤:java
上一篇:界面編程實戰分析------計算器實作用戶互動;多執行緒---售票視窗分析;一文帶你分析界面編程與多執行緒【Java養成】
