1. 程式人生 > >java中的介面懂多少

java中的介面懂多少

介面很多人都會寫,也明白它用在什麼地方,會產生哪些效果。特別是很多人一提到介面,就脫口而出規範、設計模式,那麼對於介面又瞭解多少呢?本章試著從java中介面的定義和使用出發,談談java中介面具有哪些特性。

一、介面

首先,瞭解介面interface就必須和類class區分開,為什麼這麼說呢?因為介面和類是兩個概念,但介面又和類具有一定的關係。有人不禁會問,怎麼說呢?類class是從java.lang.Object類派生而來,但介面interface並不是從某一個特定介面派生而來,兩個interface可能沒有任何交集,但兩個class一定存在交集。介面不是Object的子類,但介面卻隱士申明瞭Object中的所有可繼承方法,類似於Object子類繼承了它的所有可繼承方法,這裡申明的方法都是abstract形式,也即只有申明,沒有方法體block。
其次,介面的申明形式如下:

[annotation] [modifier] interface identitor [extends interfacelist] {
  [public | static |  ]  [FieldConstant];  
  [public | abstract |  ]  [AbstractMethod];
  [public | static |  ]  [MemberType];
}

其中,”[ ]”表示可有可無,而[ a | b ]表示從a或b中選擇一個。annotation表示介面的註解;modifier表示介面的修飾符,正如前面所說top interface只能是public, nested interface可是static,在class中還可以是protected或private;identitor表示該介面的名稱,一般取名時字首為”I” + interfaceName 或者 interfaceName + “able”;interfacelist表示繼承父介面列表,如Comparable, Serializable,Iterator等;FieldConstant表示介面中的欄位;AbstractMethod表示介面中的abstract方法;MemeberType表示介面中的成員,如class,interface。

二、interface種類

java將介面分為兩種:normal interface,annotation type。normal這個單詞不用解釋,既然有兩種,那麼他們肯定存在差異,也具有一定的共性。下面分別談談這兩種interface。

三、normal interface

通用介面又可以分為外層介面top interface、內嵌介面nested interface,也即是作為一個單獨的介面,還是被嵌入於類中、介面中的介面。它們的介面體body定義相同,唯一的區別在於修飾符的差異。nested interface的修飾符可以是static、public、protected或private(這兩者只能是作為class中的內嵌接口才能修飾,而作為top interface中的內嵌介面進行修飾,會編譯報錯)。
1、FieldConstant常量欄位
在介面中定義的欄位都被修飾符public static final預設進行了修飾,雖然可以顯示的新增public、static或final,但是建議不要新增,因為這是多餘的行為,可以新增只是說明欄位的修飾符。此處,還需注意一個問題,那就是常量的初始化問題,因為class中的常量可以在定義的時候進行初始化,也可以在建構函式中程序初始化,那麼問題是介面中的常量何時進行初始化?因為不能顯示的呼叫介面的初始化,所以常見在定義的時候就必須初始化,否則編譯器會丟擲初始化Error。其次,介面中的FieldConstant能否被子類繼承?可以,如果class實現了interface,那麼該介面的例項引用可以引用該介面中的常量欄位,如果subinterface繼承了該介面,那麼subinterface繼承了該介面的所有常量欄位。

public class Test implements ITest {
    public static void main(String... args) {
        ITest test = new Test();
        //通過例項引用介面ITest的常量欄位
        System.out.println(test.strTest); //hello world
        //通過介面引用常量欄位
        System.out.println(ITest.strTest); //hello world
        //通過子介面引用常量欄位
        System.out.println(ISubTest1.strTest); // hello world
    }
}
public interface ITest {
    //欄位常量在定義的時候必須進行初始化
    String strTest = "hello world";
} 
public interface ISubTest1 extends ITest {}

提問:介面中的常量欄位能否hide父介面中相同名稱的欄位?肯定可以!
2、AbstractMethod抽象方法
介面中定義的方法沒有方法體,並且修飾符為public abstract預設進行修飾,也就是可以給介面中的方法新增這兩種修飾符,但是建議不新增,完全是畫蛇添足,理由和常量欄位一樣。既然是方法,那麼就必然涉及到方法的重寫、過載以及實現問題。

public interface ITest {
    public abstract testMethod();
    //可以不用新增public abstract修飾符,編譯器預設新增
    test();
}
  • 重寫override
    在class中重寫,子類通過重寫父類的方法來改變通過該方法名進行引用時,所具有的行為。那麼,interface的方法重寫是什麼樣的呢?除了方法體,方法重寫的簽名和返回值都和class中方法的重寫要求相同。
public interface ITest {
    //定義一個待重寫的方法
    Object test() throws IOException, ClassCastException;

    String test2();
}

public interface ISubTest extends ITest {
    //第一種,返回值型別是重寫方法的子型別
    String test() throws IOException, ClassCastException;
    //第二種,異常子句重寫
    Object test() throws IOException;
    String test() throws CLassCastException;
    String test() throws EOFException; 
    //第三種,正常重寫
    Object test();
    String test(Object obj) throws IOException;
}

public class Test implements ISubTest {
    //實現子介面的方法,如果子介面沒有重寫父介面的方法,則必須實現父介面的方法。也即,實現子介面申明的、繼承的方法。

    //第一種,返回值型別是重寫方法的子型別
    String test() throws IOException, ClassCastException {
        return null;
    }
    //第二種,異常子句重寫
    Object test() throws IOException {
        return null;
    }
    String test() throws CLassCastException {
        return null;
    }
    String test() throws EOFException {
        return null;
    }
    //第三種,正常重寫
    Object test() {
        return null;
    }
    String test(Object obj) throws IOException {
        return null;
    }

    //實現子介面繼承的方法
    String test2(){
        return null;
    }
}
  • 過載overload
    過載相對於重寫來說沒那麼複雜,它所要求的是方法的簽名不同,所謂方法簽名是指方法的返回值和方法的引數組合,如String test(Object obj)的簽名signatrue為(Object)String。但是,這裡並不能根據返回值的不同來判定過載,而在JVM的底層中是根據signature來進行實現,只是編譯器進行了過濾處理。
public interface ITest {
    //原始方法
    String test();

    //第一種,引數個數不同
    String test(String str);

    //第二種,引數的型別不同
    String test(int a);

    //第三種,引數的型別和個數不同
    String test(String str1, String str2);
}
  • 方法的實現
    介面中方法的實現都必須反映到實現類中,同時允許class通過繼承類實現介面interface。此外,class實現implements介面,那麼該class必須實現介面中的所有abstract方法(後面會說JDK8的新特性)。
    public class Test {
        //父類中定義了test1, 記得實現時修飾符的全限必須是public
        public void test1() {
        }
    }
    class A extends Test implements ITest1, ITest2 {
        //實現ITest2介面方法,記得實現時修飾符的全限必須是public
        public String test2() {
            return null;
        }
    }

    interface ITest1 {
        //方法被A的父類Test實現
        void test1();
    }

    interface ITest2 {
        //方法被A實現
        String test2();
    }

提問:介面中欄位和方法是否可以具有相同名稱?肯定可以!
3、MemberType成員型別
class中可以申明內部類innerClass、內部介面innerInterface和內部列舉innerEnum,介面也同樣可以,此時對於innerClass、innerInterface成員型別的預設修飾符為public static,那麼在定義時可以顯示的指定這兩個修飾符,但不建議。但innerEnum是內部列舉型別,此時它的預設修飾符為public static final,也就是說在介面中定義的列舉都是常量列舉。

public interface ITest {
    //修飾符預設為public static 
    class NestedClass {
        private int a;
        NestedClass (int a) {
            this.a = a;
        }
    }

    //預設修飾符為 public static
    interface INestedInterface {

    }

    //預設修飾符為public static final
    enum NestedEnum {
        RED, YELLOW,GREEN;
    }
}

提問:介面中的欄位是否可以和成員型別具有相同的名字?可以;介面中的方法能否具有和成員型別相同的名字呢?可以。
4、內嵌介面NestedInterface
介面可以作為top interface的內嵌成員,也可以作為class中的內嵌成員。無論作為class還是interface的內嵌成員,其中介面體的定義內如如前所述,但是作為class的成員時,介面申明的修飾符還可以是protected 、private或者static。

四、介面中的defaut方法

考慮這樣一種場景,某個已釋出的jar中的某個介面需要整合一個新的功能函式,那麼此時需要往介面中新增一個abstract方法,而所有implements這個介面的class都必須實現這個abstract方法,如果採用一個介面介面卡來實現程式,也就需要在介面卡類中進行實現即可,其他情況是否就很麻煩?JDK8中在介面中引入了default方法,它是一個介面例項方法,可以用來繼承。那麼default方法和介面的abstract方法有什麼相同點和特性?具體體現在重寫、過載和實現。

public interface ITest {
   //abstract抽象方法
   void test();

   //default預設方法
   default void testDefault() {
    System.out.println("hello world");
   }

   //@complile-error預設方法不能作為abstract方法的具體實現
   /*default void test() {
   }*/
}
  • 重寫
    方法重寫的方式就四種,default方法和普通的abstract方法沒有區別,都必須滿足下列四種情況之一:1、具有和父介面方法相同的返回值、名稱和丟擲異常;2、具有和父介面方法相同名稱、丟擲異常,但返回值型別是子型別;3、具有和父介面方法相同的名稱、返回值,但丟擲異常相容(子異常型別);4、具有和父介面方法相同的名稱,但返回值和丟擲異常相容。
    但default方法不能重寫abstract方法,它只能重寫父類的default方法,同時default方法不能具有和abstract方法相同的名稱;
public interface ITest {
    //抽象方法
    Object test();
    //預設方法
    default Object testDefault() {
     System.out.println("hello world");
    }
}
public interface ISubTest extends ITest {
    //@compile-error編譯錯誤
    //預設方法不能作為abstract方法的重寫方法
    /*default Object test() {
         System.out.println("test");
    }*/

    //預設方法可以重寫預設方法
    default String testDefault() {
        System.out.prinln("hello world");
    }
}