天天看點

【Java】為什麼會産醬油象類,抽象類的定義,方法及使用原則

首先為什麼會有抽象類和接口的産生?

當提到這裡時必須先講一講方法覆寫

方法覆寫:定義了方法名稱,傳回值類型,參數的類型及個數完全相同,
被覆寫的方法不能有比子類更為嚴格的通路控制權限。
           

方法覆寫:多态的關鍵所在。

是以為了強制要求子類必須覆寫父類方法産生了抽象類與接口.

多态:一個類執行個體的相同方法在不同情形下有不同的表現形式。
           

回顧:

向上轉型:參數統一化

父類 父類引用 = new 子類執行個體();
father per = new Student();
           

向下轉型:要發生向下轉型必須先發生向上轉型

子類 子類引用 = (子類) 父類執行個體;
Student stu = (Student) per;
           

是以不管是向上轉型還是向下轉型都發生了方法覆寫。

抽象類說明符abstract:

一個類被聲明為abstract,則表示該類是無具體對象的抽象類,
即該類不能進行執行個體化操作.不能用new關鍵字建立該類的對象,
隻能由其子類的抽象方法的具體實作來完成.
           

抽象類:

Abstract:

定義:抽象類是普通類的超級,隻是比普通類多了一些抽象方法而已。
           

首先知道:

抽象方法:使用抽象方法定義的隻有方法聲明沒有方法體的方法.

文法:

public abstract void test();
//隻有方法聲明,沒有方法體的方法
           

- 抽象類中包含抽象方法,則抽象類也必須使用abstract來定義,表示抽象類

本地方法(Java調用c同名方法):使用native定義的隻有方法聲明沒有方法體的方法。

public native int hashCode();
           

注意:隻有方法聲明沒有方法體的方法就是抽象方法(×)

因為在Java中還有native(本地方法);

抽象類的使用限制或者使用原則

(抽象類一定不能new)

  • a.所有抽象類必須有子類(final與abstract不能同時出現)

    原因:abstract一定有子類,而final一定沒有子類(不能繼承)。 (final可以修飾類)

因為由子類的抽象方法的具體實作來完成。

例子:

//具體定義
abstract class Person{
    private String name;
    private int age;
    public void setName(String name){
        this.name = name;
    }
    //隻聲明而為實作的方法(不能直接使用,隻能由子類的抽象方法來實作完成)
    public abstract void getPerson();
}
public class Test{
    public static void main(String[] args) {

    }
}
           
  • b.抽象類的子類必須覆寫所有抽象方法(或者子類也使用abstract關鍵字定義)
定義抽象類的目的就是想讓類繼承,實作覆寫

private與abstract不能同時出現,抽象方法必須被覆寫而private方法無法被覆寫
           
  • c.抽象類方法無法直接産生執行個體化對象,但可以通過子類向上轉型進行執行個體化。

(解釋:抽象類是“半成品”,不能直接new 自己;由于存在向上轉型,如果子類是一個普通類,可以把他變為抽象類進行使用)

且子類依然遵循對象執行個體化流程,先調用抽象類構造方法而後再調用子類構造。

(解釋:先調用父類構造,産生父類對象,才有子類對象)

(抽象類是普通類的超級,普通類有構造方法,那抽象類一定有構造方法)

- d.抽象類可以沒有抽象方法,但是此時任然不能直接執行個體化對象。

例子:

//具體定義
abstract class Person{
    private String name;
    private int age;
    public void setName(String name){
        this.name = name;
    }
    //隻聲明而為實作的方法(不能直接使用)
    public abstract void getPersonInfo();
}
class Student extends Person{
    public void getPersonInfo(){
        System.out.println("111");
    }
}
public class Test{
    public static void main(String[] args) {
        //通過向上轉型,把Student對象指向了一個父類的引用
        Person per = new Student();
        //這時調用per.getPersonInfo();執行輸出
        per.getPersonInfo();
    }
}
           

筆試題:

//請問輸出是多少?
A.0
B.30
C.100
D.編譯錯誤

abstract class Person {
    public Person() {
        this.print();
    }
    public abstract void print();
}    
class Student extends Person{
    private int num = 100;
    public Student(int num){
        this.num = num;
    }
    public void print(){
        System.out.println(num);
    }
}
public class Test{
    public static void main(String[] args) {
        new Student(30);
    }
}
           

輸出:A.0

解釋:
一:**從入口開始看**,
第一步調用子類Student這個類的構造方法,将30傳值,但是
在**子類構造中有一個隐藏的super();(因為繼承關系)**,是以先調用
父類構造方法再到子類。故當調用子類這個有參構造時連方法都進不去。
二:是以先在父類(**本質上第一步從父類開始**),Person的無參構造
調用了print方法(this表示本類對象),this在父類中,且用abstract關鍵字修飾,
是以此時print方法調用不了。
三:故去尋找子類中有無print方法,至子類中的print方法中,
子類的print方法覆寫了父類,故從子類的覆寫後的方法進行輸出,
此時的num還未進行this.num = num;這一行的指派。
四:實際上private int num = 100;這一行在編譯的時候在構造方法中執行,
但是在主方法中有new,是以開辟了一塊新空間,所有屬性都有預設值,故輸出結果為0;
           

擴充:

擴充:
public class Test{
    public static void main(String[] args) {
        new Student(30).print();
    }
}
           

輸出0,30

此時Student這個構造方法已經全部執行完成,最後才調用print方法,
print方法是通過子類Student類new調用,print方法被覆寫,
是以調用的一定是被覆寫後的方法。(先等于100,在等于30),
故輸出30.
           

例子:

abstract class Person{
    public abstract void print();
    //在抽象類内部提供了一個實作好的子類,
    //(靜态方法可通過類名稱直接調用)
    public static Person getInstance(){
        //方法内部類,繼承了抽象類并實作,
        //方法傳回了實作好的子類
        class Student extends Person{
            public void print(){}
        }
        return new Student();
    }
}

public class Test{
    public static void main(String[] args) {
        Person per = new Student();
        per.print();
    }
}
           
  • 匿名内部類:等同于方法内部類,就是沒有名字,但是匿名内部類必須繼承一個抽象類或者實作一個接口,除此之外與方法内部類完全一緻

例子:

abstract class Person{
    public abstract void print();
    //在抽象類内部提供了一個實作好的子類(靜态方法可通過類名稱直接調用)
    public static Person getInstance(){
        //匿名内部類,隐藏了沒有名字的類
        //等同于class 無名 extends Person{}
        //直接執行個體化:new Person();
        return new Person(){
            //内部類
            public void print(){}
        }
    }
}
           

.