當前位置: 妍妍網 > 碼農

Java 語法糖,提高程式碼效率神器!

2024-03-02碼農

引言:語法糖經常是大廠面試官常問的一個知識點,關於 Java 的語法糖很多人可能只是知道其中的某幾個,但卻對整體的結構不了解,本文將詳細介紹 Java 語法糖的知識。

題目

什麽是 Java 語法糖?

推薦解析

什麽是語法糖?

語法糖是指一種程式語言的語法結構,它並不提供新的功能,但使程式碼更易讀、更易寫,提高了開發效率。這些語法糖通常是一種編譯器提供的便利性,它將一種常見的模式或程式碼結構用更簡潔、更易懂的語法表示出來。

語法糖的目的是什麽?

語法糖的目的是提高程式碼的可讀性和編寫效率,而不是引入新的功能或改變語言的本質。開發者可以使用語法糖來編寫更簡潔、更清晰的程式碼,而不必深入了解底層的實作細節。

Java 的語法糖

Switch 支持 String 與列舉

舉個例子

public classswitchDemoString{
publicstaticvoidmain(String[] args){
String str = "nihao";
switch (str) {
case"nihao":
System.out.println("nihao");
break;
case"hello":
System.out.println("hello");
break;
default:
break;
}
}
}

Switch (字串) 是用字串的 hashcode 進行比較,case 中再利用 equals 進行判斷,因為可能會產生 hash 沖突的情況。

泛型

對於 Java 虛擬機器來說,需要在編譯階段透過型別擦除的方式進行解語法糖。

型別擦除的主要過程如下:1.將所有的泛型參數用其最左邊界(最頂級的父型別)型別替換。2.移除所有的型別參數。

舉個例子

Map<String, String> map = new HashMap<String, String>();
map.put("xiaobaitiao""nihao");

解語法糖之後

Map map = new HashMap();
map.put("xiaobaitiao""nihao");

在虛擬機器中,泛型的實作涉及到型別擦除(Type Erasure)的概念。型別擦除是指在編譯時泛型型別資訊被擦除,而在執行時,泛型的例項僅保留原始型別(raw type)的資訊。這導致在虛擬機器中,所有泛型類的型別參數在編譯時都會被擦除,並且泛型類並沒有自己獨有的 class 類物件。

具體來說,泛型類在編譯後的字節碼中不會保留其泛型資訊,而是被替換為原始型別。例如,對於 List<String> List<Integer> ,在執行時只存在一個 List 類的物件,而無法透過 List<String>. class List<Integer>. class 這樣的語法來獲取類物件。

這樣的設計是為了保持 Java 的回溯相容性,因為泛型是在 Java 5引入的,而在引入泛型之前的程式碼中是沒有泛型資訊的。因此,為了確保與舊程式碼的互操作性,Java 編譯器使用了型別擦除來處理泛型。

自動裝箱和拆箱

這個語法糖應該是較為熟知的,自動裝箱就是 Java 自動將原始型別值轉換成對應的物件,比如將 int 的變量轉換成 Integer 物件,這個過程叫做裝箱,反之將 Integer 物件轉換成 int 型別值,這個過程叫做拆箱。因為不用人工去轉化,因此是自動裝箱和拆箱。

舉個例子(自動裝箱)

publicstaticvoidmain(String[] args){
int num = 1;
Integer n = num;
}

反編譯

publicstaticvoidmain(String args[])
{
int num = 1;
Integer n = Integer.valueOf(num);
}

自動拆箱

publicstaticvoidmain(String[] args){
Integer num = 10;
int n = num;
}

反編譯

publicstaticvoidmain(String args[])
{
Integer num = Integer.valueOf(10);
int n = num.intValue();
}

總結:裝箱用 valueOf() 方法,拆箱用 xxxValue 方法,比如 intValue。

可變長參數

publicstaticvoidprint(String... strs)
{
for (int i = 0; i < strs.length; i++)
{
System.out.println(strs[i]);
}
}

可以用... 去代表可變長參數,相當於一個一維字串陣列。一般不太常用。

斷言

在 Java 中, assert 關鍵字是從 JAVA SE 1.4 引入的,為了避免和老版本的 Java 程式碼中使用了 assert 關鍵字導致錯誤,Java 在執行的時候預設是不啟動斷言檢查的。

舉個例子

public classTest{
publicstaticvoidmain(String args[]){
int a = 1;
int b = 1;
assert a == b;
System.out.println("Hello Word");
}
}

當 a不等於b 時,會丟擲斷言的異常,相等才會輸出 HelloWorld,反編譯後相當於一個 if else。

數值字面量

在 java 7 中,數值字面量,不管是整數還是浮點數,都允許在數位之間插入任意多個底線。

public classTest{
publicstaticvoidmain(String... args){
int i = 10_000;
System.out.println(i);
}
}

反編譯後會自動把底線去掉,就是為了方便閱讀。

For-Each

For-Each 是程式設計師都會經常用到的,底層也是普遍都講過是利用 for 迴圈和叠代器,因此使用要註意 fail-fast 機制,建議了解 Stream 流的知識。

舉個例子

publicstaticvoidmain(String... args){
String[] strs = {"Hello"};
for (String s : strs) {
System.out.println(s);
}
List<String> strList = ImmutableList.of("Hello");
for (String s : strList) {
System.out.println(s);
}
}

publicstatictransientvoidmain(String args[])
{
String strs[] = {
"Hello"
};
String args1[] = strs;
int i = args1.length;
for(int j = 0; j < i; j++)
{
String s = args1[j];
System.out.println(s);
}
List strList = ImmutableList.of("Hello");
String s;
for(Iterator iterator = strList.iterator(); iterator.hasNext(); System.out.println(s))
s = (String)iterator.next();
}

註意事項

1)方法多載,但由於泛型擦除,導致編譯失敗

publicstaticvoidmethod(List<String> list){
System.out.println("Hello World String list");
}
publicstaticvoidmethod(List<Long> list){
System.out.println("Hello World Long list");
}

2)try catch 中的異常不能再去區別異常的泛型型別,同樣是泛型擦除機制。

3)自動拆箱和裝箱,但是整數值有緩存機制。

publicstaticvoidmain(String[] args){
Integer a = 129;
Integer b = 129;
Integer c = 100;
Integer d = 100;
System.out.println(a == b);
System.out.println(c == d);
}

輸出結果

false
true

-128 到 127 和緩存機制可以實作重用,也就是說使用的是相同的物件參照,這個也比較經典。

4)增強 for 迴圈,上文有提到過,增強 for 底層有用到叠代器,叠代器在遍歷的時候物件不允許修改或者刪除。因此 CMS 異常也就是 fail-fast 多執行緒修改,解決方法可以直接用叠代器或者 Stream 流的方法,推薦 Stream 流掌握。

總結

語法糖在大多數情況下被我們所使用,能夠較大地提升開發效率,但是在開發效率提升的同時,我們要明白語法糖的底層原理,也就是反編譯後的程式碼,JVM 虛擬機器是怎麽最佳化的,有哪些最佳化機制,可能會發生什麽問題,這是我們需要註意的點,避免踩坑。

其他補充

魚聰明 AI 的回答:

魚聰明 AI 地址:https://www.yucongming.com/

Java 的語法糖是指一些在語法層面上提供的便利性,使得程式碼更加簡潔和易讀。這些語法糖並不引入新的功能,而是提供了一種更方便的語法形式。以下是 Java 中常見的語法糖:

  1. 泛型: Java 中的泛型提供了參數化型別的能力,使得在編寫集合類等泛型類時可以更安全和更易用。但實際上,Java 的泛型是透過型別擦除來實作的,所以在執行時泛型資訊是不可見的。

  2. 自動裝箱和拆箱: 基本數據型別和對應的包裝型別之間可以自動進行轉換,這使得程式碼更易讀。例如, int Integer 的轉換。

  3. 增強的 for 迴圈: 提供了更簡潔的方式來叠代集合或陣列,避免了手動管理索引的麻煩。

  4. 列舉型別: 提供了更簡潔、型別安全的列舉定義方式,避免了使用常量的寫死。

  5. 可變參數(Varargs): 允許方法接受可變數量的參數,而無需手動建立陣列。

  6. Lambda 運算式: 引入了函數語言程式設計的概念,使得可以更輕松地編寫簡潔的匿名函式。

  7. Diamond 語法(菱形語法): 在建立泛型例項時,允許省略型別參數的重復聲明。

使用語法糖可能遇到的坑:

  1. 泛型型別擦除: 雖然泛型提供了更安全的集合操作,但由於型別擦除的存在,可能會導致在執行時無法獲取泛型型別資訊。

  2. 自動裝箱拆箱帶來的效能開銷: 自動裝箱和拆箱可能導致額外的效能開銷,特別是在大量數據操作時。

  3. Lambda 運算式的閉包問題: 在使用 Lambda 運算式時,需要註意對外部變量的存取,因為 Lambda 運算式預設是對外部變量的「讀取」操作,對於局部變量需要是最終的(effectively final)。

總結:

Java 的語法糖提供了一系列便捷性,使得程式碼更加簡潔、易讀,並提高了開發效率。然而,在使用語法糖時需要註意可能遇到的一些陷阱,如泛型型別擦除、自動裝箱拆箱的效能開銷,以及 Lambda 運算式的閉包問題。理解這些細節有助於更好地利用語法糖,同時避免潛在的問題。

推薦文章

文章:https://zhuanlan.zhihu.com/p/600475561

歡迎交流

在閱讀完本文之後,你應該了解什麽是語法糖,語法的目的,在 Java 中常見語法糖的套用,以及需要註意的事項,如何利用好語法糖加快開發效率並且避免踩坑是一個重點,接下來我將提出關於語法糖的三個問題,小夥伴可以評論區留言踴躍發表見解,一起交流進步!

1)如何在執行時獲取泛型類的資訊進行處理?

2)如何有效避免或降低自動裝箱和拆箱帶來的效能問題?

3)如何避免或解決 Lambda 運算式的閉包問題?

往期推薦