国产成人精品18p,天天干成人网,无码专区狠狠躁天天躁,美女脱精光隐私扒开免费观看

Java中有哪些常見(jiàn)的語(yǔ)法糖

發(fā)布時(shí)間:2021-07-03 23:03 來(lái)源:億速云 閱讀:0 作者:Leah 欄目: 開(kāi)發(fā)技術(shù)

Java中有哪些常見(jiàn)的語(yǔ)法糖,很多新手對此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細講解,有這方面需求的人可以來(lái)學(xué)習下,希望你能有所收獲。

語(yǔ)法糖

語(yǔ)法糖(Syntactic Sugar),也稱(chēng)糖衣語(yǔ)法,是由英國計算機學(xué)家 Peter.J.Landin  發(fā)明的一個(gè)術(shù)語(yǔ),指在計算機語(yǔ)言中添加的某種語(yǔ)法,這種語(yǔ)法對語(yǔ)言的功能并沒(méi)有影響,但是更方便程序員使用。簡(jiǎn)而言之,語(yǔ)法糖讓程序更加簡(jiǎn)潔,有更高的可讀性。

有意思的是,在編程領(lǐng)域,除了語(yǔ)法糖,還有語(yǔ)法鹽和語(yǔ)法糖精的說(shuō)法,篇幅有限這里不做擴展了。

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

很多人說(shuō)Java是一個(gè)“低糖語(yǔ)言”,其實(shí)從Java 7開(kāi)始Java語(yǔ)言層面上一直在添加各種糖,主要是在“Project  Coin”項目下研發(fā)。盡管現在Java有人還是認為現在的Java是低糖,未來(lái)還會(huì )持續向著(zhù)“高糖”的方向發(fā)展。

解語(yǔ)法糖

前面提到過(guò),語(yǔ)法糖的存在主要是方便開(kāi)發(fā)人員使用。但其實(shí),Java虛擬機并不支持這些語(yǔ)法糖。這些語(yǔ)法糖在編譯階段就會(huì )被還原成簡(jiǎn)單的基礎語(yǔ)法結構,這個(gè)過(guò)程就是解語(yǔ)法糖。

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

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

Java  中最常用的語(yǔ)法糖主要有泛型、變長(cháng)參數、條件編譯、自動(dòng)拆裝箱、內部類(lèi)等。本文主要來(lái)分析下這些語(yǔ)法糖背后的原理。一步一步剝去糖衣,看看其本質(zhì)。

糖塊一、 switch 支持 String 與枚舉

前面提到過(guò),從Java 7 開(kāi)始,Java語(yǔ)言中的語(yǔ)法糖在逐漸豐富,其中一個(gè)比較重要的就是Java 7中switch開(kāi)始支持String。

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

對于int類(lèi)型,直接進(jìn)行數值的比較。對于char類(lèi)型則是比較其ascii碼。

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

那么接下來(lái)看下switch對String得支持,有以下代碼:

public class switchDemoString {     public static void main(String[] args) {         String str = "world";         switch (str) {         case "hello":             System.out.println("hello");             break;         case "world":             System.out.println("world");             break;         default:             break;         }     } }

反編譯后內容如下:

public class switchDemoString {     public switchDemoString()     {     }     public static void main(String args[])     {         String str = "world";         String s;         switch((s = str).hashCode())         {         default:             break;         case 99162322:             if(s.equals("hello"))                 System.out.println("hello");             break;         case 113318802:             if(s.equals("world"))                 System.out.println("world");             break;         }     } }

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

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

糖塊二、 泛型

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

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

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

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

也就是說(shuō),對于Java虛擬機來(lái)說(shuō),他根本不認識Map

類(lèi)型擦除的主要過(guò)程如下:

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

2.移除所有的類(lèi)型參數。

以下代碼:

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

解語(yǔ)法糖之后會(huì )變成:

Map map = new HashMap();   map.put("name", "hollis");   map.put("wechat", "Hollis");   map.put("blog", "www.hollischuang.com");

以下代碼:

public static <A extends Comparable<A>> A max(Collection<A> xs) {     Iterator<A> xi = xs.iterator();     A w = xi.next();     while (xi.hasNext()) {         A x = xi.next();         if (w.compareTo(x) < 0)             w = x;     }     return w; }

類(lèi)型擦除后會(huì )變成:

public static Comparable max(Collection xs){     Iterator xi = xs.iterator();     Comparable w = (Comparable)xi.next();     while(xi.hasNext())     {         Comparable x = (Comparable)xi.next();         if(w.compareTo(x) < 0)             w = x;     }     return w; }

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

糖塊三、 自動(dòng)裝箱與拆箱

自動(dòng)裝箱就是Java自動(dòng)將原始類(lèi)型值轉換成對應的對象,比如將int的變量轉換成Integer對象,這個(gè)過(guò)程叫做裝箱,反之將Integer對象轉換成int類(lèi)型值,這個(gè)過(guò)程叫做拆箱。參考:一文讀懂什么是Java中的自動(dòng)拆裝箱

因為這里的裝箱和拆箱是自動(dòng)進(jìn)行的非人為轉換,所以就稱(chēng)作為自動(dòng)裝箱和拆箱。

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

先來(lái)看個(gè)自動(dòng)裝箱的代碼:

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

反編譯后代碼如下:

public static void main(String args[]) {     int i = 10;     Integer n = Integer.valueOf(i); }

再來(lái)看個(gè)自動(dòng)拆箱的代碼:

public static void main(String[] args) {      Integer i = 10;     int n = i; }

反編譯后代碼如下:

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

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

所以,裝箱過(guò)程是通過(guò)調用包裝器的valueOf方法實(shí)現的,而拆箱過(guò)程是通過(guò)調用包裝器的 xxxValue方法實(shí)現的。

糖塊四 、 方法變長(cháng)參數

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

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

public static void main(String[] args)     {         print("Holis", "公眾號:Hollis", "博客:www.hollischuang.com", "QQ:907607222");     }  public static void print(String... strs) {     for (int i = 0; i < strs.length; i++)     {         System.out.println(strs[i]);     } }

反編譯后代碼:

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

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

糖塊五 、 枚舉

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

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

答案很明顯不是,enum就和class一樣,只是一個(gè)關(guān)鍵字,他并不是一個(gè)類(lèi)。

那么枚舉是由什么類(lèi)維護的呢,我們簡(jiǎn)單的寫(xiě)一個(gè)枚舉:

public enum t {     SPRING,SUMMER; }

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

public final class T extends Enum {     private T(String s, int i)     {         super(s, i);     }     public static T[] values()     {         T at[];         int i;         T at1[];         System.arraycopy(at = ENUM$VALUES, 0, at1 = new T[i = at.length], 0, i);         return at1;     }      public static T valueOf(String s)     {         return (T)Enum.valueOf(demo/T, s);     }      public static final T SPRING;     public static final T SUMMER;     private static final T ENUM$VALUES[];     static     {         SPRING = new T("SPRING", 0);         SUMMER = new T("SUMMER", 1);         ENUM$VALUES = (new T[] {             SPRING, SUMMER         });     } }

通過(guò)反編譯后代碼我們可以看到,public final class T extends  Enum,說(shuō)明,該類(lèi)是繼承了Enum類(lèi)的,同時(shí)final關(guān)鍵字告訴我們,這個(gè)類(lèi)也是不能被繼承的。

當我們使用enmu來(lái)定義一個(gè)枚舉類(lèi)型的時(shí)候,編譯器會(huì )自動(dòng)幫我們創(chuàng )建一個(gè)final類(lèi)型的類(lèi)繼承Enum類(lèi),所以枚舉類(lèi)型不能被繼承。

糖塊六 、 內部類(lèi)

內部類(lèi)又稱(chēng)為嵌套類(lèi),可以把內部類(lèi)理解為外部類(lèi)的一個(gè)普通成員。

內部類(lèi)之所以也是語(yǔ)法糖,是因為它僅僅是一個(gè)編譯時(shí)的概念。

outer.java里面定義了一個(gè)內部類(lèi)inner,一旦編譯成功,就會(huì )生成兩個(gè)完全不同的.class文件了,分別是outer.class和outer$inner.class。所以?xún)炔款?lèi)的名字完全可以和它的外部類(lèi)名字相同。

public class OutterClass {     private String userName;      public String getUserName() {         return userName;     }      public void setUserName(String userName) {         this.userName = userName;     }      public static void main(String[] args) {      }      class InnerClass{         private String name;          public String getName() {             return name;         }          public void setName(String name) {             this.name = name;         }     } }

以上代碼編譯后會(huì )生成兩個(gè)class文件:OutterClass$InnerClass.class 、OutterClass.class 。

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

Parsing OutterClass.class... Parsing inner class OutterClass$InnerClass.class... Generating OutterClass.jad

他會(huì )把兩個(gè)文件全部進(jìn)行反編譯,然后一起生成一個(gè)OutterClass.jad文件。文件內容如下:

public class OutterClass {     class InnerClass     {         public String getName()         {             return name;         }         public void setName(String name)         {             this.name = name;         }         private String name;         final OutterClass this$0;          InnerClass()         {             this.this$0 = OutterClass.this;             super();         }     }      public OutterClass()     {     }     public String getUserName()     {         return userName;     }     public void setUserName(String userName){         this.userName = userName;     }     public static void main(String args1[])     {     }     private String userName; }

糖塊七 、條件編譯

&mdash;般情況下,程序中的每一行代碼都要參加編譯。但有時(shí)候出于對程序代碼優(yōu)化的考慮,希望只對其中一部分內容進(jìn)行編譯,此時(shí)就需要在程序中加上條件,讓編譯器只對滿(mǎn)足條件的代碼進(jìn)行編譯,將不滿(mǎn)足條件的代碼舍棄,這就是條件編譯。

如在C或CPP中,可以通過(guò)預處理語(yǔ)句來(lái)實(shí)現條件編譯。其實(shí)在Java中也可實(shí)現條件編譯。我們先來(lái)看一段代碼:

public class ConditionalCompilation {     public static void main(String[] args) {         final boolean DEBUG = true;         if(DEBUG) {             System.out.println("Hello, DEBUG!");         }          final boolean ONLINE = false;          if(ONLINE){             System.out.println("Hello, ONLINE!");         }     } }

反編譯后代碼如下:

public class ConditionalCompilation {      public ConditionalCompilation()     {     }      public static void main(String args[])     {         boolean DEBUG = true;         System.out.println("Hello, DEBUG!");         boolean ONLINE = false;     } }

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

當if(ONLINE)為false的時(shí)候,編譯器就沒(méi)有對其內的代碼進(jìn)行編譯。

所以,Java語(yǔ)法的條件編譯,是通過(guò)判斷條件為常量的if語(yǔ)句實(shí)現的。根據if判斷條件的真假,編譯器直接把分支為false的代碼塊消除。通過(guò)該方式實(shí)現的條件編譯,必須在方法體內實(shí)現,而無(wú)法在正整個(gè)Java類(lèi)的結構或者類(lèi)的屬性上進(jìn)行條件編譯。

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

糖塊八 、 斷言

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

如果要開(kāi)啟斷言檢查,則需要用開(kāi)關(guān)-enableassertions或-ea來(lái)開(kāi)啟。

看一段包含斷言的代碼:

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

反編譯后代碼如下:

public class AssertTest {    public AssertTest()     {     }     public static void main(String args[]) {     int a = 1;     int b = 1;     if(!$assertionsDisabled && a != b)         throw new AssertionError();     System.out.println("\u516C\u4F17\u53F7\uFF1AHollis");     if(!$assertionsDisabled && a == b)     {         throw new AssertionError("Hollis");     } else     {         System.out.println("\u53***\u5BA2\uFF1Awww.hollischuang.com");         return;     } }  static final boolean $assertionsDisabled = !com/hollis/suguar/AssertTest.desiredAssertionStatus();   }

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

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

-enableassertions會(huì )設置$assertionsDisabled字段的值。

糖塊九 、 數值字面量

在java 7中,數值字面量,不管是整數還是浮點(diǎn)數,都允許在數字之間插入任意多個(gè)下劃線(xiàn)。這些下劃線(xiàn)不會(huì )對字面量的數值產(chǎn)生影響,目的就是方便閱讀。

比如:

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

反編譯后:

public class Test {   public static void main(String[] args)   {     int i = 10000;     System.out.println(i);   } }

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

糖塊十 、 for-each

增強for循環(huán)(for-each)相信大家都不陌生,日常開(kāi)發(fā)經(jīng)常會(huì )用到的,他會(huì )比f(wàn)or循環(huán)要少寫(xiě)很多代碼,那么這個(gè)語(yǔ)法糖背后是如何實(shí)現的呢?

public static void main(String... args) {     String[] strs = {"Hollis", "公眾號:Hollis", "博客:www.hollischuang.com"};     for (String s : strs) {         System.out.println(s);     }     List<String> strList = ImmutableList.of("Hollis", "公眾號:Hollis", "博客:www.hollischuang.com");     for (String s : strList) {         System.out.println(s);     } }

反編譯后代碼如下:

public static transient void main(String args[]) {     String strs[] = {         "Hollis", "\u516C\u4F17\u53F7\uFF1AHollis", "\u53***\u5BA2\uFF1Awww.hollischuang.com"     };     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("Hollis", "\u516C\u4F17\u53F7\uFF1AHollis", "\u53***\u5BA2\uFF1Awww.hollischuang.com");     String s;     for(Iterator iterator = strList.iterator(); iterator.hasNext(); System.out.println(s))         s = (String)iterator.next();  }

代碼很簡(jiǎn)單,for-each的實(shí)現原理其實(shí)就是使用了普通的for循環(huán)和迭代器。

糖塊十一 、 try-with-resource

Java里,對于文件操作IO流、數據連接等開(kāi)銷(xiāo)非常昂貴的資源,用完之后必須及時(shí)通過(guò)close方法將其關(guān)閉,否則資源會(huì )一直處于打開(kāi)狀態(tài),可能會(huì )導致內存泄露等問(wèn)題。

關(guān)閉資源的常用方式就是在finally塊里是釋放,即調用close方法。比如,我們經(jīng)常會(huì )寫(xiě)這樣的代碼:

public static void main(String[] args) {     BufferedReader br = null;     try {         String line;         br = new BufferedReader(new FileReader("d:\\hollischuang.xml"));         while ((line = br.readLine()) != null) {             System.out.println(line);         }     } catch (IOException e) {         // handle exception     } finally {         try {             if (br != null) {                 br.close();             }         } catch (IOException ex) {             // handle exception         }     } }

從Java 7開(kāi)始,jdk提供了一種更好的方式關(guān)閉資源,使用try-with-resources語(yǔ)句,改寫(xiě)一下上面的代碼,效果如下:

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

看,這簡(jiǎn)直是一大福音啊,雖然我之前一般使用IOUtils去關(guān)閉流,并不會(huì )使用在finally中寫(xiě)很多代碼的方式,但是這種新的語(yǔ)法糖看上去好像優(yōu)雅很多呢。

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

public static transient void main(String args[])     {         BufferedReader br;         Throwable throwable;         br = new BufferedReader(new FileReader("d:\\ hollischuang.xml"));         throwable = null;         String line;         try         {             while((line = br.readLine()) != null)                 System.out.println(line);         }         catch(Throwable throwable2)         {             throwable = throwable2;             throw throwable2;         }         if(br != null)             if(throwable != null)                 try                 {                     br.close();                 }                 catch(Throwable throwable1)                 {                     throwable.addSuppressed(throwable1);                 }             else                 br.close();             break MISSING_BLOCK_LABEL_113;             Exception exception;             exception;             if(br != null)                 if(throwable != null)                     try                     {                         br.close();                     }                     catch(Throwable throwable3)                       {                         throwable.addSuppressed(throwable3);                     }                 else                     br.close();         throw exception;         IOException ioexception;         ioexception;     } }

其實(shí)背后的原理也很簡(jiǎn)單,那些我們沒(méi)有做的關(guān)閉資源的操作,編譯器都幫我們做了。

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

糖塊十二、Lambda表達式

關(guān)于lambda表達式,有人可能會(huì )有質(zhì)疑,因為網(wǎng)上有人說(shuō)他并不是語(yǔ)法糖。其實(shí)我想糾正下這個(gè)說(shuō)法。

Labmda表達式不是匿名內部類(lèi)的語(yǔ)法糖,但是他也是一個(gè)語(yǔ)法糖。實(shí)現方式其實(shí)是依賴(lài)了幾個(gè)JVM底層提供的lambda相關(guān)api。

先來(lái)看一個(gè)簡(jiǎn)單的lambda表達式。遍歷一個(gè)list:

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

為啥說(shuō)他并不是內部類(lèi)的語(yǔ)法糖呢,前面講內部類(lèi)我們說(shuō)過(guò),內部類(lèi)在編譯之后會(huì )有兩個(gè)class文件,但是,包含lambda表達式的類(lèi)編譯后只有一個(gè)文件。

反編譯后代碼如下:

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

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

再來(lái)看一個(gè)稍微復雜一點(diǎn)的,先對List進(jìn)行過(guò)濾,然后再輸出:

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

反編譯后代碼如下:

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

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

所以,lambda表達式的實(shí)現其實(shí)是依賴(lài)了一些底層的api,在編譯階段,編譯器會(huì )把lambda表達式進(jìn)行解糖,轉換成調用內部api的方式。

可能遇到的坑

泛型&mdash;&mdash;當泛型遇到重載

public class GenericTypes {      public static void method(List<String> list) {           System.out.println("invoke method(List<String> list)");       }        public static void method(List<Integer> list) {           System.out.println("invoke method(List<Integer> list)");       }   }

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

泛型&mdash;&mdash;當泛型遇到catch

泛型的類(lèi)型參數不能用在Java異常處理的catch語(yǔ)句中。因為異常處理是由JVM在運行時(shí)刻來(lái)進(jìn)行的。由于類(lèi)型信息被擦除,JVM是無(wú)法區分兩個(gè)異常類(lèi)型MyException和MyException

泛型&mdash;&mdash;當泛型內包含靜態(tài)變量

public class StaticTest{     public static void main(String[] args){         GT<Integer> gti = new GT<Integer>();         gti.var=1;         GT<String> gts = new GT<String>();         gts.var=2;         System.out.println(gti.var);     } } class GT<T>{     public static int var=0;     public void nothing(T x){} }

以上代碼輸出結果為:2!由于經(jīng)過(guò)類(lèi)型擦除,所有的泛型類(lèi)實(shí)例都關(guān)聯(lián)到同一份字節碼上,泛型類(lèi)的所有靜態(tài)變量是共享的。

自動(dòng)裝箱與拆箱&mdash;&mdash;對象相等比較

public static void main(String[] args) {     Integer a = 1000;     Integer b = 1000;     Integer c = 100;     Integer d = 100;     System.out.println("a == b is " + (a == b));     System.out.println(("c == d is " + (c == d))); }

輸出結果:

a == b is false c == d is true

在Java  5中,在Integer的操作上引入了一個(gè)新功能來(lái)節省內存和提高性能。整型對象通過(guò)使用相同的對象引用實(shí)現了緩存和重用。

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

只適用于自動(dòng)裝箱。使用構造函數創(chuàng )建對象不適用。

增強for循環(huán)

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

會(huì )拋出ConcurrentModificationException異常。

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

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

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。

精品无码久久午夜福利| 欧美高潮呻吟久久AV无码| 中国A级毛片免费观看| 国产成人手机高清在线观看网站| 国产精品丝袜高跟鞋| 麻豆国产97在线 | 欧洲|