日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

JAVA中的抽象類與接口

在Java中什么時候應該選擇抽象類而不是接口?接受挑戰吧!了解這些Java語言元素之間的區別以及如何在你的程序中使用它們。

在Java代碼中,甚至在Java開發工具包(JDK)本身中,都有大量的抽象類和接口。每個代碼元素都有一個基本的目的:

  • 接口是一種代碼契約,必須由一個具體的類來實現。
  • 抽象類與普通類相似,不同的是它們可以包括抽象方法,也就是沒有主體的方法。抽象類不能被實例化。

許多開發者認為接口和抽象類是相似的,但它們實際上是完全不同的。讓我們來探討一下它們之間的主要區別。

接口的本質

從本質上講,接口是一個契約,所以它依賴于一個實現來達到其目的。一個接口永遠不可能有狀態,所以它不能使用可變的實例變量。一個接口只能使用最終變量。

何時使用接口

接口對于解耦代碼和實現多態性非常有用。我們可以在JDK中看到一個例子,就是List 接口:

public interface List<E> extends Collection<E> {    int size();    boolean isEmpty();    boolean add(E e);    E remove(int index);    void clear();}復制代碼

正如你可能注意到的,這段代碼很短,而且描述性很強。我們可以很容易地看到方法的簽名,我們將用一個具體的類來實現接口中的方法。

List 接口包含一個契約,可以由ArrayList,Vector,LinkedList, 和其他類來實現。

為了使用多態性,我們可以簡單地用List 來聲明我們的變量類型,然后選擇任何一個可用的實例化。這里有一個例子:

List list = new ArrayList();System.out.println(list.getClass()); List list = new LinkedList(); System.out.println(list.getClass());復制代碼

下面是這段代碼的輸出:

class java.util.ArrayListclass java.util.LinkedList復制代碼

在這種情況下,ArrayList,LinkedList, 和Vector 的實現方法都是不同的,這就是使用接口的一個很好的場景。如果你注意到許多類都屬于一個父類,其方法動作相同,但行為不同,那么使用接口是個好主意。

接下來,讓我們來看看我們可以用接口做的幾件事。

重寫一個接口方法

記住,接口是一種必須由具體類來實現的契約。接口方法是隱含的抽象的,也需要一個具體類的實現。

這里有一個例子:

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!");  }}復制代碼

下面是這段代碼的輸出:

Challenge done!復制代碼

注意這個細節,接口方法是隱式抽象的。這意味著我們不需要明確地將它們聲明為抽象的。

常量變量

另一條要記住的規則是,一個接口只能包含常量變量。因此,下面的代碼是可以的:

public class Challenger {    int number = 7;  String name = "Java Challenger";}復制代碼

注意,這兩個變量都是隱含的final 和static 。這意味著它們是常量,不依賴于一個實例,而且不能被改變。

如果我們試圖改變Challenger 接口中的變量,例如,像這樣:

Challenger.number = 8;Challenger.name = "Another Challenger";復制代碼

我們會觸發一個編譯錯誤,像這樣:

Cannot assign a value to final variable 'number'Cannot assign a value to final variable 'name'復制代碼

缺省方法

當默認方法在Java 8中被引入時,一些開發者認為它們會和抽象類一樣。然而這并不正確,因為接口不能有狀態。

默認方法可以有一個實現,而抽象方法則不能。默認方法是lambdas和流的偉大創新的結果,但我們應該謹慎使用它們。

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!");  }}復制代碼

下面是輸出結果:

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 { }復制代碼

下面是輸出結果:

mutated challenger復制代碼

抽象類中的抽象方法

就像接口一樣,抽象類可以有抽象方法。抽象方法是一個沒有主體的方法。與接口不同,抽象類中的抽象方法必須明確地聲明為抽象的。這里有一個例子:

public abstract class AbstractMethods {  abstract void doSomething();}復制代碼

試圖聲明一個沒有實現的方法,而且沒有abstract 關鍵字,像這樣:

public abstract class AbstractMethods {   void doSomethingElse();}復制代碼

導致了一個編譯錯誤,像這樣:

Missing method body, or declare abstract復制代碼

什么時候使用抽象類

當你需要實現可改變狀態時,使用抽象類是一個好主意。作為一個例子,Java集合框架包括AbstractList類,它使用變量的狀態。

在你不需要維護類的狀態的情況下,通常使用一個接口更好。

實踐中的抽象類

設計模式中的模板方法是使用抽象類的好例子。模板方法模式在具體方法中操作實例變量。

抽象類和接口的區別

從面向對象編程的角度來看,接口和抽象類的主要區別是,接口不能有狀態,而抽象類可以用實例變量來有狀態。

另一個關鍵區別是,類可以實現一個以上的接口,但它們只能擴展一個抽象類。這是一個基于多重繼承(擴展一個以上的類)會導致代碼死鎖的設計決定。Java的工程師們決定要避免這種情況。

另一個區別是,接口可以被類實現,也可以被接口擴展,但類只能被擴展。

還需要注意的是,lambda表達式只能用于功能接口(指只有一個方法的接口),而只有一個抽象方法的抽象類不能使用lambdas。

接受Java代碼挑戰吧!

讓我們通過一個Java代碼挑戰來探索接口和抽象類的主要區別。我們在下面提供了代碼挑戰,你也可以用視頻的形式觀看抽象類與接口的挑戰。

在下面的代碼中,同時聲明了一個接口和一個抽象類,而且代碼中還使用了lambdas:

public class AbstractResidentEvilInterfaceChallenge {  static int nemesisRaids = 0;  public static void main(String[] args) {    Zombie zombie = () -> System.out.println("Graw!!! " + nemesisRaids++);    System.out.println("Nemesis raids: " + nemesisRaids);    Nemesis nemesis = new Nemesis() { public void shoot() { shoots = 23; }};    Zombie.zombie.shoot();    zombie.shoot();    nemesis.shoot();    System.out.println("Nemesis shoots: " + nemesis.shoots +        " and raids: " + nemesisRaids);  }}interface Zombie {  Zombie zombie = () -> System.out.println("Stars!!!");  void shoot();}abstract class Nemesis implements Zombie {   public int shoots = 5;}復制代碼

你認為當我們運行這段代碼時,會發生什么?請從下列選項中選擇一個。

選項A

     Compilation error at line 4復制代碼

選項B

          Graw!!! 0     Nemesis raids: 23     Stars!!!     Nemesis shoots: 23 and raids:1復制代碼

選項C

          Nemesis raids: 0     Stars!!!     Graw!!! 0     Nemesis shoots: 23 and raids: 1復制代碼

選項D

          Nemesis raids: 0     Stars!!!     Graw!!! 1     Nemesis shoots: 23 and raids:1復制代碼

選項E

     Compilation error at line 6復制代碼

Java代碼挑戰視頻

你為這個挑戰選擇了正確的輸出嗎?請觀看視頻或繼續閱讀以了解答案。

了解接口和抽象類及方法

這個Java代碼挑戰展示了許多關于接口、抽象方法等的重要概念。逐行瀏覽代碼會讓我們了解到輸出中發生的很多事情。

代碼挑戰的第一行包括Zombie 接口的lambda表達式。請注意,在這個lambda中,我們正在增加一個靜態字段。實例字段在這里也可以使用,但在lambda之外聲明的局部變量就不行了。因此,到目前為止,這段代碼可以正常編譯。還要注意的是,lambda表達式還沒有執行,所以nemesisRaids 字段還不會被遞增。

在這一點上,我們將打印nemesisRaids 字段,它沒有被增加,因為λ表達式還沒有被調用,只是被聲明。因此,這一行的輸出將是:

Nemesis raids: 0復制代碼

這個Java代碼挑戰中另一個有趣的概念是,我們正在使用一個匿名的內層類。這基本上意味著任何將實現Nemesis 抽象類的方法的類。我們并沒有真正實例化Nemesis 抽象類,因為它實際上是一個匿名的類。還要注意的是,第一個具體的類在擴展它們的時候總是有義務實現抽象的方法。

在Zombie 接口里面,我們用一個lambda表達式聲明了zombie static Zombie 接口。因此,當我們調用zombie shoot 方法時,我們會打印以下內容:

Stars!!!復制代碼

下一行代碼調用了我們在開始時創建的lambda表達式。因此,nemesisRaids 這個變量將被遞增。然而,由于我們使用的是后增量運算符,它將只在這條代碼語句之后被增量。接下來的輸出將是:

Graw!!! 0 復制代碼

現在,我們將從nemesis 中調用shoot 方法,這將改變其shoots 實例變量為23 。 注意,這部分代碼展示了接口和抽象類之間的最大區別。

最后,我們打印nemesis.shoots 和nemesisRaids 的值。因此,輸出結果將是:

Nemesis shoots: 23 and raids: 1復制代碼

綜上所述,正確的輸出是選項C:

     Nemesis raids: 0     Stars!!!     Graw!!! 0     Nemesis shoots: 23 and raids: 1

分享到:
標簽:抽象類
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定