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

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

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

JAVA中的抽象類與接口

在Java中什么時(shí)候應(yīng)該選擇抽象類而不是接口?接受挑戰(zhàn)吧!了解這些Java語(yǔ)言元素之間的區(qū)別以及如何在你的程序中使用它們。

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

  • 接口是一種代碼契約,必須由一個(gè)具體的類來(lái)實(shí)現(xiàn)。
  • 抽象類與普通類相似,不同的是它們可以包括抽象方法,也就是沒(méi)有主體的方法。抽象類不能被實(shí)例化。

許多開(kāi)發(fā)者認(rèn)為接口和抽象類是相似的,但它們實(shí)際上是完全不同的。讓我們來(lái)探討一下它們之間的主要區(qū)別。

接口的本質(zhì)

從本質(zhì)上講,接口是一個(gè)契約,所以它依賴于一個(gè)實(shí)現(xiàn)來(lái)達(dá)到其目的。一個(gè)接口永遠(yuǎn)不可能有狀態(tài),所以它不能使用可變的實(shí)例變量。一個(gè)接口只能使用最終變量。

何時(shí)使用接口

接口對(duì)于解耦代碼和實(shí)現(xiàn)多態(tài)性非常有用。我們可以在JDK中看到一個(gè)例子,就是List 接口:

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

正如你可能注意到的,這段代碼很短,而且描述性很強(qiáng)。我們可以很容易地看到方法的簽名,我們將用一個(gè)具體的類來(lái)實(shí)現(xiàn)接口中的方法。

List 接口包含一個(gè)契約,可以由ArrayList,Vector,LinkedList, 和其他類來(lái)實(shí)現(xiàn)。

為了使用多態(tài)性,我們可以簡(jiǎn)單地用List 來(lái)聲明我們的變量類型,然后選擇任何一個(gè)可用的實(shí)例化。這里有一個(gè)例子:

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

下面是這段代碼的輸出:

class java.util.ArrayListclass java.util.LinkedList復(fù)制代碼

在這種情況下,ArrayList,LinkedList, 和Vector 的實(shí)現(xiàn)方法都是不同的,這就是使用接口的一個(gè)很好的場(chǎng)景。如果你注意到許多類都屬于一個(gè)父類,其方法動(dòng)作相同,但行為不同,那么使用接口是個(gè)好主意。

接下來(lái),讓我們來(lái)看看我們可以用接口做的幾件事。

重寫(xiě)一個(gè)接口方法

記住,接口是一種必須由具體類來(lái)實(shí)現(xiàn)的契約。接口方法是隱含的抽象的,也需要一個(gè)具體類的實(shí)現(xiàn)。

這里有一個(gè)例子:

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

下面是這段代碼的輸出:

Challenge done!復(fù)制代碼

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

常量變量

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

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

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

如果我們?cè)噲D改變Challenger 接口中的變量,例如,像這樣:

Challenger.number = 8;Challenger.name = "Another Challenger";復(fù)制代碼

我們會(huì)觸發(fā)一個(gè)編譯錯(cuò)誤,像這樣:

Cannot assign a value to final variable 'number'Cannot assign a value to final variable 'name'復(fù)制代碼

缺省方法

當(dāng)默認(rèn)方法在Java 8中被引入時(shí),一些開(kāi)發(fā)者認(rèn)為它們會(huì)和抽象類一樣。然而這并不正確,因?yàn)榻涌诓荒苡袪顟B(tài)。

默認(rèn)方法可以有一個(gè)實(shí)現(xiàn),而抽象方法則不能。默認(rèn)方法是lambdas和流的偉大創(chuàng)新的結(jié)果,但我們應(yīng)該謹(jǐn)慎使用它們。

JDK中使用默認(rèn)方法的一個(gè)方法是forEach() ,它是Iterable 接口的一部分。我們可以簡(jiǎn)單地重用forEach 方法,而不是將代碼復(fù)制到每個(gè)Iterable 的實(shí)現(xiàn)中:

default void forEach(Consumer<? super T> action) {   // Code implementation here…復(fù)制代碼

任何Iterable 實(shí)現(xiàn)都可以使用forEach() 方法,而不需要新的方法實(shí)現(xiàn)。然后,我們可以用一個(gè)默認(rèn)方法來(lái)重用代碼。

讓我們來(lái)創(chuàng)建我們自己的默認(rèn)方法:

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

下面是輸出結(jié)果:

Challenger doing a challenge!復(fù)制代碼

關(guān)于默認(rèn)方法,需要注意的是,每個(gè)默認(rèn)方法都需要一個(gè)實(shí)現(xiàn)。默認(rèn)方法不能是靜態(tài)的。

現(xiàn)在,讓我們繼續(xù)討論抽象類。

抽象類的本質(zhì)

抽象類可以有實(shí)例變量的狀態(tài)。這意味著一個(gè)實(shí)例變量可以被使用和變異。這里有一個(gè)例子:

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 { }復(fù)制代碼

下面是輸出結(jié)果:

mutated challenger復(fù)制代碼

抽象類中的抽象方法

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

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

試圖聲明一個(gè)沒(méi)有實(shí)現(xiàn)的方法,而且沒(méi)有abstract 關(guān)鍵字,像這樣:

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

導(dǎo)致了一個(gè)編譯錯(cuò)誤,像這樣:

Missing method body, or declare abstract復(fù)制代碼

什么時(shí)候使用抽象類

當(dāng)你需要實(shí)現(xiàn)可改變狀態(tài)時(shí),使用抽象類是一個(gè)好主意。作為一個(gè)例子,Java集合框架包括AbstractList類,它使用變量的狀態(tài)。

在你不需要維護(hù)類的狀態(tài)的情況下,通常使用一個(gè)接口更好。

實(shí)踐中的抽象類

設(shè)計(jì)模式中的模板方法是使用抽象類的好例子。模板方法模式在具體方法中操作實(shí)例變量。

抽象類和接口的區(qū)別

從面向?qū)ο缶幊痰慕嵌葋?lái)看,接口和抽象類的主要區(qū)別是,接口不能有狀態(tài),而抽象類可以用實(shí)例變量來(lái)有狀態(tài)。

另一個(gè)關(guān)鍵區(qū)別是,類可以實(shí)現(xiàn)一個(gè)以上的接口,但它們只能擴(kuò)展一個(gè)抽象類。這是一個(gè)基于多重繼承(擴(kuò)展一個(gè)以上的類)會(huì)導(dǎo)致代碼死鎖的設(shè)計(jì)決定。Java的工程師們決定要避免這種情況。

另一個(gè)區(qū)別是,接口可以被類實(shí)現(xiàn),也可以被接口擴(kuò)展,但類只能被擴(kuò)展。

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

接受Java代碼挑戰(zhàn)吧!

讓我們通過(guò)一個(gè)Java代碼挑戰(zhàn)來(lái)探索接口和抽象類的主要區(qū)別。我們?cè)谙旅嫣峁┝舜a挑戰(zhàn),你也可以用視頻的形式觀看抽象類與接口的挑戰(zhàn)。

在下面的代碼中,同時(shí)聲明了一個(gè)接口和一個(gè)抽象類,而且代碼中還使用了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;}復(fù)制代碼

你認(rèn)為當(dāng)我們運(yùn)行這段代碼時(shí),會(huì)發(fā)生什么?請(qǐng)從下列選項(xiàng)中選擇一個(gè)。

選項(xiàng)A

     Compilation error at line 4復(fù)制代碼

選項(xiàng)B

          Graw!!! 0     Nemesis raids: 23     Stars!!!     Nemesis shoots: 23 and raids:1復(fù)制代碼

選項(xiàng)C

          Nemesis raids: 0     Stars!!!     Graw!!! 0     Nemesis shoots: 23 and raids: 1復(fù)制代碼

選項(xiàng)D

          Nemesis raids: 0     Stars!!!     Graw!!! 1     Nemesis shoots: 23 and raids:1復(fù)制代碼

選項(xiàng)E

     Compilation error at line 6復(fù)制代碼

Java代碼挑戰(zhàn)視頻

你為這個(gè)挑戰(zhàn)選擇了正確的輸出嗎?請(qǐng)觀看視頻或繼續(xù)閱讀以了解答案。

了解接口和抽象類及方法

這個(gè)Java代碼挑戰(zhàn)展示了許多關(guān)于接口、抽象方法等的重要概念。逐行瀏覽代碼會(huì)讓我們了解到輸出中發(fā)生的很多事情。

代碼挑戰(zhàn)的第一行包括Zombie 接口的lambda表達(dá)式。請(qǐng)注意,在這個(gè)lambda中,我們正在增加一個(gè)靜態(tài)字段。實(shí)例字段在這里也可以使用,但在lambda之外聲明的局部變量就不行了。因此,到目前為止,這段代碼可以正常編譯。還要注意的是,lambda表達(dá)式還沒(méi)有執(zhí)行,所以nemesisRaids 字段還不會(huì)被遞增。

在這一點(diǎn)上,我們將打印nemesisRaids 字段,它沒(méi)有被增加,因?yàn)?lambda;表達(dá)式還沒(méi)有被調(diào)用,只是被聲明。因此,這一行的輸出將是:

Nemesis raids: 0復(fù)制代碼

這個(gè)Java代碼挑戰(zhàn)中另一個(gè)有趣的概念是,我們正在使用一個(gè)匿名的內(nèi)層類。這基本上意味著任何將實(shí)現(xiàn)Nemesis 抽象類的方法的類。我們并沒(méi)有真正實(shí)例化Nemesis 抽象類,因?yàn)樗鼘?shí)際上是一個(gè)匿名的類。還要注意的是,第一個(gè)具體的類在擴(kuò)展它們的時(shí)候總是有義務(wù)實(shí)現(xiàn)抽象的方法。

在Zombie 接口里面,我們用一個(gè)lambda表達(dá)式聲明了zombie static Zombie 接口。因此,當(dāng)我們調(diào)用zombie shoot 方法時(shí),我們會(huì)打印以下內(nèi)容:

Stars!!!復(fù)制代碼

下一行代碼調(diào)用了我們?cè)陂_(kāi)始時(shí)創(chuàng)建的lambda表達(dá)式。因此,nemesisRaids 這個(gè)變量將被遞增。然而,由于我們使用的是后增量運(yùn)算符,它將只在這條代碼語(yǔ)句之后被增量。接下來(lái)的輸出將是:

Graw!!! 0 復(fù)制代碼

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

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

Nemesis shoots: 23 and raids: 1復(fù)制代碼

綜上所述,正確的輸出是選項(xiàng)C:

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

分享到:
標(biāo)簽:抽象類
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定