鍍金池/ 教程/ Java/ 理解 Java 的三大特性之繼承
Java 集合細節(jié)(四):保持 compareTo 和 equals 同步
Iterator
使用序列化實現(xiàn)對象的拷貝
fail-fast 機制
關(guān)鍵字 final
Vector
HashTable
Java 集合細節(jié)(一):請為集合指定初始容量
強制類型轉(zhuǎn)換
數(shù)組之一:認識 JAVA 數(shù)組
Java 集合細節(jié)(三):subList 的缺陷
hashCode
ArrayList
數(shù)組之二
List 總結(jié)
LinkedList
Java 提高篇(九)—–實現(xiàn)多重繼承
Java 的四舍五入
關(guān)鍵字 static
理解 Java 的三大特性之多態(tài)
抽象類與接口
集合大家族
異常(二)
Java 集合細節(jié)(二):asList 的缺陷
Map 總結(jié)
TreeSet
equals() 方法總結(jié)
Java 提高篇(十)—–詳解匿名內(nèi)部類
HashMap
Stack
詳解內(nèi)部類
TreeMap
異常(一)
詳解 Java 定時任務(wù)
HashSet
字符串
理解 Java 的三大特性之繼承
理解 Java 的三大特性之封裝
代碼塊

理解 Java 的三大特性之繼承

在《Think in Java》中有這樣一句話:復(fù)用代碼是 Java 眾多引人注目的功能之一。但要想成為極具革命性的語言,僅僅能夠復(fù)制代碼并對加以改變是不夠的,它還必須能夠做更多的事情。在這句話中最引人注目的是“復(fù)用代碼”,盡可能的復(fù)用代碼使我們程序員一直在追求的,現(xiàn)在我來介紹一種復(fù)用代碼的方式,也是 Java 三大特性之一—繼承。

繼承

在講解之前我們先看一個例子,該例子是前篇博文( Java提高篇—–理解java 的三大特性之封裝)的。

從這里我們可以看出, Wife、Husband 兩個類除了各自的 husband、wife 外其余部分全部相同,作為一個想最大限度實現(xiàn)復(fù)用代碼的我們是不能夠忍受這樣的重復(fù)代碼,如果再來一個小三、小四、小五……(扯遠了大笑)我們是不是也要這樣寫呢?那么我們?nèi)绾蝸韺崿F(xiàn)這些類的可復(fù)用呢?利用繼承??!

首先我們先離開軟件編程的世界,從常識中我們知道丈夫、妻子、小三、小四…,他們都是人,而且都有一些共性,有名字、年齡、性別、頭等等,而且他們都能夠吃東西、走路、說話等等共同的行為,所以從這里我們可以發(fā)現(xiàn)他們都擁有人的屬性和行為,同時也是從人那里繼承來的這些屬性和行為的。

從上面我們就可以基本了解了繼承的概念了,繼承是使用已存在的類的定義作為基礎(chǔ)建立新類的技術(shù),新類的定義可以增加新的數(shù)據(jù)或新的功能,也可以用父類的功能,但不能選擇性地繼承父類。通過使用繼承我們能夠非常方便地復(fù)用以前的代碼,能夠大大的提高開發(fā)的效率。

對于 Wife、Husband 使用繼承后,除了代碼量的減少我們還能夠非常明顯的看到他們的關(guān)系。

繼承所描述的是“is-a”的關(guān)系,如果有兩個對象A和B,若可以描述為“A是B”,則可以表示 A 繼承 B,其中 B 是被繼承者稱之為父類或者超類,A 是繼承者稱之為子類或者派生類。

實際上繼承者是被繼承者的特殊化,它除了擁有被繼承者的特性外,還擁有自己獨有得特性。例如貓有抓老鼠、爬樹等其他動物沒有的特性。同時在繼承關(guān)系中,繼承者完全可以替換被繼承者,反之則不可以,例如我們可以說貓是動物,但不能說動物是貓就是這個道理,其實對于這個我們將其稱之為“向上轉(zhuǎn)型”,下面介紹。

誠然,繼承定義了類如何相互關(guān)聯(lián),共享特性。對于若干個相同或者相識的類,我們可以抽象出他們共有的行為或者屬相并將其定義成一個父類或者超類,然后用這些類繼承該父類,他們不僅可以擁有父類的屬性、方法還可以定義自己獨有的屬性或者方法。

同時在使用繼承時需要記住三句話:

1、子類擁有父類非 private 的屬性和方法。

2、子類可以擁有自己屬性和方法,即子類可以對父類進行擴展。

3、子類可以用自己的方式實現(xiàn)父類的方法。(以后介紹)。

綜上所述,使用繼承確實有許多的優(yōu)點,除了將所有子類的共同屬性放入父類,實現(xiàn)代碼共享,避免重復(fù)外,還可以使得修改擴展繼承而來的實現(xiàn)比較簡單。

誠然,講到繼承一定少不了這三個東西:構(gòu)造器、protected 關(guān)鍵字、向上轉(zhuǎn)型。

構(gòu)造器

通過前面我們知道子類可以繼承父類的屬性和方法,除了那些 private 的外還有一樣是子類繼承不了的—構(gòu)造器。對于構(gòu)造器而言,它只能夠被調(diào)用,而不能被繼承。 調(diào)用父類的構(gòu)造方法我們使用 super() 即可。

對于子類而已,其構(gòu)造器的正確初始化是非常重要的,而且當(dāng)且僅當(dāng)只有一個方法可以保證這點:在構(gòu)造器中調(diào)用父類構(gòu)造器來完成初始化,而父類構(gòu)造器具有執(zhí)行父類初始化所需要的所有知識和能力。


    public class Person {  
        protected String name;
        protected int age;
        protected String sex;

        Person(String name){
            System.out.println("Person Constrctor-----" + name);
        }
    }

    public class Husband extends Person{
        private Wife wife;

        Husband(){
            super("chenssy");
            System.out.println("Husband Constructor...");
        }

        public static void main(String[] args) {
            Husband husband  = new Husband();
        }
    }

    Output:
    Person Constrctor-----chenssy
    Husband Constructor...

所以綜上所述:對于繼承而已,子類會默認調(diào)用父類的構(gòu)造器,但是如果沒有默認的父類構(gòu)造器,子類必須要顯示的指定父類的構(gòu)造器,而且必須是在子類構(gòu)造器中做的第一件事(第一行代碼)。

protected 關(guān)鍵字

private 訪問修飾符,對于封裝而言,是最好的選擇,但這個只是基于理想的世界,有時候我們需要這樣的需求:我們需要將某些事物盡可能地對這個世界隱藏,但是仍然允許子類的成員來訪問它們。這個時候就需要使用到 protected。

對于 protected 而言,它指明就類用戶而言,他是 private,但是對于任何繼承與此類的子類而言或者其他任何位于同一個包的類而言,他卻是可以訪問的。


    public class Person {
        private String name;
        private int age;
        private String sex;

        protected String getName() {
           return name;
        }

        protected void setName(String name) {
            this.name = name;
        }

        public String toString(){
            return "this name is " + name;
        }

        /** 省略其他setter、getter方法 **/
    }

    public class Husband extends Person{
        private Wife wife;

        public  String toString(){
            setName("chenssy");    //調(diào)用父類的setName();
            return  super.toString();    //調(diào)用父類的toString()方法
        }

        public static void main(String[] args) {
            Husband husband = new Husband();

            System.out.println(husband.toString());
        }
    }

    Output:
    this name is chenssy

從上面示例可以看書子類 Husband 可以明顯地調(diào)用父類 Person 的 setName()。

誠然盡管可以使用 protected 訪問修飾符來限制父類屬性和方法的訪問權(quán)限,但是最好的方式還是將屬性保持為 private (我們應(yīng)當(dāng)一致保留更改底層實現(xiàn)),通過 protected 方法來控制類的繼承者的訪問權(quán)限。

向上轉(zhuǎn)型

在上面的繼承中我們談到繼承是 is-a 的相互關(guān)系,貓繼承與動物,所以我們可以說貓是動物,或者說貓是動物的一種。這樣將貓看做動物就是向上轉(zhuǎn)型。如下:


    public class Person {
        public void display(){
            System.out.println("Play Person...");
        }

        static void display(Person person){
            person.display();
        }
    }

    public class Husband extends Person{
        public static void main(String[] args) {
            Husband husband = new Husband();
            Person.display(husband);      //向上轉(zhuǎn)型
        }
    }

在這我們通過 Person.display(husband)。這句話可以看出 husband 是 person 類型。

將子類轉(zhuǎn)換成父類,在繼承關(guān)系上面是向上移動的,所以一般稱之為向上轉(zhuǎn)型。由于向上轉(zhuǎn)型是從一個叫專用類型向較通用類型轉(zhuǎn)換,所以它總是安全的,唯一發(fā)生變化的可能就是屬性和方法的丟失。這就是為什么編譯器在“未曾明確表示轉(zhuǎn)型”活“未曾指定特殊標記”的情況下,仍然允許向上轉(zhuǎn)型的原因。

謹慎繼承

上面講了繼承所帶來的諸多好處,那我們是不是就可以大肆地使用繼承呢?送你一句話:慎用繼承。

首先我們需要明確,繼承存在如下缺陷:

1、父類變,子類就必須變。

2、繼承破壞了封裝,對于父類而言,它的實現(xiàn)細節(jié)對與子類來說都是透明的。

3、繼承是一種強耦合關(guān)系。

所以說當(dāng)我們使用繼承的時候,我們需要確信使用繼承確實是有效可行的辦法。那么到底要不要使用繼承呢?《Think in Java》中提供了解決辦法:問一問自己是否需要從子類向父類進行向上轉(zhuǎn)型。如果必須向上轉(zhuǎn)型,則繼承是必要的,但是如果不需要,則應(yīng)當(dāng)好好考慮自己是否需要繼承。

慎用繼承?。。。。。。。。。。。。。。。。。。。。。。。。。。?/strong>

上一篇:代碼塊下一篇:equals() 方法總結(jié)