文章目錄
- 一、何為繼承
- 二、繼承語法
- 三、父類成員訪問
- 1.成員變量
- 2.成員方法
- 四、super關鍵字
- 五、子類構造方法
- 六、super和this辨析
- 七、再談初始化
- 八、protected關鍵字
- 九、繼承方式
- 十、final關鍵字
- 十一、繼承與組合
根據我們學過的類的知識,我們來定義兩個類:一個貓類,一個狗類:
class Dog {public String name;public int age;public String sex;public void eat() {System.out.println(this.name + "吃飯");}public void sleep() {System.out.println(this.name + "睡覺");}public void wang() {System.out.println(this.name + "汪汪叫");}
}
class Cat {public String name;public int age;public String sex;public void eat() {System.out.println(this.name + "吃飯");}public void sleep() {System.out.println(this.name + "睡覺");}public void miao() {System.out.println(this.name + "喵喵叫");}
}
仔細觀察代碼會發現,兩個類中存在許多共性,代碼就會比較冗余。為了解決這個問題,面向對象思想中提出了繼承的概念,用于對共性進行抽取,實現代碼復用,減少冗余。
一、何為繼承
繼承是面向對象思想中可以實現代碼復用的一種重要手段,它允許程序員在保持原有類(父類/基類/超類) 的基礎上進行擴展,增添新功能,由此產生的新的類,稱為子類/派生類。
繼承主要解決:對共性進行抽取,從而實現代碼的復用。
二、繼承語法
在Java中借助extends關鍵字實現繼承關系,語法格式為:
修飾符 class 子類 extends 父類 {
//語句
}
我們根據語法對貓和狗類的共性進行抽取:
class Animal {public String name;public int age;public String sex;public void eat() {System.out.println(name + "吃飯");}public void sleep() {System.out.println(name + "睡覺");}
}
class Dog extends Animal {public void wang() {System.out.println(name + "汪汪叫");}
}
class Cat extends Animal {public void miao() {System.out.println(name + "喵喵叫");}
}
在繼承時我們需要注意:
1.子類繼承父類之后,會繼承父類中的成員方法和成員變量(靜態成員除外)。
2.子類繼承父類之后,必須有子類獨有的成員,需體現出與父類的不同,否則沒必要繼承。
三、父類成員訪問
1.成員變量
我們可以在子類中直接訪問從父類中繼承下來的成員變量:
class Fu {public int a;public int b;
}
class Zi extends Fu {public int c;public void test() {a = 10;b = 20;c = 30;System.out.println(a + " " + b + " " + c + " ");}
}
public class Test2 {public static void main(String[] args) {Zi zi = new Zi();zi.test();}
}
但是當父類和子類的成員變量名相同時,會訪問哪個呢:
class Fu {public int a = 1;
}
class Zi extends Fu {public int a = 0;public void test() {System.out.println(a);}
}
public class Test2 {public static void main(String[] args) {Zi zi = new Zi();zi.test(); //0}
}
由運行結果可知:成員變量的訪問遵循就近原則,如果子類中有,則優先使用子類的成員變量;如果子類中沒有,則使用父類的成員變量;若父類和子類均沒有,則編譯報錯。
2.成員方法
我們可以在子類中直接訪問從父類中繼承下來的成員方法:
class Fu {public void test1() {System.out.println("嘻嘻");}
}
class Zi extends Fu {public void test2() {System.out.println("哈哈");}public void test() {test1();test2();}
}
public class Test2 {public static void main(String[] args) {Zi zi = new Zi();zi.test();}
}
但是當父類和子類的成員方法名相同時,會訪問哪個呢:
class Fu {public void test1() {System.out.println("嘻嘻");}public void test2(int x) {System.out.println("xixi");}
}
class Zi extends Fu {public void test1() {System.out.println("哈哈");}public void test2() {System.out.println("haha");}public void test() {test1(); //哈哈test2(); //haha(調用子類)test2(666); //xixi(調用父類)}
}
public class Test2 {public static void main(String[] args) {Zi zi = new Zi();zi.test();}
}
由運行結果可知:成員方法的訪問遵循就近原則,如果子類中有,則優先使用子類的成員方法;當父類和子類的 同名方法的 參數列表不同時,根據調用方法時傳遞的參數,選擇合適的方法;如果子類中沒有,則使用父類的成員方法;若父類和子類均沒有,則編譯報錯。
但是當我們想在子類中訪問父類的 同名的成員時,應該怎么辦呢?此時,就可以使用super關鍵字。
四、super關鍵字
Java中提供了super關鍵字,幫助我們在子類中訪問父類的成員。
class Fu {public int a = 1;public int b = 2;public int c = 3;
}
class Zi extends Fu {public int a = 11;public int d = 4;public void test() {System.out.println(a); //11System.out.println(b); //2System.out.println(c); //3System.out.println(d); //4System.out.println(super.a); //1}
}
public class Test2 {public static void main(String[] args) {Zi zi = new Zi();zi.test();}
}
對于super關鍵字:
1.如果super在當前類中被使用,則當前類一定為子類。
2.super只能訪問從父類中繼承過來的成員。
3.使用this訪問,既可以訪問父類成員也可以訪問子類成員,但當訪問父類和子類中同名的成員時,子類成員優先被訪問。
因此,子類test方法中以下寫法均正確:
System.out.println(this.a); //11System.out.println(this.b); //2System.out.println(this.c); //3System.out.println(this.d); //4System.out.println(super.a); //1System.out.println(super.b); //2System.out.println(super.c); //3
注意:super關鍵字只能在非靜態方法中使用!!!
五、子類構造方法
class Animal {public String name;public int age;public String sex;//一個參數的構造方法public Animal(String name) {this.name = name;}public void eat() {System.out.println(name + "吃飯");}public void sleep() {System.out.println(name + "睡覺");}
}
class Dog extends Animal {public void wang() {System.out.println(name + "汪汪叫");}
}
我們為動物類提供帶有一個參數的構造方法,此時狗類繼承動物類時,就會報錯。這是因為:當子類繼承父類之后,需要先幫助父類進行構造,再構造子類自己。 那又如何幫助父類進行構造呢:在子類中調用父類的構造方法。
構造方法的作用就是對 對象中的成員變量進行初始化,因此需要先調用父類的構造方法,對從父類繼承下來的成員完成構造,再調用子類自己的構造方法,對子類中獨有的成員進行構造。
于是,我們對代碼進行修改:
class Animal {public String name;public int age;public String sex;public Animal(String name) {this.name = name;}public void eat() {System.out.println(name + "吃飯");}public void sleep() {System.out.println(name + "睡覺");}
}
class Dog extends Animal {public Dog(String name) {super(name);}public void wang() {System.out.println(name + "汪汪叫");}
}
總結一下:
1.當父類顯式定義無參或者使用默認提供的構造方法時,子類構造方法的第一行均默認有隱藏的super()調用,即調用父類構造方法。
2.如果父類的構造方法帶有參數,則需要顯式定義子類的構造方法,并在子類構造方法中調用合適的父類構造方法。
3.在子類構造方法中,super()只能出現在子類構造方法的第一行,因此子類構造方法中只能出現一次super()。
六、super和this辨析
【相同點】
1.均為Java中的關鍵字。
2.均可以對成員進行訪問,且super()和this()都需要在構造方法的首行使用,因此不能同時存在于同一個構造方法。
3.只能在類的非靜態方法中使用,用于訪問非靜態的成員。
【不同點】
1.this代表當前對象的引用,當前對象即調用成員的對象;super代表在子類對象中 對從父類繼承下來的成員 的引用。
2.在非靜態成員方法中,this既可以訪問子類的成員,也可以訪問父類的成員;super只能訪問父類的成員。
3.在構造方法中,this()用于調用當前類中的構造方法;super()用于調用父類的構造方法。
4.當子類繼承父類后,子類的構造方法中一定會存在super()語句,哪怕沒有顯式定義,編譯器也會自動添加;this()不寫則沒有。
七、再談初始化
在上一章節中,我們已經學習了有關代碼塊的知識。在繼承關系中,各個代碼塊的執行順序又是怎樣的呢?
class Animal {public String name;public int age;public String sex;public Animal(String name) {this.name = name;System.out.println("動物構造");}static {System.out.println("動物靜態代碼塊");}{System.out.println("動物實例代碼塊");}
}
class Dog extends Animal {public Dog(String name) {super(name);System.out.println("小狗構造");}static {System.out.println("小狗靜態代碼塊");}{System.out.println("小狗實例代碼塊");}
}
public class Test {public static void main(String[] args) {Dog dog1 = new Dog("小狗1");System.out.println("======================");Dog dog2 = new Dog("小狗2");}
}
運行結果為:
由運行結果我們可以得出:
1.靜態代碼塊優先執行,且父類優先于子類執行。
2.父類的實例代碼塊和構造方法其次執行。
3.子類的實例代碼塊和構造方法最后執行。
4.靜態代碼塊只執行一次,第二次實例化對象時,父類和子類的靜態代碼塊均不再執行。
八、protected關鍵字
依舊是這張圖片,我們來學習一下protected關鍵字。
學習了繼承之后,我們對private關鍵字再補充一點:在父類中被private修飾的成員雖然在子類中不能直接訪問,但是也繼承到了子類中。
九、繼承方式
在Java中支持單繼承、多層繼承、不同類繼承同一個類,但不支持多繼承!!!
十、final關鍵字
final關鍵字可以用來修飾變量、方法和類。
final修飾變量時,表示變量不能被修改,即常量:
public static void main(String[] args) {final int a = 10;a = 20; //×}
final修飾類時,代表類不能被繼承:
final class A {
}
//錯誤×
class B extends A {
}
被final修飾的類,也叫密封類。
final還可以修飾方法,這一點我們以后再介紹。
我們來看一段代碼:
final int[] array = {1,2,3,4,5};array = new int[10]; //1array[0] = 10; //2
這段代碼會在1處報錯:
十一、繼承與組合
如果將繼承比作is-a的關系,那么組合就是has-a的關系。
組合和繼承類似,也是一種表達類和類之間關系的方式,也可以達到代碼復用的效果。組合的思想體現在代碼上 就是將一個類的實例作為另一個類的成員變量,并無特殊的語法格式:
class Old {}
class Home {public Old[] olds;public Home() {this.olds = new Old[2];}
}
Ending。