1. 程式人生 > >Java基礎-第六章(面向物件5)

Java基礎-第六章(面向物件5)

一、介面

(一) 介面的定義和使用

多個抽象類的抽象就是介面。
在Java中最小的程式單元就是類,介面其實是一個特殊的類。
Java中的介面表示規範,用於定義一組抽象方法,表示某一類事物必須具備的功能,要求實現類必須來實現介面並提供方法實現。

定義類的語法: [public] class 類名{}
定義介面語法: [public] interface 介面名{},(在這裡還沒有考慮介面的父介面等)
介面起名問題: 表示具有某些能力的,習慣以able結尾。

成功編譯之後和類一樣,具有一份位元組碼。

介面存在的成員:
① 介面中沒有構造器,所以介面不能建立物件(不能new),介面中不能定義普通方法。
② 介面中定義的成員變數,實質上是全域性靜態常量。預設使用public static final來修飾。
③ 介面中定義的方法都是公共的抽象方法,預設使用abstract來修飾方法。一般在介面中定義方法不使用修飾符。
④ 介面中定義的內部類都是公共的靜態內部類。預設使用public static 來修飾內部類。

標誌介面:
介面中沒有任何的成員,就僅僅是一個介面的定義,就是一個標誌。其他的類實現該介面,就屬於該家族。我們可以通過第三方程式碼賦予該介面實現類特殊功能(不推薦)。

常量介面:
使用介面來封裝多個常量資訊,稱之為常量介面,其目的和常量類相同(不推薦)。

我們使用的介面,主要都包含了抽象方法。

介面的特點和介面的繼承

介面的特點:

① 沒有構造方法,也不能顯示定義構造器,不能例項化。
② 介面只能繼承介面,不能繼承類,且介面可以多繼承(類是單繼承關係)。
③ 接口裡的方法全是抽象的,預設修飾符是 public abstract。
④ 接口裡的欄位全是全域性靜態常量,預設修飾符是 public static final。
⑤ 接口裡的內部類全是公共靜態的,預設修飾符是 public static。

類和類之間存在是繼承關係:使用extends來表示。
介面和介面之間只能是繼承關係:使用extends來表示。
介面和實現類之間只能是實現關係(繼承關係):使用implements來表示。

介面的實現者:實現類

介面僅僅只是定義了某一類事物應該具有的某些功能,但是沒有提供任何實現。
此時,我們得提供類再讓該類去實現介面,並覆蓋介面中的方法,從而實現類介面中定義的功能。

介面和實現類之間的關係,嚴格上稱之為“實現關係”,使用implements來表示。

但是在開發,有時候為了方便也把這個實現關係稱之為:特殊繼承關係。
所以可以這樣理解:介面是實現類的父類,實現類就是介面的子類。

面向介面程式設計:

介面 變數 = 建立實現類物件; // 體現類多型的思

介面和實現類的多型關係才是我們見的最多的。

類實現介面的語法:一個類可以實現多個介面,從而也彌補了類的單繼承問題。

[修飾符] class 實現類名 extents 父類 implements 介面1,介面2{}

注意:
介面中定義的方法是公共的抽象的,所以實現類必須覆蓋介面中的方法,並且方法必須使用public修飾。

(二) 介面和抽象類

介面和抽象類的區別:

相同點:
① 都位於繼承的頂端,用於被其他實現或繼承。
② 都不能例項化。
③ 都可以定義抽象方法,其子類都必須覆寫這些抽象方法。

不同點:
① 介面沒有構造方法,抽象類有構造方法。
② 抽象類可包含普通方法和抽象方法,介面只能包含抽象方法。
③ 一個類只能繼承一個直接的父類(可能是抽象類),卻可以實現多個介面(介面彌補了Java的單繼承)。
④ 變數:接口裡預設的是public static final,抽象類預設的是包訪問許可權。
⑤ 方法:接口裡預設的是public abstract,抽象類預設的是包訪問許可權。
⑥ 內部類:接口裡預設的是public static,抽象類預設的是包訪問許可權。

如果介面和實現類可以完成相同的功能,儘量使用介面,面向介面程式設計。
設計模式:介面和抽象類集合使用的(介面卡模式)。

(三) 面向介面程式設計

多型的好處:把實現類物件賦給介面型別變數,遮蔽了實現類之間的實現差異,從而可以做到通用程式設計。

public class Main {
    public static void main(String[] args) {
        // 呼叫
        MotherBoard.pluginIn(new Mouse());
    }
}

// usb
interface IUSB {
    void swapData();
}

// 滑鼠
class Mouse implements IUSB{

    @Override
    public void swapData() {
        System.out.println("滑鼠");
    }
}

// 印表機
class Print implements IUSB{

    @Override
    public void swapData() {
        System.out.println("印表機");
    }
}

// 主機板
class MotherBoard{
    public static void pluginIn(IUSB usb){
        usb.swapData();
    }
}

二、內部類

內部類:定義在類結構中的另一個類。
類中定義的成員:欄位、方法、內部類。

為什麼使用內部類:
① 增強封裝,把內部類隱藏在外部類之內,不許其他的類訪問該類。
② 內部類能提高程式碼的可讀性和可維護性,把小型類嵌入到外部類結構上程式碼更靠近。
③ 內部類可以直接訪問外部類的成員。

// 外部類/宿主類
public class Out(){
    // 內部類/巢狀類
    public class In(){}
}

內部類根據使用不同的修飾符或者定位的位置不同,分成四種內部類:
例項內部類: 內部類沒有使用static修飾。
靜態內部類: 內部類使用的static修飾。
區域性內部類: 在方法中定義的內部類。
匿名內部類*: 適合僅使用一次的類,屬於區域性內部類的特殊情況。

例項內部類、靜態內部類和區域性內部類的區別:

- 例項內部類 靜態內部類 區域性內部類
主要特徵 內部類的例項引用特定的外部類的例項 內部類的例項不與外部類的任何例項關聯 可見範圍是所在的方法
可用的修飾符 訪問控制修飾符:abstract,final 訪問控制修飾符:static,abstract,final abstract,final
可以訪問外部類的那些成員 可以直接訪問外部類的所用成員 只能訪問外部類的靜態成員 可以直接訪問外部內的所用成員,並且能訪問所在方法的final型別的變數和引數
擁有成員型別 只能擁有例項成員 可以擁有靜態成員和例項成員 只能擁有例項成員
外部類如何訪問內部類的成員 必須通過內部類的例項來訪問 對於靜態成員,可以通過內部類的完整類名來訪問 必須通過內部類的例項來訪問

外部類的訪問修飾符:要麼使用public,要麼預設。
內部類可以看做是外部類的一個成員,好比欄位,那麼內部類可以用時public/預設/protected/private修飾還可以使用static修飾。

對於每個內部類來說: Java編譯器會生成獨立的.class檔案。
成員內部類:外部類名數字內部類名稱。
匿名內部類:外部類名$數字。

(一) 例項內部類

例項內部類: 沒有使用static修飾內部類,說明內部類屬於外部類的物件,不屬於內部類本身。
特點:
① 建立例項內部類前,必須存在外部類物件,通過外部類物件建立內部類物件(當存在內部類物件時,一定存在外部類物件)。
② 例項內部類的例項自動持有外部類的例項的引用,內部類可以直接訪問外部類的成員。
③ 外部類中不能直接訪問內部類的成員,必須通過內部類的例項去訪問。
④ 例項內部類中不能定義靜態成員,只能定義例項成員。
⑤ 如果例項內部類和外部類存在同名的欄位或方法,那麼內部類中:this.xxx:表示訪問內部類成員,外部類.this.xxx:表示訪問外部類成員。

// 外部類
public class Outter{
    String name = "name";
    // 例項內部類
    public class Inner{
        public void test(){
            System.out.println(new Outter().name);
        }
    }
}

(二) 靜態內部類

靜態內部類: 使用static修飾的內部類。
特點:
① 靜態內部類的例項不會自動持有外部類的特定例項的引用,在建立內部類的例項時,不必建立外部類的例項。
② 靜態內部類可以直接訪問外部類的靜態成員,如果訪問外部類的例項成員,必須通過外部的例項去訪問。
③ 在靜態內部類中可以定義靜態成員和例項成員。
④ 測試類可以通過完整的類名直接訪問靜態內部類的靜態成員。

(三) 區域性內部類(不建議使用)

區域性內部類: 在方法中定義的內部類,其可見範圍是當前方法和區域性變數是同一個級別。
① 不能使用public,private,protected,static修飾符。
② 區域性內部類只能在當前方法中使用。
③ 區域性內部類和例項內部類一樣,不能包含靜態成員。
④ 區域性內部類和例項內部類一樣,可以訪問外部類的所有成員。
⑤ 區域性內部類訪問的區域性變數必須使用final修飾(在Java8中是自動隱式加上final,但是依然是常量,不能改變值)。

原因: 如果當前方法不是mian方法,那麼當前方法呼叫完畢之後,當前方法的棧幀即被銷燬,方法內部的區域性變數空間也會被銷燬。然而區域性內部類是定義在方法中的,而且方法中會建立區域性內部類物件,而區域性內部類會去訪問區域性變數,噹噹前方法被銷燬的時候,物件還在堆記憶體,依然持有對區域性變數的引用,但是方法被銷燬的時候區域性變數也被銷燬了。
此時出現:在堆記憶體中,一個物件引用著一個不存在的資料,為了避免該問題,我們使用final修飾區域性變數,從而變成常量,永駐記憶體空間,即使方法銷燬之後,該區域性變數也在記憶體中,物件可以繼續持有。

class LocalInnerClassDemo{
    static String name = "x";
    public static void main(){
        System.out.println("Hello World");
        // 區域性變數
        final int age = 23;
        // 區域性內部類
        class Inner{
            String info = "INFO";
            public void test(){
                System.out.println(name);
                System.out.println(info);
                System.out.println(age);
            }
        }
    }
}

區域性內部類只能訪問final修飾的區域性變數。

(四) 匿名內部類

匿名內部類(Anonymous)是一個沒有名稱的區域性內部類,適合只使用一次的類。
在開發中經常有這樣的類,只需要定義一次,使用一次就可以丟棄了,此時:不應該白白定義在一個檔案中。
在Java/Android的事件處理中:不同的按鈕點選之後,應該有不同的響應操作,首選使用匿名內部類。
特點:
① 匿名內部類本身沒有構造器,但是會呼叫父類的構造器。
② 匿名內部類儘管沒有構造器,但是可以在匿名內部類中提供一段例項初始化程式碼塊,JVM在呼叫父類構造器後,會執行該段程式碼。
③ 內部類處理可以繼承之外,還可以實現介面。

格式:

new 父類構造器([實參列表]) 或 介面(){
    // 匿名內部類的類體部分
}

注意: 匿名內部類必須繼承一個父類或者實現一個介面,但最多隻能繼承一個父類或實現一個介面。

// 建立一個匿名內部類
MotherBoard.pluginIn(new IUSB(){
    // 匿名內部類體
    public void swapData(){
        System.out.println("鍵盤");
    }
})

三、列舉

列舉是從Java5開始提供的一種新的資料型別,是一個特殊的類,就是固定的多個常量物件的集合。

定義格式:
[修飾符] enum 列舉類名{
    常量A,常量B,常量C;
}

列舉的特點:
① 列舉的直接父類java.lang.Enum,但是不能顯示繼承Enum。
② 列舉就相當於一個類,可以定義構造方法,成員變數,普通方法和抽象方法。
③ 預設私有的構造方法,即使不寫訪問許可權也是private(假構造器,底層沒有無引數構造器)。
④ 每個例項分別用一個全域性常量表示,列舉的物件是固定的,例項個數有限,不能使用new關鍵字。
⑤ 列舉例項必須位於列舉內中最開始的部分,列舉例項列表的後面要有分號與其他成員相分隔。
⑥ 列舉例項後有花括號時,該例項是列舉類的匿名內部類物件(檢視編譯後的class檔案)。

列舉的使用:
① 列舉中都是全域性公共的靜態常量,可以直接使用列舉類名呼叫。
② 因為java.lang.Enum類是所有列舉類的父類,所以所有的列舉物件都可以呼叫Enum類中的方法。
③ 編譯器生成的列舉類的靜態方法。
④ 從Java5開始出現列舉,switch也支援操作列舉型別。

反饋與建議

感謝你閱讀這篇部落格。如果您喜歡這篇部落格就請關注我和朋友一起分享吧!