Java之抽象類和接口

抽象類的產(chǎn)生:

當(dāng)編寫一個(gè)類時(shí),我們往往會(huì)為該類定義一些方法,這些方法是用來描述該類的功能具體實(shí)現(xiàn)方式,那么這些方法都有具體的方法體。

但是有的時(shí)候,某個(gè)父類只是知道子類應(yīng)該包含怎么樣的方法,但是無法準(zhǔn)確知道子類如何實(shí)現(xiàn)這些方法。比如一個(gè)圖形類應(yīng)該有一個(gè)求周長(zhǎng)的方法,但是不同的圖形求周長(zhǎng)的算法不一樣。那該怎么辦呢?

分析事物時(shí),發(fā)現(xiàn)了共性內(nèi)容,就出現(xiàn)向上抽取。會(huì)有這樣一種特殊情況,就是方法功能聲明相同,但方法功能主體不同。那么這時(shí)也可以抽取,但只抽取方法聲明,不抽取方法主體。那么此方法就是一個(gè)抽象方法。

寵物店的案例中,貓、狗、鴨子都能叫,因此將叫聲的方法主體抽象到父類中,聲明為抽象方法

1.抽象類

1.1.抽象類和抽象方法的定義

抽象類:使用abstract 關(guān)鍵字修飾的類叫做抽象類

abstract class 類名 {

}

抽象類和普通類的區(qū)別:
1.抽象類需要修飾符abstract修飾,普通方法不允許
2.普通類可以實(shí)例化,抽象類不能實(shí)例化
抽象方法:使用abstract修飾的方法叫抽象方法,抽象方法不允許有方法體。

訪問修飾符 abstract 返回類型 方法名 ();

抽象方法和普通方法的區(qū)別:
1.抽象方法需要修飾符abstract修飾,普通方法不允許
2.抽象方法沒有方法體,普通方法有方法體
注:
● 子類如果不是抽象類,則子類必須重寫抽象類中的全部抽象類方法

1.2.抽象類的規(guī)則

抽象類專門用于繼承關(guān)系,在繼承關(guān)系中充當(dāng)父類。

(1)抽象類不允許被實(shí)例化,既不讓new

為什么呢?抽象類不是一個(gè)完整的類,因?yàn)槌橄箢愔锌赡苡谐橄蠓椒?,而抽象方法沒有方法體,沒有方法體的方法是半成品,所以不允許實(shí)例化。

抽象類不能實(shí)例化
(2)抽象類中可以有屬性,普通方法,構(gòu)造方法,main方法
public abstract class Shape {
    //抽象類中可以定義屬性
    double param1;
    double param2;
    static final double PI = 3.14;
    
    //抽象類中可以定義普通方法
    public void sayHello(){ }
    
    //抽象類中可以定義抽象方法
    public abstract double daC();

    //抽象類中可以定義main方法
    public static void main(String[] args) {
 
    }
}
(3)如果一個(gè)類中包含抽象方法,那么這個(gè)類必須是抽象類
public abstract class Shape {
    //抽象方法
    public abstract double daC();
}

錯(cuò)誤的情況示例:

錯(cuò)誤示例
(4)抽象類中可以沒有抽象方法
public abstract class Shape {
    //抽象類中可以沒有抽象方法
}
(5)父類可以通過抽象方法要求子類實(shí)現(xiàn)抽象方法。

子類繼承抽象父類后,子類從父類繼承了抽象方法,由于此時(shí)子類中包含了抽象方法,所以子類也必須是抽象類。抽象類不允許實(shí)例化,導(dǎo)致無法創(chuàng)建子類對(duì)象。若要能夠?qū)嵗宇悓?duì)象,就必須保證子類不是抽象類。只要子類實(shí)現(xiàn)了重寫從父類繼承的抽象方法,那么子類中就沒有抽象方法了,就可以實(shí)例化了。

代碼示例:

abstract class Shape {
    public static final Double PI = 3.1415926;
    //定義抽象方法
    public abstract double calcC(double n1,double n2);
}
class Rectangle extends Shape{
    //實(shí)現(xiàn)抽象方法
    @Override
    public double calcC(double n1, double n2) {
        return (n1 + n2) * 2;
    }
}
class Circle extends Shape{
    //實(shí)現(xiàn)抽象方法
    @Override
    public double calcC(double n1, double n2) {
        return 2 * Shape.PI * n1;
    }
}

public class TestAbs{
    public static void main(String[] args) {
        //實(shí)例化Rectangle對(duì)象
        Shape shape1 = new Rectangle();
        double c1 = shape1.calcC(3, 4);
        System.out.println(c1);

        //實(shí)例化Circle對(duì)象
        shape1 = new Circle();
        double c2 = shape2.calcC(3, 4);
        System.out.println(c2);
    }
}

1.3.誰不與abstract共存

  • private:私有的方法子類是無法繼承到的,也不存在覆蓋,而abstract和private一起使用修飾方法,abstract既要子類去實(shí)現(xiàn)這個(gè)方法,而private修飾子類根本無法得到父類這個(gè)方法?;ハ嗝?。
  • final:final修飾的類不允許被繼承,與abstract相悖。
  • static:static是靜態(tài),只有一份,用于共享。abstract是為了讓子類重寫,是多份,用于子類私有。

1.4.抽象類的優(yōu)勢(shì)

抽象類中己經(jīng)實(shí)現(xiàn)的方法可以被其子類使用,使代碼可以被復(fù)用,同時(shí)提供了抽象方法,保證了子類具有自身的獨(dú)特性。

1.5.抽象類的局限性

在有些應(yīng)用場(chǎng)合,僅僅使用抽象類和抽象方法會(huì)有一定的局限性。下面通過“寵物店”來進(jìn)一步分析、認(rèn)識(shí)這種局限性,并學(xué)會(huì)使用接口來改進(jìn)設(shè)計(jì)。

寵物店中,貓是喵喵的叫,獵豹也是喵喵的叫,狗是汪汪的叫,鴨子是嘎嘎的叫,如果還有寵物魚,干脆不叫,因此在類圖中設(shè)計(jì)的結(jié)果如下。

設(shè)計(jì)結(jié)果

此時(shí),使用抽象類就會(huì)出現(xiàn)以下問題:

第一,叫的方法不再通用,因?yàn)橛胁唤械膶櫸?/p>

第二,子類繼承寵物抽象類之后,寫出來的叫的方法可能會(huì)出現(xiàn)代碼重復(fù)的情況,如獵豹和貓都是“喵喵”叫,這時(shí)候,就不再符合代碼復(fù)用的要求。

對(duì)于第一個(gè)叫的方法不再通用問題,最自然的想法就是將叫這個(gè)方法變?yōu)槌橄蠓椒ǎ缓笥善渥宇惾?shí)現(xiàn),這樣做雖然解決了第一個(gè)問題,但是會(huì)造成代碼冗余的問題,如這里的獵豹和貓的叫方法也會(huì)一樣,也就是第二個(gè)問題更加突出。要解決上述問題,最理想的方式就是使用接口。

2.接口

2.1.接口與定義規(guī)范

接口是interface,相當(dāng)于抽象類,在繼承關(guān)系中充當(dāng)父類的角色,在接口中通過定義抽象方法來指定規(guī)范,子類去實(shí)現(xiàn)接口,要實(shí)現(xiàn)接口中的所有抽象方法。
接口可以多實(shí)現(xiàn)。也就實(shí)現(xiàn)了子類的多繼承問題。

與定義類的class不同,接口定義時(shí)需要使用interface關(guān)鍵字。
接口定義語法格式:

public interface 接口名 {
    抽象方法1;
    抽象方法2;
    抽象方法3;
}

定義接口:

public interface IShout {

}

定義接口就是定義規(guī)范,接口中的抽象方法就是具體的規(guī)范。

2.2.實(shí)現(xiàn)類與遵循規(guī)范

接口實(shí)現(xiàn)語法格式:

class 類 implements 接口 {
    重寫接口中方法
} 

在類實(shí)現(xiàn)接口后,該類就會(huì)將接口中的抽象方法繼承過來,此時(shí)該類需要重寫該抽象方法,完成具體的邏輯。
實(shí)現(xiàn)接口:

interface IShout extends IFly{//extends Object 接口不是類,所以沒有默認(rèn)繼承Object
    //接口沒有最高層,類的最高層是Object
    
    //接口中不允許有普通方法,所以接口中的方法都是抽象的
    /*public void sayHello(){
        
    }*/
    
    //接口不是類,所以沒有構(gòu)造
    /*
    public IShout(){
    }
    */
    
    //定義常量
    String TYPE="動(dòng)物";
    
    //定義抽象方法
    void shout();
}

class Rabbit implements IShout{
    //實(shí)現(xiàn)shout()方法
    @Override
    public void shout() {
        
    }
    //實(shí)現(xiàn)fly()方法
    @Override
    public void fly() {
        
    }
}

實(shí)現(xiàn)接口就是遵循接口的規(guī)范。

2.3.接口的多實(shí)現(xiàn)

了解了接口的特點(diǎn)后,那么想想為什么要定義接口,使用抽象類描述也沒有問題,接口到底有啥用呢?
接口最重要的體現(xiàn):解決多繼承的弊端。將多繼承這種機(jī)制在java中通過多實(shí)現(xiàn)完成了。

interface Fu1{
    void show1();//接口定義規(guī)范show1()
}
interface Fu2{
    void show2();//接口定義規(guī)范show2()
}
class Zi implements Fu1,Fu2 {//多實(shí)現(xiàn)。同時(shí)實(shí)現(xiàn)多個(gè)接口。
    public void show1(){}//子類實(shí)現(xiàn)show1()規(guī)范
    public void show2(){}//子類實(shí)現(xiàn)show2()規(guī)范
}

怎么解決多繼承的弊端呢?

  • 弊端:多繼承時(shí),當(dāng)多個(gè)父類中有相同功能時(shí),子類調(diào)用會(huì)產(chǎn)生不確定性。
  • 其實(shí)核心原因就是在于多繼承父類中功能有主體,而導(dǎo)致調(diào)用運(yùn)行時(shí),不確定運(yùn)行哪個(gè)主體內(nèi)容。

為什么多實(shí)現(xiàn)能解決了呢?

  • 因?yàn)榻涌谥械墓δ芏紱]有方法體,由子類來明確。

2.4. 類繼承類同時(shí)實(shí)現(xiàn)接口

  • 接口和類之間可以通過實(shí)現(xiàn)(implements)產(chǎn)生關(guān)系
  • 類與類之間可以通過繼承(extends)產(chǎn)生關(guān)系
  • 當(dāng)一個(gè)類已經(jīng)繼承了一個(gè)父類,它又需要擴(kuò)展額外的功能,這時(shí)接口就派上用場(chǎng)了。
  • 子類通過繼承父類擴(kuò)展功能,通過繼承擴(kuò)展的功能都是子類應(yīng)該具備的基礎(chǔ)功能。如果子類想要繼續(xù)擴(kuò)展其他類中的功能呢?這時(shí)通過實(shí)現(xiàn)接口來完成。
class Fu {
    public void show(){}
}
interface Inter {
    pulbic abstract void show1();
}
interface Outer {
    pulbic abstract void show2();
}
class Zi extends Fu implements Inter,Outer {
    public void show1() {
    }
    public void show2() {
    }
}

接口的出現(xiàn)避免了單繼承的局限性。父類中定義的事物的基本功能。接口中定義的事物的擴(kuò)展功能。

2.5.接口的規(guī)定

  1. 接口中可以定義變量,但是變量必須有固定的修飾符修飾,public static final 所以接口中的變量也稱之為常量,其值不能改變。
  2. 接口中可以定義方法,方法也有固定的修飾符,public abstract
  3. 接口不可以new,不可以實(shí)例化。
  4. 子類必須覆蓋掉接口中所有的抽象方法后,子類才可以實(shí)例化。否則子類是一個(gè)抽象類。
  5. 接口不能繼承類
  6. 接口可以繼承接口
  7. 接口沒有最高層,類的最高層是Object
  8. 接口中不允許有普通方法,所以接口中的方法都是抽象(除了default方法和static方法)
  9. 接口不是類,所以沒有構(gòu)造

2.6.接口的繼承

學(xué)習(xí)類的時(shí)候,知道類與類之間可以通過繼承產(chǎn)生關(guān)系,接口和類之間可以通過實(shí)現(xiàn)產(chǎn)生關(guān)系,那么接口與接口之間會(huì)有什么關(guān)系。

多個(gè)接口之間可以使用extends進(jìn)行繼承。

interface Fu1{
    void show();
}
interface Fu2{
    void show1();
}
interface Fu3{
    void show2();
}
interface Zi extends Fu1,Fu2,Fu3{
    void show3();
}

在開發(fā)中如果多個(gè)接口中存在相同方法,這時(shí)若有個(gè)類實(shí)現(xiàn)了這些接口,那么就要實(shí)現(xiàn)接口中的方法,由于接口中的方法是抽象方法,子類實(shí)現(xiàn)后也不會(huì)發(fā)生調(diào)用的不確定性。

2.7. JDK1.8中接口的新特性

2.7.1.default方法

JDK1.8中可以定義默認(rèn)方法,默認(rèn)方法是由default關(guān)鍵字修飾的

默認(rèn)方法必須有方法實(shí)現(xiàn),也可以被重寫。

interface IShout {

    //定義默認(rèn)方法,有方法實(shí)現(xiàn),子類可以直接使用,也可以重寫
    public default void method(){
        System.out.println(" interface default method ");
    }
}

public class Rabbit implements IShout{

    @Override
    public void method() {
        System.out.println(" rabbit method is running. ");
    }

    public static void main(String[] args) {
        Rabbit rabbit = new Rabbit();
        rabbit.method();
    }
}

2.7.2.static方法

interface IShout {

    //定義抽象方法,有方法實(shí)現(xiàn),可以直接使用接口名調(diào)用
    public static void method(){
        System.out.println(" interface static method ");
    }
}

public class Rabbit implements IShout{

    public static void main(String[] args) {
        IShout.method();
    }
}

接口中的static方法由接口名調(diào)用,static方法只有一份

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 《abstract關(guān)鍵字》 —————————— 抽象類:含有抽象方法的類。抽象類必須也要用abstract關(guān)鍵字...
    好大一顆星閱讀 798評(píng)論 0 0
  • 對(duì)于面向?qū)ο缶幊虂碚f,抽象是它的一大特征之一。在Java中,可以通過兩種形式來體現(xiàn)OOP的抽象:接口和抽象類。接口...
    Q南南南Q閱讀 428評(píng)論 0 3
  • Java抽象類、接口、內(nèi)部類(靜態(tài)內(nèi)部類,非靜態(tài)內(nèi)部類、匿名內(nèi)部類)、枚舉類 知識(shí)點(diǎn)總結(jié) 一.抽象類 類的基本特...
    writeanewworld閱讀 1,218評(píng)論 0 1
  • 抽象類與接口是java語言中對(duì)抽象概念進(jìn)行定義的兩種機(jī)制,正是由于他們的存在才賦予java強(qiáng)大的面向?qū)ο蟮哪芰?。?..
    洋芋掉到碗里去了閱讀 371評(píng)論 0 0
  • 寫在前面 本文背景為JDK 1.8 ,為個(gè)人學(xué)習(xí)筆記整理,略有凌亂。如有紕漏,請(qǐng)務(wù)必指出。 抽象類 抽象類即是聲明...
    Van96閱讀 415評(píng)論 0 1

友情鏈接更多精彩內(nèi)容