在《Think in java》中有這樣一句話:複用代碼是Java衆多引人注目的功能之一。但要想成爲極具革命性的語言,僅僅可以複製代碼並對加以改變是不夠的,它還必須可以作更多的事情。在這句話中最引人注目的是「複用代碼」,儘量的複用代碼使咱們程序員一直在追求的,如今我來介紹一種複用代碼的方式,也是java三大特性之一---繼承。html
在講解以前咱們先看一個例子,該例子是前篇博文(java提升篇-----理解java的三大特性之封裝)的。java
從這裏咱們能夠看出,Wife、Husband兩個類除了各自的husband、wife外其他部分所有相同,做爲一個想最大限度實現複用代碼的咱們是不可以忍受這樣的重複代碼,若是再來一個小3、小4、小五……(扯遠了)咱們是否是也要這樣寫呢?那麼咱們如何來實現這些類的可複用呢?利用繼承!!程序員
首先咱們先離開軟件編程的世界,從常識中咱們知道丈夫、妻子、小3、小四…,他們都是人,並且都有一些共性,有名字、年齡、性別、頭等等,並且他們都可以吃東西、走路、說話等等共同的行爲,因此從這裏咱們能夠發現他們都擁有人的屬性和行爲,同時也是從人那裏繼承來的這些屬性和行爲的。編程
從上面咱們就能夠基本瞭解了繼承的概念了,繼承是使用已存在的類的定義做爲基礎創建新類的技術,新類的定義能夠增長新的數據或新的功能,也能夠用父類的功能,但不能選擇性地繼承父類。經過使用繼承咱們可以很是方便地複用之前的代碼,可以大大的提升開發的效率。安全
對於Wife、Husband使用繼承後,除了代碼量的減小咱們還可以很是明顯的看到他們的關係。this
繼承所描述的是「is-a」的關係,若是有兩個對象A和B,若能夠描述爲「A是B」,則能夠表示A繼承B,其中B是被繼承者稱之爲父類或者超類,A是繼承者稱之爲子類或者派生類。spa
實際上繼承者是被繼承者的特殊化,它除了擁有被繼承者的特性外,還擁有本身獨有得特性。例如貓有抓老鼠、爬樹等其餘動物沒有的特性。同時在繼承關係中,繼承者徹底能夠替換被繼承者,反之則不能夠,例如咱們能夠說貓是動物,但不能說動物是貓就是這個道理,其實對於這個咱們將其稱之爲「向上轉型」,下面介紹。3d
誠然,繼承定義了類如何相互關聯,共享特性。對於若干個相同或者相識的類,咱們能夠抽象出他們共有的行爲或者屬相併將其定義成一個父類或者超類,而後用這些類繼承該父類,他們不只能夠擁有父類的屬性、方法還能夠定義本身獨有的屬性或者方法。code
同時在使用繼承時須要記住三句話:htm
一、子類擁有父類非private的屬性和方法。
二、子類能夠擁有本身屬性和方法,即子類能夠對父類進行擴展。
三、子類能夠用本身的方式實現父類的方法。(之後介紹)。
綜上所述,使用繼承確實有許多的優勢,除了將全部子類的共同屬性放入父類,實現代碼共享,避免重複外,還可使得修改擴展繼承而來的實現比較簡單。
誠然,講到繼承必定少不了這三個東西:構造器、protected關鍵字、向上轉型。
經過前面咱們知道子類能夠繼承父類的屬性和方法,除了那些private的外還有同樣是子類繼承不了的---構造器。對於構造器而言,它只可以被調用,而不能被繼承。 調用父類的構造方法咱們使用super()便可。
對於子類而已,其構造器的正確初始化是很是重要的,並且當且僅當只有一個方法能夠保證這點:在構造器中調用父類構造器來完成初始化,而父類構造器具備執行父類初始化所須要的全部知識和能力。
public class Person { protected String name; protected int age; protected String sex; Person(){ System.out.println("Person Constrctor..."); } } public class Husband extends Person{ private Wife wife; Husband(){ System.out.println("Husband Constructor..."); } public static void main(String[] args) { Husband husband = new Husband(); } } Output: Person Constrctor... Husband Constructor...
經過這個示例能夠看出,構建過程是從父類「向外」擴散的,也就是從父類開始向子類一級一級地完成構建。並且咱們並無顯示的引用父類的構造器,這就是java的聰明之處:編譯器會默認給子類調用父類的構造器。
可是,這個默認調用父類的構造器是有前提的:父類有默認構造器。若是父類沒有默認構造器,咱們就要必須顯示的使用super()來調用父類構造器,不然編譯器會報錯:沒法找到符合父類形式的構造器。
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...
因此綜上所述:對於繼承而已,子類會默認調用父類的構造器,可是若是沒有默認的父類構造器,子類必需要顯示的指定父類的構造器,並且必須是在子類構造器中作的第一件事(第一行代碼)。
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"); //調用父類的setName(); return super.toString(); //調用父類的toString()方法 } public static void main(String[] args) { Husband husband = new Husband(); System.out.println(husband.toString()); } } Output: this name is chenssy
從上面示例能夠看書子類Husband能夠明顯地調用父類Person的setName()。
誠然儘管可使用protected訪問修飾符來限制父類屬性和方法的訪問權限,可是最好的方式仍是將屬性保持爲private(咱們應當一致保留更改底層實現),經過protected方法來控制類的繼承者的訪問權限。
在上面的繼承中咱們談到繼承是is-a的相互關係,貓繼承與動物,因此咱們能夠說貓是動物,或者說貓是動物的一種。這樣將貓看作動物就是向上轉型。以下:
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); //向上轉型 } }
在這咱們經過Person.display(husband)。這句話能夠看出husband是person類型。
將子類轉換成父類,在繼承關係上面是向上移動的,因此通常稱之爲向上轉型。因爲向上轉型是從一個叫專用類型向較通用類型轉換,因此它老是安全的,惟一發生變化的可能就是屬性和方法的丟失。這就是爲何編譯器在「不曾明確表示轉型」活「不曾指定特殊標記」的狀況下,仍然容許向上轉型的緣由。
上面講了繼承所帶來的諸多好處,那咱們是否是就能夠大肆地使用繼承呢?送你一句話:慎用繼承。
首先咱們須要明確,繼承存在以下缺陷:
一、父類變,子類就必須變。
二、繼承破壞了封裝,對於父類而言,它的實現細節對與子類來講都是透明的。
三、繼承是一種強耦合關係。
因此說當咱們使用繼承的時候,咱們須要確信使用繼承確實是有效可行的辦法。那麼到底要不要使用繼承呢?《Think in java》中提供瞭解決辦法:問一問本身是否須要從子類向父類進行向上轉型。若是必須向上轉型,則繼承是必要的,可是若是不須要,則應當好好考慮本身是否須要繼承。
慎用繼承!!!!!!!!!!!!!!!!!!!!!!!!!!!