内射老阿姨1区2区3区4区_久久精品人人做人人爽电影蜜月_久久国产精品亚洲77777_99精品又大又爽又粗少妇毛片

Java語(yǔ)法糖的示例分析

這篇文章將為大家詳細(xì)講解有關(guān)Java語(yǔ)法糖的示例分析,小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。

在額爾古納等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場(chǎng)前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供成都做網(wǎng)站、網(wǎng)站設(shè)計(jì)、外貿(mào)營(yíng)銷網(wǎng)站建設(shè) 網(wǎng)站設(shè)計(jì)制作按需設(shè)計(jì)網(wǎng)站,公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),品牌網(wǎng)站建設(shè),成都全網(wǎng)營(yíng)銷推廣,外貿(mào)網(wǎng)站制作,額爾古納網(wǎng)站建設(shè)費(fèi)用合理。

語(yǔ)法糖

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

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

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

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

解語(yǔ)法糖

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

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

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

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

糖塊一、 switch 支持 String 與枚舉

前面提到過(guò),從Java 7 開(kāi)始,Java語(yǔ)言中的語(yǔ)法糖在逐漸豐富,其中一個(gè)比較重要的就是Java 7中switch開(kāi)始支持String。
在開(kāi)始coding之前先科普下,Java中的swith自身原本就支持基本類型。比如int、char等。

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

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

那么接下來(lái)看下switch對(duì)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;
    }
  }
}

反編譯后內(nèi)容如下:

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í)現(xiàn)的。還好hashCode()方法返回的是int,而不是long。

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

糖塊二、 泛型

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

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

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

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

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

1.將所有的泛型參數(shù)用其最左邊界(最頂級(jí)的父類型)類型替換。
2.移除所有的類型參數(shù)。

以下代碼:

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;
}

類型擦除后會(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;
}

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

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

自動(dòng)裝箱就是Java自動(dòng)將原始類型值轉(zhuǎn)換成對(duì)應(yīng)的對(duì)象,比如將int的變量轉(zhuǎn)換成Integer對(duì)象,這個(gè)過(guò)程叫做裝箱,反之將Integer對(duì)象轉(zhuǎn)換成int類型值,這個(gè)過(guò)程叫做拆箱。

因?yàn)檫@里的裝箱和拆箱是自動(dòng)進(jìn)行的非人為轉(zhuǎn)換,所以就稱作為自動(dòng)裝箱和拆箱。

原始類型byte, short, char, int, long, float, double和 boolean對(duì)應(yīng)的封裝類為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();
}

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

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

糖塊四 、 方法變長(zhǎng)參數(shù)

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

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

public static void main(String[] args) {
  print("Holis", "公眾號(hào):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", "\u535A\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ù)在被使用的時(shí)候,他首先會(huì)創(chuàng)建一個(gè)數(shù)組,數(shù)組的長(zhǎng)度就是調(diào)用該方法是傳遞的實(shí)參的個(gè)數(shù),然后再把參數(shù)值全部放到這個(gè)數(shù)組當(dāng)中,然后再把這個(gè)數(shù)組作為參數(shù)傳遞到被調(diào)用的方法中。

糖塊五 、 枚舉

Java SE5提供了一種新的類型-Java的枚舉類型,關(guān)鍵字enum可以將一組具名的值的有限集合創(chuàng)建為一種新的類型,而這些具名的值可以作為常規(guī)的程序組件使用,這是一種非常有用的功能。參考:Java的枚舉類型用法介紹
要想看源碼,首先得有一個(gè)類吧,那么枚舉類型到底是什么類呢?是enum嗎?

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

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

public enum t {
  SPRING,SUMMER;
}

然后我們使用反編譯,看看這段代碼到底是怎么實(shí)現(xiàn)的,反編譯后代碼內(nèi)容如下:
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ō)明,該類是繼承了Enum類的,同時(shí)final關(guān)鍵字告訴我們,這個(gè)類也是不能被繼承的。

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

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

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

內(nèi)部類之所以也是語(yǔ)法糖,是因?yàn)樗鼉H僅是一個(gè)編譯時(shí)的概念。

outer.java里面定義了一個(gè)內(nèi)部類inner,一旦編譯成功,就會(huì)生成兩個(gè)完全不同的.class文件了,分別是outer.class和outer$inner.class。所以內(nè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 。

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

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

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

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;
}

糖塊七 、條件編譯

—般情況下,程序中的每一行代碼都要參加編譯。但有時(shí)候出于對(duì)程序代碼優(yōu)化的考慮,希望只對(duì)其中一部分內(nèi)容進(jìn)行編譯,此時(shí)就需要在程序中加上條件,讓編譯器只對(duì)滿足條件的代碼進(jìn)行編譯,將不滿足條件的代碼舍棄,這就是條件編譯。
如在C或CPP中,可以通過(guò)預(yù)處理語(yǔ)句來(lái)實(shí)現(xiàn)條件編譯。其實(shí)在Java中也可實(shí)現(xiàn)條件編譯。我們先來(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ā)現(xiàn),在反編譯后的代碼中沒(méi)有System.out.println("Hello, ONLINE!");,這其實(shí)就是條件編譯。
當(dāng)if(ONLINE)為false的時(shí)候,編譯器就沒(méi)有對(duì)其內(nèi)的代碼進(jìn)行編譯。

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

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

糖塊八 、 斷言

在Java中,assert關(guān)鍵字是從JAVA SE 1.4 引入的,為了避免和老版本的Java代碼中使用了assert關(guān)鍵字導(dǎo)致錯(cuò)誤,Java在執(zhí)行的時(shí)候默認(rèn)是不啟動(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("公眾號(hào):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("\u535A\u5BA2\uFF1Awww.hollischuang.com");
      return;
    }
  }
  static final boolean $assertionsDisabled = !com/hollis/suguar/AssertTest.desiredAssertionStatus();
}

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

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

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

糖塊九 、 數(shù)值字面量

在java 7中,數(shù)值字面量,不管是整數(shù)還是浮點(diǎn)數(shù),都允許在數(shù)字之間插入任意多個(gè)下劃線。這些下劃線不會(huì)對(duì)字面量的數(shù)值產(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ō)編譯器并不認(rèn)識(shí)在數(shù)字字面量中的_,需要在編譯階段把他去掉。

糖塊十 、 for-each

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

public static void main(String... args) {
  String[] strs = {"Hollis", "公眾號(hào):Hollis", "博客:www.hollischuang.com"};
  for (String s : strs) {
    System.out.println(s);
  }
  List<String> strList = ImmutableList.of("Hollis", "公眾號(hào):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", "\u535A\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", "\u535A\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í)現(xiàn)原理其實(shí)就是使用了普通的for循環(huán)和迭代器。

糖塊十一 、 try-with-resource

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

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

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ǔ)句,改寫一下上面的代碼,效果如下:

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中寫很多代碼的方式,但是這種新的語(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ǔ)法糖的作用就是方便程序員的使用,但最終還是要轉(zhuǎn)成編譯器認(rèn)識(shí)的語(yǔ)言。

糖塊十二、Lambda表達(dá)式

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

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

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

public static void main(String... args) {
  List<String> strList = ImmutableList.of("Hollis", "公眾號(hào):Hollis", "博客:www.hollischuang.com");

  strList.forEach( s -> { System.out.println(s); } );
}

為啥說(shuō)他并不是內(nèi)部類的語(yǔ)法糖呢,前面講內(nèi)部類我們說(shuō)過(guò),內(nèi)部類在編譯之后會(huì)有兩個(gè)class文件,但是,包含lambda表達(dá)式的類編譯后只有一個(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í)是調(diào)用了java.lang.invoke.LambdaMetafactory#metafactory方法,該方法的第四個(gè)參數(shù)implMethod指定了方法實(shí)現(xiàn)??梢钥吹竭@里其實(shí)是調(diào)用了一個(gè)lambda$main$0方法進(jìn)行了輸出。

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

public static void main(String... args) {
  List<String> strList = ImmutableList.of("Hollis", "公眾號(hào):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表達(dá)式分別調(diào)用了lambda1和lambda0兩個(gè)方法。

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

可能遇到的坑

泛型——當(dāng)泛型遇到重載

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è)重載的函數(shù),因?yàn)樗麄兊膮?shù)類型不同,一個(gè)是List另一個(gè)是List,但是,這段代碼是編譯通不過(guò)的。因?yàn)槲覀兦懊嬷v過(guò),參數(shù)List和List編譯之后都被擦除了,變成了一樣的原生類型List,擦除動(dòng)作導(dǎo)致這兩個(gè)方法的特征簽名變得一模一樣。

泛型——當(dāng)泛型遇到catch

泛型的類型參數(shù)不能用在Java異常處理的catch語(yǔ)句中。因?yàn)楫惓L幚硎怯蒍VM在運(yùn)行時(shí)刻來(lái)進(jìn)行的。由于類型信息被擦除,JVM是無(wú)法區(qū)分兩個(gè)異常類型MyException<String>和MyException<Integer>的

泛型——當(dāng)泛型內(nèi)包含靜態(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){}
}

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

自動(dòng)裝箱與拆箱——對(duì)象相等比較

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)));
}

輸出結(jié)果:

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

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

適用于整數(shù)值區(qū)間-128 至 +127。
只適用于自動(dòng)裝箱。使用構(gòu)造函數(shù)創(chuàng)建對(duì)象不適用。

增強(qiáng)for循環(huán)

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

會(huì)拋出ConcurrentModificationException異常。

Iterator是工作在一個(gè)獨(dú)立的線程中,并且擁有一個(gè) mutex 鎖。 Iterator被創(chuàng)建之后會(huì)建立一個(gè)指向原來(lái)對(duì)象的單鏈索引表,當(dāng)原來(lái)的對(duì)象數(shù)量發(fā)生變化時(shí),這個(gè)索引表的內(nèi)容不會(huì)同步改變,所以當(dāng)索引指針往后移動(dòng)的時(shí)候就找不到要迭代的對(duì)象,所以按照 fail-fast 原則 Iterator 會(huì)馬上拋出java.util.ConcurrentModificationException異常。
所以 Iterator 在工作的時(shí)候是不允許被迭代的對(duì)象被改變的。但你可以使用 Iterator 本身的方法remove()來(lái)刪除對(duì)象,Iterator.remove() 方法會(huì)在刪除當(dāng)前迭代對(duì)象的同時(shí)維護(hù)索引的一致性。

關(guān)于“Java語(yǔ)法糖的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。

新聞名稱:Java語(yǔ)法糖的示例分析
網(wǎng)址分享:http://www.rwnh.cn/article16/jeshgg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供搜索引擎優(yōu)化、網(wǎng)站建設(shè)、品牌網(wǎng)站建設(shè)、微信小程序、企業(yè)網(wǎng)站制作、建站公司

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

h5響應(yīng)式網(wǎng)站建設(shè)
衡东县| 庆元县| 南宁市| 靖边县| 南部县| 稷山县| 长宁县| 洪泽县| 小金县| 大宁县| 闸北区| 合山市| 顺昌县| 敦煌市| 吴江市| 分宜县| 横峰县| 运城市| 墨竹工卡县| 安乡县| 元氏县| 施甸县| 建瓯市| 日土县| 莫力| 房山区| 钦州市| 宁德市| 嘉义县| 深圳市| 广昌县| 吉隆县| 会宁县| 思南县| 札达县| 咸宁市| 穆棱市| 恩平市| 磐安县| 永新县| 金塔县|