在Java中,一個沒有方法體的方法應該定義為抽象方法,而如果一個類中含有抽象方法,則該類必須定義為一個抽象類。接口是功能的集合,同樣可看做是一種特殊的數據類型,是比抽象類更為抽象的類,接口只描述所應該具備的方法,并沒有具體實現,具體的實現由接口的實現類(相當于接口的子類)來完成。這樣將功能的定義與實現分離,優化了程序設計
~
本篇主要記錄內容包括:抽象類、接口、抽象類和接口的差異、接口的新特性——默認方法和靜態方法、 標記接口等相關內容
- 上一篇內容:Java基礎:Java面向對象
- 下一篇內容:Java基礎:Java異常機制
- 更多知識學習:全網最全的 Java 技術棧內容梳理(持續更新中)
文章目錄
- 一、抽象類
- 1、抽象類概述
- 2、抽象類特點
- 二、接口
- 1、接口概述
- 2、接口特點
- 三、抽象類和接口的差異
- 1、相同點
- 2、不同點
- 四、接口的新特性——默認方法和靜態方法(jdk8之后)
- 1、默認方法
- 2、靜態方法
- 3、注意事項
- 五、標記接口(標簽接口)
一、抽象類
1、抽象類概述
在Java中,一個沒有方法體的方法應該定義為抽象方法,而如果一個類中含有抽象方法,則該類必須定義為一個抽象類
抽象類通常作為一個框架,把子類將實現的抽象方法組織起來,簡化或限制子類的設計
抽象方法定義的格式:public abstract 返回值類型 方法名(參數);
抽象類定義的格式:abstract class 類名 {}
2、抽象類特點
- 抽象類和抽象方法都需要被
abstract
修飾。抽象方法一定要定義在抽象類中; static、final、private
不能與abstract
同時出現;- 抽象類不一定有抽象方法,但是含有抽象方法的類必須是抽象類;
- 構造方法,類方法(用
static
修飾的方法),不能聲明為抽象方法; - 抽象類本身不能實例化(但是多態機制可以用子類實例化),不可以直接創建對象,原因:調用抽象方法沒有意義;
- 只有覆蓋了抽象類中所有的抽象方法后,其子類才可以創建對象。否則該子類還是一個抽象類;
- 抽象類只定義了類的部分行為(包含具體行為), 這些行為是 子類共有的,其它行為由子類實現的抽象方法提供
- 抽象類的成員變量:既可以變量,又可以是常量;
- 抽象類的構造方法:用于父類數據的初始化
- 子類繼承抽象類時,構造方法不會被覆蓋。 而且,在實例化子類對象時首先調用的是抽象類中的構造方法再調用子類中的
二、接口
1、接口概述
接口是功能的集合,同樣可看做是一種特殊的數據類型,是比抽象類更為抽象的類,接口只描述所應該具備的方法,并沒有具體實現,具體的實現由接口的實現類(相當于接口的子類)來完成。這樣將功能的定義與實現分離,優化了程序設計
接口的聲明:使用interface
代替了原來的class
其他步驟與定義類相同
接口的實現類:使用implements
關鍵字實現接口,當類實現接口的時候,類要實現接口中所有的方法。否則,類必須聲明為抽象類
2、接口特點
接口中方法的修飾符:public abstract(默認不寫)
- 接口是隱式抽象的,當聲明一個接口的時候,不必使用 abstract 關鍵字;
- 接口中定義的變量,固定的修飾符為
public static final
(默認不寫,也可以選擇性寫)所以接口中的變量也稱之為常量,其值不能改變; - 接口中定義的方法,固定的修飾符為
public abstract
(默認不寫,也可以選擇性寫),不能指定其它的訪問控制修飾符; - 接口中的成員變量必須顯式初始化;
- 接口中成員方法定義的固定格式:
public abstract 返回值類型 方法名字(參數列表)
; - 子類必須覆蓋接口中所有的抽象方法后,子類才可以實例化,否則子類是一個抽象類;
- 接口的重載條件和普通類沒有任何區別,只是重載的方法沒有方法體;
- 接口中的方法也可以覆蓋(
Override
),但沒有實際的意義,因為接口中不提供方法的實現; - 一個接口能繼承另一個接口,和類之間的繼承方式比較相似。接口的繼承使用extends關鍵字,子接口繼承父接口的方法。在Java中,類的多繼承是不合法,但接口允許多繼承。
三、抽象類和接口的差異
1、相同點
- 抽象類和接口都位于繼承的頂端,用于被其他類實現或繼承.
- 都不能直接實例化對象.
- 都包含抽象方法,其子類都必須覆蓋這些抽象方法.
2、不同點
- 抽象類中的方法可以有方法體,就是能實現方法的具體功能,但是接口中的方法不行
- 抽象類中的成員變量可以是各種類型的,而接口中的成員變量只能是
public static final
類型的 - 接口中不能含有靜態代碼塊以及靜態方法(用
static
修飾的方法),而抽象類是可以有靜態代碼塊和靜態方法; - 一個類只能繼承一個抽象類,而一個類卻可以實現多個接口;
總的來說,優先選用接口,盡量少用抽象類,需要定義子類的行為,又要為子類提供共性功能時才選用抽象類
四、接口的新特性——默認方法和靜態方法(jdk8之后)
1、默認方法
- 可以在不影響已有類的情況下,修改接口;
- 可以有方法實現;
- 父類中的默認方法可以被子接口繼承;
- 子接口可以覆蓋父接口中的默認方法,甚至還可以把父接口中的默認方法覆蓋為抽象方法;
- 實現接口后,因為默認方法不是抽象方法,所以可以不重寫,但是如果開發需要,也可以重寫;
- 默認方法使用
default
關鍵字,只能通過接口實現類的對象來調用; - 注意:默認方法的訪問權限也是默認
public
2、靜態方法
- 可以有方法實現;
- 可以直接通過接口名來訪問;
- 靜態方法沒有方法覆蓋,因為靜態方法沒有運行時多態
interface Test{//這個是默認方法default String get(String aa){System.out.println("我是jdk1.8默認實現方法...");return "";} //這個是靜態方法 static void staticmethod(){System.out.println("我是靜態方法");}
}
3、注意事項
- 接口默認方法、靜態方法可以有多個;
- 默認方法通過實例調用,靜態方法通過接口名調用;
default
默認方法關鍵字只能用在接口中;- 默認方法可以被繼承,如果繼承了多個接口,多個接口都定義了多個同樣的默認方法,實現類需要重寫默認方法不然會報錯;
- 靜態方法不能被繼承及覆蓋,所以只被具體所在的接口調用
五、標記接口(標簽接口)
Java中的標記接口(Marker Interface),又稱標簽接口(Tag Interface),是沒有任何方法和屬性的接口,它不對實現它的類有任何語義上的要求,它僅僅表明實現它的類屬于一個特定的類型
在Java中很容易找到標記接口的例子,比如JDK中
的Serialzable
接口就是一個標記接口
標記接口并不是Java語言獨有的,而是計算機科學中的一種通用的設計理念。
The tag/marker interface pattern is a design pattern in computer science, used with languages that provide run-time type information about objects. It provides a means to associate metadata with a class where the language does not have explicit support for such metadata.
具體說的就是,標記接口是計算機科學中的一種設計思路,用于給那些面向對象的編程語言描述對象。因為編程語言本身并不支持為類維護元數據,而標記接口可以用作描述類的元數據,彌補了這個功能上的缺失。對于實現了標記接口的類,我們就可以在運行時通過反射機制去獲取元數據。
以Serializable
接口為例,如果一個類實現了這個接口,則表示這個類可以被序列化。因此,我們實際上是通過了Serializable
這個接口給該類標記了【可被序列化】的元數據,打上了【可被序列化】的標簽。這也是標記/標簽接口名字的由來。
具體在Java中,標記接口主要有以下兩種目的:
- 建立一個公共的父接口:
例如,EventListener
接口,一個由幾十個其它接口擴展的Java API,當一個接口繼承了EventListener
接口,Java虛擬機(JVM)就知道該接口將要被用于一個事件的代理方案。同樣的,你可以使用一個標記接口來建立一組接口的父接口。 - 向一個類添加數據類型:
這是標記接口最初的目的,實現標記接口的類不需要定義任何接口方法(因為標記接口根本就沒有方法),但是該類通過Java的多態性可以變成一個接口類型。
java.awt.event
包中的MouseListener
接口繼承的java.util.EventListener
接口定義如下:package java.util; public interface EventListener {}
Java源碼中幾個標記接口的優秀例子:
java.io.Serializable
:未實現此接口的類將無法使其任何狀態序列化或反序列化。為保證serialVersionUID
值跨不同Java編譯器實現的一致性,序列化類必須聲明一個明確的serialVersionUID
值。java.lang.Cloneable
:表明Object.clone()
方法可以合法地對該類實例進行按字段復制。實現此接口的類應該使用公共方法重寫Object.clone
(它是受保護的)。如果在沒有實現 Cloneable接口的實例上調用Object的clone()方法,則會導致拋出CloneNotSupportedException
異常。java.util.RandomAccess
:用來表明其支持快速(通常是固定時間)隨機訪問。此接口的主要目的是允許一般的算法更改其行為,從而在將其應用到隨機或連續訪問列表時能提供良好的性能。java.rmi.Remote:Remote
接口用于標識其方法可以從非本地虛擬機上調用的接口。任何遠程對象都必須直接或間接實現此接口。只有在遠程接口(擴展java.rmi.Remote
的接口)中指定的這些方法才可遠程使用。