java面試分享-------接口和抽象類的含義和區別
先來看定義:
接口
是對行爲的抽象,它是抽象方法的集合,利用接口可以達到API定義和實現分離的目的。接口,不能實例化;不能包含任何非常量成員,任何feld都是隱含着public static final的意義;同時,沒有非靜態方法實現,也就是說要麼是抽象方法,要麼是靜態方法。
抽象類
是不能實例化的類,用abstract關鍵字修飾class,其目的主要是代碼重用。除了不能實例化,形式上和一般的Java類並沒有太大區別,可以有一個或者多個抽象方法,也可以沒有抽象方法。抽象類大多用於抽取相關Java類的共用方法實現或者是共同成員變量,然後通過繼承的方式達到代碼複用的目的。
具體來說,接口就是 行爲的抽象,就是你能做什麼的抽象,而抽象類是你是由什麼做的。
比如 ArrayList
,源碼裏面是這樣的:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
其中,繼承了一個抽象類和四個接口。
抽象類要怎麼寫呢?來看看 ArrayList
的繼承的抽象類 AbstractList
:
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
protected AbstractList() {
}
public boolean add(E e) {
add(size(), e);
return true;
}
abstract public E get(int index);
...
}
我們不難發現,抽象類中有 具體的方法實現、抽象函數等,
我們再來看看ArrayLis
t繼承的接口List:
public interface List<E> extends Collection<E> {
int size();
boolean isEmpty();
boolean contains(Object o);
Iterator<E> iterator();
Object[] toArray();
<T> T[] toArray(T[] a);
default void sort(Comparator<? super E> c) {
Object[] a = this.toArray();
Arrays.sort(a, (Comparator) c);
ListIterator<E> i = this.listIterator();
for (Object e : a) {
i.next();
i.set((E) e);
}
}
可以看出,接口裏面所有的方法都沒有函數體(除了java8後來增加的 default 方法),再來看 ArrayList的源碼:
public boolean add(E e) {
...
}
public E get(int index) {
...
}
public int size() {
...
}
public <T> T[] toArray(T[] a) {
...
}
我們很容易看見,ArrayList
實現了繼承的抽象類和接口的方法,那麼什麼該實現什麼不該實現呢?有下面的定義:
- 一個普通類(非接口)繼承了抽象類時,必須要實現其所有抽象方法,而不一定要重寫其非抽象方法,而它非抽象方法均繼承。
- 一個接口繼承了抽象類時,不用繼承所有的抽象方法。
- 一個抽象類繼承了另一個抽象類時,也不用實現其所有的抽象方法。
- 一個普通類繼承了接口時,必須重寫其所有抽象方法。
- 一個接口繼承了另一個接口時,繼承者就擁有兩個接口的並集。
- 一個抽象類繼承了接口時,不必實現其所有抽象方法。只有繼承了抽象類的類才需要實現抽象類中的所有抽象方法和接口中未被抽象類重寫的所有抽象方法
綜上,我們給出瞭如下區別
相同點:
(1)都不能被實例化
(2)接口的實現類或抽象類的子類都只有實現了接口或抽象類中的方法後才能實例化。
不同點:
(1)接口只有定義,不能有方法的實現,java 1.8中可以定義default方法體,而抽象類可以有定義與實現,方法可在抽象類中實現。
(2)實現接口的關鍵字爲implements,繼承抽象類的關鍵字爲extends。一個類可以實現多個接口,但一個類只能繼承一個抽象類。所以,使用接口可以間接地實現多重繼承。
(3)接口強調特定功能的實現,而抽象類強調所屬關係。
(4)接口成員變量默認爲public static final,必須賦初值,不能被修改;其所有的成員方法都是public、abstract的。抽象類中成員變量默認default,可在子類中被重新定義,也可被重新賦值;抽象方法被abstract修飾,不能被private、static、synchronized和native等修飾,必須以分號結尾,不帶花括號。
(5)接口被用於常用的功能,便於日後維護和添加刪除,而抽象類更傾向於充當公共類的角色,不適用於日後重新對立面的代碼修改。功能需要累積時用抽象類,不需要累積時用接口。
接口vs抽象類vs類
- 支持多重繼承:接口支持;抽象類不支持;類不支持;
- 支持抽象函數:接口語義上支持;抽象類支持;類不支持;
- 允許函數實現:接口不允許;抽象類支持;類允許;
- 允許實例化:接口不允許;抽象類不允許;類允許;
- 允許部分函數實現:接口不允許;抽象類允許;類不允許。
- 定義的內容:接口中只能包括public函數以及public static fnal常量;抽象類與類均無任何限制。
- 使用時機:當想要支持多重繼承,或是爲了定義一種類型請使用接口;當打算提供帶有部分實現的“模板”類,而將一些功能需要延遲實現請使用抽象類;當你打算提供完整的具體實現請使用類。