深刻理解java繼承從「我爸是李剛」講起

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

一、繼承的概述

1.一、繼承的由來

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

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

1.二、繼承的定義

繼承:就是子類繼承父類的屬性行爲,使得子類對象具備與父類相同的屬性、相同的行爲。子類能夠直接訪問父類中的非私有的屬性和行爲。this

這裏再聲明一點,父類又稱爲超類或者基類。而子類又稱爲派生類這點很基礎!編碼

1.三、繼承的優勢

  1. 提升代碼的複用性
  2. 類與類之間產生關係,爲多態作了完美的鋪墊(不理解不要緊,以後我會再寫一篇多態的文章)

雖然繼承的優勢不少可是Java只支持單繼承,不支持多繼承.net

1.四、繼承的格式

經過 extends 關鍵字,能夠聲明一個子類繼承另一個父類,定義格式以下:code

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

二、關於繼承以後的成員變量

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

2.一、成員變量不重名

若是子類父類中出現不重名的成員變量,這時的訪問是沒有影響的。代碼以下:blog

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.二、 成員變量重名

若是子類父類中出現重名的成員變量,這時的訪問是有影響的。代碼以下:

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.三、關於繼承中成員變量值得思考的一個問題

同窗你有沒有想過這樣一個問題。若是父類中的成員變量
非私有:子類中能夠直接訪問。
私有:子類是不能直接訪問的。以下:
在這裏插入圖片描述
固然,同窗你要本身體驗體驗編譯報錯過程,看圖沒體驗感不得勁,~嘔,你這無處安放的魅力,無理的要求,我佛了,行吧~

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.一、成員方法不重名
若是子類父類中出現不重名的成員方法,這時的調用是沒有影響的。對象調用方法時,會先在子類中查找有沒有對 應的方法,若子類中存在就會執行子類中的方法,若子類中不存在就會執行父類中相應的方法。代碼以下:

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.二、成員方法重名 【方法重寫】
成員方法重名大致也能夠分兩種狀況:

一、方法名相同返回值類型、參數列表卻不相同(優先在子類查找,沒找到就去父類)
二、方法名、返回值類型、參數列表都相同,沒錯這就是重寫(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.三、繼承中重寫方法的意義
子類能夠根據須要,定義特定於本身的行爲。既沿襲了父類的功能名稱,又根據子類的須要從新實現父類方法,從而進行擴展加強。好比李剛會開車,李小剛就牛了,在父類中進行擴展加強還會開車撞人,代碼以下:

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.父類成員方法,表示調用父類的成員方法。

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

一、方法重寫時, 方法名與形參列表必須一致。
二、子類方法覆蓋父類方法時,必需要保證子類權限 >= 父類權限。
三、方法重寫時,子類的返回值類型必需要 <= 父類的返回值類型。
四、方法重寫時,子類拋出的異常類型要 <= 父類拋出的異常類型。

粗心的同窗看黑板,look 這裏【注意:只有訪問權限是>=,返回值、異常類型都是<=

下面以修飾權限爲例,以下:

在這裏插入圖片描述

四、關於繼承以後的構造方法

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

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() 不寫也默認存在,並且只能是在第一句代碼中,不在第一句代碼中行不行,答案是固然不行,這樣會編譯失敗,以下:
在這裏插入圖片描述

五、關於繼承的多態性支持的例子

直接上代碼了喔

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類型這個參數,這個時候,你就應該知道子類繼承就是父類,換句話說就是子類自然就是父類,好比中國人確定是人,可是人不必定是中國人(多是火星人也多是非洲人),因此父類作爲參數類型,直接傳子類的參數進去是能夠的,反過來,子類作爲參數類型,傳父類的參數進去,就須要強制類型轉換。

六、super與this的用法

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

在每次建立子類對象時,先初始化父類空間,再建立其子類對象自己。目的在於子類對象中包含了其對應的父類空間,即可以包含其父類的成員,若是父類成員非private修飾,則子類能夠隨意使用父類成員。代碼體如今子類的構 造方法調用時,必定先調用父類的構造方法。理解圖解以下:
在這裏插入圖片描述
#### 5.一、 super和this的含義:

super :表明父類的存儲空間標識(能夠理解爲父親的引用)。

 

this :表明當前對象的引用(誰調用就表明誰)。

#### 5.二、 super和this訪問成員

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

#### 5.三、super和this訪問構造方法

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

#### 5.四、super()和this()能不能同時使用?

不能同時使用,thissuper不能同時出如今一個構造函數裏面,由於this必然會調用其它的構造函數,其它的構造函數必然也會有super語句的存在,因此在同一個構造函數裏面有相同的語句,就失去了語句的意義,編譯器也不會經過。
#### 5.五、總結一下super與this

子類的每一個構造方法中均有默認的super(),調用父類的空參構造。手動調用父類構造會覆蓋默認的super()super()this() 都必須是在構造方法的第一行,因此不能同時出現

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

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

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

歡迎各位關注個人公衆號,一塊兒探討技術,嚮往技術,追求技術...

在這裏插入圖片描述

相關文章
相關標籤/搜索