1. 程式人生 > >Java 繼承(extends)詳解

Java 繼承(extends)詳解

一、繼承問題的引出

繼承性是面向物件的第二大主要特徵。
下面首先編寫兩個程式:Person類、Student類。

Person類:

class Person {
         private String name ;
         private int age ;
         public void setName(String name) {
                   this.name = name ;
         }
         public void setAge(int age) {
                   this
.age = age ; } public String getName() { return this.name ; } public int getAge(){ return this.age ; } }

Student類:

class Student {
         private String name ;
         private int age ;
         private String school ;
         public
void setName(String name) { this.name = name ; } public void setAge(int age) { this.age = age ; } public void setSchool(String school) { this.school = school ; } public String getName() { return
this.name ; } public int getAge(){ return this.age ; } public String getSchool() { return this.school ; } }

以上兩個程式通過程式碼的比較可以清楚的發現:程式碼之中存在了重複,而按照之前所學,這種重複是不可能消除的。

在所有的程式碼之中,最具有重複意義的就是連結串列類,針對於每一個簡單Java類或者是說其他的任何類,都編寫一個連結串列程式,程式碼量龐大,而且不方便維護。

二、繼承的概念

繼承是面向物件最顯著的一個特性。繼承是從已有的類中派生出新的類,新的類能吸收已有類的資料屬性和行為,並能擴充套件新的能力。

在Java之中,如果要實現繼承的關係,可以使用如下的語法:

class 子類 extends 父類 {}

子類又被稱為派生類; 父類又被稱為超類(Super Class)。

觀察繼承的基本實現:

package com.wz.extendsdemo;

class Person {
    private String name;
    private int age;

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

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return this.name;
    }

    public int getAge() {
        return this.age;
    }
}

class Student extends Person { // Student類繼承了Person類
}

public class TestDemo {
    public static void main(String args[]) {
        Student stu = new Student(); // 例項化的是子類
        stu.setName("張三"); // Person類定義
        stu.setAge(20); // Person類定義
        System.out.println("姓名:" + stu.getName() + ",年齡:" + stu.getAge());
    }
}

執行結果:

姓名:張三,年齡:20

通過程式碼可以發現,子類(Student)並沒有定義任何的操作,而在主類中所使用的全部操作都是由Person類定義的,這證明:子類即使不擴充父類,也能維持父類的操作。

在子類之中擴充父類的功能:

package com.wz.extendsdemo;

class Person {
    private String name;
    private int age;

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

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return this.name;
    }

    public int getAge() {
        return this.age;
    }
}

class Student extends Person { // Student類繼承了Person類
    private String school; // 子類的屬性

    public void setSchool(String school) {
        this.school = school;
    }

    public String getSchool() {
        return this.school;
    }
}

public class TestDemo {
    public static void main(String args[]) {
        Student stu = new Student(); // 例項化的是子類
        stu.setName("張三"); // Person類定義
        stu.setAge(20); // Person類定義
        stu.setSchool("清華大學"); // Student類擴充方法
        System.out.println("姓名:" + stu.getName() + ",年齡:" + stu.getAge() + ",學校:" + stu.getSchool());
    }
}

執行結果:

姓名:張三,年齡:20,學校:清華大學

以上的程式碼,子類對於父類的功能進行了擴充(擴充了一個屬性和兩個方法)。但是思考一下:子類從外表看是擴充了父類的功能,但是對於以上的程式碼,子類還有一個特點:子類實際上是將父類定義的更加的具體化的一種手段。父類表示的範圍大,而子類表示的範圍小

三、繼承的限制

雖然繼承可以進行類功能的擴充,但是其在定義的時候也是會存在若干種限制的。

限制一:一個子類只能夠繼承一個父類,存在單繼承侷限。
錯誤的寫法:

class A {}
class B {}
class C extends A,B {}          // 一個子類繼承了兩個父類

以上操作稱為多重繼承,實際上以上的做法就是希望一個子類,可以同時繼承多個類的功能,但是以上的語法不支援而已,但是可以換種方式完成同樣的操作。

正確的寫法:

class A {}
class B extends A {}
class C extends B {}

C實際上是屬於(孫)子類,這樣一來就相當於B類繼承了A類的全部方法,而C類又繼承了A和B類的方法,這種操作稱為多層繼承。

結論:Java之中只允許多層繼承,不允許多重繼承,Java存在單繼承侷限。

限制二:在一個子類繼承的時候,實際上會繼承父類之中的所有操作(屬性、方法),但是需要注意的是,對於所有的非私有(no private)操作屬於顯式繼承(可以直接利用物件操作),而所有的私有操作屬於隱式繼承(間接完成)。

package com.wz.extendsdemo;

class A {
    private String msg;

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public String getMsg() {
        return this.msg;
    }
}

class B extends A {
    public void print() {
        //System.out.println(msg); // 錯誤: msg定義為private,不可見
    }
}

public class TestDemo {
    public static void main(String args[]) {
        B b = new B();
        b.setMsg("張三");
        System.out.println(b.getMsg());
    }
}

此時對於A類之中的msg這個私有屬性發現無法直接進行訪問,但是卻發現可以通過setter、getter方法間接的進行操作。

限制三:在繼承關係之中,如果要例項化子類物件,會預設先呼叫父類構造,為父類之中的屬性初始化,之後再呼叫子類構造,為子類之中的屬性初始化,即:預設情況下,子類會找到父類之中的無參構造方法。

package com.wz.extendsdemo;

class A {
    public A() {         // 父類無參構造
              System.out.println("*************************") ;
    }
}
class B extends A {
    public B() {         // 子類構造
              System.out.println("#########################");
    }
}
public class TestDemo {
    public static void main(String args[]) {
              B b = new B() ;   // 例項化子類物件
    }
}

執行結果:

*************************
#########################

這個時候雖然例項化的是子類物件,但是發現它會預設先執行父類構造,呼叫父類構造的方法體執行,而後再例項化子類物件,呼叫子類的構造方法。而這個時候,對於子類的構造而言,就相當於隱含了一個super()的形式:

class B extends A {
    public B() { // 子類構造
        super(); // 呼叫父類構造
        System.out.println("#########################");
    }
}

現在預設呼叫的是無參構造,而如果這個時候父類沒有無參構造,則子類必須通過super()呼叫指定引數的構造方法:

package com.wz.extendsdemo;

class A {
    public A(String msg) { // 父類構造
        System.out.println("*************************");
    }
}

class B extends A {
    public B() { // 子類構造
        super("Hello"); // 呼叫父類構造
        System.out.println("#########################");
    }
}

public class TestDemo {
    public static void main(String args[]) {
        B b = new B(); // 例項化子類物件
    }
}

執行結果:

*************************
#########################

在任何的情況下,子類都逃不出父類構造的呼叫,很明顯,super呼叫父類構造,這個語法和this()很相似:super呼叫父類構造時,一定要放在構造方法的首行上。