聊聊Java常量池

從老生常談的包裝類型“==” 和 “equals” 說起

最近在刷leetcode題時(shí),又學(xué)到了很多之前沒有注意到的點(diǎn),隨便寫寫,想到哪,寫到哪吧。

  • 請看如下代碼:
    public static void main(String[] args) {
        //這里改成Integer integerA = new Integer(12)會(huì)對最終結(jié)果有影響,后面會(huì)說明
        Integer integerA = 12;
        Integer integerB = 12;
        System.out.println("integerA="+integerA+",integerB="+integerB);
        if(integerA == integerB){
            System.out.println("integerA == integerB");
        }else {
            System.out.println("integerA != integerB");
        }

        if(integerA.equals(integerB)){
            System.out.println("integerA equals integerB");
        }else {
            System.out.println("integerA not equals integerB");
        }

    }
  • 上述代碼輸出為:
integerA=12,integerB=12
integerA == integerB
integerA equals integerB
  • 修改變量A、B的值為integerA=128,integerB=128之后,輸出為:
integerA=128,integerB=128
integerA != integerB
integerA equals integerB
  • 此時(shí)integerA != integerB
  • equals方法:這里Integer 包裝類重寫了equals方法,比較的是內(nèi)置基礎(chǔ)類型的值,同一個(gè)值是同一地址??梢赃@樣理解,在編譯時(shí),IntegerA 和 IntegerB是引用類型的對象,他們兩個(gè)有自己的地址,其中Integer對象中的value變量的地址是同一個(gè)。
    //Integer.java
    public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }
    
        /**
     * Returns the value of this {@code Integer} as an
     * {@code int}.
     */
    public int intValue() {
        return value;
    }
  • “==”比較的是引用或者說地址是否相等(8種基本類型可以理解為直接比較的值)正常情況下,IntegerA 和 IntegerB作為一個(gè)引用 類型的對象調(diào)用“==”比較,都應(yīng)該不同,而這里變量等于12時(shí),,IntegerA 和 IntegerB是同一個(gè)引用,正是因?yàn)閖ava常量池。
  • 關(guān)于jvm內(nèi)存可以參考:JVM內(nèi)存模型。注意jdk1.7中的常量池移到了堆內(nèi)存中,同時(shí)在jdk1.8中移除整個(gè)永久代,對應(yīng)的是元空間(Metaspace)區(qū)域的引進(jìn)。Oracle jdk1.7說明,RFE 6962931

何為常量池?

  • 通俗的講,即為常量共享的一種方案。可以參考這篇文章,講的很詳細(xì)。Java常量池理解與總結(jié)
  • 5種包裝類(Byte,Short,Integer,Character,Long) ,內(nèi)存中只緩存-128 到 127范圍內(nèi)的數(shù)據(jù),其中Byte類型全部進(jìn)行了緩存。這也是為什么當(dāng)時(shí)我們初始化的值比128小時(shí),引用都是用的同一個(gè)的原因。如果我們初始化時(shí)使用new Integer(12),則不會(huì)相同。直接上代碼:
Integer.java
/**
     * Returns an {@code Integer} instance representing the specified
     * {@code int} value.  If a new {@code Integer} instance is not
     * required, this method should generally be used in preference to
     * the constructor {@link #Integer(int)}, as this method is likely
     * to yield significantly better space and time performance by
     * caching frequently requested values.
     *
     * This method will always cache values in the range -128 to 127,
     (劃重點(diǎn),內(nèi)存中只緩存-128 to 127范圍內(nèi)的數(shù)據(jù))
     * inclusive, and may cache other values outside of this range.
     *
     * @param  i an {@code int} value.
     * @return an {@code Integer} instance representing {@code i}.
     * @since  1.5
     */
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
    
    
    Byte.java
    /**
     * Returns a {@code Byte} instance representing the specified
     * {@code byte} value.
     * If a new {@code Byte} instance is not required, this method
     * should generally be used in preference to the constructor
     * {@link #Byte(byte)}, as this method is likely to yield
     * significantly better space and time performance since
     * all byte values are cached.
     *(全部 byte 值已經(jīng)緩存)
     *
     * @param  b a byte value.
     * @return a {@code Byte} instance representing {@code b}.
     * @since  1.5
     */
    public static Byte valueOf(byte b) {
        final int offset = 128;
        //這里加偏移量是因?yàn)閎yte對應(yīng)的整數(shù)值和緩存索引值并非相等
        return ByteCache.cache[(int)b + offset];
    }
    
   
    /**
     *  Byte.java中的內(nèi)部類
     */
        private static class ByteCache {
        private ByteCache(){}
        static final Byte cache[] = new Byte[-(-128) + 127 + 1];
        static {
            for(int i = 0; i < cache.length; i++)
                cache[i] = new Byte((byte)(i - 128));
        }
    }

知道了這些有什么用

  • 享元模式Flyweight Design Pattern in Java
  • 享元模式(Flyweight Pattern)主要用于減少創(chuàng)建對象的數(shù)量,以減少內(nèi)存占用和提高性能。這種類型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式,減少對象數(shù)量從而改善應(yīng)用所需的對象結(jié)構(gòu)的方式。實(shí)現(xiàn)方式一般是通過HashMap完成。java常量池的設(shè)計(jì)初中也是為了減少內(nèi)存占用,同時(shí)保證訪問安全。繼承Number的包裝類常量池存儲(chǔ)使用數(shù)組,String使用繼承自HashTable的SStringTable:
op StringTable::basic_add(int index_arg, Handle string, jchar* name,
                           int len, unsigned int hashValue_arg, TRAPS) {

  // ...

  HashtableEntry<oop, mtSymbol>* entry = new_entry(hashValue, string()); // 新建
  add_entry(index, entry);    // 添加
  return string();
}
  • 說到底,設(shè)計(jì)模式是為了解決實(shí)際問題,而不是炫技,從問題出發(fā),找出解決問題的方案,如果該方案是通用的,也就成了設(shè)計(jì)模式了。
  • 在查資料的過程中,好多人的文章已經(jīng)過時(shí)了,有的說的不準(zhǔn)確甚至錯(cuò)誤的。甄別的過程,也是蠻花力氣的。要養(yǎng)成獨(dú)立思考的習(xí)慣,有矛盾的,以官方資料為準(zhǔn)。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容