日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java学习系列之抽象类和接口的区别和联系

發布時間:2024/3/13 java 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java学习系列之抽象类和接口的区别和联系 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

導讀 本文首先分別介紹抽象類和接口的基礎概念、特征和應用場景,然后介紹其區別和聯系。

1 抽象類

1.1 定義抽象類

在Java中被abstract關鍵字修飾的類稱為抽象類,被abstract關鍵字修飾的方法稱為抽象方法,抽象方法只有方法的聲明,沒有方法體。

public abstract class AbstractPlayer {public abstract void eat(); }

關于抽象類的命名,《阿里的 Java 開發手冊》上有強調,“抽象類命名要使用 Abstract 或 Base 開頭”。

1.2 抽象類的特征

1 抽象類是用來捕捉子類的通用特性的,它不能被實例化,只能被繼承。如果嘗試通過new關鍵字實例化的話,編譯器會報錯,提示類是抽象的不能被實例化:

抽象類的子類通過extends關鍵詞來繼承抽象類:

public class BasketballPlayer extends AbstractPlayer { }

但是要注意:使用extends只能單繼承,同事繼承多個抽象類編譯器會報錯class cannot extend multiple classes。

2 包含抽象方法的一定是抽象類,但抽象類不一定含有抽象方法
當我們嘗試在一個普通類中定義抽象方法的時候,編譯器會有兩處錯誤提示。第一處在類級別上,提示“這個類必須通過 abstract 關鍵字定義”,見下圖。

第二處在嘗試定義 abstract 的方法上,提示“抽象方法所在的類不是抽象的”,見下圖。

而在抽象類中,可以定義普通方法,如下面的代碼所示:

public abstract class AbstractPlayer {public abstract void play();public void sayName(String name) {System.out.println("My name is " + name);} }

3 抽象類中的抽象方法只能為public或protected(如果是private則不能被子類繼承),默認為public。

4 抽象類中的抽象方法只有方法體,沒有具體實現,但可以有普通方法。

5 如果一個子類實現了父類(抽象類)的所有抽象方法,那么該子類可以不必是抽象類,否則就是抽象類

public abstract class AbstractPlayer {public abstract void eat(); }/*** AbstractFootballPlayer沒有實現AbstractPlayer的抽象方法play,因此他也只能是抽象類*/ public abstract class AbstractFootballPlayer extends AbstractPlayer{public abstract void run(); }/*** FootballPlayer需要實現AbstractFootballPlayer中的所有抽象方法,否則它仍然是一個抽象類*/ public class FootballPlayer extends AbstractFootballPlayer{@Overridepublic void run() {}@Overridevoid play() {} }

6 抽象類可以包含屬性、方法、構造方法等,但是構造方法不能用于實例化,主要用途是被子類調用。

public abstract class AbstractPlayer {// 可以定義構造函數AbstractPlayer(int count) {this.count = count;}// 可以定義靜態常量public static final int MAX_COUNT = 5;// 可以定義變量public int count;// 可以定義普通函數public void sayName(String name) {System.out.println("My name is " + name);}// 可以定義抽象方法abstract void play(); }// 如果抽象類定義了構造函數,其子類就需要調用抽象類的構造函數 public class BasketballPlayer extends AbstractPlayer{BasketballPlayer(int count) {super(count);}@Overridevoid play() {System.out.println(this.MAX_COUNT);System.out.println(this.count);}public static void main(String[] args) {AbstractPlayer player = new BasketballPlayer(11);player.sayName("Tom");player.play();} }

1.3 抽象類的應用場景

場景1:希望一些通用的功能能夠被多個子類復用

比如說,AbstractPlayer 抽象類中有一個普通的方法 sleep(),表明所有運動員都需要休息,那么這個方法就可以被子類復用。

abstract class AbstractPlayer {public void sleep() {System.out.println("運動員也要休息而不是挑戰極限");} }// 子類 BasketballPlayer 繼承了 AbstractPlayer 類,就擁有了 sleep() 方法。 class BasketballPlayer extends AbstractPlayer { }// 子類 FootballPlayer 繼承了 AbstractPlayer 類,也就擁有了 sleep() 方法。 class FootballPlayer extends AbstractPlayer { }// BasketballPlayer 的對象可以直接調用父類的 sleep() 方法 BasketballPlayer basketballPlayer = new BasketballPlayer(); basketballPlayer.sleep();// FootballPlayer 的對象可以直接調用父類的 sleep() 方法 FootballPlayer footballPlayer = new FootballPlayer(); footballPlayer.sleep();

場景2:希望所有子類能夠自行實現在抽象類中定義的抽象方法

比如說,AbstractPlayer 抽象類中定義了一個抽象方法 play(),表明所有運動員都可以從事某項運動,但需要對應子類去擴展實現,表明籃球運動員打籃球,足球運動員踢足球。

abstract class AbstractPlayer {abstract void play(); }public class BasketballPlayer extends AbstractPlayer {@Overridevoid play() {System.out.println("我是張伯倫,我籃球場上得過 100 分,");} }public class FootballPlayer extends AbstractPlayer {@Overridevoid play() {System.out.println("我是C羅,我能接住任意高度的頭球");} }

2 接口

2.1 定義接口

Java中的接口使用interface關鍵字修飾,接口是方法的集合。

public interface Runnable {public abstract void run(); }

2.2 接口的特征

public interface Electronic {// 常量String LED = "LED";// 抽象方法int getElectricityUse();// 靜態方法static boolean isEnergyEfficient(String electtronicType) {return electtronicType.equals(LED);}// 默認方法default void printDescription() {System.out.println("電子");} }

1 接口中可以含有 變量和方法,但是要注意,接口中定義的變量會在編譯的時候自動加上 public static final 修飾符(并且只能是public static final變量,用private修飾會報編譯錯誤),而方法會被隱式地指定為public abstract方法且只能是public abstract方法(用其他關鍵字,比如private、protected、static、 final等修飾會報編譯錯誤)。

2 Java8以前接口中所有的方法不能有具體的實現,也就是說,接口中的方法必須都是抽象方法。
從這里可以隱約看出接口和抽象類的區別,接口是一種極度抽象的類型,它比抽象類更加“抽象”,并且一般情況下不在接口中定義變量

3 從 Java 8 開始,接口中允許有靜態方法,比如上例中的 isEnergyEfficient() 方法。
靜態方法無法由(實現了該接口的)類的對象調用,它只能通過接口名來調用,比如說 Electronic.isEnergyEfficient(“LED”)。

4 從 Java8 開始,接口中允許定義default方法,比如上例中的 printDescription() 方法。
default方法提供了默認的實現,實現該接口的子類可以不修改default方法直接使用模式實現,也可以override。

public class Computer implements Electronic{// 抽象方法必須在子類中實現@Overridepublic int getElectricityUse() {return 0;}// 可以選擇覆寫該default方法,不覆寫也不會報錯@Overridepublic void printDescription() {System.out.println("計算機");}public static void main(String[] args) {Computer computer = new Computer();computer.printDescription();} }

5 接口不允許直接實例化,否則編譯器會報錯:

6 接口可以是空的,既可以不定義變量,也可以不定義方法。最典型的例子就是 Serializable 接口,Serializable 接口用來為序列化的具體實現提供一個標記,也就是說,只要某個類實現了 Serializable 接口,那么它就可以用來序列化了。

public interface Serializable { }

7 接口支持多繼承,即一個接口可以extends多個接口,間接的解決了Java中類的單繼承問題。

2.3 接口的應用場景

場景1 作為標記,使某些實現類具有我們想要的功能

比如說,實現了 Cloneable 接口的類具有拷貝的功能,實現了 Comparable 或者 Comparator 的類具有比較功能。Cloneable 和 Serializable 一樣,都屬于標記型接口,它們內部都是空的。實現了 Cloneable 接口的類可以使用 Object.clone() 方法,否則會拋出 CloneNotSupportedException。

public class CloneableTest implements Cloneable {@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}public static void main(String[] args) throws CloneNotSupportedException {CloneableTest c1 = new CloneableTest();CloneableTest c2 = (CloneableTest) c1.clone();} }

運行后沒有報錯。現在把 implements Cloneable 去掉。

public class CloneableTest {@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}public static void main(String[] args) throws CloneNotSupportedException {CloneableTest c1 = new CloneableTest();CloneableTest c2 = (CloneableTest) c1.clone();} }

運行后拋出 CloneNotSupportedException:

Exception in thread "main" java.lang.CloneNotSupportedException: com.cmower.baeldung.interface1.CloneableTestat java.base/java.lang.Object.clone(Native Method)at com.cmower.baeldung.interface1.CloneableTest.clone(CloneableTest.java:6)at com.cmower.baeldung.interface1.CloneableTest.main(CloneableTest.java:11)

場景2 借助接口實現多重繼承

如果有兩個類共同繼承(extends)一個父類,那么父類的方法就會被兩個子類重寫。然后,如果有一個新類同時繼承了這兩個子類,那么在調用重寫方法的時候,編譯器就不能識別要調用哪個類的方法了。這也正是著名的菱形問題,見下圖。


而借助接口可以達到多重繼承的目的。

場景3 實現多態

使用抽象類和接口都可以實現多態,下面舉個栗子:

public interface Shape {String name(); }// Circle 類實現了 Shape 接口,并重寫了 name() 方法 public class Circle implements Shape {@Overridepublic String name() {return "圓";} }// Square 類也實現了 Shape 接口,并重寫了 name() 方法 public class Square implements Shape {@Overridepublic String name() {return "正方形";} }// 調用看看效果 List<Shape> shapes = new ArrayList<>(); Shape circleShape = new Circle(); Shape squareShape = new Square();shapes.add(circleShape); shapes.add(squareShape);for (Shape shape : shapes) {System.out.println(shape.name()); }// 輸出結果為: // 圓 // 正方形

這就實現了多態,變量 circleShape、squareShape 的引用類型都是 Shape,但執行 shape.name() 方法的時候,Java 虛擬機知道該去調用 Circle 的 name() 方法還是 Square 的 name() 方法。

場景4 接口在設計模式中的應用

在使用接口的時候,經常會用到三種模式,分別是策略模式、適配器模式和工廠模式。

策略模式

策略模式的思想是,針對一組算法,將每一種算法封裝到具有共同接口的實現類中,接口的設計者可以在不影響調用者的情況下對算法做出改變。示例如下:

// 接口:教練 interface Coach {// 方法:防守void defend(); }// 何塞·穆里尼奧 class Hesai implements Coach {@Overridepublic void defend() {System.out.println("防守贏得冠軍");} }// 德普·瓜迪奧拉 class Guatu implements Coach {@Overridepublic void defend() {System.out.println("進攻就是最好的防守");} }public class Demo {// 參數為接口public static void defend(Coach coach) {coach.defend();}public static void main(String[] args) {// 為同一個方法傳遞不同的對象defend(new Hesai());defend(new Guatu());} }

適配器模式

適配器模式的思想是,針對調用者的需求對原有的接口進行轉接。生活當中最常見的適配器就是HDMI(英語:High Definition Multimedia Interface,中文:高清多媒體接口)線,可以同時發送音頻和視頻信號。適配器模式的示例如下:

interface Coach {void defend();void attack(); }// 抽象類實現接口,并置空方法 abstract class AdapterCoach implements Coach {public void defend() {};public void attack() {}; }// 新類繼承適配器 class Hesai extends AdapterCoach {public void defend() {System.out.println("防守贏得冠軍");} }public class Demo {public static void main(String[] args) {Coach coach = new Hesai();coach.defend();} }

工廠模式

所謂的工廠模式理解起來也不難,就是什么工廠生產什么,比如說寶馬工廠生產寶馬,奔馳工廠生產奔馳,A 級學院畢業 A 級教練,C 級學院畢業 C 級教練。示例如下:

// 教練 interface Coach {void command(); }// 教練學院 interface CoachFactory {Coach createCoach(); }// A級教練 class ACoach implements Coach {@Overridepublic void command() {System.out.println("我是A級證書教練");}}// A級教練學院 class ACoachFactory implements CoachFactory {@Overridepublic Coach createCoach() {return new ACoach();}}// C級教練 class CCoach implements Coach {@Overridepublic void command() {System.out.println("我是C級證書教練");}}// C級教練學院 class CCoachFactory implements CoachFactory {@Overridepublic Coach createCoach() {return new CCoach();}}public class Demo {public static void create(CoachFactory factory) {factory.createCoach().command();}public static void main(String[] args) {// 對于一支球隊來說,需要什么樣的教練就去找什么樣的學院// 學院會介紹球隊對應水平的教練。create(new ACoachFactory());create(new CCoachFactory());} }

3 抽象類和接口的共同點和區別

3.1 共同點

  • 都不能被實例化
  • 都可以包含抽象方法
  • 都可以有默認實現的方法(Java 8 可以用 default 關鍵在接口中定義默認方法)

3.2 區別

  • 接口主要用于對類的行為進行約束,你實現了某個接口就具有了對應的行為。抽象類主要用于代碼復用,強調的是所屬關系(比如說我們抽象了一個發送短信的抽象類)。
  • 一個類只能繼承一個類,但是可以實現多個接口(如果想實現多繼承就用接口)。
  • 接口中的成員變量只能是 public static final 類型的,不能被修改且必須有初始值,而抽象類的成員變量默認 default,可在子類中被重新定義,也可被重新賦值。

4 抽象類和接口的應用場景

1、如果你擁有一些方法并且想讓它們中的一些有默認實現,那么使用抽象類吧。
2、如果你想實現多重繼承,那么你必須使用接口。由于Java不支持多繼承,子類不能夠繼承多個類,但可以實現多個接口。因此你就可以使用接口來解決它。
3、如果基本功能在不斷改變,那么就需要使用抽象類。如果不斷改變基本功能并且使用接口,那么就需要改變所有實現了該接口的類。

本文參考博客

  • JavaGuide-接口和抽象類有什么共同點和區別?
  • Java抽象類,看這一篇就夠了,豁然開朗
  • Java接口,看這一篇就夠了,簡單易懂
  • Java 抽象類和接口的區別,看這一篇就夠了

總結

以上是生活随笔為你收集整理的Java学习系列之抽象类和接口的区别和联系的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。