編譯器處理--語法糖
java中類加載的完整流程大概包括
編譯器處理-->字節(jié)碼文件-->類加載階段-->運行期優(yōu)化
第一部分 編譯器處理 主要涉及到一些java語言中的語法糖,本文列出了大部分語法糖優(yōu)化及其底層轉(zhuǎn)換原理,同時這部分涉及到面試時兩個常見考點
- 泛型擦除
- lambda表達(dá)式和匿名內(nèi)部類的原理
語法糖
java編譯器把 *.java 源碼編譯為 *.class 字節(jié)碼的過程中,自動生成和轉(zhuǎn)換的一些代碼,主要是為了減輕程序員的負(fù)擔(dān)
生成默認(rèn)無參構(gòu)造方法
其實是調(diào)用父類的構(gòu)造方法
自動拆/裝箱
Integer.valueOf(1);//裝箱
integer.intValue();//拆箱
泛型集合
泛型是在 JDK 5 開始加入的特性,但 java 在編譯泛型代碼后會執(zhí)行 泛型擦除 的動作,即泛型信息在編譯為字節(jié)碼之后就丟失了,實際的類型都當(dāng)做了 Object 類型來處理:
public class Candy3 {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(10); // 實際調(diào)用的是 List.add(Object e)
Integer x = list.get(0); // 實際調(diào)用的是 Object obj = List.get(int index);
}
}
所以在取值時,編譯器真正生成的字節(jié)碼中,還要額外做一個類型轉(zhuǎn)換的操作:
// 需要將 Object 轉(zhuǎn)為 Integer
Integer x = (Integer)list.get(0);
如果前面的 x 變量類型修改為 int 基本類型那么最終生成的字節(jié)碼是:
// 需要將 Object 轉(zhuǎn)為 Integer,并執(zhí)行拆箱操作
int x = ((Integer)list.get(0)).intValue();
foreach循環(huán)
- 對數(shù)組:底層是fori循環(huán)
- 對實現(xiàn)了Iterable接口的集合:迭代器循環(huán)
switch字符串
轉(zhuǎn)為switch比較hash碼+equals
switch枚舉類
通過jvm層面生成一個內(nèi)部類用一個int數(shù)組來存儲枚舉類的序號及其對應(yīng)的整數(shù)值,數(shù)組大小即為枚舉值的個數(shù)
switch通過枚舉類序號和對應(yīng)整數(shù)值作比較
枚舉類
生成一個final修飾,繼承Enum接口的class類
匿名內(nèi)部類
會為接口創(chuàng)建一個新的類,類中重寫目標(biāo)方法,然后外部調(diào)用時等于new一個新的類對象,然后調(diào)用目標(biāo)方法
注:
如果目標(biāo)方法是有參數(shù)的,那么創(chuàng)建class類時會將參數(shù)作為成員變量,并通過有參構(gòu)造來賦值
這也是為什么匿名內(nèi)部類的方法參數(shù)需要final修飾,因為新創(chuàng)建class內(nèi)的成員變量只有在初始化時能賦予值,然后則無法修改,因此需要外部變量被final修飾來確保不變性
lambda表達(dá)式與匿名內(nèi)部類
-
lambda表達(dá)式通過編譯時生成一個實現(xiàn)了函數(shù)式接口的匿名類
具體詳細(xì)實現(xiàn):
首次執(zhí)行 Lambda 時:
- JVM 通過
invokedynamic
觸發(fā)LambdaMetafactory
。 - 動態(tài)生成一個類實現(xiàn)目標(biāo)接口,覆蓋其唯一抽象方法。
- 該方法內(nèi)部調(diào)用編譯時生成的靜態(tài)方法(即 Lambda 體)。
- 后續(xù)調(diào)用直接使用已生成的類,無需重復(fù)創(chuàng)建。
- JVM 通過
-
函數(shù)式接口
@FunctionalInterface 注解
- 確保唯一抽象方法:被
@FunctionalInterface
注解的接口必須有且僅有一個抽象方法(允許有默認(rèn)方法、靜態(tài)方法或覆蓋Object
類的方法,如toString()
)。 - 防御性編程:防止接口被意外修改,破壞已有的 Lambda 實現(xiàn),如果接口不滿足條件(如沒有抽象方法或多個抽象方法),編譯器會報錯。
- 確保唯一抽象方法:被
-
和匿名內(nèi)部類的區(qū)別:
函數(shù)式接口
- 匿名內(nèi)部類:可以繼承類或?qū)崿F(xiàn)任意接口,不限于函數(shù)式接口。
- Lambda 表達(dá)式:必須實現(xiàn)一個函數(shù)式接口,即只有一個抽象方法的接口。
編譯時行為
- 匿名內(nèi)部類:在編譯時會創(chuàng)建一個完整的類文件,每個匿名內(nèi)部類都是一個獨立的類。
- Lambda 表達(dá)式:在編譯時會被轉(zhuǎn)換為一個實現(xiàn)了函數(shù)式接口的匿名類的實例,這個匿名類通常不會生成單獨的類文件。
記錄fengdongnan的知識產(chǎn)出文檔,歡迎大家來一起交流學(xué)習(xí)