【JAVA基礎】JAVA8中 抽象類和接口的異同

1. 抽象類

什麼是抽象類:

Java語言中,可以通過把類或者類中的某些方法聲明爲 abstract 來表示一個類是抽象類。抽象類跟普通類區別不大,唯一的區別就是抽象類中可以包含抽象方法,且抽象類不可以被實例化

那我們爲什麼要使用抽象類呢?

這裏我個人覺得,在自下而上的類的繼承層次結構中上移,位於上層的類更具有通用性,甚至有可能,在高層中的一些基類,有一些方法是不用實現的,具有抽象性的。例如:
一個最高的基類是動物,裏面可能有各種方法(動作),例如:吃、行動、睡覺,對於動物這個最高的基類,這些方法都不需要實現,讓它的子類去實現就好,就相當於這些方法都是抽象的,意味着將實現交給了子類。而對於子類,例如有一個子類羊,它向下還有更多的子類,例如:a類羊、b類羊、等等,對於羊這個類,對於行動這個方法,不同的子類羊行動的方式不同,因此這裏依舊不需要實現,所以依舊這些方法可以定義爲抽象的。
因此,在上面這個例子中,可以看出抽象類的使用場景:一個類有部分or全部的方法是不需要實現的(交付給子類實現),這樣子的類爲抽象類

擴展抽象類的方式

其實從上面的例子中,我們不難總結出擴展抽象類的兩種選擇:

  • 在子類中保留部分的抽象方法,這樣子子類也要定義爲抽象類
  • 在子類中實現全部的抽象方法,這樣子子類就可以不定義爲抽象類

幾個不能和 abstract 共存的關鍵字

  1. final:被 final 修飾的類不能有子類(不能被繼承)。而被 abstract 修飾的類一定是一個父類(一定要被繼承),因此矛盾,無法共存
  2. private:抽象類中的私有的抽象方法,不被子類所知,就無法被複寫。而抽象方法是一定要在子類中被實現的,因此矛盾,無法共存
  3. static:如果static可以修飾抽象方法,那麼連對象都省了,直接類名調用就可以了,可是抽象方法運行沒意義

關於抽象類的幾個注意點

  1. 即使類中沒有抽象方法,也可以將類定義爲抽象類
  2. 抽象類不可以被實例化
  3. 抽象方法和抽象類都必須被 abstract 關鍵字修飾

2. 接口

什麼是接口

接口是對類的一組需求描述,這些類要遵從接口描述的統一格式進行定義。

Java8 對接口的變動有什麼

主要變動的地方有兩個:

  • 第一,在接口中增加了靜態方法,可以直接在接口處申明靜態方法並實現,調用方法同普通類的靜態方法,舉例:
// 在接口中寫上靜態方法
public interface MyInterface {
    public static void test() {
        System.out.println("實現靜態方法");
    }
}

// 在其他類中調用
public class Main {
    public static void main(String[] args) {
        MyInterface.test();
    }
}

調用結果:
在這裏插入圖片描述

  • 第二,Java8 中允許接口中包含具有具體實現的方法,該方法稱爲“默認方法”,使用 default 關鍵字修飾
// 在接口中實現默認方法
public interface MyInterface {
    default void testDefault() {
        System.out.println("實現默認方法");
    }
}

// 在實現類中調用方法
public class Main implements MyInterface{
    public static void main(String[] args) {
        Main m = new Main();
        m.testDefault();
    }
}

執行結果:
在這裏插入圖片描述
但在接口中可以寫實現類的話,就會引發兩個問題:

  • 第一,如果類A實現兩個接口,兩個接口各自實現了一個相同的方法,我在類A中調用該方法,是調用哪個接口的默認方法呢?
  • 第二,如果類A實現的一個接口中的默認方法,跟類A繼承的類中的一個方法相同,我調用的是基類的方法,還是接口的默認方法呢?

關於這兩個問題,有一個原則:默認方法的“類優先”原則
什麼意思呢?就是:若一個接口中定義了一個默認方法,而另外一個父類或接口又定義了一個同名的方法時:

  • 選擇父類中的方法,如果一個父類中提供了具體的實現,那麼接口具有相同名稱和參數的默認方法會被忽略
  • 接口衝突。如果實現的兩個接口都提供了一個具有相同名稱和參數列表的方法(不管方法是否是默認方法),那麼在實現類中必須覆蓋方法來解決衝突

具體例子:
① 父類接口衝突

// 父類
public class Father {
    public void sayHello() {
        System.out.println("hello, this is Father");
    }
}

// 接口
public interface MyInterface {
    default void sayHello() {
        System.out.println("hello, this is MyInterface");
    }
}

// 子類
public class Main extends Father implements MyInterface{
    public static void main(String[] args) {
        Main m = new Main();
        m.sayHello();
    }
}

執行的結果:父類中的實現
在這裏插入圖片描述
② 兩個接口衝突

// 接口1:
public interface MyInterface {
    default void sayHello() {
        System.out.println("hello, this is MyInterface");
    }
}

// 接口2:
public interface MyInterface1 {
    default void sayHello() {
        System.out.println("hello, this is MyInterface1");
    }
}

當類A同時實現這兩個接口時,會發現提示:
在這裏插入圖片描述
因此,我們需要在類A中覆蓋該方法,才能使程序正常運行

3. 抽象類和接口的相同點

  • 都不能被實例化
  • 接口的實現類或抽象類的子類,都只有在實現了接口或抽象類中的方法後,才能被實例化
  • 從 Java8 開始,抽象類和接口都可以有方法的實現(Java8 之前接口只有定義)

4. 抽象類和接口的不同點

  • 接口需要實現(implements),且一個類可以實現多個接口,抽象類需要被繼承(extends),且只能單繼承,即一個類只有一個父類
  • 接口的設計理念是 “has - a” ,關係,即強調特定功能的實現,而抽象類強調所屬關係,即 “is - a” 關係
  • 接口中定義的成員變量默認爲 public static final,不能被修改且需要賦初始值。而抽象類的成員變量跟普通類的相同
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章