初始化塊總在構造器執行前被調用
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語句 -0個或1個,必須放在文件開始
imporm | import static語句 -0個或多個,必須放在所有類定義之前
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)輸出1
Boolean.compare(true,true)輸出0
Boolean.compare(false,true)輸出-1
- 包裝類的緩存有限,-128~127
Integer ina = 2;
Integer inb = 2;
ina == inb 輸出true
Integer biga = 128;
Integer bigb = 128;
biga == bigb輸出false
- 因爲包裝類是引用類型,只有包裝類指向同一個引用時以下才相等
new Integer(2) == new Integer(2)
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(45);
正確:p.setAge(23);
錯誤: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() * 31;
}
@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 = {2,4,6,8};
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",2,9);
可以寫成:
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*5)
18、枚舉類
- 一個類的對象是有限而且固定的,這種實例有限而且固定的類,叫做枚舉類
- 枚舉類是一種特殊的類,它可以有自己的成員變量、方法,可以實現一個或者多個接口,可以定義自己的構造器
- 與普通類的區別:
枚舉類可以實現一個或多個接口,使用enum定義的枚舉類默認繼承了java.lang.Enum類,而不是默認繼承Object類,因此枚舉類不能顯式繼承其它父類;java.lang.Enum類實現了java.langSerializable和java.lang.Comparable兩個接口
使用enum定義、非抽象的枚舉類默認會使用final修飾,因此枚舉類不能派生子類
枚舉類的構造器只能使用private訪問控制符,如果省略了構造器的訪問控制符,也是默認使用private修飾
枚舉類的所有實例必須在枚舉類的第一行顯式列出,否則這個枚舉類永遠都不能產生實例;列出這些實例時,系統會自動添加public static final修飾,無須顯式添加 - values()方法可以遍歷所有枚舉值
for(SessonEnum s:SeasonEnum.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(3, 4));
System.out.println(Operation.MINUS.eval(3, 4));
System.out.println(Operation.TIMES.eval(3, 4));
System.out.println(Operation.DIVIDE.eval(3, 4));
}
}