天天看點

怒肝倆月,新鮮出爐史上最有趣的Java小白手冊,第一版,每個 Java 初學者都應該收藏9)

八、Java 構造方法

假設現在有一個 Writer 類,它有兩個字段,姓名和年紀:

public class Writer {
    private String name;
    private int age;
    @Override
    public String toString() {
        return "Writer{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}      

重寫了 toString() 方法,用于列印 Writer 類的詳情。由于沒有構造方法,意味着當我們建立 Writer 對象時,它的字段值并沒有初始化:

Writer writer = new Writer();

System.out.println(writer.toString());

輸出結果如下所示:

Writer{name='null', age=0}

name 是字元串類型,是以預設值為 null,age 為 int 類型,是以預設值為 0。

讓我們為 Writer 類主動加一個無參的構造方法:

public Writer() {

   this.name = "";

   this.age = 0;

}

構造方法也是一個方法,隻不過它沒有傳回值,預設傳回建立對象的類型。需要注意的是,目前構造方法沒有參數,它被稱為無參構造方法。如果我們沒有主動建立無參構造方法的話,編譯器會隐式地自動添加一個無參的構造方法。這就是為什麼,一開始雖然沒有構造方法,卻可以使用 new Writer() 建立對象的原因,隻不過,所有的字段都被初始化成了預設值。

接下來,讓我們添加一個有參的構造方法:

public Writer(String name, int age) {

   this.name = name;

   this.age = age;

現在,我們建立 Writer 對象的時候就可以通過對字段值初始化值了。

Writer writer1 = new Writer("沉默王二",18);

System.out.println(writer1.toString());

來看一下列印結果:

Writer{name='沉默王二', age=18}

可以根據字段的數量添加不同參數數量的構造方法,比如說,我們可以單獨為 name 字段添加一個構造方法:

public Writer(String name) {

為了能夠兼顧 age 字段,我們可以通過 this 關鍵字調用其他的構造方法:

   this(name,18);

把作者的年齡都預設初始化為 18。如果需要使用父類的構造方法,還可以使用 super 關鍵字,手冊後面有詳細的介紹。

九、Java 抽象類

當我們要完成的任務是确定的,但具體的方式需要随後開個會投票的話,Java 的抽象類就派上用場了。這句話怎麼了解呢?搬個小闆凳坐好,聽我來給你講講。

01、抽象類的 5 個關鍵點

1)定義抽象類的時候需要用到關鍵字 abstract,放在 class 關鍵字前。

public abstract class AbstractPlayer {

關于抽象類的命名,阿裡出品的 Java 開發手冊上有強調,“抽象類命名要使用 Abstract 或 Base 開頭”,記住了哦。

2)抽象類不能被執行個體化,但可以有子類。

嘗試通過 new 關鍵字執行個體化的話,編譯器會報錯,提示“類是抽象的,不能執行個體化”。

怒肝倆月,新鮮出爐史上最有趣的Java小白手冊,第一版,每個 Java 初學者都應該收藏9)

通過 extends 關鍵字可以繼承抽象類,繼承後,BasketballPlayer 類就是 AbstractPlayer 的子類。

public class BasketballPlayer extends AbstractPlayer {

3)如果一個類定義了一個或多個抽象方法,那麼這個類必須是抽象類。

當在一個普通類(沒有使用 abstract 關鍵字修飾)中定義了抽象方法,編譯器就會有兩處錯誤提示。

第一處在類級别上,提醒你“這個類必須通過 abstract 關鍵字定義”,or 的那個資訊沒必要,見下圖。

怒肝倆月,新鮮出爐史上最有趣的Java小白手冊,第一版,每個 Java 初學者都應該收藏9)

第二處在方法級别上,提醒你“抽象方法所在的類不是抽象的”,見下圖。

怒肝倆月,新鮮出爐史上最有趣的Java小白手冊,第一版,每個 Java 初學者都應該收藏9)

4)抽象類可以同時聲明抽象方法和具體方法,也可以什麼方法都沒有,但沒必要。就像下面這樣:

   abstract void play();

   public void sleep() {

       System.out.println("運動員也要休息而不是挑戰極限");

   }

5)抽象類派生的子類必須實作父類中定義的抽象方法。比如說,抽象類中定義了 play() 方法,子類中就必須實作。

   @Override

   void play() {

       System.out.println("我是張伯倫,籃球場上得過 100 分");

如果沒有實作的話,編譯器會提醒你“子類必須實作抽象方法”,見下圖。

怒肝倆月,新鮮出爐史上最有趣的Java小白手冊,第一版,每個 Java 初學者都應該收藏9)

02、什麼時候用抽象類

與抽象類息息相關的還有一個概念,就是接口,我們留到下一篇文章中詳細說,因為要說的知識點還是蠻多的。你現在隻需要有這樣一個概念就好,接口是對行為的抽象,抽象類是對整個類(包含成員變量和行為)進行抽象。

(是不是有點明白又有點不明白,别着急,翹首以盼地等下一篇文章出爐吧)

除了接口之外,還有一個概念就是具體的類,就是不通過 abstract 修飾的普通類,見下面這段代碼中的定義。

public class BasketballPlayer {

  public void play() {

       System.out.println("我是詹姆斯,現役第一人");

有接口,有具體類,那什麼時候該使用抽象類呢?

1)我們希望一些通用的功能被多個子類複用。比如說,AbstractPlayer 抽象類中有一個普通的方法 sleep(),表明所有運動員都需要休息,那麼這個方法就可以被子類複用。

雖然 AbstractPlayer 類可以不是抽象類——把 abstract 修飾符去掉也能滿足這種場景。但 AbstractPlayer 類可能還會有一個或者多個抽象方法。

BasketballPlayer 繼承了 AbstractPlayer 類,也就擁有了 sleep() 方法。

1

2

BasketballPlayer 對象可以直接調用 sleep() 方法:

BasketballPlayer basketballPlayer = new BasketballPlayer();

basketballPlayer.sleep();

FootballPlayer 繼承了 AbstractPlayer 類,也就擁有了 sleep() 方法。

public class FootballPlayer extends AbstractPlayer {

FootballPlayer 對象也可以直接調用 sleep() 方法:

FootballPlayer footballPlayer = new FootballPlayer();

footballPlayer.sleep();

2)我們需要在抽象類中定義好 API,然後在子類中擴充實作。比如說,AbstractPlayer 抽象類中有一個抽象方法 play(),定義所有運動員都可以從事某項運動,但需要對應子類去擴充實作。

BasketballPlayer 繼承了 AbstractPlayer 類,擴充實作了自己的 play() 方法。

       System.out.println("我是張伯倫,我籃球場上得過 100 分,");

FootballPlayer 繼承了 AbstractPlayer 類,擴充實作了自己的 play() 方法。

       System.out.println("我是C羅,我能接住任意高度的頭球");

3)如果父類與子類之間的關系符合 is-a 的層次關系,就可以使用抽象類,比如說籃球運動員是運動員,足球運動員是運動員。