精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

不了解這12個語法糖,別說你會Java!

開發 開發工具
本文從 Java 編譯原理角度,深入字節碼及 class 文件,抽絲剝繭,了解 Java 中的語法糖原理及用法,幫助大家在學會如何使用 Java 語法糖的同時,了解這些語法糖背后的原理

本文從 Java 編譯原理角度,深入字節碼及 class 文件,抽絲剝繭,了解 Java 中的語法糖原理及用法,幫助大家在學會如何使用 Java 語法糖的同時,了解這些語法糖背后的原理。

[[265207]]

語法糖

語法糖(Syntactic Sugar),也稱糖衣語法,是由英國計算機學家 Peter.J.Landin 發明的一個術語,指在計算機語言中添加的某種語法,這種語法對語言的功能并沒有影響,但是更方便程序員使用。簡而言之,語法糖讓程序更加簡潔,有更高的可讀性。

有意思的是,在編程領域,除了語法糖,還有語法鹽和語法糖精的說法,篇幅有限這里不做擴展了。

我們所熟知的編程語言中幾乎都有語法糖。作者認為,語法糖的多少是評判一個語言夠不夠牛逼的標準之一。

很多人說Java是一個“低糖語言”,其實從Java 7開始Java語言層面上一直在添加各種糖,主要是在“Project Coin”項目下研發。盡管現在Java有人還是認為現在的Java是低糖,未來還會持續向著“高糖”的方向發展。

解語法糖

前面提到過,語法糖的存在主要是方便開發人員使用。但其實,Java虛擬機并不支持這些語法糖。這些語法糖在編譯階段就會被還原成簡單的基礎語法結構,這個過程就是解語法糖。

說到編譯,大家肯定都知道,Java語言中,javac命令可以將后綴名為.java的源文件編譯為后綴名為.class的可以運行于Java虛擬機的字節碼。

如果你去看com.sun.tools.javac.main.JavaCompiler的源碼,你會發現在compile()中有一個步驟就是調用desugar(),這個方法就是負責解語法糖的實現的。

Java 中最常用的語法糖主要有泛型、變長參數、條件編譯、自動拆裝箱、內部類等。本文主要來分析下這些語法糖背后的原理。一步一步剝去糖衣,看看其本質。

糖塊一、 switch 支持 String 與枚舉

前面提到過,從Java 7 開始,Java語言中的語法糖在逐漸豐富,其中一個比較重要的就是Java 7中switch開始支持String。

在開始coding之前先科普下,Java中的swith自身原本就支持基本類型。比如int、char等。

對于int類型,直接進行數值的比較。對于char類型則是比較其ascii碼。

所以,對于編譯器來說,switch中其實只能使用整型,任何類型的比較都要轉換成整型。比如byte。short,char(ackii碼是整型)以及int。

那么接下來看下switch對String得支持,有以下代碼:

  1. public class switchDemoString { 
  2.     public static void main(String[] args) { 
  3.         String str = "world"
  4.         switch (str) { 
  5.         case "hello"
  6.             System.out.println("hello"); 
  7.             break; 
  8.         case "world"
  9.             System.out.println("world"); 
  10.             break; 
  11.         default
  12.             break; 
  13.         } 
  14.     } 

反編譯后內容如下:

  1. public class switchDemoString 
  2.     public switchDemoString() 
  3.     { 
  4.     } 
  5.     public static void main(String args[]) 
  6.     { 
  7.         String str = "world"
  8.         String s; 
  9.         switch((s = str).hashCode()) 
  10.         { 
  11.         default
  12.             break; 
  13.         case 99162322: 
  14.             if(s.equals("hello")) 
  15.                 System.out.println("hello"); 
  16.             break; 
  17.         case 113318802: 
  18.             if(s.equals("world")) 
  19.                 System.out.println("world"); 
  20.             break; 
  21.         } 
  22.     } 

看到這個代碼,你知道原來字符串的switch是通過equals()和hashCode()方法來實現的。還好hashCode()方法返回的是int,而不是long。

仔細看下可以發現,進行switch的實際是哈希值,然后通過使用equals方法比較進行安全檢查,這個檢查是必要的,因為哈??赡軙l生碰撞。因此它的性能是不如使用枚舉進行switch或者使用純整數常量,但這也不是很差。

糖塊二、 泛型

我們都知道,很多語言都是支持泛型的,但是很多人不知道的是,不同的編譯器對于泛型的處理方式是不同的。

通常情況下,一個編譯器處理泛型有兩種方式:Code specialization和Code sharing。

C++和C#是使用Code specialization的處理機制,而Java使用的是Code sharing的機制。

Code sharing方式為每個泛型類型創建唯一的字節碼表示,并且將該泛型類型的實例都映射到這個唯一的字節碼表示上。將多種泛型類形實例映射到唯一的字節碼表示是通過類型擦除(type erasue)實現的。

也就是說,對于Java虛擬機來說,他根本不認識Map。

類型擦除的主要過程如下:

1.將所有的泛型參數用其最左邊界(最***的父類型)類型替換。

2.移除所有的類型參數。

以下代碼:

  1. Map<String, String> map = new HashMap<String, String>();   
  2. map.put("name""hollis");   
  3. map.put("wechat""Hollis");   
  4. map.put("blog""www.hollischuang.com");   

解語法糖之后會變成:

  1. Map map = new HashMap();   
  2. map.put("name""hollis");   
  3. map.put("wechat""Hollis");   
  4. map.put("blog""www.hollischuang.com");   

以下代碼:

  1. public static <A extends Comparable<A>> A max(Collection<A> xs) { 
  2.     Iterator<A> xi = xs.iterator(); 
  3.     A w = xi.next(); 
  4.     while (xi.hasNext()) { 
  5.         A x = xi.next(); 
  6.         if (w.compareTo(x) < 0) 
  7.             w = x; 
  8.     } 
  9.     return w; 

類型擦除后會變成:

  1. public static Comparable max(Collection xs){ 
  2.     Iterator xi = xs.iterator(); 
  3.     Comparable w = (Comparable)xi.next(); 
  4.     while(xi.hasNext()) 
  5.     { 
  6.         Comparable x = (Comparable)xi.next(); 
  7.         if(w.compareTo(x) < 0) 
  8.             w = x; 
  9.     } 
  10.     return w; 

虛擬機中沒有泛型,只有普通類和普通方法,所有泛型類的類型參數在編譯時都會被擦除,泛型類并沒有自己獨有的Class類對象。比如并不存在List.class或是List.class,而只有List.class。

糖塊三、 自動裝箱與拆箱

自動裝箱就是Java自動將原始類型值轉換成對應的對象,比如將int的變量轉換成Integer對象,這個過程叫做裝箱,反之將Integer對象轉換成int類型值,這個過程叫做拆箱。

因為這里的裝箱和拆箱是自動進行的非人為轉換,所以就稱作為自動裝箱和拆箱。

原始類型byte, short, char, int, long, float, double 和 boolean 對應的封裝類為Byte, Short, Character, Integer, Long, Float, Double, Boolean。

先來看個自動裝箱的代碼:

  1. public static void main(String[] args) { 
  2.     int i = 10; 
  3.     Integer n = i; 

反編譯后代碼如下:

  1. public static void main(String args[]) 
  2.     int i = 10; 
  3.     Integer n = Integer.valueOf(i); 

再來看個自動拆箱的代碼:

  1. public static void main(String[] args) { 
  2.  
  3.     Integer i = 10; 
  4.     int n = i; 

反編譯后代碼如下:

  1. public static void main(String args[]) 
  2.     Integer i = Integer.valueOf(10); 
  3.     int n = i.intValue(); 

從反編譯得到內容可以看出,在裝箱的時候自動調用的是Integer的valueOf(int)方法。而在拆箱的時候自動調用的是Integer的intValue方法。

所以,裝箱過程是通過調用包裝器的valueOf方法實現的,而拆箱過程是通過調用包裝器的 xxxValue方法實現的。

糖塊四 、 方法變長參數

可變參數(variable arguments)是在Java 1.5中引入的一個特性。它允許一個方法把任意數量的值作為參數。

看下以下可變參數代碼,其中print方法接收可變參數:

  1. public static void main(String[] args) 
  2.     { 
  3.         print("Holis""公眾號:Hollis""博客:www.hollischuang.com""QQ:907607222"); 
  4.     } 
  5.  
  6. public static void print(String... strs) 
  7.     for (int i = 0; i < strs.length; i++) 
  8.     { 
  9.         System.out.println(strs[i]); 
  10.     } 

反編譯后代碼:

  1. public static void main(String args[]) 
  2.     print(new String[] { 
  3.         "Holis""\u516C\u4F17\u53F7:Hollis""\u53***\u5BA2\uFF1Awww.hollischuang.com""QQ\uFF1A907607222" 
  4.     }); 
  5.  
  6. public static transient void print(String strs[]) 
  7.     for(int i = 0; i < strs.length; i++) 
  8.         System.out.println(strs[i]); 
  9.  

從反編譯后代碼可以看出,可變參數在被使用的時候,他首先會創建一個數組,數組的長度就是調用該方法是傳遞的實參的個數,然后再把參數值全部放到這個數組當中,然后再把這個數組作為參數傳遞到被調用的方法中。

糖塊五 、 枚舉

Java SE5提供了一種新的類型-Java的枚舉類型,關鍵字enum可以將一組具名的值的有限集合創建為一種新的類型,而這些具名的值可以作為常規的程序組件使用,這是一種非常有用的功能。參考:Java的枚舉類型用法介紹

要想看源碼,首先得有一個類吧,那么枚舉類型到底是什么類呢?是enum嗎?

答案很明顯不是,enum就和class一樣,只是一個關鍵字,他并不是一個類。

那么枚舉是由什么類維護的呢,我們簡單的寫一個枚舉:

  1. public enum t { 
  2.     SPRING,SUMMER; 

然后我們使用反編譯,看看這段代碼到底是怎么實現的,反編譯后代碼內容如下:

  1. public final class T extends Enum 
  2.     private T(String s, int i) 
  3.     { 
  4.         super(s, i); 
  5.     } 
  6.     public static T[] values() 
  7.     { 
  8.         T at[]; 
  9.         int i; 
  10.         T at1[]; 
  11.         System.arraycopy(at = ENUM$VALUES, 0, at1 = new T[i = at.length], 0, i); 
  12.         return at1; 
  13.     } 
  14.  
  15.     public static T valueOf(String s) 
  16.     { 
  17.         return (T)Enum.valueOf(demo/T, s); 
  18.     } 
  19.  
  20.     public static final T SPRING; 
  21.     public static final T SUMMER; 
  22.     private static final T ENUM$VALUES[]; 
  23.     static 
  24.     { 
  25.         SPRING = new T("SPRING", 0); 
  26.         SUMMER = new T("SUMMER", 1); 
  27.         ENUM$VALUES = (new T[] { 
  28.             SPRING, SUMMER 
  29.         }); 
  30.     } 

通過反編譯后代碼我們可以看到,public final class T extends Enum,說明,該類是繼承了Enum類的,同時final關鍵字告訴我們,這個類也是不能被繼承的。

當我們使用enmu來定義一個枚舉類型的時候,編譯器會自動幫我們創建一個final類型的類繼承Enum類,所以枚舉類型不能被繼承。

糖塊六 、 內部類

內部類又稱為嵌套類,可以把內部類理解為外部類的一個普通成員。

內部類之所以也是語法糖,是因為它僅僅是一個編譯時的概念。

outer.java里面定義了一個內部類inner,一旦編譯成功,就會生成兩個完全不同的.class文件了,分別是outer.class和outer$inner.class。所以內部類的名字完全可以和它的外部類名字相同。

  1. public class OutterClass { 
  2.     private String userName; 
  3.  
  4.     public String getUserName() { 
  5.         return userName; 
  6.     } 
  7.  
  8.     public void setUserName(String userName) { 
  9.         this.userName = userName; 
  10.     } 
  11.  
  12.     public static void main(String[] args) { 
  13.  
  14.     } 
  15.  
  16.     class InnerClass{ 
  17.         private String name
  18.  
  19.         public String getName() { 
  20.             return name
  21.         } 
  22.  
  23.         public void setName(String name) { 
  24.             this.name = name
  25.         } 
  26.     } 

以上代碼編譯后會生成兩個class文件:OutterClass$InnerClass.class 、OutterClass.class 。

當我們嘗試使用jad對OutterClass.class文件進行反編譯的時候,命令行會打印以下內容:

  1. Parsing OutterClass.class... 
  2. Parsing inner class OutterClass$InnerClass.class... 
  3. Generating OutterClass.jad 

他會把兩個文件全部進行反編譯,然后一起生成一個OutterClass.jad文件。文件內容如下:

  1. public class OutterClass 
  2.     class InnerClass 
  3.     { 
  4.         public String getName() 
  5.         { 
  6.             return name
  7.         } 
  8.         public void setName(String name
  9.         { 
  10.             this.name = name
  11.         } 
  12.         private String name
  13.         final OutterClass this$0; 
  14.  
  15.         InnerClass() 
  16.         { 
  17.             this.this$0 = OutterClass.this; 
  18.             super(); 
  19.         } 
  20.     } 
  21.  
  22.     public OutterClass() 
  23.     { 
  24.     } 
  25.     public String getUserName() 
  26.     { 
  27.         return userName; 
  28.     } 
  29.     public void setUserName(String userName){ 
  30.         this.userName = userName; 
  31.     } 
  32.     public static void main(String args1[]) 
  33.     { 
  34.     } 
  35.     private String userName; 

糖塊七 、條件編譯

—般情況下,程序中的每一行代碼都要參加編譯。但有時候出于對程序代碼優化的考慮,希望只對其中一部分內容進行編譯,此時就需要在程序中加上條件,讓編譯器只對滿足條件的代碼進行編譯,將不滿足條件的代碼舍棄,這就是條件編譯。

如在C或CPP中,可以通過預處理語句來實現條件編譯。其實在Java中也可實現條件編譯。我們先來看一段代碼:

  1. public class ConditionalCompilation { 
  2.     public static void main(String[] args) { 
  3.         final boolean DEBUG = true
  4.         if(DEBUG) { 
  5.             System.out.println("Hello, DEBUG!"); 
  6.         } 
  7.  
  8.         final boolean ONLINE = false
  9.  
  10.         if(ONLINE){ 
  11.             System.out.println("Hello, ONLINE!"); 
  12.         } 
  13.     } 

反編譯后代碼如下:

  1. public class ConditionalCompilation 
  2.  
  3.     public ConditionalCompilation() 
  4.     { 
  5.     } 
  6.  
  7.     public static void main(String args[]) 
  8.     { 
  9.         boolean DEBUG = true
  10.         System.out.println("Hello, DEBUG!"); 
  11.         boolean ONLINE = false
  12.     } 

首先,我們發現,在反編譯后的代碼中沒有System.out.println("Hello, ONLINE!");,這其實就是條件編譯。

當if(ONLINE)為false的時候,編譯器就沒有對其內的代碼進行編譯。

所以,Java語法的條件編譯,是通過判斷條件為常量的if語句實現的。根據if判斷條件的真假,編譯器直接把分支為false的代碼塊消除。通過該方式實現的條件編譯,必須在方法體內實現,而無法在正整個Java類的結構或者類的屬性上進行條件編譯。

這與C/C++的條件編譯相比,確實更有局限性。在Java語言設計之初并沒有引入條件編譯的功能,雖有局限,但是總比沒有更強。

糖塊八 、 斷言

在Java中,assert關鍵字是從JAVA SE 1.4 引入的,為了避免和老版本的Java代碼中使用了assert關鍵字導致錯誤,Java在執行的時候默認是不啟動斷言檢查的(這個時候,所有的斷言語句都將忽略!)。

如果要開啟斷言檢查,則需要用開關-enableassertions或-ea來開啟。

看一段包含斷言的代碼:

  1. public class AssertTest { 
  2.     public static void main(String args[]) { 
  3.         int a = 1; 
  4.         int b = 1; 
  5.         assert a == b; 
  6.         System.out.println("公眾號:Hollis"); 
  7.         assert a != b : "Hollis"
  8.         System.out.println("博客:www.hollischuang.com"); 
  9.     } 

反編譯后代碼如下:

  1. public class AssertTest { 
  2.    public AssertTest() 
  3.     { 
  4.     } 
  5.     public static void main(String args[]) 
  6.     int a = 1; 
  7.     int b = 1; 
  8.     if(!$assertionsDisabled && a != b) 
  9.         throw new AssertionError(); 
  10.     System.out.println("\u516C\u4F17\u53F7\uFF1AHollis"); 
  11.     if(!$assertionsDisabled && a == b) 
  12.     { 
  13.         throw new AssertionError("Hollis"); 
  14.     } else 
  15.     { 
  16.         System.out.println("\u53***\u5BA2\uFF1Awww.hollischuang.com"); 
  17.         return
  18.     } 
  19.  
  20. static final boolean $assertionsDisabled = !com/hollis/suguar/AssertTest.desiredAssertionStatus(); 
  21.  
  22.  

很明顯,反編譯之后的代碼要比我們自己的代碼復雜的多。所以,使用了assert這個語法糖我們節省了很多代碼。

其實斷言的底層實現就是if語言,如果斷言結果為true,則什么都不做,程序繼續執行,如果斷言結果為false,則程序拋出AssertError來打斷程序的執行。

-enableassertions會設置$assertionsDisabled字段的值。

糖塊九 、 數值字面量

在java 7中,數值字面量,不管是整數還是浮點數,都允許在數字之間插入任意多個下劃線。這些下劃線不會對字面量的數值產生影響,目的就是方便閱讀。

比如:

  1. public class Test { 
  2.     public static void main(String... args) { 
  3.         int i = 10_000; 
  4.         System.out.println(i); 
  5.     } 

反編譯后:

  1. public class Test 
  2.   public static void main(String[] args) 
  3.   { 
  4.     int i = 10000; 
  5.     System.out.println(i); 
  6.   } 

反編譯后就是把_刪除了。也就是說編譯器并不認識在數字字面量中的_,需要在編譯階段把他去掉。

糖塊十 、 for-each

增強for循環(for-each)相信大家都不陌生,日常開發經常會用到的,他會比for循環要少寫很多代碼,那么這個語法糖背后是如何實現的呢?

  1. public static void main(String... args) { 
  2.     String[] strs = {"Hollis""公眾號:Hollis""博客:www.hollischuang.com"}; 
  3.     for (String s : strs) { 
  4.         System.out.println(s); 
  5.     } 
  6.     List<String> strList = ImmutableList.of("Hollis""公眾號:Hollis""博客:www.hollischuang.com"); 
  7.     for (String s : strList) { 
  8.         System.out.println(s); 
  9.     } 

反編譯后代碼如下:

  1. public static transient void main(String args[]) 
  2.     String strs[] = { 
  3.         "Hollis""\u516C\u4F17\u53F7\uFF1AHollis""\u53***\u5BA2\uFF1Awww.hollischuang.com" 
  4.     }; 
  5.     String args1[] = strs; 
  6.     int i = args1.length; 
  7.     for(int j = 0; j < i; j++) 
  8.     { 
  9.         String s = args1[j]; 
  10.         System.out.println(s); 
  11.     } 
  12.  
  13.     List strList = ImmutableList.of("Hollis""\u516C\u4F17\u53F7\uFF1AHollis""\u53***\u5BA2\uFF1Awww.hollischuang.com"); 
  14.     String s; 
  15.     for(Iterator iterator = strList.iterator(); iterator.hasNext(); System.out.println(s)) 
  16.         s = (String)iterator.next(); 
  17.  

代碼很簡單,for-each的實現原理其實就是使用了普通的for循環和迭代器。

糖塊十一 、 try-with-resource

Java里,對于文件操作IO流、數據庫連接等開銷非常昂貴的資源,用完之后必須及時通過close方法將其關閉,否則資源會一直處于打開狀態,可能會導致內存泄露等問題。

關閉資源的常用方式就是在finally塊里是釋放,即調用close方法。比如,我們經常會寫這樣的代碼:

  1. public static void main(String[] args) { 
  2.     BufferedReader br = null
  3.     try { 
  4.         String line; 
  5.         br = new BufferedReader(new FileReader("d:\\hollischuang.xml")); 
  6.         while ((line = br.readLine()) != null) { 
  7.             System.out.println(line); 
  8.         } 
  9.     } catch (IOException e) { 
  10.         // handle exception 
  11.     } finally { 
  12.         try { 
  13.             if (br != null) { 
  14.                 br.close(); 
  15.             } 
  16.         } catch (IOException ex) { 
  17.             // handle exception 
  18.         } 
  19.     } 

從Java 7開始,jdk提供了一種更好的方式關閉資源,使用try-with-resources語句,改寫一下上面的代碼,效果如下:

  1. public static void main(String... args) { 
  2.     try (BufferedReader br = new BufferedReader(new FileReader("d:\\ hollischuang.xml"))) { 
  3.         String line; 
  4.         while ((line = br.readLine()) != null) { 
  5.             System.out.println(line); 
  6.         } 
  7.     } catch (IOException e) { 
  8.         // handle exception 
  9.     } 

看,這簡直是一大福音啊,雖然我之前一般使用IOUtils去關閉流,并不會使用在finally中寫很多代碼的方式,但是這種新的語法糖看上去好像優雅很多呢。

反編譯以上代碼,看下他的背后原理:

  1. public static transient void main(String args[]) 
  2.     { 
  3.         BufferedReader br; 
  4.         Throwable throwable; 
  5.         br = new BufferedReader(new FileReader("d:\\ hollischuang.xml")); 
  6.         throwable = null
  7.         String line; 
  8.         try 
  9.         { 
  10.             while((line = br.readLine()) != null
  11.                 System.out.println(line); 
  12.         } 
  13.         catch(Throwable throwable2) 
  14.         { 
  15.             throwable = throwable2; 
  16.             throw throwable2; 
  17.         } 
  18.         if(br != null
  19.             if(throwable != null
  20.                 try 
  21.                 { 
  22.                     br.close(); 
  23.                 } 
  24.                 catch(Throwable throwable1) 
  25.                 { 
  26.                     throwable.addSuppressed(throwable1); 
  27.                 } 
  28.             else 
  29.                 br.close(); 
  30.             break MISSING_BLOCK_LABEL_113; 
  31.             Exception exception; 
  32.             exception; 
  33.             if(br != null
  34.                 if(throwable != null
  35.                     try 
  36.                     { 
  37.                         br.close(); 
  38.                     } 
  39.                     catch(Throwable throwable3) 
  40.                       { 
  41.                         throwable.addSuppressed(throwable3); 
  42.                     } 
  43.                 else 
  44.                     br.close(); 
  45.         throw exception; 
  46.         IOException ioexception; 
  47.         ioexception; 
  48.     } 

其實背后的原理也很簡單,那些我們沒有做的關閉資源的操作,編譯器都幫我們做了。

所以,再次印證了,語法糖的作用就是方便程序員的使用,但最終還是要轉成編譯器認識的語言。

糖塊十二、Lambda表達式

關于lambda表達式,有人可能會有質疑,因為網上有人說他并不是語法糖。其實我想糾正下這個說法。

Labmda表達式不是匿名內部類的語法糖,但是他也是一個語法糖。實現方式其實是依賴了幾個JVM底層提供的lambda相關api。

先來看一個簡單的lambda表達式。遍歷一個list:

  1. public static void main(String... args) { 
  2.     List<String> strList = ImmutableList.of("Hollis""公眾號:Hollis""博客:www.hollischuang.com"); 
  3.  
  4.     strList.forEach( s -> { System.out.println(s); } ); 

為啥說他并不是內部類的語法糖呢,前面講內部類我們說過,內部類在編譯之后會有兩個class文件,但是,包含lambda表達式的類編譯后只有一個文件。

反編譯后代碼如下:

  1. public static /* varargs */ void main(String ... args) { 
  2.     ImmutableList strList = ImmutableList.of((Object)"Hollis", (Object)"\u516c\u4f17\u53f7\uff1aHollis", (Object)"\u535a\u5ba2\uff1awww.hollischuang.com"); 
  3.     strList.forEach((Consumer<String>)LambdaMetafactory.metafactory(nullnullnull, (Ljava/lang/Object;)V, lambda$main$0(java.lang.String ), (Ljava/lang/String;)V)()); 
  4.  
  5. private static /* synthetic */ void lambda$main$0(String s) { 
  6.     System.out.println(s); 

可以看到,在forEach方法中,其實是調用了java.lang.invoke.LambdaMetafactory#metafactory方法,該方法的第四個參數implMethod指定了方法實現??梢钥吹竭@里其實是調用了一個lambda$main$0方法進行了輸出。

再來看一個稍微復雜一點的,先對List進行過濾,然后再輸出:

  1. public static void main(String... args) { 
  2.     List<String> strList = ImmutableList.of("Hollis""公眾號:Hollis""博客:www.hollischuang.com"); 
  3.  
  4.     List HollisList = strList.stream().filter(string -> string.contains("Hollis")).collect(Collectors.toList()); 
  5.  
  6.     HollisList.forEach( s -> { System.out.println(s); } ); 

反編譯后代碼如下:

  1. public static /* varargs */ void main(String ... args) { 
  2.     ImmutableList strList = ImmutableList.of((Object)"Hollis", (Object)"\u516c\u4f17\u53f7\uff1aHollis", (Object)"\u535a\u5ba2\uff1awww.hollischuang.com"); 
  3.     List<Object> HollisList = strList.stream().filter((Predicate<String>)LambdaMetafactory.metafactory(nullnullnull, (Ljava/lang/Object;)Z, lambda$main$0(java.lang.String ), (Ljava/lang/String;)Z)()).collect(Collectors.toList()); 
  4.     HollisList.forEach((Consumer<Object>)LambdaMetafactory.metafactory(nullnullnull, (Ljava/lang/Object;)V, lambda$main$1(java.lang.Object ), (Ljava/lang/Object;)V)()); 
  5.  
  6. private static /* synthetic */ void lambda$main$1(Object s) { 
  7.     System.out.println(s); 
  8.  
  9. private static /* synthetic */ boolean lambda$main$0(String string) { 
  10.     return string.contains("Hollis"); 

兩個lambda表達式分別調用了lambda$main$1和lambda$main$0兩個方法。

所以,lambda表達式的實現其實是依賴了一些底層的api,在編譯階段,編譯器會把lambda表達式進行解糖,轉換成調用內部api的方式。

可能遇到的坑

泛型——當泛型遇到重載

  1. public class GenericTypes { 
  2.     public static void method(List<String> list) {   
  3.         System.out.println("invoke method(List<String> list)");   
  4.     }   
  5.  
  6.     public static void method(List<Integer> list) {   
  7.         System.out.println("invoke method(List<Integer> list)");   
  8.     }   
  9. }   

上面這段代碼,有兩個重載的函數,因為他們的參數類型不同,一個是List另一個是List,但是,這段代碼是編譯通不過的。因為我們前面講過,參數List和List編譯之后都被擦除了,變成了一樣的原生類型List,擦除動作導致這兩個方法的特征簽名變得一模一樣。

泛型——當泛型遇到catch

泛型的類型參數不能用在Java異常處理的catch語句中。因為異常處理是由JVM在運行時刻來進行的。由于類型信息被擦除,JVM是無法區分兩個異常類型MyException和MyException的。

泛型——當泛型內包含靜態變量

  1. public class StaticTest{ 
  2.     public static void main(String[] args){ 
  3.         GT<Integer> gti = new GT<Integer>(); 
  4.         gti.var=1; 
  5.         GT<String> gts = new GT<String>(); 
  6.         gts.var=2; 
  7.         System.out.println(gti.var); 
  8.     } 
  9. class GT<T>{ 
  10.     public static int var=0; 
  11.     public void nothing(T x){} 

以上代碼輸出結果為:2!由于經過類型擦除,所有的泛型類實例都關聯到同一份字節碼上,泛型類的所有靜態變量是共享的。

自動裝箱與拆箱——對象相等比較

  1. public static void main(String[] args) { 
  2.     Integer a = 1000; 
  3.     Integer b = 1000; 
  4.     Integer c = 100; 
  5.     Integer d = 100; 
  6.     System.out.println("a == b is " + (a == b)); 
  7.     System.out.println(("c == d is " + (c == d))); 

輸出結果:

  1. a == b is false 
  2. c == d is true 

在Java 5中,在Integer的操作上引入了一個新功能來節省內存和提高性能。整型對象通過使用相同的對象引用實現了緩存和重用。

適用于整數值區間-128 至 +127。

只適用于自動裝箱。使用構造函數創建對象不適用。

增強for循環

  1. for (Student stu : students) {     
  2.     if (stu.getId() == 2)      
  3.         students.remove(stu);     

會拋出ConcurrentModificationException異常。

Iterator是工作在一個獨立的線程中,并且擁有一個 mutex 鎖。 Iterator被創建之后會建立一個指向原來對象的單鏈索引表,當原來的對象數量發生變化時,這個索引表的內容不會同步改變,所以當索引指針往后移動的時候就找不到要迭代的對象,所以按照 fail-fast 原則 Iterator 會馬上拋出java.util.ConcurrentModificationException異常。參考:一不小心就讓Java開發者踩坑的fail-fast是個什么鬼?

所以 Iterator 在工作的時候是不允許被迭代的對象被改變的。但你可以使用 Iterator 本身的方法remove()來刪除對象,Iterator.remove() 方法會在刪除當前迭代對象的同時維護索引的一致性。

總結

前面介紹了12種Java中常用的語法糖。所謂語法糖就是提供給開發人員便于開發的一種語法而已。

但是這種語法只有開發人員認識。要想被執行,需要進行解糖,即轉成JVM認識的語法。

當我們把語法糖解糖之后,你就會發現其實我們日常使用的這些方便的語法,其實都是一些其他更簡單的語法構成的。

有了這些語法糖,我們在日常開發的時候可以大大提升效率,但是同時也要避免過渡使用。使用之前***了解下原理,避免掉坑。

【本文是51CTO專欄作者Hollis的原創文章,作者微信公眾號Hollis(ID:hollischuang)】

戳這里,看該作者更多好文 

責任編輯:武曉燕 來源: 51CTO專欄
相關推薦

2021-07-12 07:01:39

AST前端abstract sy

2023-02-12 21:54:32

架構AI元宇宙

2011-03-29 15:44:41

對日軟件外包

2019-05-23 11:42:04

Java語法糖編程語言

2024-01-15 13:18:11

數據管理CIO

2019-04-03 09:10:35

Rediskey-value數據庫

2019-11-21 15:08:13

DevOps云計算管理

2010-08-19 10:12:34

路由器標準

2024-09-11 16:34:38

語法糖Java語言

2020-11-30 06:27:35

Java泛型Object

2014-05-06 10:31:21

KillallLinux命令行

2017-12-26 11:37:32

云原生CNCF容器

2025-08-06 06:15:00

2013-11-11 10:07:43

靜態路由配置

2018-04-16 23:14:39

SD-WANSDN網絡

2018-07-16 09:00:32

LinuxBash數組

2015-06-05 09:52:41

公有云風險成本

2017-03-13 17:25:00

移動支付技術支撐易寶

2025-08-28 06:05:00

2023-02-16 07:38:38

非機械鍵電路薄膜,
點贊
收藏

51CTO技術棧公眾號

日韩精品毛片| 无码久久精品国产亚洲av影片| 欧美h版在线观看| 亚洲不卡av一区二区三区| 国产免费一区二区三区| 无码人妻精品一区二区三区9厂| 欧美hd在线| 精品国产污污免费网站入口| 成年网站在线免费观看| 蜜桃视频在线观看www社区 | www.黄色网| 九九九伊在线综合永久| 一区二区三区欧美日韩| 水蜜桃亚洲精品| 欧美一级在线免费观看| 美女一区二区久久| 7777精品视频| 农村黄色一级片| av亚洲免费| 亚洲国产天堂网精品网站| 午夜久久久精品| 国模私拍一区二区国模曼安| 亚洲视频网在线直播| 美脚丝袜一区二区三区在线观看| 亚洲国产999| 久久成人免费日本黄色| 日韩美女视频中文字幕| 欧美一级高潮片| 一级欧洲+日本+国产| 一本大道久久加勒比香蕉| 日韩精品人妻中文字幕有码| 国产精品一区二区三区www| 91精品福利视频| 5月婷婷6月丁香| 成人女同在线观看| 亚洲精品欧美激情| 最新av在线免费观看| 国产乱子伦三级在线播放| xfplay精品久久| 国产乱人伦精品一区二区| www.中文字幕| 国产精品小仙女| 亚洲综合自拍一区| 国产精品-色哟哟| 久久国产精品免费| 国产专区精品视频| 在线观看不卡的av| 蜜桃视频一区二区三区 | www欧美在线观看| 欧美日韩一区二区欧美激情 | 国产精品一品二品| 成人免费网视频| 97精品人妻一区二区三区香蕉| 美腿丝袜在线亚洲一区| 国产精品综合不卡av| 中文字幕一区二区在线视频 | 国产综合在线观看| 久久久99精品免费观看| 日韩精品资源| 亚洲成人三级| 亚洲欧美激情在线| 波多野结衣与黑人| f2c人成在线观看免费视频| 亚洲国产精品一区二区久久恐怖片 | 欧美视频免费一区二区三区| 久久综合色8888| 日本不卡高清视频一区| 92国产在线视频| 中文字幕在线观看不卡视频| 久久久久亚洲av无码专区喷水| caoporn免费在线视频| 一区二区三区不卡在线观看 | 久久综合影音| 国产精品久久久久久久久粉嫩av| 夜夜嗨aⅴ一区二区三区| 国产麻豆视频精品| 国产精品一区二区三区免费观看| 丝袜视频国产在线播放| 国产精品丝袜91| 免费cad大片在线观看| 成人bbav| 欧美色图第一页| 国产精品无码自拍| 国产一区二区三区探花| 久久久电影免费观看完整版| 伊人365影院| 秋霞av亚洲一区二区三| 99国产在线视频| 欧美777四色影视在线| 亚洲欧洲国产日本综合| 日本十八禁视频无遮挡| 伊人久久精品| 亚洲激情在线观看视频免费| 一二三四国产精品| 亚洲国产高清一区二区三区| 国产精品久久久av久久久| 国产v在线观看| 久久先锋影音av鲁色资源网| 成人在线观看www| 欧美亚洲大片| 亚洲精品一区二区三区福利 | 激情小视频在线| 有码一区二区三区| 欧美日韩在线观看不卡| 牛牛影视久久网| 欧美成人免费全部| 波多野结衣高清视频| 国产成人小视频| 亚洲蜜桃在线| 性国裸体高清亚洲| 日韩精品一区二区三区在线| 日韩视频在线观看免费视频| 9色精品在线| 国产精品国产精品国产专区蜜臀ah| 国产精品久久久久一区二区国产| 亚洲福利视频导航| 日本亚洲一区二区三区| 精品日产免费二区日产免费二区| 久久久久久网址| 国产成年妇视频| 国产精品久久久久久久久免费樱桃| 欧美牲交a欧美牲交| 一区中文字幕| 成年人精品视频| 亚洲系列在线观看| 国产嫩草影院久久久久| 日韩黄色片视频| 日韩精品福利一区二区三区| 欧美精品电影在线| 性欧美8khd高清极品| 亚洲视频 欧洲视频| 亚洲77777| 精品国产乱码久久久久久1区2匹| 欧美性受xxxx白人性爽| 国精产品乱码一区一区三区四区| 一区二区三区在线视频观看| 特级黄色片视频| 五月开心六月丁香综合色啪| 国产女人精品视频| 欧美天天影院| 777亚洲妇女| 免费看一级大片| 狠狠色丁香婷综合久久| 一区在线电影| 国产精品高清一区二区| 久久精品国产欧美亚洲人人爽| 亚洲在线精品视频| 国产精品久久久久久久久久免费看| 午夜视频你懂的| 日韩成人综合| 91精品国产综合久久香蕉的用户体验| 777电影在线观看| 7777精品伊人久久久大香线蕉 | 无码人妻一区二区三区免费n鬼沢| 欧美淫片网站| 激情视频一区二区| 综合另类专区| 中文字幕日韩精品在线观看| 伊人亚洲综合网| 亚洲日本中文字幕区| 少妇性l交大片7724com| 激情综合电影网| 久久精品人成| 外国电影一区二区| 久久精品2019中文字幕| 精品人妻午夜一区二区三区四区 | 免费成人午夜视频| 九九亚洲视频| 国产在线拍揄自揄视频不卡99| 97caopor国产在线视频| 亚洲аv电影天堂网| 国产原创视频在线| 国产精品久久夜| 亚洲成人精品在线播放| 国产日韩精品视频一区二区三区| 色999五月色| 精品成人18| 国产91精品久| 国产剧情在线| 亚洲精品国产精品国产自| 黄色网址中文字幕| 夜夜嗨av一区二区三区网页| 亚洲av无码一区二区三区观看| 丝袜美腿亚洲一区二区图片| 亚洲美女自拍偷拍| 亚洲欧美tv| 91九色蝌蚪国产| 天堂网在线最新版www中文网| 色偷偷偷综合中文字幕;dd| www.香蕉视频| 欧美亚洲国产bt| 黄色小视频在线免费看| 欧美国产禁国产网站cc| 黄色激情在线观看| 日本视频中文字幕一区二区三区 | 久久性天堂网| 91传媒免费视频| 激情综合网站| 国产原创精品| 国产一区二区视频在线看| 日韩暖暖在线视频| 激情图片在线观看高清国产| 曰本色欧美视频在线| 欧美熟女一区二区| 欧美精选在线播放| 久久久蜜桃一区二区| 亚洲一区二区在线视频| 人人艹在线视频| 91美女蜜桃在线| 国产a√精品区二区三区四区| 免费高清在线视频一区·| 久久久久免费看黄a片app| 天堂美国久久| 婷婷精品国产一区二区三区日韩| 国产欧美一区二区三区米奇| 91美女片黄在线观看游戏| 性感美女一区二区在线观看| 欧美精品第一页在线播放| jizz性欧美10| 日韩在线观看免费全集电视剧网站| 欧美日韩在线中文字幕| 亚洲精品ady| 成人免费观看在线视频| 欧美一区二区三区在线观看| 综合久久中文字幕| 91成人在线免费观看| 久久亚洲精品国产| 五月天久久比比资源色| 国产在线视频第一页| 亚洲精品成人精品456| 国产成人在线网址| 国产精品成人在线观看| 少妇的滋味中文字幕bd| 国产欧美日韩卡一| 亚洲精品国产熟女久久久| 久久精品免费在线观看| aaaaaav| 91丨国产丨九色丨pron| 久久人妻少妇嫩草av无码专区| 懂色av一区二区三区免费看| 杨幂一区二区国产精品| 国产真实乱偷精品视频免| 亚洲午夜精品一区| 国内精品视频一区二区三区八戒| 日韩高清第一页| 精品一区二区三区在线视频| 午夜国产福利在线观看| 国产在线视频一区二区| 国产黑丝在线视频| 国产高清视频一区| 精品久久久久一区二区| 91原创在线视频| 免费看黄色的视频| 国产精品视频你懂的| 国产精品夜夜夜爽阿娇| 亚洲美女视频在线| 国产乡下妇女做爰毛片| 黄色精品在线看| 亚洲欧美偷拍一区| 欧美色中文字幕| 国产视频手机在线观看| 精品国产91亚洲一区二区三区婷婷| 韩国av免费在线观看| 亚洲精品一区二区久| av大全在线免费看| 久久99久久久久久久噜噜| caoporn视频在线| 国产精品福利网| 国产在线一区不卡| 九色综合日本| 精品国产乱码久久久久久蜜坠欲下| 亚洲蜜桃在线| 在线日本高清免费不卡| 无人在线观看的免费高清视频| 蓝色福利精品导航| 精品国产免费久久久久久婷婷| 99精品黄色片免费大全| 国产一二三四视频| 亚洲一区二区在线免费观看视频| 日韩视频在线观看一区| 欧美日韩一区二区三区免费看| 亚洲精品视频91| 亚洲视频在线视频| 亚洲欧美成人影院| 日本欧美一二三区| 精品中文视频| 欧美一级日本a级v片| 91精品国产成人观看| 欧美视频在线播放一区| 久久99精品视频| 亚洲国产第一区| 亚洲欧美激情插| 亚洲天堂男人av| 日韩欧美激情在线| 国产日本在线观看| 久久久久久久久久久亚洲| 123成人网| 精品国产中文字幕| 欧美国产专区| 伊人国产在线视频| 91香蕉视频黄| 久久久久亚洲av成人片| 欧美日韩在线播放三区四区| 无码精品黑人一区二区三区| 日韩亚洲欧美中文高清在线| 黑人巨大亚洲一区二区久 | 好男人在线视频www| www.久久久久| 国产精品亚洲一区二区三区在线观看| 99高清视频有精品视频| 成人亚洲一区| 毛葺葺老太做受视频| 成人一区在线观看| 波多野结衣家庭教师| 91激情在线视频| 亚洲欧美丝袜中文综合| 欧美国产日本高清在线| 色综合久久久| 亚洲成人在线视频网站| 欧美在线综合| 国产伦精品一区二区三区妓女| 51漫画成人app入口| 欧美精品video| 亚洲一区导航| 四虎永久在线精品免费一区二区| 激情六月综合| 亚洲第一天堂久久| 国产无一区二区| 秋霞精品一区二区三区| 国产视频久久网| 中文在线аv在线| 久久精品午夜一区二区福利| 很黄很黄激情成人| 国产毛片久久久久久| 亚洲男人天堂一区| 国产日韩在线观看一区| 久久精品国产综合| 国产日韩一区二区三免费高清 | 欧美另类视频| 日本高清免费观看| 亚洲日本成人在线观看| 国产男女无套免费网站| 久久影视免费观看| 国产一区二区三区视频在线| 日韩视频一二三| 国产成人精品三级| 精品一区免费观看| 日韩黄色av网站| 成人软件在线观看| 亚洲欧美一区二区原创| 麻豆精品在线看| www.xxxx日本| 欧美r级在线观看| 成人免费网站观看| 日本高清久久一区二区三区| 日本大胆欧美人术艺术动态| 日日操免费视频| 3d动漫精品啪啪1区2区免费| 中文字幕有码在线视频| 国产精品久久波多野结衣| 国产精品毛片一区二区三区| 爱爱免费小视频| 欧洲精品在线观看| 成人高清免费在线| 国产精品v欧美精品v日韩| 亚洲精品九九| 人与嘼交av免费| 91精品国产欧美日韩| 国产美女情趣调教h一区二区| 久久草视频在线看| 男女视频一区二区| 岛国毛片在线观看| 日韩精品极品视频| 亚洲精品大片| 国产精品网站免费| 国产精品午夜在线观看| 亚洲精品久久久久久动漫器材一区 | 蜜臀av亚洲一区中文字幕| 国产盗摄x88av| 亚洲精品色婷婷福利天堂| 国产福利亚洲| 国产精品va在线观看无码| 久久久精品国产99久久精品芒果 | 精品成人av| 51xx午夜影福利| 久久婷婷国产综合精品青草| 国产精品爽爽久久久久久| 韩国精品久久久999| 日本一二区不卡| youjizz.com日本| 欧美午夜精品久久久| 国产丝袜精品丝袜| 亚洲精品欧洲精品| 99久久婷婷国产综合精品电影| 中文字幕日日夜夜| 69视频在线播放| 亚洲天天影视网| 免费看黄色av| 亚洲国产欧美自拍| 麻豆一区在线|