JavaRush /Java Blog /Random-TW /喝咖啡休息#140。Java中的抽象類別和接口

喝咖啡休息#140。Java中的抽象類別和接口

在 Random-TW 群組發布
來源:InfoWorld 今天您將了解開發人員在哪些情況下應使用抽象類別,在哪些情況下應使用介面。我們還將識別 Java 語言的這些元素之間的差異以及如何在程式中使用它們。 喝咖啡休息#140。 Java 中的抽象類別和介面 - 1抽象類別和介面在 Java 程式碼甚至 Java 開發工具包 (JDK) 本身中非常常見。每個元素都有不同的用途:
  • 介面是 Java 語言中的一種構造,有助於實作抽象方法和靜態常數。
  • 抽象類別與常規類別類似,只不過它們可以包含抽象方法,即沒有主體的方法。無法建立抽象類別。
許多開發人員認為介面和抽象類別很相似,但實際上並不完全正確。讓我們看看它們之間的主要區別。

什麼是介面

從本質上講,介面是一個契約,因此它取決於實作來定義其創建的目的。介面不能使用可變實例變量,只能使用final變數。

何時使用介面

介面對於分離程式碼和實作多態性非常有用。我們可以在 JDK 中透過List介面看到這一點:
public interface List<E> extends Collection<E> {

    int size();
    boolean isEmpty();
    boolean add(E e);
    E remove(int index);
    void clear();
}
您可能已經注意到,這段程式碼雖然簡短,但具有很強的描述性。我們可以很容易地看到方法簽名,它將用於使用特定類別實作介面中的方法。List介麵包含可由ArrayListVectorLinkedList和其他類別實作的協定。要使用多態性,我們可以簡單地使用List宣告變數的類型,然後選擇任何可用的實例。這是另一個例子:
List list = new ArrayList();
System.out.println(list.getClass());

 List list = new LinkedList();
 System.out.println(list.getClass());
輸出是:
類別 java.util.ArrayList 類別 java.util.LinkedList
在這種情況下, ArrayListLinkedListVector 的實作方法是不同的,這是使用該介面的絕佳場景。如果您注意到許多類別屬於具有相同方法操作但行為不同的父類別。在這種情況下,建議使用該介面。接下來,讓我們來看看使用介面的幾個選項。

介面方法重寫

我們已經知道,介面是一種必須由具體類別來實現的契約。介面方法是隱式抽象的,需要類別的具體實作。這是一個例子:
public class OverridingDemo {
  public static void main(String[] args) {
    Challenger challenger = new JavaChallenger();
    challenger.doChallenge();
  }
}

interface Challenger {
  void doChallenge();
}

class JavaChallenger implements Challenger {
  @Override
  public void doChallenge() {
    System.out.println("Challenge done!");
  }
}
結論:
挑戰完成!
請注意,介面方法是隱式抽象的。這意味著我們不需要明確地聲明它們是抽象的。

常量變數

另一個要記住的規則是介面只能包含常數變數。這是一個例子:
public class Challenger {

  int number = 7;
  String name = "Java Challenger";

}
這裡兩個變數都是隱式的finalstatic。這意味著它們是恆定的,獨立於實例,並且不能更改。現在我們將嘗試更改Challenger介面中的變量,如下所示:
Challenger.number = 8;
Challenger.name = "Another Challenger";
這會導致編譯錯誤:
無法為最終變數「number」賦值 無法為最終變數「name」賦值

預設方法

當 Java 8 中引入預設方法時,一些開發人員認為它們與抽象類別相同。然而,事實並非如此,因為介面不能擁有狀態。預設方法可能有實現,但抽象方法沒有。預設方法是 lambda 表達式和流創新的結果,但我們必須謹慎使用它們。JDK中使用預設方法的方法是forEach() ,它是Iterable介面的一部分。我們可以簡單地重複使用forEach方法,而不是將程式碼複製到每個Iterable實作中:
default void forEach(Consumer<? super T> action) {
  // Code implementation here...
任何Iterable實作都可以使用forEach()方法,而不需要新的方法實作。然後我們可以使用預設方法重用程式碼。讓我們創建自己的預設方法:
public class DefaultMethodExample {

  public static void main(String[] args) {
    Challenger challenger = new JavaChallenger();
    challenger.doChallenge();
  }

}

class JavaChallenger implements Challenger { }

interface Challenger {

  default void doChallenge() {
    System.out.println("Challenger doing a challenge!");
  }
}
結果:
挑戰者正在挑戰!
對於預設方法,需要注意的是每個這樣的方法都需要實作。預設方法不能是靜態的。現在讓我們繼續討論抽象類別。

抽象類別的本質

抽象類別可以具有帶有實例變數的狀態。這意味著實例變數可以被使用和修改。這是一個例子:
public abstract class AbstractClassMutation {

  private String name = "challenger";

  public static void main(String[] args) {
    AbstractClassMutation abstractClassMutation = new AbstractClassImpl();
    abstractClassMutation.name = "mutated challenger";
    System.out.println(abstractClassMutation.name);
  }

}

class AbstractClassImpl extends AbstractClassMutation { }
結論:
變異挑戰者

抽象類別中的抽象方法

與介面一樣,抽象類別可以具有抽象方法。抽象方法是沒有主體的方法。與介面不同,抽象類別中的抽象方法必須明確聲明為抽象。這是一個例子:
public abstract class AbstractMethods {

  abstract void doSomething();

}
這裡嘗試聲明一個沒有實作和抽象 關鍵字的方法:
public abstract class AbstractMethods {
   void doSomethingElse();
}
不幸的是,它會導致編譯錯誤:
缺少方法體,或宣告為抽象

何時使用抽象類別

當您需要實作可變狀態時,建議使用抽象類別。例如,Java Collections Framework 包含一個使用變數狀態的AbstractList類別。在不需要維護類別狀態的情況下,通常最好使用介面。

抽象類別和介面之間的區別

從物件導向程式設計的角度來看,介面和抽象類別之間的主要區別在於介面不能具有狀態,而抽象類別可以具有具有實例變數的狀態。另一個關鍵區別是類別可以實作多個接口,但它們只能擴展一個抽象類別。此解決方案基於以下事實:多重繼承(擴展多個類別)可能導致程式碼死鎖。Java 語言的開發人員決定避免這種情況。另一個區別是,介面可以由類別實現,也可以由介面擴展,但類別只能擴展。需要注意的是,lambda 表達式只能與函數式介面(即只有一種方法的介面)一起使用,而只有一種抽象方法的抽象類別不能使用 lambda 表達式。以下是抽象類別和介面之間的更多區別。 介面:
  • 只能有最終靜態變數。介面永遠不能改變它自己的狀態。
  • 一個類別可以實作多個介面。
  • 可以使用implements關鍵字來實現。一個介面可以擴充另一個介面。
  • 方法只能使用靜態最終欄位、參數或局部變數。
  • Java 中只有函數式介面才能使用 lambda 函數。
  • 不能有構造函數。
  • 可能有抽象方法。
  • 可以有預設方法和靜態方法(Java 8 中引入)。
  • 可以擁有帶有實作的私有方法(在 Java 9 中引入)。
抽象類別:
  • 可以有任何實例或靜態變量,可變或不可變。
  • 一個類別只能擴充一個抽象類別。
  • 可能有可變字段、參數或局部變數的實例。
  • 只有一個抽象方法的抽象類別不能使用 lambda 表達式。
  • 可能有一個構造函數。
  • 可以有任何方法。
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION