• 
    

      <address id="upfr9"><pre id="upfr9"><strike id="upfr9"></strike></pre></address>
      1. <address id="upfr9"><tr id="upfr9"></tr></address><dl id="upfr9"></dl>

        不了解這12個(gè)語(yǔ)法糖,別說(shuō)你會(huì)Java!

        2021-03-02    分類: 網(wǎng)站建設(shè)

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

        語(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開始Java語(yǔ)言層面上一直在添加各種糖,主要是在“Project Coin”項(xiàng)目下研發(fā)。盡管現(xiàn)在Java有人還是認(rèn)為現(xiàn)在的Java是低糖,未來(lái)還會(huì)持續(xù)向著“高糖”的方向發(fā)展。

        解語(yǔ)法糖

        前面提到過(guò),語(yǔ)法糖的存在主要是方便開發(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 開始,Java語(yǔ)言中的語(yǔ)法糖在逐漸豐富,其中一個(gè)比較重要的就是Java 7中switch開始支持String。

        在開始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得支持,有以下代碼:

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

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

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

        看到這個(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)的。

        也就是說(shuō),對(duì)于Java虛擬機(jī)來(lái)說(shuō),他根本不認(rèn)識(shí)Map。

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

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

        2.移除所有的類型參數(shù)。

        以下代碼:

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

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

        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; 

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

        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; 

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

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

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

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

        從反編譯得到內(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ù):

        1. public static void main(String[] args) 
        2.     { 
        3.         print("Holis", "公眾號(hào):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", "\u535A\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.  

        從反編譯后代碼可以看出,可變參數(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è)枚舉:

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

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

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

        通過(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è)完全不同的.c

        分享題目:不了解這12個(gè)語(yǔ)法糖,別說(shuō)你會(huì)Java!
        本文URL:http://www.jbt999.com/news3/103703.html

        成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供虛擬主機(jī)、建站公司、全網(wǎng)營(yíng)銷推廣、關(guān)鍵詞優(yōu)化網(wǎng)站策劃、自適應(yīng)網(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;郵箱:[email protected]。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

        h5響應(yīng)式網(wǎng)站建設(shè)

      2. 
        

          <address id="upfr9"><pre id="upfr9"><strike id="upfr9"></strike></pre></address>
          1. <address id="upfr9"><tr id="upfr9"></tr></address><dl id="upfr9"></dl>
            大香蕉伊人av | 国产免费污污 | 欧美大屌在线观看视频 | 中文天堂网视频在线 | www.jingpin | 久久激情亚洲色 | 青青网站黄色 | 亚洲啪啪网 | 男女操网站 | 午夜操穴 |