Java 的繼承機制是一種複用類的技術,從原理上來說,是更好的使用了組合技術,是以要了解繼承,首先需要了解類的組合技術是如何實作類的複用的。
使用組合技術複用類
假設現在的需求是要建立一個具有基本類型,String 類型以及一個其他非基本類型的對象。該如何處理呢?
對于基本類型的變量,在新類中成員變量處直接定義即可,但對于非基本類型變量,不僅需要在類中聲明其引用,并且還需要手動初始化這個對象。
這裡需要注意的是,編譯器并不會預設将所有的引用都建立對象,因為這樣的話在很多情況下會增加不必要的負擔,是以,在合适的時機初始化合适的對象,可以通過以下幾個位置做初始化操作:
- 在定義對象的地方,先于構造方法執行。
- 在構造方法中。
- 在正要使用之前,這個被稱為惰性初始化。
- 使用執行個體初始化。
class Soap {
private String s;
Soap() {
System.out.println("Soap()");
s = "Constructed";
}
public String tiString(){
return s;
}
}
public class Bath {
// s1 初始化先于構造函數
private String s1 = "Happy", s2 = "Happy", s3, s4;
private Soap soap;
private int i;
private float f;
public Both() {
System.out.println("inSide Both");
s3 = "Joy";
f = 3.14f;
soap = new Soap();
}
{
i = 88;
}
public String toString() {
if(s4 == null){
s4 = "Joy"
}
return "s1 = " + s1 +"\n" +
"s2 = " + s2 +"\n" +
"s3 = " + s3 +"\n" +
"s4 = " + s4 +"\n" +
"i = " + i +"\n" +
"f = " + f +"\n" +
"soap = " + soap;
}
}
繼承
Java 中的繼承由 extend 關鍵字實作,組合的文法比較平實,而繼承是一種特殊的文法。當一個類繼承自另一個類時,那麼這個類就可以擁有另一個類的域和方法。
class Cleanser{
private String s = "Cleanser";
public void append(String a){
s += a;
}
public void apply(){
append("apply");
}
public void scrub(){
append("scrub");
}
public String toString(){
return s;
}
public static void main(String args){
Cleanser c = new Cleanser();
c.apply();
System.out.println(c);
}
}
public class Deter extends Cleanser{
public void apply(){
append("Deter.apply");
super.scrub();
}
public void foam(){
append("foam");
}
public static void main(String args){
Deter d = new Deter();
d.apply();
d.scrub();
d.foam();
System.out.println(d);
Cleanser.main(args);
}
}
上面的代碼中,展示了繼承文法中的一些特性:
- 子類可以直接使用父類中公共的方法和成員變量(通常為了保護資料域,成員變量均為私有)
- 子類中可以覆寫父類中的方法,也就是子類重寫了父類的方法,此時若還需要調用被覆寫的父類的方法,則需要用到
來指定是調用父類中的方法。super
- 子類中可以自定義父類中沒有的方法。
- 可以發現上面兩個類中均有 main 方法,指令行中調用的哪個類就執行哪個類的 main 方法,例如:
。java Deter
繼承文法的原理
接下來我們将通過建立子類對象來分析繼承文法在我們看不到的地方做了什麼樣的操作。
可以先思考一下,如何了解使用子類建立的對象呢,首先這個對象中包含子類的所有資訊,但是也包含父類的所有公共的資訊。
下面來看一段代碼,觀察一下子類在建立對象初始化的時候,會不會用到父類相關的方法。
class Art{
Art() {
System.out.println("Art Construct");
}
}
class Drawing extends Art {
Drawing() {
System.out.println("Drawing Construct");
}
}
public class Cartoon extends Drawing {
public Cartoon() {
System.out.println("Cartoon construct");
}
public void static main(String args) {
Cartoon c = new Cartoon();
}
}
/*output:
Art Construct
Drawing Construct
Cartoon construct
*/
通過觀察代碼可以發現,在執行個體化
Cartoon
時,事實上是從最頂層的父類開始向下逐個執行個體化,也就是最終執行個體化了三個對象。編譯器會預設在子類的構造方法中增加調用父類預設構造方法的代碼。
是以,繼承可以了解為編譯器幫我們完成了類的特殊組合技術,即在子類中存在一個父類的對象,使得我們可以用子類對象調用父類的方法。而在開發者看來隻不過是使用了一個關鍵字。
注意:雖然繼承很接近組合技術,但是繼承擁有其他更多的差別于組合的特性,例如父類的對象我們是不可見的,對于父類中的方法也做了相應的權限校驗等。
那麼,如果類中的構造方法是帶參的,該如何操作呢?(使用super關鍵字顯示調用)
見代碼:
class Game {
Game(int i){
System.out.println("Game Construct");
}
}
class BoardGame extends Game {
BoardGame(int j){
super(j);
System.out.println("BoardGame Construct");
}
}
public class Chess extends BoardGame{
Chess(){
super(99);
System.out.println("Chess construct");
}
public static void main(String args) {
Chess c = new Chess();
}
}
/*output:
Game Construct
BoardGame Construct
Chess construc
*/