一文總結:抽象類(abstract)與介面(interface)的特點和程式碼展示

2020-08-08 21:23:09

本篇文章已同步到:https://www.bithachi.cn/posts/77de2e90.html

1. 什麼是抽象類?

隨着繼承層次中一個個新子類的定義,類變得越來越具體,而父類別則更一般,更通用。類的設計應該保證父類別和子類能夠共用特徵。有時將一個父類別設計得非常抽象,以至於它沒有具體的範例,這樣的類叫做抽象類

抽象類的出現使物件導向更加規範,比如人是一個抽象的概念,沒有具體的物件,我們應該將其抽象成一個類,它無法生成物件範例,卻擁有一些人通用的屬性和方法。有些方法人類都是一樣的行爲,比如說都是用嘴吃飯,用耳朵聽聲音等;但有些則不是,比如工作,不同的崗位人們有不同的工作行爲,那麼這些行爲的具體實現在子類完成,Person父類別只負責定義。

2. 抽象類與抽象方法的特點

  • 用abstract關鍵字來修飾一個類,這個類叫做抽象類。
  • 抽象類可以有構造器,可以使用this關鍵字,子類可以使用super關鍵字
  • 用abstract來修飾一個方法,該方法叫做抽象方法。抽象方法只能宣告,具體實現應該交給子類完成
    抽象方法:只有方法的宣告,沒有方法的實現。以分號結束:
    比如:public abstract void talk();
  • 抽象類中不一定包含抽象方法,但是含有抽象方法的類必須被宣告爲抽象類。
  • 抽象類不能被範例化。抽象類是用來被繼承的,抽象類的子類必須定義父類別的抽象方法,並提供方法體。若沒有定義全部的抽象方法,仍爲抽象類。
  • 不能用abstract修飾變數、程式碼塊、構造器;
  • 不能用abstract修飾私有(private)方法、靜態(static)方法、final的方法、final的類
  • 抽象類可以參照非抽象類的子類物件,並呼叫子類實現抽象類定義的方法和抽象類本身不是抽象的方法。
package Random_name.sgm.abstract_interface;

/**
 * @Program: JavaSE
 * @ClassName: TestMain
 * @Author: Mr.BitHachi
 * @CreateTime: 2020-08-08 12:07
 * @Version: V1.0
 * @Description:
 **/

abstract class A {
    private int aa=10;
    static final String str="可以有static屬性";
    static{
        System.out.println("可以有程式碼塊");
    }
    A(int aa){
        this.aa=aa;
    }
    public abstract void m1();
    public void m2() {
        System.out.println("A類中定義的m2方法");
        System.out.println("A類中的屬性a="+this.aa);
    }
}

class B extends A {
    private int bb=10;
    B(int aa,int bb){
        super(aa);
        this.bb=20;
    }
    public void m1() {
        System.out.println("B類中定義的m1方法");
    }
    public void m2() {
        System.out.println("B類中重寫的的m2方法");
    }

    void m3(){
        super.m2();
    }
}

class Test {
    public static void main(String args[]) {
        A a = new B(20,20);
        a.m1();
        a.m2();
        B b=new B(30,30);
        b.m3();
    }
}

執行結果:

可以有程式碼塊
B類中定義的m1方法
B類中重寫的的m2方法
A類中定義的m2方法
A類中的屬性a=30

3. 抽象類的應用

抽象類的應用與多型應用相關聯。

抽象類體現的是一種模板模式的設計,抽象類作爲多個子類的通用模板,子類在抽象類的基礎上進行擴充套件、改造,但子類總體上會保留抽象類的行爲方式。

抽象類解決的問題:

  1. 當功能內部一部分實現是確定的,一部分實現是不確定的。這時可以把不確定的部分暴露出去,讓子類去實現。
  2. 在軟件開發中實現一個演算法時,整體步驟很固定、通用,這些步驟已經在父類別中寫好了。但是某些部分易變,易變部分可以抽象出來,供不同子類實現。這就是一種模板模式

模板方法設計模式是程式設計中經常用得到的模式。各個框架、類庫中都有他的影子,比如常見的有:

  1. 數據庫存取的封裝
  2. Junit單元測試
  3. JavaWeb的Servlet中關於doGet/doPost方法呼叫
  4. Hibernate中模板程式
  5. Spring中JDBCTemlate、HibernateTemplate等

4. 什麼是介面?

  • 介面(英文:Interface),在JAVA程式語言中是一個抽象型別,是抽象方法的集合,介面通常以interface來宣告。
  • 有時必須從幾個類中派生出一個子類,繼承它們所有的屬性和方法。但是,Java不支援多重繼承。有了介面,就可以得到多重繼承的效果。
  • 有時必須從幾個類中抽取出一些共同的行爲特徵,而它們之間又沒有is-a的關係,僅僅是具有相同的行爲特徵而已。
  • 介面就是規範,定義的是一組規則,體現了現實世界中「如果你是/要…則必須能…」的思想。繼承是一個"是不是"的關係,而介面實現則是 "能不能"的關係。

image-20200808185924554

5.介面的特點

  • 介面(interface)是抽象方法和全域性常數值定義的集合(jdk8之前)。jdk8之後可以定義static和default的方法和方法體
  • 介面的特點:
    1. 用interface來定義。
    2. 介面中沒有構造器
    3. 介面不能用於範例化物件。
    4. 介面中不能擁有程式碼塊
    5. 介面中的所有成員變數都預設是由public static final修飾的。
    6. 介面中的所有抽象方法都預設是由public abstract修飾的。
    7. 一個類可以實現多個介面,介面也可以繼承(extends)其它一個或多個介面。
    8. 定義Java類的語法格式:先寫extends,後寫implements
      class SubClass extends SuperClass implements InterfaceA{ }
    9. 實現介面的類中必須提供介面中所有方法的具體實現內容,方可範例化。否則,仍爲抽象類。
    10. 介面的主要用途就是被實現類實現。(面向介面程式設計)
    11. 與繼承關係類似,介面與實現類之間存在多型性
    12. 介面和類是並列關係,或者可以理解爲一種特殊的類。從本質上講,介面是一種特殊的抽象類,這種抽象類中只包含常數和方法的定義(JDK7.0及之前),而沒有變數和方法的實現。

介面的定義舉例:

image-20200808190415337

JDK8中關於介面的改進:

  • Java 8中,你可以爲介面新增靜態方法和預設方法。從技術角度來說,這是完全合法的,只是它看起來違反了介面作爲一個抽象定義的理念。

  • 靜態方法:使用 static 關鍵字修飾。可以通過介面直接呼叫靜態方法,並執行其方法體。我們經常在相互一起使用的類中使用靜態方法。可以在標準庫中找到像Collection/Collections或者Path/Paths這樣成對的介面和類。

  • 預設方法:預設方法使用 default 關鍵字修飾。可以通過實現類物件來呼叫。我們在已有的介面中提供新方法的同時,還保持了與舊版本程式碼的相容性。比如:java 8 API中對Collection、List、Comparator等介面提供了豐富的預設方法。

    1. 若一個介面中定義了一個預設方法,而另外一個介面中也定義了一個同名同參數的方法(不管此方法是否是預設方法),在實現類同時實現了這兩個介面時,會出現: ·介面衝突·。
      解決辦法:實現類必須覆蓋介面中同名同參數的方法,來解決衝突。
    2. 若一個介面中定義了一個預設方法,實現其介面的類的父類別中也定義了一個同名同參數的非抽象方法,則不會出現衝突問題。因爲此時遵守: 類優先原則。介面中具有相同名稱和參數的預設方法會被忽略。
  • 介面中靜態方法和預設方法必須給出方法體,不能只定義

根據介面的特點,綜合舉例:

/************  一個類可以實現多個無關的介面  ************/

interface Runner {
    int a=10;
    public void run();//jdk8之前只能宣告非static和defult的方法

    static void fun2() {//jdk8之後可以定義static和defult的方法,不能宣告,必須給出方法體
        System.out.println("interface Runner static fun2");
    }
    default void fun3(){
        System.out.println(this.a);//可以使用this,但是不能更改其值,因爲預設預設public static final
        System.out.println("interface Runner default fun3");
    }
}


interface Swimmer {
    public double swim();

    default void fun3(){
        System.out.println("interface Swimmer default fun3");
    }

    default void skip(){
        System.out.println("interface Swimmer default skip");
    }
}


class Creator{
    public void eat() {
       System.out.println("eat");
    }

    public  void skip(){
        System.out.println("class Creator skip");
    }
}


class Man extends Creator implements Runner ,Swimmer{
    public void run() {
        System.out.println("run");
    }
    public double swim() {
        System.out.println("swim");
        return 1.1d;
    }

    public void fun3(){
        System.out.println("class Man  fun3");//類覆蓋兩個介面中預設的方法fun3()
        Runner.super.fun3();//呼叫原介面中預設的方法
        Swimmer.super.fun3();
    }
}

 

class Test{
    public static void main(String args[]){
        Test t = new Test();
        Man m = new Man();
        t.m1(m);//介面多型參照,與繼承關係類似,介面與實現類之間存在多型性
        t.m2(m);
        t.m3(m);
        System.out.println("-----------");
        Runner.fun2();
        System.out.println("-----------");
        System.out.println(Runner.a);
        System.out.println("-----------");
        m.fun3();
        System.out.println("-----------");
        m.skip();
    }
    public void m1(Runner f) {//介面多型參照
        f.run();
    }
    public void m2(Swimmer s) {//介面多型參照
        s.swim();
    }
    public void m3(Creator a) {//介面多型參照
        a.eat();
    }
}

執行結果:

run
swim
eat
-----------
interface Runner static fun2
-----------
10
-----------
class Man  fun3
10
interface Runner default fun3
interface Swimmer default fun3
-----------
class Creator skip

6.介面面試題

題一:

interface A {
    int x = 0;
}

class B {
    int x = 1;
}

class C extends B implements A {
    public void pX() {
    	System.out.println(x);//Error
        System.out.println(super.x);//1
        System.out.println(A.x);//0
    }
    public static void main(String[] args) {
        new C().pX();
    }
}

7.抽象類與介面的對比

image-20200808195958458