1. 程式人生 > 其它 >JAVA新手入門08~語法糖

JAVA新手入門08~語法糖

技術標籤:Java

Java的語法糖,就是編譯器為了方便大家寫程式碼,對於一些程式碼,做了一些簡化的語法,可以讓大家在平時寫程式碼的時候,更方便寫出容易閱讀的程式碼,但是在編譯完之後,編譯器會把這些程式碼,再還原回來。在Java中,有1個大家最經常使用的語法糖,就是整形變數的自動裝箱,Integer a = 10;很明顯10不是一個物件,Integer是一個物件,不同的東西,不能賦值,為啥還可以這麼做呢? 這個東西就是Java的語法糖。

Java中的語法糖有以下幾種:

  1. 泛型(JDK1.5)

  2. 自動裝箱和拆箱(JDK1.5)

  3. 變長引數(JDK1.5)

  4. 實現Iterable介面的foreach語法(JDK1.5)

  5. 介面AutoCloseable支援的try-with-resource用法(JDK1.7)

  6. switch支援列舉和String(JDK1.7)

1、泛型(JDK1.5)

泛型是1個程式語言是否友好的重要標誌,時下火熱的Go語言,至今還沒有實現泛型,Java中的泛型是通過型別擦除實現的,並不是真正意義上的泛型,由於本文不打算深入講解泛型,在此不過多敘述。看以下泛型編譯之後,被JVM實際執行的程式碼是什麼樣子的

原始碼(1.5之前)

List arr = new ArrayList();
arr.add("this is first");
arr.add(123);
for
(Object obj : arr) { System.out.println(obj); } // Class檔案(1.5之前): List arr = new ArrayList(); arr.add("this is first"); arr.add(123); Iterator var6 = arr.iterator(); while(var6.hasNext()) { Object obj = var6.next(); System.out.println(obj); }

JDK1.5之後,為了相容1.5之前的程式碼,老程式碼可以保持不變,新程式碼如果是:

List<String> arr = new ArrayList<String>();
arr.add("this is first");
arr.add(123);

會報編譯器錯誤,原理是一樣的,都會在編譯期轉變成Object物件,然後強制轉換成需要的型別。

2、自動裝箱和拆箱(JDK1.5)

直接上程式碼吧,這個比較容易理解,就是JAVA會自動在基本型別和基本型別的包裝類之間,進行自動轉換,當然這只是一種語法糖,程式碼編譯成class檔案的時候,就會自動把這個轉換成對應的方法:

//原始碼
int a = 1290;
Integer b = 1000;
Integer a1 = a;  // 自動裝箱
int b1 = b.intValue(); //自動拆箱

// Class檔案:
int a = 1290;
Integer b = new Integer(1000);
Integer a1 = Integer.valueOf(a);
int b1 = b; 

其它的如:

Character ch = 'a'
String s = "abc";

Integer a = 10;
int b = a + 100; //操作符做運算觸發自動拆箱

//函式呼叫引數是包裝類,觸發自動拆箱/裝箱
int c = add(10,30);
private int add(Integer a,Integer b) {
return a + b;
}

Java中的以下幾種型別都支援自動拆箱,裝箱。

  • Primitive type Wrapper class
  • boolean Boolean
  • byte Byte
  • char Character
  • float Float
  • int Integer
  • long Long
  • short Short
  • double Double

資料來源:
https://docs.oracle.com/javase/tutorial/java/data/autoboxing.html

3、變長引數(JDK1.5)

變長引數指的是,一個方法的引數,可以有多個,具體有多少個,不確定,如果沒有語法糖,我們一般會傳一個List的引用進去,下面來看看,Java的這個變長引數是怎麼實現的(強制轉換為陣列),不過平時我用的也不多。

//原始碼:
private void printStr(String... arr) {
    for (String s : arr) {
    	System.out.println(s);
    }
}
//class檔案:
private void printStr(String... arr) {
    String[] var2 = arr;    //這裡可以看出來,先把引數轉為陣列,然後讀取陣列來做的
    int var3 = arr.length;

    for(int var4 = 0; var4 < var3; ++var4) {
      String s = var2[var4];
      System.out.println(s);
    }
}

4、實現Iterable介面的foreach語法(JDK1.5)

這個是平時用到的最多的一個語法糖了,如果你剛開始寫Java,會遇到很多這種程式碼,算是用的比較多的一個了,其實上面程式碼中有體現,直接轉Iterator迭代實現,所以需要實現了Iterable介面的類,才可以使用這個語法糖。

// 原始碼
List<String> arr = new ArrayList<String>();
for (String s : arr) {
	System.out.println(s);
}
//class檔案:
Iterator var6 = arr.iterator();
while(var6.hasNext()) {
  String s = (String)var6.next();
  System.out.println(s);
}

5、介面AutoCloseable支援的try-with-resource用法(JDK1.7)

這個用的就更少了,我很少在實際的專案中見到這樣的程式碼,也可能是因為平時讀取資源的檔案不多,平時很多時候都是CRUD了,僅僅是1個糖,編譯後,又還原了最初的樣子,還是傳統的try{} catch() {} finally{} 這個結構。

//原始碼:

String content = "this is what you want to write to file";
try (BufferedWriter out = new BufferedWriter(new
       FileWriter("/tmp/a.txt", true))) {
       out.write(content);
       System.out.println("content is :" + content);
} catch (IOException e) {
// error processing code
} finally {

}

//class
try {
   BufferedWriter out = new BufferedWriter(new FileWriter("/tmp/a.txt", true));
   Throwable var8 = null;
  try {
    out.write(content);
    System.out.println("content is :" + content);  
  } catch (Throwable var26) {
    var8 = var26;
    throw var26;
  } finally {
    if (out != null) {
      if (var8 != null) {
        try {
          out.close();
        } catch (Throwable var25) {
          var8.addSuppressed(var25);
        }
      } else {
        out.close();
      }
  }
}
} catch (IOException var28) {

} finally {

}

6、switch支援列舉和String(JDK1.7)

這個就比較冷門了,平時程式設計使用switch的時候,很少使用String來做列舉,一般都是用int的比較多。

//原始碼:
String s = "abc";
  switch (s) {
    case "abc":
        System.out.println("this is abc");
        break;
    case "c":
        System.out.println("this is c");
        break;
    default:
        break;
  }


//class檔案:
String s = "abc";
byte var3 = -1;
switch(s.hashCode()) {
  case 99:
    if (s.equals("c")) {
       var3 = 1;
    }
    break;
    case 96354:
      if (s.equals("abc")) {
        var3 = 0;
      }
}

switch(var3) {
    case 0:
      System.out.println("this is abc");
      break;
    case 1:
      System.out.println("this is c");
}

可見是計算了string的hashcode,然後把hashcode轉換為了整型的switch,為了處理hash衝突,使用equals來做最後的確認,並不是在底層就支援String,所以具體在使用的時候,需要知道原理是什麼,如果String串特別長,很明顯效能就不太好,不適合使用。

結尾

語法糖是java中的一個小小的插曲,不會對你的程式設計生涯造成什麼太大的影響,即使不專門學習,也能在專案中學習到,因為用到的實在太多了,學了一下,感覺也沒有提升太多的知識,歸根到底,還是因為這個東西太簡單了,如果說非要有一點要注意的地方,就是整形的自動裝箱和拆箱,會有一個小小的坑,那就是日常使用的時候,經常會寫這樣的程式碼:

Integer a = 10;
int b = 20;
if(a != null && a == b) { // a != null 如果省略了,可能會空指標
	do something
} else {
	do something
}

當我們在比較a和b的值的時候,需要心裡清楚,這是Java的自動拆箱,有的時候也會有空指標異常,所以一定要保證a != null 說到包裝類的比較,還有一個小知識點,需要注意的地方,就是包裝類的快取:

Integer a = 127;
Integer b = 127;
boolean b1 = a == b;

Integer a0 = 128;
Integer b0 = 128;
boolean b2 = a0 == b0;

System.out.println("b1 is :" + b1);
System.out.println("b2 is :" + b2)

執行上面的程式碼,b1和b2是true還是false呢? 實際的結果是b1 is : true b2 is : false,為什麼呢? 因為Java對於包裝類,有1個快取,a和b指向的是同1個物件,這個快取的範圍是 -128 到 127 之間,在這個範圍內,2個包裝類都是相等的。感興趣的話可以看Integer的原始碼,裡面有1個IntegerCache的內部類。

文字完。

關注我的部落格,獲取更多Java程式設計知識: 雙King的技術部落格