深入理解java抽象類

抽象類概念

在面向對象的概念中,所有的對象都是通過類來描述的,但是並不是所有的類都描述了對象,有些類裏面並沒有包含足夠的信息來描述對象,這些類被認爲是抽象類。

抽象類與普通類的區別就在於抽象類不能被實例化,這就決定了抽象類必須有子類實現它的抽象方法

定義與使用

抽象類只是在普通類的基礎上擴充了一些抽象方法而已,所謂的抽象方法指的是隻聲明而未實現的方法(即沒有方法體)。所有抽象方法要求使用abstract關鍵字來定義,並且抽象方法所在的類也一定要使用abstract關鍵字來定義,表示抽象類。

定義

下面我們寫一個抽象類看看例子:

abstract class Person{
    private String name ; // 屬性
    public String getName(){ // 普通方法
        return this.name;
    }
    public void setName(String name){
        this.name = name ;
    }
    // {}爲方法體,所有抽象方法上不包含方法體
    public abstract void getPersonInfo() ; //抽象方法
}

可以看到抽象類前面要加abstract修飾,抽象方法也要加abstract關鍵字
需要注意的是:一個類中如果有抽象方法,那麼這個類一定是抽象類,但是一個類如果是抽象類,它是可以沒有抽象方法的。

使用原則

  • 所有的抽象類必須有子類。
  • 抽象類的子類必須覆寫抽象類的所有抽象方法(子類不是抽象類)【方法覆寫一定要考慮權限問題,權限盡量都用public】
  • 抽象類的對象可以通過對象多態性利用子類爲其實例化
  • private與abstract不能同時使用。

下面我們寫一個使用抽象類的代碼:

abstract class Person{
    private String name ; // 屬性
    public String getName(){ // 普通方法
        return this.name;
    }
    public void setName(String name){
        this.name = name ;
    }
    // {}爲方法體,所有抽象方法上不包含方法體
    public abstract void getPersonInfo() ; //抽象方法
}
class Student extends Person{
    public void getPersonInfo(){//實現父類的抽象方法
        System.out.println("I am a student");
    }
}
public class Test{
    public static void main(String[] args) {
        Person per = new Student() ; //實例化子類,向上轉型
        per.getPersonInfo() ; //被子類所覆寫的方法
    }
}

抽象類的相關規定

在抽象類中也允許提供構造方法,並且子類也照樣遵循對象實例化流程。實例化子類時一定先調用父類構造方法。例如:

abstract class Person{
    private String name ; // 屬性
    public Person(){ //構造方法
        System.out.println("**********");
    }
    public String getName(){ // 普通方法
        return this.name;
    }
    public void setName(String name){
        this.name = name ;
    }
    // {}爲方法體,所有抽象方法上不包含方法體
    public abstract void getPersonInfo() ; //抽象方法
}
class Student extends Person{
    public Student(){ //構造方法
        System.out.println("##########");
    }
    public void getPersonInfo(){
//空實現。
    }
}
public class Test {
    public static void main(String[] args) {
        new Student();
    }
}

結果如下:
在這裏插入圖片描述

所以說子類實例化是構造方法會先調用父類的構造方法。如果父類沒有無參構造,那麼子類構造必須使用super明確指出使用父類哪個構造方法。

總結

  • 抽象類不能被實例化(初學者很容易犯的錯),如果被實例化,就會報錯,編譯無法通過。只有抽象類的非抽象子類可以創建對象。

  • 抽象類中不一定包含抽象方法,但是有抽象方法的類必定是抽象類。

  • 抽象類中的抽象方法只是聲明,不包含方法體,就是不給出方法的具體實現也就是方法的具體功能。

  • 構造方法,類方法(用static修飾的方法)不能聲明爲抽象方法。

  • 抽象類的子類必須給出抽象類中的抽象方法的具體實現,除非該子類也是抽象類

挖坑練習

這道題我一開始也做錯了,所以在這裏整理一下:
看如下代碼:輸出結果是什麼呢?

abstract class A{
    public A(){ //3.調用父類構造
        this.print() ; //4.調用被子類覆寫的方法
    }
    public abstract void print() ;
}
class B extends A{
    private int num = 100 ;
    public B(int num) { //2.調用子類實例化對象
        super() ; //3.隱含一行語句,實際要先調用父類構造
        this.num = num ; //7.爲類中屬性初始化
    }
    public void print() { //5.此時子類對象的屬性還沒有被初始化
        System.out.println(this.num) ; //6.對應其數據類型的默認值
    }
}
public class Test{
    public static void main(String[] args) {
        new B(30) ; //1.實例化子類對象
        new B(30).print();
    }
}

這道題很容易入坑,main方法裏面實例化了一個B的對象並傳了30進去,那麼在B類中首先會調用構造方法,在調用B的構造方法時,這時候又因爲B繼承於A所以又先調用了A的構造方法,A的構造方法裏面又調用了A的print方法,但是A裏面print方法時一個抽象方法,是由子類去實現的,所以這時候又來到了B類中的print方法,注意:此時num還並沒有賦值,所以輸出的應該是num類型的默認值0。
main方法中第二條語句在第一條語句的基礎上又多了一個.print(),前面的步驟和第一條語句是一樣的流程,只是後面.print()時,是指的B的print方法,而此時num是30,已經將值傳進來了,所以第二條語句輸出的結果應該是:
0
30

結果如下:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章