天天看點

《瘋狂Java講義》學習筆記(五)面向對象

初始化塊總在構造器執行前被調用

1、類

  • 類是某一批對象的抽象,對象才是一個具體存在的實體
  • 類包含:構造器、成員變量和方法
  • 成員變量:用于定義該類或該類的執行個體所包含的狀态資料

    方法:用于定義該類或該類的執行個體的行為特征或者功能實作

    構造器:用于構造該類的執行個體,通過new關鍵字來調用,傳回該類執行個體

  • 成員變量

    修飾符:public、protected、private、省略(四選一)、static、final(兩個可以同時)

    類型:任何類型

    名稱:首字母小寫,後面每個單詞首字母大寫,其它字母小寫,單詞之間不要有分隔符

  • 方法

    修飾符:public、protected、private、省略(四選一)、abstract、final(兩選一)、static可以與前二組合

    傳回值:任何類型,用return傳回,沒有則方法名前需要用void關鍵字來修飾

    名稱:首字母小寫,後面沒個單詞首字母大寫,其它字母小寫,單詞之間不要有分隔符

    參數:0至多個參數組成,英文逗号隔開

  • static用于修飾方法、成員變量等,static修飾的成員表明它屬于這個類本身,而不屬于該類的單個執行個體,非static修飾的普通方法、成員變量則屬于該類的單個執行個體,不屬于該類。
  • 構造器:不能有傳回值,也不能用void修飾,否則被認為是方法

    修飾符:public、protected、private、省略(四選一)

    名稱:必須與類名相同

    參數:和定義方法格式相同

  • Java的對象引用就是C裡的指針,隻是Java把這個指針封裝起來了
  • this關鍵字總是指向調用該方法的對象

    構造器中引用該構造器正在初始化的對象

    在方法中引用調用該方法的對象

  • static修飾的方法中不能使用this引用
  • 靜态方法無法直接通路非靜态方法

2、方法

  • 函數和方法的差別:結構化程式設計是由函數組成,面向對象程式設計是由類組成,類包含方法。
  • 方法的所屬性主要展現:

    方法不能獨立定義,方法隻能在類體裡定義

    方法要麼屬于類,要麼屬于執行個體

    方法不能獨立運作,執行方法時必須使用類或對象來作為調用者(類.方法或執行個體.方法)

  • 方法的參數采用複制值的方式傳遞,即原先的值不會被改變,但如果參數是引用對象則會被改變,如:對象、數組等
  • 可變形參可以用String… strs來表示,參數也可以用數組來傳遞new String[]{“aa”,”bb”}
  • 方法重載:同一個類中,方法名相同,形參不同(數量或類型不同),但和傳回值、修飾符無關
  • Java中确定一個方法的3個要素:

    調用者:類或執行個體對象

    方法名:方法的辨別

    形參清單:當調用方法時,系統将會根據傳入的實參清單比對

  • 成員變量:執行個體變量或類變量

    局部變量:形參、方法局部變量、代碼塊局部變量

  • 必須先給方法局部變量和代碼塊局部變量指定初始化值,否則不能通路它們,但全局變量可以
  • 定義局部變量後,系統并未為這個變量配置設定記憶體空間,直到等到程式為這個變量賦初始化值時,系統才會為局部變量配置設定記憶體,并将初始化值儲存到這塊記憶體中
  • 不應把所有變量都定義成全局變量:

    增大了變量的生存時間,導緻更大的記憶體開銷

    擴大了變量的作用域,不利于提高程式内聚性

3、封裝

  • 封裝是将對象的狀态資訊隐藏在對象内部,不允許外部程式直接通路對象内部資訊,而是通過該類所提供的方法來實作對内部資訊的操作和通路
  • 封裝的好處

    隐藏類的實作細節

    讓使用者隻能通過事先預定的方法來通路資料,進而可以在該方法裡加入控制邏輯,限制對成員變量的不合理通路

    可進行資料檢查,進而有利于保證對象資訊的完整性

    便于修改,提高代碼的可維護性

  • 封裝的原則

    将對象的成員變量和實作細節隐藏起來,不允許外部直接通路

    把方法暴露出來,讓方法來控制對這些成員變量進行安全的通路和操作

  • 作用域修飾符

    private:隻能本class能通路

    default:同一個package下都可以通路

    protected:同一個package下都可以通路,子類可以通路

    public:全可以通路

  • 對于外部類而言,可以用pubulic和default來修飾,但不能用protected和private,因為沒有存在類的内部,也就沒有其所在類子類和沒有所在類内部的範圍
  • 沒有用public修飾的類,檔案名與類名可以不同,否則,必須相同
  • 作用域修飾符的使用原則

    類中的大部分成員變量應該使用private來修飾,隻有一些static修飾的、類似全局變量的成員變量,才考慮使用public修飾

    用于輔助實作該類的工具方法,應該使用private修飾

    如果方法僅希望被子類重寫,而不想被外界直接調用,應該使用protected來修飾

4、包package

  • package機制提供了類的多層命名空間,用于解決類的命名沖突、類檔案管理等問題
  • 一般在類檔案的第一行需要标注package
  • 同一個包的類不必于相同的目錄下
  • 為Java類添加包必須在Java源檔案中通過package語句指定,單靠目錄名是沒法指定的
  • package用小寫字母命名,建議用公司域名倒寫來作為包名,如:com.shuiwujia
  • 可以使用improt static導入靜态類,此後不用類.方法(),直接使用方法()即可
  • Java源檔案大體結構
package語句 -個或個,必須放在檔案開始
imporm | import static語句 -個或多個,必須放在所有類定義之前
public classDefinition | interfaceDefinition | enumDefinition  -0個或1個
classDefinition | interfaceDefinition | enumDefinition
           
  • 預設已導入java.lang包,不需要顯式import
  • 常用Java包
java.lang:核心類,如:String、Math、System和Tread等
java.util:大量工具類/接口和集合架構類/接口,如:Arrays和List、Set等
java.net:網絡程式設計相關的類/接口
java.io:輸入/輸出程式設計相關的類/接口
java.text:格式化相關的類
java.sql進行JDBC資料庫程式設計的相關類/接口
           

5、構造器

  • 構造器最大的用處就是在建立對象時執行初始化,是建立對象的重要途徑(即使使用工廠模式、反射等方式建立對象,其實質依然是依賴于構造器),Java類必須包含一個或一個以上的構造器
  • 在構造器中調用其他構造器要使用this關鍵字,this(name,color)

6、繼承

  • 子類不能獲得父類的構造器
  • Java隻有一個直接父類,但可以有無限個間接父類
  • 子類包含與父類同名方法的現象被稱為方法重寫(Override),也叫覆寫
  • 重寫要遵循“兩同兩小一大“規則

    兩同:方法名相同,形參清單相等

    兩小:子類方法傳回值類型應比父類傳回值類型更小或者相等,子類方法聲明抛出的異常類應比父類方法聲明抛出的異常類更小或相等

    一大:子類方法的通路權限應比父類方法的通路權限更大或相等

  • 覆寫方法和被覆寫方法要麼都是類方法,要麼都是執行個體方法,必須同類
  • 當子類覆寫了父類方法後,子類的對象将無法通路父類中被覆寫的方法,但可以在方法中調用父類中被覆寫的方法,使用super(執行個體方法)或父類類名(類方法)
  • 當父類的方法用private修飾時,子類無法覆寫,即使子類定義一樣的方法,也不屬于覆寫
  • 子類定義一個與父類方法有相同的方法名,但參數清單不同的方法,就會形成父類方法和子類方法的重載
  • 子類查找變量的順序:

    局部變量

    成員變量

    父類的成員變量

    java.lang.Object類

  • 當程式建立一個子類對象時,系統不僅會為該類中定義的執行個體變量配置設定記憶體,也會為它從父類繼承得到的所有執行個體變量配置設定記憶體,即使子類定義與父類中同名的執行個體變量。
  • 當系統建立對象F時,如果該類有兩個父類(直接父類A和間接父類B),假設F有兩個執行個體變量,A有兩個執行個體變量,B有三個執行個體變量,那麼F對象将會儲存2+3+2個執行個體變量

7、多态

  • Java引用變量有兩個類型:一個是編譯時類型,一個是運作時類型。編譯時類型由聲明該變量時使用的類型決定,運作時類型由實際賦給該變量的對象決定,如Person person = new girl();
  • 如果編譯時類型和運作時類型不一緻,就可能出現多态。
  • 多态:相同類型的變量、調用同一個方法時呈現出多種不同的行為特征
  • instanceof:判斷前面對象是否是後面的類,或者其子類、實作類的執行個體,如果是傳回true,否則傳回false

8、繼承與組合

  • 繼承是實作類複用的重要手段,但繼承會破壞封裝。而組合方式實作類複用則能提供更好的封裝性
  • 為了保證父類有良好的封裝性,設計父類通常應遵循如下原則:

    盡量隐藏父類的内部資料,把父類的所有成員變量都設定成private通路類型,不要讓子類直接通路父類的成員變量

    不要讓子類可以随意通路、修改父類的方法,父類中的工具方法,應該使用private修飾,讓子類無法通路;需要被外部類調用的方法,但又不希望被子類重寫,可以使用public final修飾;隻希望某個方法被子類重寫而不希望被其它類自由通路,可以使用protected來修飾

    盡量不要在父類構造器中調用将要被子類重寫的方法,否則可能會造成報錯,父類構造器中調用被子類重寫的方法時,是直接調用子類的方法,如果方法中含有子類的執行個體程式設計時,有可能會報空指針異常

  • 使用繼承的條件:

    子類需要額外增加屬性,而不僅僅是屬性值的改變,如:Student需要grade屬性,而Person不一定需要

    子類需要增加自己獨有的行為方式,包括增加新方法或重寫父類的方法,如:Teacher需要Teaching方法,而Person不一定需要

  • 繼承要表達的是一種(is-a)的關系,而組合表達的是(has-a)的關系

    Student之于Person用繼承

    Arm之于Person用組合

9、初始化塊

  • 初始化塊是構造器的補充,初始化塊總是在構造器執行之前執行,實際上編譯後的代碼不存在初始化塊,而會還原到每個構造器中,且位于構造器所有代碼的前面
  • 靜态初始化塊也叫類初始化塊,先于初始化塊運作
  • 如果兩個構造器中有相同的初始化代碼,且這些初始化代碼無須接收參數,就可以把它們放在初始化塊中定義,提高代碼複用,提高應用的可維護性

10、包裝類

  • 包裝類的比較需要使用compare(val1,val2)來進行
Boolean.compare(true,false)輸出
Boolean.compare(true,true)輸出
Boolean.compare(false,true)輸出-
           
  • 包裝類的緩存有限,-128~127
Integer ina = ;
Integer inb = ;
ina == inb 輸出true
Integer biga = ;
Integer bigb = ;
biga == bigb輸出false
           
  • 因為包裝類是引用類型,隻有包裝類指向同一個引用時以下才相等
new Integer() == new Integer()
           

11、 ==和equals方法

  • 除基本類型外,隻有引用對象相同==才傳回true,比對内容是否相等應當使用equals
  • 常量池專門用于管理在編譯時被确定并儲存在已編譯的.class檔案中的一些資料,包括關于類、方法、接口的常量,還包括字元串常量
  • String str1 = "hello"與String str2 = new String("hello")的差別

    前者稱為直接量,會儲存在常量池,并不會産生多個副本,是以用==比對的時候,傳回true

    後者是運作時建立出來的,被儲存在堆記憶體中,不會放入常量池,是以用==比對的時候,傳回false

String s1 = "瘋狂Java";
String s2 = "瘋狂";
String s3 = "Java";
String s4 = "瘋狂" + "Java";
String s5 = s2 + s3;
s1 == s4; //傳回true
s1 == s5; //傳回false,因為s5是運作時确定
           
  • 對于對象而言,使用equals方法與使用==方法一樣,都需要指向同一個對象才會傳回true
  • 比較兩個對象需要重寫equals方法
public boolean equals(Object obj){
    if(this == obj){
        return true
    }else if(obj != null && obj.getClass == Person.class){
        Person p = (Person)obj;
        if(this.getId.equals(p.getId)){
            return true;
        }
    }
}
           

12、類成員

  • Java類裡隻能包含成員變量、方法、構造器、初始化塊、内部類(包括接口、枚舉)5種成員,以static來修飾的成員就是類成員(構造器除外),類成員屬于整個類,而不屬于單個對象(即使new的對象=null,也可以正常調用)
  • 單例類

    如果一個類始終隻能建立一個執行個體,則這個類被稱為單例類

    第一:構造器使用private修飾,進而把所有構造器隐藏起來

    第二:需要提供一個public方法作為該類的通路點,且方法必須使用static修飾,因為調用該方法前還不存在對象,隻能是類方法

    第三:還必須緩存已經建立的對象,保證隻建立一個對象,是以需要使用static成員變量

class Singleton{
    private static Singleton instance;
    private Singleton(){}
    public static Singleton getInstance(){
        if(instance == null){
            instance = new Singleton()
        }
        return instance;
    }
}
           

13、final修飾符

  • final修飾的變量不可被改變,一旦獲得了初始值,該final變量的值就不能被重新指派
  • 與普通成員變量不同,final修飾的成員變量必須由程式員顯式地指定初始值,否則不通過編譯:

    類變量:必須在靜态初始化塊中指定初始值或聲明類變量時指定初始值,而且隻能在兩個地方的其中之一指定

    執行個體變量:必須在非靜态初始化塊、聲明該執行個體變量或構造器中指定初始值,而且隻能在三個地方的其中之一指定

    如果以上不指定,此後該變量将一直是系統預設配置設定的0、false、null或’\u0000’

  • 系統不會對局部變量進行初始化,局部變量必須由程式員顯式初始化,final修飾的局部變量在定義時沒有指定預設值,則可以在後面代碼中對該final變量賦初始值,但隻能指派一次,不能重複;如果已經指定預設值,則後面代碼中不能再指派。
  • final修飾基本類型變量,不能對基本類型變量重新指派,是以基本類型變量不能被改變。但對于引用類型變量而言,它儲存的僅僅是一個引用,final隻保證這個引用類型變量所引用的位址不會改變,但這個對象完全可以改變。
final Person p = new Person();
正确:p.setAge();
錯誤:p = null;
           
  • 當定義final變量時就為該變量指定了初始值,而且該初始值可以在編譯時就确定下來,那麼這個final變量本質上就是一個“宏變量”
  • 當父類方法使用public final修飾時,子類重寫該方法會出現編譯時錯誤,原因是final方法不允許子類重寫;但父類方法使用private final修飾時,子類可以重寫該方法(其實不是重寫,隻是定義了一個新的方法)
  • 當子類繼承父類時,将可以通路到父類内部資料,并可以通過重寫父類方法來改變父類方法的實作細節,這可能導緻一些不安全的因素。為了保證這個類不可被繼承,可以使用final修飾這個類

14、不可變類(建立該類的執行個體後,該執行個體的執行個體變量是不可改變的)

  • 建立了該類的執行個體後,該執行個體的執行個體變量不可改變,成為不可變類
  • 不可變類遵守的規則:

    使用private和final修飾符來修飾該類的成員變量

    提供帶參數構造器,用于根據傳入參數來初始化類裡的成員變量

    僅提供getter方法,不系統setter方法

    如果有必要,重寫hashCode()和equals()方法

public class Address {
    private final String detail;
    private final String postCode;
    public Address() {
        this.detail = "";
        this.postCode = "";
    }
    public Address(String detail, String postCode) {
        this.detail = detail;
        this.postCode = postCode;
    }
    public String getDetail() {
        return detail;
    }
    public String getPostCode() {
        return postCode;
    }
    @Override
    public int hashCode() {
        return detail.hashCode() + postCode.hashCode() * ;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj != null && obj.getClass() == Address.class) {
            Address ad = (Address) obj;
            if (this.getDetail().equals(ad.getDetail())) {
                return true;
            }
        }
        return super.equals(obj);
    }
}
           

15、抽象類

  • 抽象類與抽象方法的規則:

    抽象類和抽象方法必須使用abstract修飾符來修飾,抽象方法不能有方法體

    抽象類不能被執行個體化,無法使用new關鍵字來調用抽象類的構造器建立抽象類的執行個體

    抽象類可以包含成員變量、方法(普通方法和抽象方法都可以)、構造器、初始化塊、内部類(接口、枚舉)5種成分

    抽象類的構造器不能用于建立執行個體,主要是被用于被子類調用

    含有抽象方法的類隻能被定義成抽象類

    抽象類可以不含有抽象方法,但即使沒有抽象方法的抽象類也不能建立執行個體

  • 當使用abstract修飾類時,表明這個類隻能被繼承;當使用abstract修飾方法時,表明這個方法必須由子類重寫
  • final修飾的類不能被繼承,final修飾的方法不能被重寫,是以final和abstract永遠不能同時使用
  • 由于抽象方法沒有方法體,是以abstract也不能與static同時使用
  • 抽象方法必須被其子類重寫才有意義,是以abstract也不能與private同時使用
  • 抽象類的作用

    抽象類是從多個具體類中抽象出來的父類,具有更高層次的抽象

    從多個具有相同特征的類中抽象出一個類,作為子類的模版,進而避免了子類設計的随意性

    子類在抽象類的基礎上進行擴充、改造,但子類總體會大緻保留抽象類的行為方式

16、接口(interface)

特殊的抽象類,不包含普通方法,接口裡的所有方法都是抽象的

- 接口定義的是多個類共同的公共行為規範,這些行為是與外部交流的通道,意味着接口裡通常是定義一組公用方法

- 接口的基本文法

[修飾符]interface 接口名 extends 父接口1,父接口2...{
    零個到多個常量定義
    零個到多個抽象方法定義
    零個到多個内部類、接口、枚舉定義
    零個到多個預設方法或類方法定義(Java- 
}
           
  • 接口定義的是一種規範,接口不能包含構造器和初始化塊定義
  • 接口裡可以包含成員變量(隻能是靜态常量)、方法(隻能是抽象執行個體方法、類方法和預設方法)、内部類(包含内部接口、枚舉)定義
  • 在接口中定義的成員變量,無論是否使用public static final修飾符,接口都總是使用這三個修飾符修飾
  • 接口裡沒有構造器和初始化塊,是以接口裡定義的成員變量隻能在定義時指定預設值
  • 接口裡的普通方法不管是否使用public abstract修飾,接口總是使用public abstract來修飾
  • 接口裡的普通方法不能有方法體,但類方法、預設方法都必須有方法體
  • 接口裡定義的内部類、内部接口、内部枚舉預設都采用public static來修飾,不管是否顯式寫出
  • Java8預設方法使用default來修飾,預設是public,不管是否顯式寫出(不能用static修飾)
  • Java8允許在接口中定義類方法,類方法必須用static修飾,不能與default同時使用,且預設一頂是public的
  • public接口的類名與檔案名必須相同
  • 接口支援多繼承,子接口繼承父接口,将會獲得父接口裡定義的所有抽象方法、常量
  • 接口的主要用途

    定義變量,也可以用于進行強制轉換

    調用接口中定義的常量

    被其他類實作

  • 接口和抽象類相似之處:

    都不能被執行個體化,都位于繼承樹的頂端,用于被其它類實作和繼承

    都可以包含抽象方法,實作接口或繼承抽象類的普通子類都必須實作這些抽象方法

  • 接口和抽象類的不同之處:

    接口展現一種規範,一般不能随意修改,修改後對其它類影響很大

    抽象類所展現的是一種模版式設計,是系統實作過程的中間産品

    接口裡隻能包含抽象方法和預設方法,不能為普通方法提供方法實作;抽象類則完全可以包含普通方法

    接口裡不能定義靜态方法;抽象類裡可以定義靜态方法

    接口裡隻能定義靜态常量,不能定義普通成員變量;抽象類則兩者都可以定義

    接口裡不包含構造器;抽象類裡可以包含構造器,其構造器不是用于建立對象,而是讓其子類調用這些構造器來完成抽象類的初始化操作

    接口裡不能包含初始化塊;抽象類則可以

    一個類最多隻能有一個直接父類,包括抽象類;但一個類可以直接實作多個接口,通過實作多個接口可以彌補Java單繼承的不足

  • 接口展現的是一種規範和實作分離的設計哲學,充分利用接口可以極好地降低程式各子產品之間的耦合,進而提高系統的可擴充性和可維護性
  • 簡單工廠模式

    簡單來說就是用接口替代實作類,用實作類去實作,稱為接口的引用,當後續要更換執行個體是,僅僅隻修改工廠類即可,而無須修改大量的類

    接口已經将需要用到的規範(方法)全部定義好,實作類實作接口并實作抽象方法,是以無論誰去實作,隻要符合接口的規範即可

public class OutputFactory{
    public Output getOutput(){
        // 下面既可以是Printer也可以是PrinterBetter,隻要實作了Output接口即可
        return new Printer();
    }
}
           
  • 指令模式

    某個方法需要完成某一個行為,但這個行為的具體實作無法确定,必須等到執行該方法是才能确定,叫做指令模式

    實際上指令模式就是将接口作為參數來傳遞給方法,在方法體中調用接口的方法,當調用該方法時,傳遞進去的是不同的接口實作類

public interface Command{
    void process(int[] target){
    }
    public class ProcessArray{
        public void process(int[] target,Command cmd){
        cmd.process(target);
    }
    public static void main(String[] args){
        ProcessArray pa = new ProcessArray();
        int[] target = {,,,};
        pa.process(target,new PrintCommand());
        pa.process(target,new AddCommand())
    }
}
           

17、内部類

  • 把一個類放在另一個類的内部定義,這個定在在其他類内部的類被稱為内部類,包含内部類的類稱為外部類
  • 内部類的作用

    提供更好的封裝,可以把内部類隐藏在外部類之内,不允許同一個包的其它類通路該類。如:CowLeg之于Cow,離開了Cow就沒有意義

    内部類成員可以直接通路外部類的私有資料

    匿名内部類适合于建立那些僅僅需要一次使用的類

  • 内部類比外部類可以多使用三個修飾符:private、protected、static,非靜态内部類不能擁有靜态成員
  • 非靜态内部類

    沒有用static修飾的内部類叫非靜态内部類

    當在非靜态内部類的方法内通路某個變量時,查找順序是:本方法内->内部類成員變量->外部類成員變量

    若出現同名情況,通路内部類成員變量可以使用:this.變量名,通路外部類成員變量可以使用:外部類.this.變量名

    内部類可以通路外部類的成員變量,private修飾的也可以,但外部類就不能直接通路内部類的成員變量,需要通過建立非靜态内部類對象來調用通路其執行個體成員變量

  • 靜态内部類

    用static來修飾的内部類叫做靜态内部類

    靜态内部類可以包含靜态成員,也可以包含非靜态成員

    接口裡定的内部類預設使用public static修飾

  • 使用内部類

    在外部類中使用内部類與普通類無差别

    在外部類以外使用非靜态内部類

    new OuterClass().new InnerClass()

    在外部類以外使用靜态内部類

    new OuterClass().InnerClass();

  • 把一個内部類放在方法裡定義,則這個内部類就是一個局部内部類,局部内部類僅在方法裡有效,不能使用通路控制符和static修飾符
  • 匿名内部類

    匿名内部類适合建立那種隻需要一次使用的類

new實作接口() | 父類構造器(實參清單){
    類體部分
}
           
  • 匿名内部類必須繼承一個父類,或實作一個接口,但最多隻能繼承一個父類或實作一個接口

    規則:

    匿名内部類不能是抽象類,因為建立時系統會建立對象

    匿名内部類不能定義構造器,因為沒有類名,但可以定義初始化塊

    建立匿名内部類時,必須實作接口或抽象父類裡的所有抽象方法,也允許重寫父類中的普通方法

    Java8開始,允許局部内部類、匿名内部類通路的局部變量不使用final修飾,但系統預設還是帶有final,是以還是不能重新對變量指派

  • Lambda表達式

    Java8的新特性,主要作用就是代替匿名内部類的繁瑣文法

    Lambda表達式由三部分組成:

    形參清單、箭頭、代碼塊

    Lamba表達式的兩個限制:

    目标類型必須是明确的函數式接口

    隻能實作一個方法

    函數式接口代表隻包含一個抽象方法的接口,函數式接口可以包含多個預設方法、類方法,但隻能聲明一個抽象方法

  • Lambda表達式執行個體
// 引用類方法:
interface Converter{
    Integer convert(String from)
}
Converter converter1 = from -> Integer.valueOf(from);
Integer val = converter1.convert("99");
System.out.println(val);
可以寫成:
Converter converter1 = Integer::valueOf;

// 引用特定對象的執行個體方法
Converter converter2 = from -> "fkit.org".indexOf(from);
Integer value = converter2.convert("it");
System.out.println(value);
可以寫成:
Converter converter1 = "fkit.org"::indexOf;

// 引用某類對象的執行個體方法
interface MyTest{
    String text(String a,int b,int c)
}
MyTest mt = (a,b,c) -> a.substring(b,c)
String str = mt.test("java hello my word",,);
可以寫成:
MyTest mt = String::substring

// 引用構造器
interface YourTest{
    JFrame win(String title);
}
YourTest yt = (String a) -> new JFrame(a);
JFrame jf = yt.win("my win");
可以寫成:
YourTest yt = JFrame::new;
           
  • Lambda表達式與匿名内部類的聯系與差別
    • 聯系:

      都可以直接通路final局部變量,以及外部類的成員變量

      都可以直接調用從接口中繼承的預設方法

    • 差別:

      匿名内部類可以為任意接口建立執行個體-不管接口包含多少個抽象方法,隻要匿名内部類實作所有抽象方法即可;Lambda表達式隻能為函數式接口建立執行個體

      匿名内部類可以為抽象類甚至普通類建立執行個體;但Lambda表達式隻能為函數式接口建立執行個體

      匿名内部類實作的抽象方法的方法體允許調用接口中定義的預設方法;但Lambda表達式代碼不允許調用接口中的預設方法

  • Lambda表達式與Arrays
String[] arr1 = new String[]{"java","faka","kkth","sdkjfei"};
Arrays.parallelSort(arr1,(o1,o2)->o1.length()-o2.length());
int[] arr2 = new String[]{12,-3,76,9,0};
// 前一個元素乘以後一個,[12,-36,...]
Arrays.parallelSort(arr2,(left,right)->left*right);
long[] arr3 = new long[5];
// 填充5的倍數
Arrays.parallelSetAll(arr3,operand->operand*)
           

18、枚舉類

  • 一個類的對象是有限而且固定的,這種執行個體有限而且固定的類,叫做枚舉類
  • 枚舉類是一種特殊的類,它可以有自己的成員變量、方法,可以實作一個或者多個接口,可以定義自己的構造器
  • 與普通類的差別:

    枚舉類可以實作一個或多個接口,使用enum定義的枚舉類預設繼承了java.lang.Enum類,而不是預設繼承Object類,是以枚舉類不能顯式繼承其它父類;java.lang.Enum類實作了java.langSerializable和java.lang.Comparable兩個接口

    使用enum定義、非抽象的枚舉類預設會使用final修飾,是以枚舉類不能派生子類

    枚舉類的構造器隻能使用private通路控制符,如果省略了構造器的通路控制符,也是預設使用private修飾

    枚舉類的所有執行個體必須在枚舉類的第一行顯式列出,否則這個枚舉類永遠都不能産生執行個體;列出這些執行個體時,系統會自動添加public static final修飾,無須顯式添加

  • values()方法可以周遊所有枚舉值
  • swicth支援枚舉類
int compareTo(E o):對比兩個枚舉值的順序
String name():傳回執行個體名稱
String toString():同上
int ordinal():傳回索引值
public static <T extends Enum<T>> T valueOf(Class<T> enumType,String name):用于傳回指定枚舉類中指定名稱的枚舉值
Gender g = Enum.valueOf(Gender.class,"FEMALE");
           

6)将枚舉類設計成不可變類

public enum Gender{
    MALE("男"),FEMALE("女");
    private final String name;
    private Gender(String name){
        this.name = name
    }
    private String getName(){
        return this.name;
    }
}
           
  • 實作接口的枚舉類(匿名内部類)
public interface GenderDesc{
    void info();
}
public enum Gender implements GenderDesc{
    MALE("男"){
        public void info(){
            System.out.println("男");
        }
    },
    FEMALE("女"){
        public void info(){
            System.out.println("女");
        }
    };
    private final String name;
    private Gender(String name){
        this.name = name
    }
    private String getName(){
        return this.name;
    }
}
           
  • 包含抽象方法的枚舉類
public enum Operation {
    PLUS{
        public double eval(double x,double y) {
            return x + y;
        }
    },
    MINUS{
        public double eval(double x,double y) {
            return x - y;
        }
    },
    TIMES{
        public double eval(double x,double y) {
            return x * y;
        }
    },
    DIVIDE{
        public double eval(double x,double y) {
            return x / y;
        }
    };
    public abstract double eval(double x,double y);
    public static void main(String[] args) {
        System.out.println(Operation.PLUS.eval(, ));
        System.out.println(Operation.MINUS.eval(, ));
        System.out.println(Operation.TIMES.eval(, ));
        System.out.println(Operation.DIVIDE.eval(, ));
    }
}