欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Java中常見語法糖的使用

 更新時(shí)間:2023年11月23日 14:14:18   作者:荼錦AI編程  
語法糖(Syntactic Sugar),也稱糖衣語法,是由英國(guó)計(jì)算機(jī)學(xué)家 Peter.J.Landin 發(fā)明的一個(gè)術(shù)語,指在計(jì)算機(jī)語言中添加的某種語法,本文主要為大家分享了12個(gè)java中常見的語法糖,感興趣的小伙伴可以了解下

簡(jiǎn)介

語法糖(Syntactic Sugar),也稱糖衣語法,是由英國(guó)計(jì)算機(jī)學(xué)家 Peter.J.Landin 發(fā)明的一個(gè)術(shù)語,指在計(jì)算機(jī)語言中添加的某種語法,這種語法對(duì)語言的功能并沒有影響,但是更方便程序員使用。簡(jiǎn)而言之,語法糖讓程序更加簡(jiǎn)潔,非常利于操作。事實(shí)上聽名字也能想到,加在語法中的糖讓語法變得更”甜“。

糖1:switch

switch對(duì)于char, byte, short, int類型是本身就支持的,但其實(shí)它們都是轉(zhuǎn)換成了整型,最后支持的其實(shí)是整型。但由于Java語法糖的出現(xiàn),switch也支持String和enum類型了

原代碼

public class SugarTest {
    public static void main(String[] args) {
        String str = "java";
        switch (str) {
            case "java":
                System.out.println("1");
                break;
            case "javac":
                System.out.println("2");
                break;
            default:
                System.out.println("default");
        }
    }
}

編譯后代碼

public class SugarTest {
    public SugarTest() {
    }

    public static void main(String[] args) {
        String str = "java";
        byte var3 = -1;
        switch(str.hashCode()) {
        case 3254818:
            if (str.equals("java")) {
                var3 = 0;
            }
            break;
        case 100899457:
            if (str.equals("javac")) {
                var3 = 1;
            }
        }

        switch(var3) {
        case 0:
            System.out.println("1");
            break;
        case 1:
            System.out.println("2");
            break;
        default:
            System.out.println("default");
        }

    }
}

可以看出,switch對(duì)于字符串類是比較字符串的hashcode方法以及通過equals方法確定是哪個(gè)字符串的,由于hashcode有一定概率會(huì)出現(xiàn)hash碰撞,用hashcode比較可能不太安全,所以仍需要進(jìn)一步通過equals比較

糖2:自動(dòng)拆裝箱

自動(dòng)拆裝箱實(shí)際上是通過包裝類的valueOf方法和xxxValue方法實(shí)現(xiàn)的,具體見我的一篇文章解鎖Java自動(dòng)拆裝箱的神秘面紗專門介紹

糖3:for-each語法

增強(qiáng)for循環(huán)可謂是廣泛使用,省去了去定義一個(gè)自增變量

public class SugarTest {
    public static void main(String[] args) {
        String[] strs = new String[]{"java", "python", "c++"};
        for (String str : strs) {
            System.out.println(str);
        }

        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            list.add(i);
        }

        for (Integer num : list) {
            System.out.println(num);
        }
    }
}

編譯后代碼

public class SugarTest {
    public SugarTest() {
    }

    public static void main(String[] args) {
        String[] strs = new String[]{"java", "python", "c++"};
        String[] var2 = strs;
        int i = strs.length;

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

        List list = new ArrayList();

        for(i = 0; i < 3; ++i) {
            list.add(i);
        }

        Iterator var7 = list.iterator();

        while(var7.hasNext()) {
            Integer num = (Integer)var7.next();
            System.out.println(num);
        }

    }
}

可以看出,一般的數(shù)組使用for-each背后的實(shí)現(xiàn)僅僅是把它變?yōu)槠胀╢ot循環(huán)而已,對(duì)于集合類使用for-each循環(huán)背后的實(shí)現(xiàn)是使用了Iterator遍歷的

由于使用了迭代器Iterator,所以在數(shù)組遍歷時(shí)不能修改數(shù)組(比如調(diào)用remove方法),否則根據(jù)fail-fast機(jī)制會(huì)拋出java.util.ConcurrentModificationException異常

糖4:方法變長(zhǎng)參數(shù)

Java是允許將一種類型的任意數(shù)量的值作為參數(shù)使用的

public class SugarTest {
    public static void main(String[] args) {
        test("java", "python", "c++");
    }

    public static void test(String... strs) {
        for (String str : strs) {
            System.out.println(str);
        }
    }
}

編譯后的代碼

public class SugarTest {
    public SugarTest() {
    }

    public static void main(String[] args) {
        test("java", "python", "c++");
    }

    public static void test(String... strs) {
        String[] var1 = strs;
        int var2 = strs.length;

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

    }
}

事實(shí)上就是將參數(shù)轉(zhuǎn)變?yōu)榱藢?duì)應(yīng)的數(shù)組

糖5:if條件編譯

條件編譯就是在編譯期間編譯器會(huì)選擇有效的代碼編譯,不滿足條件的代碼稱為無用代碼,會(huì)被直接丟棄

public class SugarTest {
    private static final boolean DEBUG = true;

    public static void main(String[] args) {
        if (DEBUG) {
            System.out.println("DEBUG模式");
        } else {
            System.out.println("非DEBUG模式");
        }
    }
}

編譯后的代碼

public class SugarTest {
    private static final boolean DEBUG = true;

    public SugarTest() {
    }

    public static void main(String[] args) {
        System.out.println("DEBUG模式");
    }
}

上面的代碼中編譯器在編譯時(shí)已經(jīng)能確定只用執(zhí)行DEBUG為true的代碼,那么編譯器只需要保存滿足條件的一行代碼即可,不需要全部保存

談一談條件編譯的作用

有人會(huì)問了,既然DEBUG已知為true了,還去寫個(gè)if多此一舉干嘛,這其實(shí)是為了后期便于維護(hù),就像上述代碼一樣,DEBUG模式可能是一段代碼,非DEBUG模式可能是另外一段代碼,開發(fā)者只需要改變常量DEBUG的值即可做到兩種模式下的自由切換,豈不樂哉?這樣配合編譯器的條件編譯機(jī)制又能使編譯后的代碼更為簡(jiǎn)潔,簡(jiǎn)直perfect!(通常這個(gè)常量可通過配置文件配置更為簡(jiǎn)便)

糖6:泛型

泛型可謂是個(gè)好東西了,在沒有泛型時(shí)我們返回的方法值、參數(shù)等都在編寫代碼時(shí)就必須確定它的類型,這為我們帶來了諸多不便,我們往往需要將這些參數(shù)和返回值設(shè)置為Object類型然后強(qiáng)轉(zhuǎn),但基于開發(fā)者的強(qiáng)轉(zhuǎn)是極易出現(xiàn)問題的。而有了泛型后這些問題便都不是問題了

public class SugarTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("111");
    }
}

編譯后的代碼

public class SugarTest {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("111");
    }
}

可以看出,泛型在編譯期間便直接被扔掉了。泛型的實(shí)現(xiàn)機(jī)制是通過編譯器的一種被稱為類型擦除(type erasure)的處理實(shí)現(xiàn)的,也就是編譯器不認(rèn)識(shí)List這個(gè)類,它只認(rèn)識(shí)并執(zhí)行List這個(gè)類

類型擦除主要兩個(gè)過程

  • 將所有使用到泛型參數(shù)的地方都用其最頂級(jí)的層次(頂層父類)替換掉
  • 將所有泛型擦除,即將尖括號(hào)<>刪除

如下這個(gè)例子

public class SugarTest {
    public static void main(String[] args) {
        String s = "1234";
        test(s);
    }

    public static <T extends Object> void test(T arg) {
        List<T> list = new ArrayList<>();
        list.add(arg);
    }
}

編譯后的代碼將所有泛型替換為Object型

public class SugarTest {
    public static void main(String[] args) {
        String s = "1234";
        test((Object)s);
    }

    public static void test(Object arg) {
        List list = new ArrayList();
        list.add(arg);
    }
}

糖7:內(nèi)部類

class OutClass {
    class Node {
        public Integer num;
    }
}

public class SugarTest {
    public static void main(String[] args) {
        OutClass outClass = new OutClass();
        OutClass.Node node = outClass.new Node();
        node.num = 10;
    }
}

編譯后事實(shí)上會(huì)產(chǎn)生兩個(gè)后綴為.class的字節(jié)碼文件,OutClass.class和OutClass$Node.class文件,由此可以看出內(nèi)部類并不是真正套在一個(gè)類的內(nèi)部,而是分成兩個(gè)類編譯

再看看靜態(tài)內(nèi)部類實(shí)際上是一樣的

class OutClass {
    static class Node {
        public Integer num;
    }
}

public class SugarTest {
    public static void main(String[] args) {
        OutClass.Node node = new OutClass.Node();
        node.num = 10;
    }
}

從上我們也能看出內(nèi)部類和靜態(tài)內(nèi)部類的一些區(qū)別,內(nèi)部類必須通過它的外部類的實(shí)例去new一個(gè)內(nèi)部類,而靜態(tài)內(nèi)部類可以直接new一個(gè)內(nèi)部類(但是要求內(nèi)部類存在時(shí)外部類必定也存在)

糖8:assert斷言

assert關(guān)鍵字是后來引入的,為了避免和老版本的Java代碼引入assert關(guān)鍵字而出錯(cuò),默認(rèn)Java是不開啟斷言檢查的,也就是說assert等于無用,如果要開啟斷言檢查使用選項(xiàng)-ea或-enableassertions開啟

【即java -ea SugarTest運(yùn)行】

public class SugarTest {
    public static void main(String[] args) {
        Integer num = 1;
        assert num != null : "Error!";
        System.out.println(num);
    }
}

編譯后的代碼

public class SugarTest {
    public SugarTest() {
    }
    
    public static void main(String[] args) {
        Integer num = 1;
        if(!$assertionsDisabled && num == null) {
            throw new AssertionError("Error!");
        } else {
         	System.out.println(num);
            return;
        }
    }
    
    static final boolean $assertionsDisabled = !src/SugarTest.desiredAssertionStatus();
}

斷言首先會(huì)檢查$assertionsDisabled是否為true,如果為true說明斷言被禁用了,直接執(zhí)行else的內(nèi)容,如果設(shè)置了開啟斷言則會(huì)判斷斷言后的表達(dá)式是否正確,不正確會(huì)拋出異常

糖9:數(shù)值字面量

允許在數(shù)字間插入任意數(shù)量的下劃線,對(duì)數(shù)字的值不會(huì)產(chǎn)生任何影響,主要是方便閱讀

【比如1_0000_0000可以直接看出是1億】

public class SugarTest {
    public static void main(String[] args) {
        Integer num = 1_0000_0000;
        System.out.println(num);
    }
}

編譯后的代碼

public class SugarTest {
    public SugarTest() {
    }

    public static void main(String[] args) {
        Integer num = 100000000;
        System.out.println(num);
    }
}

糖10:Enum枚舉類

枚舉類讓我們可以更方便的使用和定義一些常量,但在代碼中實(shí)際上enum就是個(gè)關(guān)鍵字,看不出來枚舉類到底是個(gè)什么神仙玩意,也看不出它是個(gè)什么對(duì)象

public enum EnumTest {
    JAVA, PYTHON, JS;
}

編譯后的代碼

public final class EnumTest extends Enum {

    public static final EnumTest JAVA;
    public static final EnumTest PYTHON;
    public static final EnumTest JS;
    private static final EnumTest $VALUES[];

    static {
        JAVA = new EnumTest("JAVA", 0);
        PYTHON = new EnumTest("PYTHON", 1);
        JS = new EnumTest("JS", 2);
        $VALUES = (new EnumTest[]{
                JAVA, PYTHON, JS
        });
    }
    private EnumTest(String s, int i) {
        super(s, i);
    }

    public static EnumTest[] values() {
        return (EnumTest[]) $VALUES.clone();
    }

    public static EnumTest valueOf(String name) {
        return (EnumTest) Enum.valueOf(EnumTest, name);
    }
}

可以看出,每一個(gè)枚舉類中的一個(gè)常量都是一個(gè)EnumTest對(duì)象,這個(gè)對(duì)象有兩個(gè)屬性,一個(gè)是常量的名字,另一個(gè)是從0開始的數(shù)值字面量

所有變量定義為final連類也定義為final,說明枚舉類的變量不能被修改該類也不能被繼承,values方法返回的是數(shù)組的深拷貝,所以即使通過該方法獲得了數(shù)組也無法修改數(shù)組中的值

枚舉類的構(gòu)造器是私有化的,所以用戶定義的每一個(gè)變量返回給用戶時(shí)都是同一個(gè)EnumTest對(duì)象,如果只在枚舉類定義一個(gè)常量,那么這個(gè)枚舉類就是個(gè)單例模式了

糖11:try-with-resource異常處理

相信大家都寫過try-catch-finally代碼,每次new一個(gè)流時(shí)一般都要在finally中將這個(gè)流close,如果流比較多,代碼就會(huì)顯得臃腫,那么這個(gè)語法糖就能讓代碼及其簡(jiǎn)潔

public class SugarTest {
    public static void main(String[] args) {
        try (BufferedReader reader = new BufferedReader(
            							new FileReader("c:\\file\\test.txt"));
             BufferedWriter writer = new BufferedWriter(new PrintWriter(System.out))) {
            String str;
            while ((str = reader.readLine()) != null) {
                System.out.println(str);
            }
            writer.write("讀取文件完畢");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

編譯后代碼

public class SugarTest {
    public SugarTest() {
    }

    public static void main(String[] args) {
        try {
            BufferedReader reader = new BufferedReader(new FileReader("c:\\file\\test.txt"));
            Throwable var2 = null;

            try {
                BufferedWriter writer = new BufferedWriter(new PrintWriter(System.out));
                Throwable var4 = null;

                try {
                    String str;
                    while((str = reader.readLine()) != null) {
                        System.out.println(str);
                    }

                    writer.write("讀取文件完畢");
                } catch (Throwable var29) {
                    var4 = var29;
                    throw var29;
                } finally {
                    if (writer != null) {
                        if (var4 != null) {
                            try {
                                writer.close();
                            } catch (Throwable var28) {
                                var4.addSuppressed(var28);
                            }
                        } else {
                            writer.close();
                        }
                    }

                }
            } catch (Throwable var31) {
                var2 = var31;
                throw var31;
            } finally {
                if (reader != null) {
                    if (var2 != null) {
                        try {
                            reader.close();
                        } catch (Throwable var27) {
                            var2.addSuppressed(var27);
                        }
                    } else {
                        reader.close();
                    }
                }

            }
        } catch (IOException var33) {
            var33.printStackTrace();
        }

    }
}

事實(shí)上這么簡(jiǎn)潔的代碼最后編譯器也只是把它轉(zhuǎn)換成了try-catch-finally而已,代碼故意采取兩個(gè)流,目的是讓讀者清楚編譯器會(huì)先對(duì)writer釋放流再對(duì)reader釋放流,也就是先寫的流后釋放

糖12:Lambda表達(dá)式

public class SugarTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("JAVA");
        list.forEach((elem) -> System.out.println(elem));
    }
}

編譯后的代碼

public class Lambda {
    public static void main(String[] arrstring) {
        List list = new ArrayList();
        list.add("JAVA");
        
        list.forEach((Consumer<String>)LambdaMetafactory.metafactory(null, null, null, (Ljava/lang/Object;)V, lambda$main$0(java.lang.String ), (Ljava/lang/String;)V)());
    }
    
    private static void lambda$main$0(String s) {
        System.out.println(s);
    }

}

可以看出編譯后端Lambda語法是新增了一個(gè)方法,方法中是匿名內(nèi)部類的方法體,然后調(diào)用LambdaMetafactory.metafactory這個(gè)方法執(zhí)行

以上就是詳解Java中常見語法糖的使用的詳細(xì)內(nèi)容,更多關(guān)于Java語法糖的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • jdk7 中HashMap的知識(shí)點(diǎn)總結(jié)

    jdk7 中HashMap的知識(shí)點(diǎn)總結(jié)

    HashMap的原理是老生常談了,不作仔細(xì)解說。一句話概括為HashMap是一個(gè)散列表,它存儲(chǔ)的內(nèi)容是鍵值對(duì)(key-value)映射。這篇文章主要總結(jié)了關(guān)于jdk7 中HashMap的知識(shí)點(diǎn),需要的朋友可以參考借鑒,一起來看看吧。
    2017-01-01
  • Java中漢字轉(zhuǎn)拼音pinyin4j用法實(shí)例分析

    Java中漢字轉(zhuǎn)拼音pinyin4j用法實(shí)例分析

    這篇文章主要介紹了Java中漢字轉(zhuǎn)拼音pinyin4j用法,結(jié)合實(shí)例形式較為詳細(xì)的分析了pinyin4j庫的具體使用技巧,需要的朋友可以參考下
    2015-12-12
  • SpringBoot配置動(dòng)態(tài)數(shù)據(jù)源的實(shí)戰(zhàn)詳解

    SpringBoot配置動(dòng)態(tài)數(shù)據(jù)源的實(shí)戰(zhàn)詳解

    Spring對(duì)數(shù)據(jù)源的管理類似于策略模式,不懂策略模式也沒關(guān)系,其實(shí)就是有一個(gè)全局的鍵值對(duì),類型是Map<String, DataSource>,當(dāng)JDBC操作數(shù)據(jù)庫之時(shí),會(huì)根據(jù)不同的key值選擇不同的數(shù)據(jù)源,本文介紹了SpringBoot配置動(dòng)態(tài)數(shù)據(jù)源的方法,需要的朋友可以參考下
    2024-08-08
  • java調(diào)用外部程序的方法及代碼演示

    java調(diào)用外部程序的方法及代碼演示

    這篇文章主要介紹了java調(diào)用外部程序的方法及代碼演示的相關(guān)資料,需要的朋友可以參考下
    2023-03-03
  • SpringBoot獲取maven打包時(shí)間的兩種方式

    SpringBoot獲取maven打包時(shí)間的兩種方式

    這篇文章主要介紹了SpringBoot獲取maven打包時(shí)間的兩種方式,文章通過代碼示例給大家講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2024-05-05
  • JavaWeb工程中集成YMP框架快速上手(二)

    JavaWeb工程中集成YMP框架快速上手(二)

    YMP是一個(gè)非常簡(jiǎn)單、易用的一套輕量級(jí)JAVA應(yīng)用開發(fā)框架,設(shè)計(jì)原則主要側(cè)重于簡(jiǎn)化工作任務(wù)、規(guī)范開發(fā)流程、提高開發(fā)效率。對(duì)YMP框架感興趣的小伙伴們可以參考一下
    2016-02-02
  • SpringBoot 搭建架構(gòu)5種方法示例詳解

    SpringBoot 搭建架構(gòu)5種方法示例詳解

    SpringBoot是基于Spring框架的便捷開發(fā)框架,通過約定優(yōu)于配置實(shí)現(xiàn)快速構(gòu)建獨(dú)立應(yīng)用,文章介紹了五種搭建SpringBoot項(xiàng)目的方法,包括使用IntelliJ IDEA、Spring官網(wǎng)、阿里云官網(wǎng)以及將現(xiàn)有Maven項(xiàng)目轉(zhuǎn)換為SpringBoot項(xiàng)目,感興趣的朋友跟隨小編一起看看吧
    2025-03-03
  • Java實(shí)現(xiàn)消息轟炸的方法 附帶源碼

    Java實(shí)現(xiàn)消息轟炸的方法 附帶源碼

    這篇文章主要介紹了Java實(shí)現(xiàn)消息轟炸的方法 附帶源碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • Spring Boot打開URL出現(xiàn)signin問題的解決

    Spring Boot打開URL出現(xiàn)signin問題的解決

    這篇文章主要介紹了Spring Boot打開URL出現(xiàn)signin問題的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • SpringCloud之@FeignClient()注解的使用詳解

    SpringCloud之@FeignClient()注解的使用詳解

    @FeignClient是SpringCloud中用于聲明一個(gè)Feign客戶端的注解,用于解決模塊方法互相調(diào)用的問題,Feign是一個(gè)聲明式的WebService客戶端,通過Feign,只需要?jiǎng)?chuàng)建一個(gè)接口,并使用注解來描述請(qǐng)求,就可以直接執(zhí)行HTTP請(qǐng)求了
    2024-11-11

最新評(píng)論