1. 程式人生 > >深入理解java繼承從“我爸是李剛”講起

深入理解java繼承從“我爸是李剛”講起

目錄

  • 1、繼承的概述
  • 2、關於繼承之後的成員變數
  • 3、關於繼承之後的成員方法
  • 4、關於繼承之後的構造方法
  • 5、關於繼承的多型性支援的例子
  • 6、super與this的用法

前言
本文主要多方面講解java繼承,旨在讓初學者通俗易懂,至於“我爸是李剛”,反正樓主也不知道誰爸是李剛。
@

1、繼承的概述

1.1、繼承的由來

至於由來簡單一句話:多個類中存在相同屬性和行為時,將這些內容抽取到單獨一個類中,那麼多個類無需再定義這些屬性和行為。

繼承描述的是事物之間的所屬關係,這種關係是 is-a

的關係。

1.2、繼承的定義

繼承:就是子類繼承父類的屬性行為,使得子類物件具有與父類相同的屬性、相同的行為。子類可以直接訪問父類中的非私有的屬性和行為。

這裡再宣告一點,父類又稱為超類或者基類。而子類又稱為派生類這點很基礎!

1.3、繼承的優點

  1. 提高程式碼的複用性。
  2. 類與類之間產生關係,為多型做了完美的鋪墊(不理解沒關係,之後我會再寫一篇多型的文章)

雖然繼承的優點很多但是Java只支援單繼承,不支援多繼承。

1.4、繼承的格式

通過 extends 關鍵字,可以宣告一個子類繼承另外一個父類,定義格式如下:

  class 父類 {
   ... 
   }
   class 子類 extends 父類 { 
   ... 
   } 

2、關於繼承之後的成員變數

當類之間產生了關係後,其中各類中的成員變數,產生了哪些影響呢? 關於繼承之後的成員變數要從兩方面下手,一是成員變數不重名方面,二是成員變數重名方面。

2.1、成員變數不重名

如果子類父類中出現不重名的成員變數,這時的訪問是沒有影響的。程式碼如下:

  class liGang {
        // 父類中的成員變數。
       String name ="李剛";//------------------------------父類成員變數是name
    }
    class LiXiaoGang extends liGang {
        // 子類中的成員變數
        String name2 ="李小剛";//--------------------------子類成員變數是name2
        // 子類中的成員方法
        public void show() {
            // 訪問父類中的name,
            System.out.println("我爸是"+name);
            // 繼承而來,所以直接訪問。
            // 訪問子類中的name2
            System.out.println("我是"+name2);
        }
    }
public class Demo {
        public static void main(String[] args) {
            // 建立子類物件
            LiXiaoGang z = new LiXiaoGang();
            // 呼叫子類中的show方法
            z.show();
        }
    }
    //演示結果: 我爸是李剛   我是李小剛

2.2、 成員變數重名

如果子類父類中出現重名的成員變數,這時的訪問是有影響的。程式碼如下:

class liGang {
        // 父類中的成員變數。
       String name ="李剛";//------------------------------父類成員變數是name
    }
    class LiXiaoGang extends liGang {
        // 子類中的成員變數
        String name ="李小剛";//---------------------------子類成員變數也是name
        // 子類中的成員方法
        public void show() {
            // 訪問父類中的name,
            System.out.println("我爸是"+name);
            // 繼承而來,所以直接訪問。
            // 訪問子類中的name2
            System.out.println("我是"+name);
        }
    }
public class Demo {
        public static void main(String[] args) {
            // 建立子類物件
            LiXiaoGang z = new LiXiaoGang();
            // 呼叫子類中的show方法
            z.show();
        }
    }
    //演示結果: 我爸是李小剛   我是李小剛

子父類中出現了同名的成員變數時,在子類中需要訪問父類中非私有成員變數時,需要使用 super 關鍵字,至於修飾父類成員變數,類似於之前學過的 this 。 使用格式 super.父類成員變數名

this表示當前物件,super則表示父類物件,用法類似!

class liGang {
        // 父類中的成員變數。
       String name ="李剛";
    }
    class LiXiaoGang extends liGang {
        // 子類中的成員變數
        String name ="李小剛";
        // 子類中的成員方法
        public void show() {
            // 訪問父類中的name,
            System.out.println("我爸是"+super.name);
            // 繼承而來,所以直接訪問。
            // 訪問子類中的name2
            System.out.println("我是"+this.name);  //當然this可省略
        }
    }
public class Demo {
        public static void main(String[] args) {
            // 建立子類物件
            LiXiaoGang z = new LiXiaoGang();
            // 呼叫子類中的show方法
            z.show();
        }
    }
    //演示結果: 我爸是李剛   我是李小剛

2.3、關於繼承中成員變數值得思考的一個問題

同學你有沒有想過這樣一個問題。如果父類中的成員變數
非私有:子類中可以直接訪問。
私有:子類是不能直接訪問的。如下:

當然,同學你要自己體驗體驗編譯報錯過程,看圖沒體驗感不得勁,~嘔,你這無處安放的魅力,無理的要求,我佛了,行吧~

  class liGang2 {
        // 父類中的成員變數。
        private String name ="李剛";

    }
    class LiXiaoGang2 extends liGang2 {
        // 子類中的成員變數
        String name ="李小剛";
        // 子類中的成員方法
        public void show() {
            // 訪問父類中的name,
            System.out.println("我爸是"+super.name);//------編譯失敗不能直接訪問父類私有屬性(成員變數)
            // 繼承而來,所以直接訪問。
            // 訪問子類中的name2
            System.out.println("我是"+this.name);  //當然this可省略
        }
    }
public class PrivateVariable {
        public static void main(String[] args) {
            // 建立子類物件
            ExtendDemo.LiXiaoGang z = new ExtendDemo.LiXiaoGang();
            // 呼叫子類中的show方法
            z.show();
        }
    }

通常開發中編碼時,我們遵循封裝的原則,使用private修飾成員變數,那麼如何訪問父類的私有成員變數呢?其實這個時候在父類中提供公共的getXxx方法和setXxx方法就可以了。程式碼如下:

class liGang {
        // 父類中的成員變數。
      private String name ="李剛";

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
    class LiXiaoGang extends liGang {
        // 子類中的成員變數
        String name ="李小剛";
        // 子類中的成員方法
        public void show() {
            // 訪問父類中的name,
            System.out.println("我爸是"+super.getName());
            // 繼承而來,所以直接訪問。
            // 訪問子類中的name2
            System.out.println("我是"+this.name);  //當然this可省略
        }
    }
public class Demo {
        public static void main(String[] args) {
            // 建立子類物件
            LiXiaoGang z = new LiXiaoGang();
            // 呼叫子類中的show方法
            z.show();
        }
    }
    //演示結果: 我爸是李剛   我是李小剛

分析如下:

3、關於繼承之後的成員方法

分析完了成員變數,現在我們一起來分析分析成員方法。
想一想,當類之間產生了關係,其中各類中的成員方法,又產生了哪些影響呢? 同樣我們依舊從兩方面分析。
#### 3.1、成員方法不重名
如果子類父類中出現不重名的成員方法,這時的呼叫是沒有影響的。物件呼叫方法時,會先在子類中查詢有沒有對 應的方法,若子類中存在就會執行子類中的方法,若子類中不存在就會執行父類中相應的方法。程式碼如下:

 class liGang3 {
        // 父類中的成員方法。
       public void zhuangRen1(){//--------------------------父類方法名zhuangRen1
           System.out.println("我叫李剛,人不是我撞的,別抓我,我不認識李小剛");
       }
    }
    class LiXiaoGang3 extends liGang3 {

        // 子類中的成員方法
        public void zhuangRen() {//--------------------------子類方法名zhuangRen
            System.out.println("有本事你們告去,我爸是李剛");  
        }
    }
    public class MemberMethod {
        public static void main(String[] args) {
            // 建立子類物件
            LiXiaoGang3 liXiaoGang = new LiXiaoGang3();
            // 呼叫子類中的show方法
            liXiaoGang.zhuangRen();
            liXiaoGang.zhuangRen1();
        }
    }
    
列印結果:有本事你們告去,我爸是李剛
        我叫李剛,人不是我撞的,別抓我,我不認識李小剛


#### 3.2、成員方法重名 【方法重寫】
成員方法重名大體也可以分兩種情況:

1、方法名相同返回值型別、引數列表卻不相同(優先在子類查詢,沒找到就去父類)
2、方法名、返回值型別、引數列表都相同,沒錯這就是重寫(Override)

這裡主要講方法重寫 :子類中出現與父類一模一樣的方法時(返回值型別,方法名和引數列表都相同),會出現覆蓋效果,也稱為重寫或者複寫。宣告不變,重新實現。 程式碼如下:

    class liGang3 {
        // 父類中的成員方法。
       public void zhuangRen(int a){
           System.out.println("我叫李剛,人不是我撞的,別抓我");
       }
    }
    class LiXiaoGang3 extends liGang3 {

        // 子類中的成員方法
        public void zhuangRen(int a) {
            System.out.println("有本事你們告去,我爸是李剛");
        }
    }
    public class MemberMethod {
        public static void main(String[] args) {
            // 建立子類物件
            LiXiaoGang3 liXiaoGang = new LiXiaoGang3();
            // 呼叫子類中的zhuangRen方法
            liXiaoGang.zhuangRen(1);

        }
    }
    結果列印:有本事你們告去,我爸是李剛

#### 3.3、繼承中重寫方法的意義
子類可以根據需要,定義特定於自己的行為。既沿襲了父類的功能名稱,又根據子類的需要重新實現父類方法,從而進行擴充套件增強。比如李剛會開車,李小剛就牛了,在父類中進行擴充套件增強還會開車撞人,程式碼如下:

 class liGang3 {
        // 父類中的成員方法。
       public void kaiChe(){
           System.out.println("我會開車");
       }
    }
    class LiXiaoGang3 extends liGang3 {
        // 子類中的成員方法
        public void kaiChe(){
            super.kaiChe();
            System.out.println("我還會撞人");
            System.out.println("我還能一撞撞倆婆娘");
        }
    }
    public class MemberMethod {
        public static void main(String[] args) {
            // 建立子類物件
            LiXiaoGang3 liXiaoGang = new LiXiaoGang3();
            // 呼叫子類中的zhuangRen方法
            liXiaoGang.kaiChe();

列印結果:   我會開車
           我還會撞人
           我還能一撞撞倆婆娘
        }
    }

不知道同學們發現了沒有,以上程式碼中在子類中使用了 super.kaiChe();super.父類成員方法,表示呼叫父類的成員方法。

最後重寫必須注意這幾點:

1、方法重寫時, 方法名與形參列表必須一致。
2、子類方法覆蓋父類方法時,必須要保證子類許可權 >= 父類許可權。
3、方法重寫時,子類的返回值型別必須要 <= 父類的返回值型別。
4、方法重寫時,子類丟擲的異常型別要 <= 父類丟擲的異常型別。

粗心的同學看黑板,look 這裡【注意:只有訪問許可權是>=,返回值、異常型別都是<=

下面以修飾許可權為例,如下:

4、關於繼承之後的構造方法

為了讓你更好的體會,首先我先編寫一個程式

   class liGang4 {
        // 父類的無參構造方法。
        public liGang4(){
            System.out.println("父類構造方法執行了。。。");
        }
    }
    class LiXiaoGang4 extends liGang4 {
        // 子類的無參構造方法。
       public LiXiaoGang4(){
           System.out.println("子類構造方法執行了====");
       }
    }
    public class ConstructionDemo {
        public static void main(String[] args) {
            // 建立子類物件
            LiXiaoGang4 z = new LiXiaoGang4();

        }
    }

用一分鐘猜想一下結果是什麼,猜好了再看下面結果:

父類構造方法執行了。。。
子類構造方法執行了====

好了,看了結果之後,你可能有疑惑。父類構造器方法怎麼執行了?我們先來分析分析,首先在main方法中例項化了子類物件,接著會去執行子類的預設構造器初始化,這個時候在構造方法中預設會在第一句程式碼中新增super();沒錯,他就是開掛般的存在,不寫也存在的!有的調~讀四聲“跳”~皮的同學就會說,你說存在就存在啊,無憑無據 ~呀,你這個該死的靚仔~ 如下:

構造方法的名字是與類名一致的,所以子類是無法繼承父類構造方法的。 構造方法的作用是初始化成員變數的。所以子類的初始化過程中,必須先執行父類的初始化動作。子類的構造方法中預設會在第一句程式碼中新增super(),表示呼叫父類的構造方法,父類成員變數初始化後,才可以給子類使用。

當然我已經強調很多遍了 super() 不寫也預設存在,而且只能是在第一句程式碼中,不在第一句程式碼中行不行,答案是當然不行,這樣會編譯失敗,如下:

5、關於繼承的多型性支援的例子

直接上程式碼了喔

class A{
    public String show(C obj) {
        return ("A and C");
    }

    public String show(A obj) {
        return ("A and A");
    }

}
class B extends A{
    public String show(B obj) {
        return ("B and B");
    }
}
class C extends B{
    public String show(A obj) {
        return ("A and B");
    }
}
public class Demo1 {
    public static void main(String[] args) {
        A a=new A();
        B b=new B();
        C c=new C();
        System.out.println("第一題 " + a.show(a));
        System.out.println("第二題 " + a.show(b));
        System.out.println("第三題 " + a.show(c));
    }
}
執行結果:
        第一題 A and A
        第二題 A and A
        第三題 A and C

其實吧,第一題和第三題都好理解,第二題就有點意思了,會發現A類中沒有B型別這個引數,這個時候,你就應該知道子類繼承就是父類,換句話說就是子類天然就是父類,比如中國人肯定是人,但是人不一定是中國人(可能是火星人也可能是非洲人),所以父類做為引數型別,直接傳子類的引數進去是可以的,反過來,子類做為引數型別,傳父類的引數進去,就需要強制型別轉換。

6、super與this的用法

瞭解他們的用法之前必須明確一點的是父類空間優先於子類物件產生

在每次建立子類物件時,先初始化父類空間,再建立其子類物件本身。目的在於子類物件中包含了其對應的父類空間,便可以包含其父類的成員,如果父類成員非private修飾,則子類可以隨意使用父類成員。程式碼體現在子類的構 造方法呼叫時,一定先呼叫父類的構造方法。理解圖解如下:

#### 5.1、 super和this的含義:

super :代表父類的儲存空間標識(可以理解為父親的引用)。

 

this :代表當前物件的引用(誰呼叫就代表誰)。

#### 5.2、 super和this訪問成員

this.成員變數 ‐‐ 本類的
super.成員變數 ‐‐ 父類的
this.成員方法名() ‐‐ 本類的
super.成員方法名() ‐‐ 父類的

#### 5.3、super和this訪問構造方法

this(...) ‐‐ 本類的構造方法
super(...) ‐‐ 父類的構造方法

#### 5.4、super()和this()能不能同時使用?

不能同時使用,thissuper不能同時出現在一個建構函式裡面,因為this必然會呼叫其它的建構函式,其它的建構函式必然也會有super語句的存在,所以在同一個建構函式裡面有相同的語句,就失去了語句的意義,編譯器也不會通過。
#### 5.5、總結一下super與this

子類的每個構造方法中均有預設的super(),呼叫父類的空參構造。手動呼叫父類構造會覆蓋預設的super()super()this() 都必須是在構造方法的第一行,所以不能同時出現。

到這裡,java繼承你get到了咩,get到了請咩一聲,隨便隨手~點個讚唄~

推薦閱讀本專欄的下一篇java文章

【java基礎之多型】理解多型的向上向下轉型從“媽媽我想吃烤山藥”講起

歡迎各位關注我的公眾號,一起探討技術,嚮往技術,追求技術...

相關推薦

深入理解java繼承

目錄 1、繼承的概述 2、關於繼承之後的成員變數 3、關於繼承之後的成員方法 4、關於繼承之後的構造方法 5、關於繼承的多型性支援的例子 6、super與this的用法

一個class檔案深入理解Java位元組碼結構

前言 我們都知道,Java程式最終是轉換成class檔案執行在虛擬機器上的,那麼class檔案是個怎樣的結構,虛擬機器又是如何處理去執行class檔案裡面的內容呢,這篇文章帶你深入理解Java位元組碼中的結構。 1.Demo原始碼 首先,編寫一個簡單的

模板方法模式深入理解Java抽象類

二話不說先上程式碼,如下所示為一個抽象類(抽象汽車模型)與它的兩個具體實現類(寶馬模型、賓士模型)的模擬程式: /* * 抽象模板類,抽象汽車模型 */ public abstract class

深入理解Java封裝、繼承、多態

釋放 prot 外部 sta itl ash 深入理解java tro 存在 轉載請註明原文地址:https://www.cnblogs.com/ygj0930/p/10830957.html 一:封裝 將類的某些信息隱藏在類內部,不允許外部程序直接訪問,

深入理解Java虛擬機- 學習筆記 - 虛擬機類加載機制

支持 pub eth 獲取 事件 必須 string 沒有 字節碼 虛擬機把描述類的數據從Class文件加載道內存,並對數據進行校驗,轉換解析和初始化,最終形成可以被虛擬機直接使用的Java類型,這就是虛擬機的類加載機制。在Java裏,類型的加載、連接和初始化過程都是在程序

JVM運行時數據區--深入理解Java虛擬機 讀後感

出棧 很好 棧幀 最大 出錯 生命周期 所有 img 就會 程序計數器 程序計數器是線程私有的區域,很好理解嘛~,每個線程當然得有個計數器記錄當前執行到那個指令。占用的內存空間小,可以把它看成是當前線程所執行的字節碼的行號指示器。如果線程在執行Java方法

深入理解JAVA集合系列四:ArrayList源碼解讀

結束 了解 數組下標 size new 數組元素 開始 ini rem 在開始本章內容之前,這裏先簡單介紹下List的相關內容。 List的簡單介紹 有序的collection,用戶可以對列表中每個元素的插入位置進行精確的控制。用戶可以根據元素的整數索引(在列表中的位置)訪

深入理解JAVA集合系列三:HashMap的死循環解讀

現在 最新 star and 場景 所有 image cap 時也 由於在公司項目中偶爾會遇到HashMap死循環造成CPU100%,重啟後問題消失,隔一段時間又會反復出現。今天在這裏來仔細剖析下多線程情況下HashMap所帶來的問題: 1、多線程put操作後,get操作導

深入理解JAVA I/O系列三:字符流詳解

buffer 情況 二進制文件 感到 復制代碼 使用範圍 轉換 fileread 方式 字符流為何存在 既然字節流提供了能夠處理任何類型的輸入/輸出操作的功能,那為什麽還要存在字符流呢?容我慢慢道來,字節流不能直接操作Unicode字符,因為一個字符有兩個字節,字節流一次只

重讀《深入理解Java虛擬機》

-xmx 垃圾收集 劃分 tac 棧內存 列表 進行 nbsp 申請 一、Java虛擬機內存區域如何劃分 1、Java虛擬機內存區域的劃分 區域名稱 作用(用途) 類型 特點 虛擬機規定異常情況 其他說明 1 程序計數器 指示當前正在執行的字節碼指

深入理解java關鍵字--static

ack col 之間 jvm -s nbsp 代碼 實例變量 family static 關鍵字是java中經常用到的一個關鍵字,在面試中也會經常遇到的一個問題,下面詳細描述這個關鍵字的相關知識點。

深入理解 Java 虛擬機之學習筆記(1)

over 信息 hotspot 體系 ima 模塊化 介紹 style 創建 本書結構: 從宏觀的角度介紹了整個Java技術體系、Java和JVM的發展歷程、模塊化,以及JDK的編譯 講解了JVM的自動內存管理,包括虛擬機內存區域的劃分原理以及各種內存溢出異常產

深入理解java虛擬機7---線程安全 & 鎖優化

err iou nan gpa egg aik risl cpn hang python%E5%AD%A6%E4%B9%A0%20%20%20%20%20%E5%8F%98%E9%87%8F%E7%9A%84%E6%93%8D%E4%BD%9C%20%E4%B8%8E%20

深入理解Java PriorityQueue

() 計算 ren span ring amp exception 刪除 聯系 深入理解Java PriorityQueue PriorityQueue 本文github地址 Java中PriorityQueue通過二叉小頂堆實現,可以用一棵完全二叉樹表示。本文從Queu

深入理解Java虛擬機》筆記04 -- 並發鎖

server som 競爭 包括 系統 cap cnblogs blocks 嘗試 Java虛擬機在操作系統層面會先盡一切可能在虛擬機層面上解決競爭關系,盡可能避免真實的競爭發生。同時,在競爭不激烈的場合,也會試圖消除不必要的競爭。實現這些手段的方法包括:偏向鎖、輕量級鎖、

深入理解Java:類加載機制及反射

指定 請求 image vm虛擬機 常量池 使用 元素 靜態 創建 一、Java類加載機制 1.概述 Class文件由類裝載器裝載後,在JVM中將形成一份描述Class結構的元信息對象,通過該元信息對象可以獲知Class的結構信息:如構造函數,屬性和方法等,J

深入理解Java虛擬機:JVM高級屬性與最佳實踐》讀書筆記(更新中)

pen 內存區域 深度 span 進化 ria 最短 描述 core 第一章:走進Java 概述 Java技術體系 Java發展史 Java虛擬機發展史 1996年 JDK1.0,出現Sun Classic VM HotSpot VM, 它是 Sun JDK 和 Open

深入理解JAVA虛擬機之JVM性能篇---基礎知識點

默認 生命周期 ima 線程 images 對象 情況 -- 是否 一、堆與棧   堆和棧是程序運行的關鍵,其間的關系有必要理清楚,兩者如下圖所示:      1. 堆:   所有線程共享,堆中只負責存儲對象信息。   2. 棧:   在Java中每個線程都會有一個相應的線

深入理解JAVA虛擬機之JVM性能篇---垃圾回收

小數據 alt tro 調優 permsize 多次 快速 com src 一、基本垃圾回收算法 1. 按基本回收策略分   1) 引用計數(Reference Counting)     對象增加一個引用,即增加一個計數,刪除一個引用則減少一個計數。垃圾回收時,只用收集計

深入理解Java:註解(Annotation)--註解處理器

fault this urn 復制代碼 lena ide set java lec 深入理解Java:註解(Annotation)--註解處理器   如果沒有用來讀取註解的方法和工作,那麽註解也就不會比註釋更有用處了。使用註解的過程中,很重要的一部分就是創建於