程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA編程入門知識 >> Java 淺析三大特性之一繼承

Java 淺析三大特性之一繼承

編輯:JAVA編程入門知識

上文Java 淺析三大特性之一封裝我們說到Java是一個注重編寫類,注重於代碼和功能復用的語言。Java實現代碼復用的方式有很多,這裡介紹一個重要的復用方式——繼承。
在介紹繼承之前,我們要明確一點,繼承是一個比較復雜的編寫類的方式,他會破壞掉父類的封裝,因此只有我們確定需要用到繼承的時候,我們才會用繼承。

繼承的概念

繼承是一種構建新類的方式,他是基於已有的類的定義為基礎,構建新的類,已有的類稱為父類,新構建的類稱為子類,子類能調用父類的非private修飾的成員,同時還可以自己添加一些新的成員,擴充父類,甚至重寫父類已有的方法,更其表現符合子類的特征。讓子類的表現更獨特,更專業。

繼承的寫法

Java規定,一個類後面緊跟 extends關鍵字,再加一個類的名字,則表示新建的類繼承自extends 後面的那個類。在我們上面這個文章最後,我們列舉了一個類,代碼如下:

public class Student {
    private String name;
    private String age;

    private String handleName(String name){
       return "I'm " + name;
    }

    private String handleAge(String age) {
        return age + " 歲";
    }

    public String getName() {
        return name;
    }

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

    public String getAge() {
        return age;
    }

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

現在我們新建一個類去繼承自他,這個類我們就叫Boys,其代碼如下:

public class Boys extends Student {
    @Override
    public void setName(String name) {
        super.setName(name);
    }

    @Override
    public String getAge() {
        return super.getAge();
    }

    @Override
    public String getName() {
        return super.getName();
    }

    @Override
    public void setAge(String age) {
        super.setAge(age);
    }
}

在上面代碼中,我們可以看到很多相似的方法,這些方法的方法簽名,參數,返回類型都是與父類的方法是一致,Java允許我們定義和父類一致的方法,以便我們在這個方法中寫入新的代碼。上面方法中,我們會看見一個新的關鍵字super,這個關鍵字與this相似,不過super表明調用方法的對象是父類的對象,那這段自動生成的代碼的意思就是如果我們不添加新的內容,那麼當我們子類調用這些方法的時候,執行的是父類的方法。同時我們在這個方法上面看見一個注解 @Override這表明這個方法是覆蓋父類的方法,而不是方法重載,下面我具體說一下什麼是方法重載,以及這個注解存在的必要。

方法重載

方法重載是很有必要的一種方式,他其實體現的是一種多態。即我定義了一種方法,這個方法可以承接很多類型的參數,而不要針對每個參數定義不同的方法。其實構造器就是方法重載的一個典型應用,也因為構造器必須要根據不同的參數,構造不同的對象,所以必須要實現方法重載。
下面這個例子,我們看一下什麼是重載

public class OverLoading {
    public void print(String args1) {
        System.out.println(args1);
    }

    public void print(String args1, String args2) {
        System.out.println(args1 + args2);
    }

    public void print(int args1) {
        System.out.println(args1);
    }

    public String print(double args1) {
        System.out.println(args1);
        return null;
    }

    //這不是方法重載,這是錯誤的語句
//    public String print(String args1) {
//        System.out.println(args1);
//        return null;
//    }

    public static void main(String[] args) {
        OverLoading ol = new OverLoading();
        ol.print(1.0);
        ol.print(1);
        ol.print("1");
        ol.print("1", "1");
    }

}

//輸出結果
1.0
1
1
11

上面的代碼當中,只有一個方法print,但這個方法卻可以接受多種參數,甚至返回的類型也不一樣,但是我們在測試時候發現,根據輸入的不同,系統會自動執行不同的print。這就是方法的重載。注意上面我有一注釋的語句,這個語句是錯誤的方法重載,所以我們要如何去區分方法的重載呢。
其實規則比較簡單,每一個重載的方法必定有不同的參數列表。條件只有這一個。
第一個當中,參數列表是String args1,第二個參數列表是String args1, String args2,第三個參數列表是int args1,第四個參數列表是double args1。這四個,每一個參數的列表都是不一樣的,所以他們是重載的方法,而第五個也就是說注釋的語句顯然參數列表是String args1與第一個是重復的,所以他不是方法重載,系統會直接報錯,告訴你這個方法已經被定義了。甚至更極端的情況,參數的順序都不一樣,也算是方法重載,但在這個例子裡,是不存在順序不一樣的,但下面這段代碼算方法重載

  public String print(int args1, String args2) {
        return null;
    }

   public String print(String agrs2, int args1) {
        return null;
    }

這個看起來好像和繼承沒什麼關系的知識點,但卻並不是這樣。因為子類會繼承父類的非private方法,所以子類也會重載父類的方法。如果子類定義了一個方法簽名和父類一致的方法,但參數列表不一樣,這就算重載了父類的方法。但我們有時並不是要這樣,我們更多的是希望子類定義一個和父類一樣的方法簽名和參數列表,而裡面實現的功能不一樣。這樣的操作叫覆寫。但有時會書寫失誤,所以我們會在覆寫方法上加入@Override ,這樣一旦我們寫成了方法重載而非覆寫就是報錯。

繼承的一些特點

在寫完繼承的寫法之後,我們可以看出來一下繼承的特點,第一,繼承是有一個特殊的關鍵字super,第二,維持繼承關系,我們可以用一個特殊的關鍵字protected這個上篇文章是講過的,第三,很有意思的一點,子類其實可以看成一個特殊的父類,是父類的一種類型。下面我們具體的說一下每一點。

super關鍵字

super之前也提到過,是區別於this一種關鍵字,他一般表示的是調用方法的對象是父類的對象。那麼我們就可以利用它去調用父類的方法,父類的非private的成員變量。當然他和this一樣,也有一種特殊的應用,即用它去調用父類的構造器。一般情況在調用子類的構造器之前,會默認先調用父類的默認構造器,如果父類沒有默認的構造器,那麼我們在子類的構造器中就要明確用super去調用父類的構造器,否則會報錯。例子如下:

public class Father {
    
    public Father(int a){
        System.out.println("這是父類的構造器");
    }
}

public class Son extends Father{
    public Son(){
        super(1);
    }
}

這裡一定要顯式的調用父類的構造器,否則編譯器無法完成父類對象的構造。關於構造器的順序,初始化過程等等我們以後再詳細討論。protected關鍵字我們在上篇文章中已經講過,這裡就不說了,我們主要說第三點,向上轉型。

向上轉型

我們使用繼承,主要是因為父類和子類之間存在一個種所屬關系,子類確實是父類的一種。比如我們可以把動物當做是父類,子類是貓,狗啊等等。貓,狗確實是動物的一種。動物所擁有的方法,貓,狗都有,所以貓,狗是一種類型的動物,既然如此,我們就可以把貓,狗向上轉型成動物類型。這是安全且一定成功的。我們可以看下面的例子。

public class Animal {
    public void run(Animal animal) {
        System.out.println("動物在奔跑");
    }
}
public class Dog extends Animal{
    public static void main(String[] args) {
        Animal animal = new Animal();
        animal.run(new Dog());
    }
}

這裡Animal類的run方法明確規定傳入的參數是Animal類型,但我們傳入Dog類型也是可以成功的。這種就是向上轉型的一種應用。這種轉型總是成功的原因,就是上面所說,Dog其實是比Animal更專業,更獨特的類型,可以看做是一個專業的類型向通用的類型轉換,由一個更大的類向更小的類轉換,這種轉換除了會丟失一些方法和屬性以外,總會是成功的。關於向上轉型還有很多要講的內容,不過這都要和多態聯系到一起,我們以後再說。

總結

繼承提供了我們復用類和代碼的一種方式,但他並不是唯一和最好的一種,當我們明確需要這種繼承的關系去編寫類的時候,或者我們需要用到向上轉型的時候,我們才會用繼承。如果不需要的話,我們可以考慮是不是還有其他的方法。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved