Java 中的語法糖

2021-03-02 程式設計師自由之路

語法糖(Syntactic Sugar),也稱糖衣語法,指在計算機語言中添加的某種語法,這種語法對語言本身的功能來說沒有什麼影響,只是為了方便程式設計師進行開發,提高開發效率,使用這種語法寫出來的程序可讀性也更高。說白了,語法糖就是對現有語法的一個封裝。

但其實,Java虛擬機是並不支持語法糖的,語法糖在程序編譯階段就會被還原成簡單的基礎語法結構,這個過程就是解語法糖。所以在Java中真正支持語法糖的是Java編譯器。

Java中的語法糖

Java程式語言提供了很多語法糖,整理了下,主要有下面幾種常用的語法糖。

switch對String和枚舉類的支持

switch對枚舉和String的支持原理其實是差不多的。下面以String為列子介紹下具體的原理。

switch關鍵字原生只能支持整數類型。如果switch後面是String類型的話,編譯器會將其轉換成String的hashCode的值,所以其實switch-case語法比較的是String的hashCode值。

如果switch後面是Enum類型的話,編譯器會將其轉換為這個枚舉定義的下標(ordinal)。其實最後都是比較的整數類型。下面以Stirng舉個列子。

原始碼

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:
            //這邊需要再次通過equals方法進行判斷,因為不同字符串的hashCode值是可能相同的,比如「Aa」和「BB」的hashCode就是一樣的
            if(s.equals("hello"))
                System.out.println("hello");
            break;
        case 113318802:
            if(s.equals("world"))
                System.out.println("world");
            break;
        }
    }
}

通過反編譯可以發現,進行switch的實際是哈希值,然後通過使用equals方法比較進行安全檢查,這個檢查是必要的,因為哈希可能會發生碰撞。因此它的性能是不如使用枚舉進行switch或者使用純整數常量。

PS:這邊順帶簡單介紹一個Java的反編譯軟體Jad。下載地址的話大家可以在網上找下。使用起來非常簡單:

jad -p SwitchDemoString.class > SwitchDemoString.java

上面的命令就可以將class文件反編譯成Java文件。這個只是Jad的最簡單的使用,詳細用法可以參考這篇博客

對泛型的支持

在JDK5中,Java語言引入了泛型機制。但是這種泛型機制其實是通過類型擦除來實現的,即Java中的泛型只在程序原始碼中有效(原始碼階段提供類型檢查),在編譯後的字節碼中自動用強制類型轉換進行替代。也就是說,Java語言中的泛型機制其實就是一顆語法糖,相較與C++、C#相比,其泛型實現實在是不那麼優雅。

/**
* 在原始碼中存在泛型
*/
public static void main(String[] args) {
    Map<String,String> map = new HashMap<String,String>(16);
    map.put("name","csx-mg");
    String name = map.get("name");
    System.out.println(name);
}

通過jad反編譯出來的代碼

public static void main(String[] args) {
    //類型擦除
    Map map = new HashMap();
    map.put("name", "csx-mg");
    //強制轉換
    String name = (String)map.get("name");
    System.out.println(name);
}

通過上面反編譯後的代碼我們發現虛擬機中其實是沒有泛型的,只有普通類和普通方法,所有泛型類的類型參數在編譯時都會被擦除,泛型類並沒有自己獨有的Class類對象。

包裝類型的自動裝箱和拆箱

我們知道在 Java 中的8個基本類型和對應的包裝類型之間是可以互相賦值的(這個過程叫自動裝箱、拆箱過程)。其實這背後的原理是編譯器做了優化。如下面代碼,將基本類型賦值給包裝類其實是調用了包裝類的valueOf()方法創建了一個包裝類再賦值給了基本類型。而包裝類賦值給基本類型就是調用了包裝類的xxxValue()方法拿到基本數據類型後再賦值的。

public static void main(String[] args) {
    Integer a = 1;
    int b = 2;
    int c = a + b;
    System.out.println(c);
}

通過jad反編譯出來的代碼

public static void main(String[] args) {
    // 自動裝箱
    Integer a = Integer.valueOf(1); 
    byte b = 2;
    //自動拆箱
    int c = a.intValue() + b;
    System.out.println(c);
}

變長方法參數

變長參數特性是在JDK 5中引入的,使用變長參數有兩個條件,一是變長的那一部分參數具有相同的類型,二是變長參數必須位於方法參數列表的最後面。變長參數同樣是Java中的語法糖,其內部實現是編譯器在編譯原始碼的時候將變長參數部分轉換成了Java數組。

public class Test {

    public static void main(String[] args) {
        m1("csx-mg","reading","writing","fishing");
    }

    public static void m1(String name,String... hobbits){
        System.out.println("l am "+name);
        System.out.print("l have hobbits:[");
        for (int i = 0; i < hobbits.length; i++) {
            if(i!=hobbits.length-1) {
                System.out.print(hobbits[i]+",");
            }else {
                System.out.print(hobbits[i]+"]");
            }
        }
    }
}

上面代碼輸出:

l am csx-mg
l have hobbits:[reading,writing,fishing]

通過jad反編譯出來的代碼

public class Test
{

    public Test()
    {
    }

    public static void main(String args[])
    {
        //入參也被轉成成了數組
        m1("csx-mg", new String[] {
            "reading", "writing", "fishing"
        });
    }

    //這邊已經將變長參數轉換成了數組
    public static transient void m1(String name, String hobbits[])
    {
        System.out.println((new StringBuilder()).append("l am ").append(name).toString());
        System.out.print("l have hobbits:[");
        for(int i = 0; i < hobbits.length; i++)
            if(i != hobbits.length - 1)
                System.out.print((new StringBuilder()).append(hobbits[i]).append(",").toString());
            else
                System.out.print((new StringBuilder()).append(hobbits[i]).append("]").toString());

    }
}

枚舉

java中類的定義使用class,枚舉類的定義使用enum。但在Java的字節碼結構中,其實並沒有枚舉類型,枚舉類型只是一個語法糖,在編譯完成後就會被編譯成一個普通的類,也是用Class修飾。這個類繼承java.lang.Enum,並被final關鍵字修飾。

public enum Fruit {
    APPLE,ORINGE
}    

將Fruit的class文件進行反編譯

//繼承java.lang.Enum並聲明為final
public final class Fruit extends Enum
{

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

    public static Fruit valueOf(String s)
    {
        return (Fruit)Enum.valueOf(Fruit, s);
    }

    private Fruit(String s, int i)
    {
        super(s, i);
    }
    //枚舉類型常量
    public static final Fruit APPLE;
    public static final Fruit ORANGE;
    private static final Fruit $VALUES[];//使用數組進行維護

    static
    {
        APPLE = new Fruit("APPLE", 0);
        ORANGE = new Fruit("ORANGE", 1);
        $VALUES = (new Fruit[] {
            APPLE, ORANGE
        });
    }
}

通過上面反編譯的代碼,我們可以知道當我們使用enmu來定義一個枚舉類型的時候,編譯器會自動幫我們創建一個final類型的類繼承Enum類,所以枚舉類型不能被繼承。

內部類

Java語言中之所以引入內部類,是因為有些時候一個類只想在一個類中有用,我們不想讓其在另外一個地方被使用。內部類之所以是語法糖,是因為其只是一個編譯時的概念,一旦編譯完成,編譯器就會為內部類生成一個單獨的class文件,名為outer$innter.class。

public class Outer {
    class Inner{
    }
}

使用javac編譯後,生成兩個class文件Outer.class和Outer$Inner.class。其中Outer$Inner.class的內容如下:

class Outer$Inner {
    Outer$Inner(Outer var1) {
        this.this$0 = var1;
    }
}

條件編譯

一般情況下,源程序中所有的行都參加編譯。但有時希望對其中一部分內容只在滿足一定條件下才進行編譯,即對一部分內容指定編譯條件,這就是「條件編譯」(conditional compile)。

Java中的條件編譯是通過編譯器的優化原則實現的:

public class ConditionalCompilation02
{
    public static void main(String[] args) {
        if(CompilationConfig.DEBUG_MODE)
        {
            System.out.println("[DEBUG MODE]You can print sth");
        }
        else
        {
            System.out.println("[RELEASE MODE]You can print sth");
        }
    }
}

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

斷言

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

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

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

public class Test {
    public static void main(String[] args) {
        System.out.println("begin to test assert...");
        boolean flag;
        //當布爾值為true的時候不會拋出錯誤
        flag = true;
        assert flag;
        //這邊會拋出AssertionError錯誤,但是需要加入vm參數-ea
        flag = false;
        assert flag;
    }
}

代碼輸出

begin to test assert...
Exception in thread "main" java.lang.AssertionError
    at com.csx.demo.spring.boot.utildemo.Test.main(Test.java:8)

通過jad反編譯出來的代碼

public class Test
{

    public Test()
    {
    }

    public static void main(String args[])
    {
        System.out.println("begin to test assert...");
        boolean flag = true;
        if(!$assertionsDisabled && !flag)
            throw new AssertionError();
        flag = false;
        if(!$assertionsDisabled && !flag)
            throw new AssertionError();
        else
            return;
    }

    static final boolean $assertionsDisabled = !com/csx/demo/spring/boot/utildemo/Test.desiredAssertionStatus();

}

通過反編譯後的代碼可以看出assert斷言就是通過對布爾標誌位進行了一個if判斷。(需要注意的是如果需要使用斷言,需要設置JVM參數-ea開始斷言)

數值字面量

Java中支持如下形式的數值字面量

十進位:默認的

八進位:整數之前加數字0來表示

十六進位:整數之前加"0x"或"0X"

二進位(新加的):整數之前加"0b"或"0B"

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

下劃線只能出現在數字中間,前後必須是數字。所以「_100」、「0b_101「是不合法的,無法通過編譯。這樣限制的動機就是可以降低實現的複雜度。有了這個限制,Java編譯器只需在掃描原始碼的時候將所發現的數字中間的下劃線直接刪除就可以了。如果不添加這個限制,編譯器需要進行語法分析才能做出判斷。比如:_100,可能是一個整數字面量100,也可能是一個變量名稱。這就要求編譯器的實現做出更複雜的改動。

public class Test {
    public static void main(String[] args) {
        //十進位
        int a = 10;
        //二進位
        int b = 0B1010;
        //八進位
        int c = 012;
        //十六進位
        int d = 0XA;

        double e = 12_234_234.23;
        System.out.println("a:"+a);
        System.out.println("b:"+b);
        System.out.println("c:"+c);
        System.out.println("d:"+d);
        System.out.println("e:"+e);
    }
}

通過jad反編譯出來的代碼

public class Test
{

    public Test()
    {
    }

    public static void main(String args[])
    {
        int a = 10;
        //編譯器已經將二進位,八進位,十六進位數轉換成了10進位數
        int b = 10;
        int c = 10;
        int d = 10;
        //編譯器已經將下滑線刪除
        double e = 12234234.23D;
        System.out.println((new StringBuilder()).append("a\uFF1A").append(a).toString());
        System.out.println((new StringBuilder()).append("b\uFF1A").append(b).toString());
        System.out.println((new StringBuilder()).append("c\uFF1A").append(c).toString());
        System.out.println((new StringBuilder()).append("d\uFF1A").append(d).toString());
        System.out.println((new StringBuilder()).append("e\uFF1A").append(e).toString());
    }
}

增強for循環

增強for循環的對象要麼是一個數組,要麼實現了Iterable接口。這個語法糖主要用來對數組或者集合進行遍歷,其在循環過程中不能改變集合的大小。增強for循環主要使代碼更加簡潔,其背後的原理是編譯器將增強for循環轉換成了普通的for循環或者while循環。

public static void main(String[] args) {
    String[] params = new String[]{"hello","world"};
    //增強for循環對象為數組
    for(String str : params){
        System.out.println(str);
    }

    List<String> lists = Arrays.asList("hello","world");
    //增強for循環對象實現Iterable接口
    for(String str : lists){
        System.out.println(str);
    }
}

編譯器編譯後的代碼

public static void main(String[] args) {
   String[] params = new String[]{"hello", "world"};
   String[] lists = params;
   int var3 = params.length;
   //數組形式的增強for退化為普通for
   for(int str = 0; str < var3; ++str) {
       String str1 = lists[str];
       System.out.println(str1);
   }

   List var6 = Arrays.asList(new String[]{"hello", "world"});
   Iterator var7 = var6.iterator();
   //實現Iterable接口的增強for使用iterator接口進行遍歷
   while(var7.hasNext()) {
       String var8 = (String)var7.next();
       System.out.println(var8);
   }

}

try-with-resource語法

當一個外部資源的句柄對象實現了AutoCloseable接口,JDK7中便可以利用try-with-resource語法更優雅的關閉資源,消除板式代碼。

public static void main(String[] args) {
    try (FileInputStream inputStream = new FileInputStream(new File("test"))) {
        System.out.println(inputStream.read());
    } catch (IOException e) {
        throw new RuntimeException(e.getMessage(), e);
    }
}

將外部資源的句柄對象的創建放在try關鍵字後面的括號中,當這個try-catch代碼塊執行完畢後,Java會確保外部資源的close方法被調用。代碼是不是瞬間簡潔許多!try-with-resource並不是JVM虛擬機的新增功能,只是JDK實現了一個語法糖,當你將上面代碼反編譯後會發現,其實對JVM虛擬機而言,它看到的依然是之前的寫法:

public static void main(String[] args) {
    try {
        FileInputStream inputStream = new FileInputStream(new File("test"));
        Throwable var2 = null;
        try {
            System.out.println(inputStream.read());
        } catch (Throwable var12) {
            var2 = var12;
            throw var12;
        } finally {
            if (inputStream != null) {
                if (var2 != null) {
                    try {
                        inputStream.close();
                    } catch (Throwable var11) {
                        var2.addSuppressed(var11);
                    }
                } else {
                    inputStream.close();
                }
            }
        }

    } catch (IOException var14) {
        throw new RuntimeException(var14.getMessage(), var14);
    }
}

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

Lambda表達式

Lambda表達式雖然看著很先進,但其實Lambda表達式的本質只是一個"語法糖",由編譯器推斷並幫你轉換包裝為常規的代碼,因此你可以使用更少的代碼來實現同樣的功能。本人建議不要亂用,因為這就和某些很高級的黑客寫的代碼一樣,簡潔,難懂,難以調試,維護人員想罵娘

lambda表達式允許你通過表達式來代替功能接口。Lambda表達式還增強了集合庫。Java SE 8添加了2個對集合數據進行批量操作的包: java.util.function 包以及java.util.stream 包。流(stream)就如同迭代器(iterator),但附加了許多額外的功能。總的來說,lambda表達式和 stream 是自Java語言添加泛型(Generics)和註解(annotation)以來最大的變化。

Lambda表達式的語法

基本語法:
(parameters) -> expression

(parameters) ->{ statements; }

Lambda表達式的一些簡單列子

// 1. 不需要參數,返回值為 5  
() -> 5  

// 2. 接收一個參數(數字類型),返回其2倍的值  
x -> 2 * x  

// 3. 接受2個參數(數字),並返回他們的差值  
(x, y) -> x – y  

// 4. 接收2個int型整數,返回他們的和  
(int x, int y) -> x + y  

// 5. 接受一個 string 對象,並在控制臺列印,不返回任何值(看起來像是返回void)  
(String s) -> System.out.print(s)

基本的Lambda例子(實現功能接口)

String[] atp = {"Rafael Nadal", "Novak Djokovic",
                "Stanislas Wawrinka",
                "David Ferrer", "Roger Federer",
                "Andy Murray", "Tomas Berdych",
                "Juan Martin Del Potro"};

        List<String> players =  Arrays.asList(atp);

        //實現功能接口
        players.forEach((String player) ->{
            System.out.println(player);
        });
        Runnable runnable = () -> {
            System.out.println("l am a new thread...");
        };
        new Thread(runnable).start();   

使用Lambdas排序集合

players.sort(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.compareTo(o2);
            }
        });
Comparator<String> comparator = (String o1,String o2) ->{return o1.compareTo(o2);};
players.sort(comparator);

使用Lambdas和Streams

Stream是對集合的包裝,通常和lambda一起使用。使用lambdas可以支持許多操作,如 map, filter, limit, sorted, count, min, max, sum, collect 等等。同樣,Stream使用懶運算,他們並不會真正地讀取所有數據,遇到像getFirst() 這樣的方法就會結束鏈式語法。

字符串對+號的支持

String s=null;
s=s+"abc";
System.out.println(s);

上面的代碼輸出什麼?

字符串+號拼接原理:運行時,兩個字符串str1, str2的拼接首先會new一個StringBuilder對象,然後分別對字符串進行append操作,最後調用toString()方法。

看下反編譯出來的代碼:

public static void main(String args[])
{
    String s = null;
    s = (new StringBuilder()).append(s).append("abc").toString();
    System.out.println(s);
}

所以答案是:nullabc

但是如果在編譯期能確定字符相加的結果,則會進行編譯期優化。

String s = "a" + "b"

對於上面的表達式,編譯器直接優化成

String s = "ab";

參考

https://www.cnblogs.com/wanshiming/p/7685684.html
https://www.cnblogs.com/franson-2016/p/5593080.html
https://www.jb51.net/article/138519.htm

相關焦點

  • Java 中的 6 顆語法糖
    Java作為一種與平臺無關的高級語言,當然也含有語法糖,這些語法糖並不被虛擬機所支持,在編譯成字節碼階段就自動轉換成簡單常用語法。Java語言並不是一開始就支持泛型的。在早期的JDK中,只能通過Object類是所有類型的父類和強制類型轉換來實現泛型的功能。強制類型轉換的缺點就是把編譯期間的問題延遲到運行時,JVM並不能為我們提供編譯期間的檢查。在JDK1.5中,Java語言引入了泛型機制。
  • Java中的6顆語法糖
    也就是說,Java語言中的泛型機制其實就是一顆語法糖,相較與C++、C#相比,其泛型實現實在是不那麼優雅。我們知道Java是一門面向對象的語言,在Java世界中有一句話是這麼說的:「萬物皆對象」。但是Java中的基本數據類型卻不是對象,他們不需要進行new操作,也不能調用任何方法,這在使用的時候有諸多不便。因此Java為這些基本類型提供了包裝類,並且為了使用方便,提供了自動裝箱與拆箱功能。自動裝箱與拆箱在使用的過程中,其實是一個語法糖,內部還是調用了相應的函數進行轉換。
  • 在Java中12個常見的語法糖!
    本文從 Java 編譯原理角度,深入字節碼及 class 文件,抽絲剝繭,了解 Java 中的語法糖原理及用法,幫助大家在學會如何使用 Java 語法糖的同時,了解這些語法糖背後的原理語法糖語法糖(Syntactic Sugar),也稱糖衣語法,是由英國計算機學家 Peter.J.Landin 發明的一個術語,指在計算機語言中添加的某種語法
  • Java 中的語法糖,真甜.
    語法糖在聊之前我們需要先了解一下 語法糖 的概念:語法糖(Syntactic sugar),也叫做糖衣語法,是英國科學家發明的一個術語,通常來說使用語法糖能夠增加程序的可讀性,從而減少程序代碼出錯的機會,真是又香又甜。語法糖指的是計算機語言中添加的某種語法,這種語法對語言的功能並沒有影響,但是更方便程式設計師使用。
  • Java 語法糖詳解
    有意思的是,在編程領域,除了語法糖,還有語法鹽和語法糖精的說法,篇幅有限這裡不做擴展了。我們所熟知的程式語言中幾乎都有語法糖。作者認為,語法糖的多少是評判一個語言夠不夠牛逼的標準之一。很多人說 Java 是一個 「低糖語言」,其實從 Java 7 開始 Java 語言層面上一直在添加各種糖,主要是在 「Project Coin」 項目下研發。
  • 不了解這 12 個語法糖,別說你會 Java!
    有意思的是,在編程領域,除了語法糖,還有語法鹽和語法糖精的說法,篇幅有限這裡不做擴展了。我們所熟知的程式語言中幾乎都有語法糖。作者認為,語法糖的多少是評判一個語言夠不夠牛逼的標準之一。但其實,Java虛擬機並不支持這些語法糖。這些語法糖在編譯階段就會被還原成簡單的基礎語法結構,這個過程就是解語法糖。說到編譯,大家肯定都知道,Java語言中,javac命令可以將後綴名為.java的源文件編譯為後綴名為.class的可以運行於Java虛擬機的字節碼。
  • 不懂這12個語法糖,別說你會Java!
    本文從 Java 編譯原理角度,深入字節碼及 class 文件,抽絲剝繭,了解 Java 中的語法糖原理及用法,幫助大家在學會如何使用 Java 語法糖的同時,了解這些語法糖背後的原理語法糖語法糖(Syntactic Sugar),也稱糖衣語法,是由英國計算機學家 Peter.J.Landin
  • 不了解這12個語法糖,別說你會Java!
    但其實,Java虛擬機並不支持這些語法糖。這些語法糖在編譯階段就會被還原成簡單的基礎語法結構,這個過程就是解語法糖。說到編譯,大家肯定都知道,Java語言中,javac命令可以將後綴名為.java的源文件編譯為後綴名為.class的可以運行於Java虛擬機的字節碼。
  • Java之編譯期優化
    概述Java語言的「編譯期」其實是一段「不確定」的操作過程,因為他可能是指一個前端編譯器把java文件轉變成class文件的過程;也可能是指虛擬機的後端運行期編譯器把字節碼轉變成機器碼的過程;還可能是指使用靜態提前編譯器直接把java文件編譯成本地機器代碼的過程。
  • 語法糖
    生活中的語法糖:xswl、yyds、nbcs解語法糖:就像我們日常說的一些"暗語"、"黑話",這些被發明出來是方便人們使用的,但是並不是所有人都能看得懂。在看得懂的人之間使用的話是很方便的,但是如果有人不懂的話,就需要解釋給他們聽。
  • Java編譯期與運行期,別傻傻分不清楚!
    畢竟總不能給你什麼以.java為後綴的文件都進行編譯吧,需要有各種校驗解析步驟在語義分析中,根據符號表所登記的內容 語義檢查和產生中間代碼,在目標代碼生成階段,當對符號表進行地址分配時,該符號表是檢查的依據。2.2 註解處理器註解與普通的Java代碼一樣,是在運行期間發揮作用的。我們可以把它看做是一組編譯器的插件,在這些插件裡面,可以讀取、修改、添加抽象語法樹中的任意元素。
  • C#7.0新特性和語法糖詳解
    對於早已熟悉了舊版本C#的開發者來說,C#7.0增加的不少新特性和語法糖能在很大程度上提升編程效率並降低出錯率。本文將闡述C#7.0給出的9個改進。1、元組——更優雅地返回多個值之所以將元組放在第一位,是因為它對C#編程體驗的提升實在是太大了。元組這個概念在以前就已經被引入了C#,只不過它是通過一個名為Tuples的泛型類來實現的。
  • 學習Java 從這些書開始吧
    其實Java並沒有想像中的那麼難,前提是做好一個心理準備,那就是你想走遠點,就得不間斷的去學習,去汲取知識而且不只是讀死書,你還會玩遊戲、拼圖、解謎題以及以意想不到的方式與java交互。在這些活動中,你會寫出一堆真正的java程序,包括了一個船艦炮戰遊戲和一個網絡聊天程序。
  • 給Java新手的一些建議——Java知識點歸納(Java基礎部分)
    Java的運行(基礎必備)這條可能出看很簡單,java程序的運行誰不會呢?不過很多時候, 我們只是單純通過IDE去執行java程序,底層IDE又是如何執行java程序呢?很多人並不了解。這個知識點是最最基本的java開發者需要掌握的,初學java,第一個肯定是教你如何在命令行中執行java程序,但是很多人一旦把java學完了,IDE用上了,就把這個都忘了。
  • Java代碼的編譯與反編譯
    主要是進行詞法分析和語法分析,又稱為源程序分析,分析過程中發現有語法錯誤,給出提示信息。反編譯作為自己開發軟體時的參考,或者直接用於自己的軟體產品中。三、 Java類的編譯與反編譯我們在最初學習Java的時候,會接觸到兩個命令:javac和java,那個時候我們就知道,javac是用來編譯Java類的,就是將我們寫好的helloworld.java文件編譯成helloworld.class文件。
  • 在Java 中安全使用接口引用
    我在過去的一年中嘗試學習並使用它們,它們的語法糖讓我愛不釋手,我尤其對?. 操作符感到驚訝,它讓我寫更少的代碼,就能夠避免空指針異常(NullPointerException)。可惜的是Java 並沒有提供這種操作符,所以本文就和大家聊聊如何在Java 中取代繁瑣的非空判斷。
  • java中包名不能以java開頭
    java中自己寫的類的包名為什麼不能以java開頭?這是因為jvm在加載類的時候,連接階段,會做安全校驗,包名startsWith("java.")在運行期會報錯。具體是在ClassLoader.java中的preDefineClass方法:if ((name != null) && name.startsWith("java."))
  • Java 14 發布了,終於可以扔掉Lombok了?
    > this.y = y; } // state-based implementations of equals, hashCode, toString // nothing else }這裡面的Piont其實就是一個純數據載體,他表示一個"點"中包含x坐標和
  • 最通俗易懂的 Java 10 新特性講解|原力計劃
    這其實只是一個新的語法糖,底層並沒有變化,在編譯時就已經把 var 轉化成具體的數據類型了,但是這樣可以減少代碼的編寫。你可以像下面這樣使用 var 語法。for 循環或者增強for 循環中。for循環中的聲明。下面演示三種使用情況。
  • Java認證:Java編程中實現中文排序
    Java認證:Java編程中實現中文排序  第一種情況:  Comparator cmp = Collator.getInstance(java.util.Locale.CHINA);  String[] arr = { 「張三」, 「李四」, 「王五」, 「劉六」 };