首先我們先離開軟件編程的世界,從常識中我們知道丈夫、妻子、小三、小四…,他們都是人,而且都有一些共性,有名字、年齡、性別、頭等等,而且他們都能夠吃東西、走路、說話等等共同的行為,所以從這裡我們可以發現他們都擁有人的屬性和行為,同時也是從人那裡繼承來的這些屬性和行為的。
從上面我們就可以基本了解了繼承的概念了,繼承是使用已存在的類的定義作為基礎建立新類的技術,新類的定義可以增加新的數據或新的功能,也可以用父類的功能,但不能選擇性地繼承父類。通過使用繼承我們能夠非常方便地復用以前的代碼,能夠大大的提高開發的效率。繼承—02
對於Wife、Husband使用繼承後,除了代碼量的減少我們還能夠非常明顯的看到他們的關系。
繼承所描述的是“is-a”的關系,如果有兩個對象A和B,若可以描述為“A是B”,則可以表示A繼承B,其中B是被繼承者稱之為父類或者超類,A是繼承者稱之為子類或者派生類。
實際上繼承者是被繼承者的特殊化,它除了擁有被繼承者的特性外,還擁有自己獨有得特性。例如貓有抓老鼠、爬樹等其他動物沒有的特性。同時在繼承關系中,繼承者完全可以替換被繼承者,反之則不可以,例如我們可以說貓是動物,但不能說動物是貓就是這個道理,其實對於這個我們將其稱之為“向上轉型”,下面介紹。
誠然,繼承定義了類如何相互關聯,共享特性。對於若干個相同或者相識的類,我們可以抽象出他們共有的行為或者屬相並將其定義成一個父類或者超類,然後用這些類繼承該父類,他們不僅可以擁有父類的屬性、方法還可以定義自己獨有的屬性或者方法。
同時在使用繼承時需要記住三句話:
1、子類擁有父類非private的屬性和方法。
2、子類可以擁有自己屬性和方法,即子類可以對父類進行擴展。
3、子類可以用自己的方式實現父類的方法。(以後介紹)。
綜上所述,使用繼承確實有許多的優點,除了將所有子類的共同屬性放入父類,實現代碼共享,避免重復外,還可以使得修改擴展繼承而來的實現比較簡單。
誠然,講到繼承一定少不了這三個東西:構造器、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...
所以綜上所述:對於繼承而已,子類會默認調用父類的構造器,但是如果沒有默認的父類構造器,子類必須要顯示的指定父類的構造器,而且必須是在子類構造器中做的第一件事(第一行代碼)。
protected關鍵字
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類型。
將子類轉換成父類,在繼承關系上面是向上移動的,所以一般稱之為向上轉型。由於向上轉型是從一個叫專用類型向較通用類型轉換,所以它總是安全的,唯一發生變化的可能就是屬性和方法的丟失。這就是為什麼編譯器在“未曾明確表示轉型”活“未曾指定特殊標記”的情況下,仍然允許向上轉型的原因。
謹慎繼承
上面講了繼承所帶來的諸多好處,那我們是不是就可以大肆地使用繼承呢?送你一句話:慎用繼承。
首先我們需要明確,繼承存在如下缺陷:
1、父類變,子類就必須變。
2、繼承破壞了封裝,對於父類而言,它的實現細節對與子類來說都是透明的。
3、繼承是一種強耦合關系。
所以說當我們使用繼承的時候,我們需要確信使用繼承確實是有效可行的辦法。那麼到底要不要使用繼承呢?《Think in java》中提供了解決辦法:問一問自己是否需要從子類向父類進行向上轉型。如果必須向上轉型,則繼承是必要的,但是如果不需要,則應當好好考慮自己是否需要繼承。
慎用繼承!!!!!!!!!!!!!!!!!!!!!!!!!!!