你真的了解Java中的Long嘛?

實際開發(fā)中,我們使用Long類型的情景應該是非常多的。可是,你真的完全掌握了Long類型嘛?

測試代碼

首先,我們來看一段測試代碼。該段代碼可以通過測試。

    @Test
    public void testLong() {
        long primaryLong = 127L;
        Long long1 = Long.valueOf(primaryLong);
        Long long2 = 127L;

        Assert.assertTrue(long1 == long2); // 1

        long1 = new Long(127L);
        Assert.assertFalse(long1 == long2); // 2

        primaryLong = 128L;
        long1 = Long.valueOf(primaryLong);
        long2 = 128L;

        Assert.assertFalse(long1 == long2);  // 3

        long1 = new Long(128L);
        Assert.assertFalse(long1 == long2);  // 4
    }

上述測試代碼中,我們比較了2個Long類型的變量long1和long2之間是否相等,這里我們用了“==”操作符,用來測試2個變量所引用的地址是否相等。

資深碼農應該都創(chuàng)建Long類型有三種方法:new Long()、Long.valueOf()和自動裝箱。上述代碼分別演示了三種方式得到的結果。

  • 代碼注析1返回為true。這說明在使用值127L時,不管使用Long.valueOf賦值創(chuàng)建Long變量還是使用自動裝箱將long轉換為Long類型,2個變量都指向同一位置。
  • 代碼注析2返回為false。這說明使用new Long(127L)創(chuàng)建出的Long變量,跟剛才使用自動裝箱創(chuàng)建的Long變量不再指向同一位置。
  • 代碼注析3、4都返回為false。這說明在使用值128L創(chuàng)建Long變量后,每次都指向不同的位置,不管是new Long、Long.valueOf還是自動裝箱。

這是怎么回事?特別是對于Long.valueOf還是自動裝箱這兩種方法,居然在值為127L和128L的時候結果不一樣?

要想徹底理解上述代碼為什么是這樣的結果,還得從源代碼入手。

分析

我們打開java.lang.Long的源代碼。其中,關鍵性代碼如下。

public final class Long extends Number implements Comparable<Long> {
    private final long value; // 1

    private static class LongCache { // 2
        private LongCache(){} // 2.1

        static final Long cache[] = new Long[-(-128) + 127 + 1]; // 2.2

        static { // 2.3
            for(int i = 0; i < cache.length; i++)
                cache[i] = new Long(i - 128);
        }
    }

    @Deprecated(since="9")
    public Long(long value) { // 3
        this.value = value;
    }

    @Deprecated(since="9")
    public Long(String s) throws NumberFormatException { // 4
        this.value = parseLong(s, 10);
    }

    public static Long valueOf(long l) { // 5
        final int offset = 128;
        if (l >= -128 && l <= 127) { // will cache
            return LongCache.cache[(int)l + offset];
        }
        return new Long(l);
    }
}
  • Long類繼承自Number基類,Number類為一個抽象類,代表數。下圖展示了Integer、Long、Byte、Short、Float和Double都是繼承自Number。


    image

    該類只有一些抽象方法XXXValue。如下圖所示。


    image
  • 注析1代表Long內部實際上存儲的還是一個變量value,用來存儲long類型原始數據。

  • 注析3、4則是Long的2個構造方法,2個方法都是對value進行賦值,不做其他處理。

  • 我們來看注析2和5:注析2是一個內部靜態(tài)類LongCache,從名字上看,該類應該是Long類型的緩存:

    • 注析2.1表示LongCache有一個private構造方法,用來阻止對該類的實例化。private構造方法通常用在工具類或者單實例類中,例如常見的單例模式就需要private構造方法。
    • 注析2.2表示該類有一個Long類型的數組cache,并且?guī)tatic final修飾。該數組的大小為-(-128) + 127 + 1=256。
      帶有final的數組一經初始化就不能再對其重新賦值,但是還是可以通過對下標的引用修改cache數組各元素的值。
    • 注析2.3表示對cache進行初始化。下標0到256分別初始化為-128到128之間的值。
  • 接下來看注析5,當參數l為-128到127之間的值時,直接返回的是LongCache內部cache數組中對應下標中的值。這里很巧妙的設置offset為128,從而將最小值-128的值的下標設置為(int)l + offset=0。對不屬于[-128, 127]范圍之類的值,則調用new Long返回。

  • 同時,留意構造方法處的@Deprecated(since="9")注解,表示從JDK9開始,構造方法已經不被推薦使用。JDK文檔中,推薦使用靜態(tài)工廠方法Long.valueOf來構造Long對象,能夠獲得更好的時間和空間表現。

根據語法標準,自動封裝實際上調用的就是Long.valueOf,而不是構造方法。

綜上分析,對于[-128, 127]的值,不管使用Long.valueOf還是自動裝箱,最終都是讀取LongCache.cache的同一下標的值,故而“==”為真。其他情況,2各不同的變量“==”比較為false。

拓展

  • 對于Short類,源代碼中ShortCache類和valueOf方法的實現和Long類型一模一樣。這里就不展開了。
  • 對于Byte類,只能容納-128到127之間的值。valueOf方法能夠通過ShortCache.cache返回所有的值。
  • 對于Float和Double類,沒有所謂的FloatCache和DoubleCache,所以每次構建的都是新對象。
  • 為避免上述代碼中出現的“==”比較出現的結果差異性。建議對象之間的之間比較,我們使用equals或者將包裝類拆裝成原始類型再使用“==”比較,上述測試代碼中,可以使用long1.longValue()或者+long1將Long轉換為long再使用“==”比較

注意,+long1一定是確保了+long1不會返回null。否則會報NullPointerException。


這里給一個小小的測試,看看大家理解的如何?

Long long1 = new Long(100L);
Long long2 = 100L;
Long long3 = 100L;
Long long4 = 128L;
Long long5 = 128L;

System.out.println(long1 == long2);
System.out.println(+long1 == long2);

System.out.println(long2 == long3);
System.out.println(+long2 == long3);

System.out.println(long4 == long5);
System.out.println(+long4 == long5);

以上代碼你認為輸出什么?

<details>
<summary>答案</summary>

<pre>
<code>
false
true
true
true
false
true
</code>
</pre>
</details>

覺得好?關注微信公眾號:技術之禪(微信號"zen_of_java")獲取更多干貨和資源!
公眾號回復"java","spring","javascript","python","english"等獲取福利。

?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 前言 看大神推薦的書單中入門有這么一本書,所以決定把這本書的精華(自認為很有用的點),或許是我自己現在能用到的點都...
    我沒有三顆心臟閱讀 2,375評論 0 6
  • 1,和媽媽結婚 溜公園的時候看到一對拍婚紗照的,帶了一堆伴郎伴娘像真結婚一樣,布丁在那看呆了,說:“哇!王子和公主...
    娜樣出彩閱讀 230評論 1 1
  • 我將寫好幾本關于MC的故事 所以會很少更 不要介意 我剛小學畢業(yè) 又要上補習班
    藍澤櫻姬閱讀 720評論 3 11
  • 蘇村校區(qū)立足“師德建設”、著眼“學生的素質與未來”,在教學活動中德育引領,與家長之間建立了良好的溝通機制,家長們對...
    碧潭止水閱讀 191評論 0 1

友情鏈接更多精彩內容