真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

分析JDK中String的存儲區(qū)與不可變性

這篇文章主要介紹“分析JDK中String的存儲區(qū)與不可變性”,在日常操作中,相信很多人在分析JDK中String的存儲區(qū)與不可變性問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”分析JDK中String的存儲區(qū)與不可變性”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

站在用戶的角度思考問題,與客戶深入溝通,找到固陽網(wǎng)站設(shè)計與固陽網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗,讓設(shè)計與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個性化、用戶體驗好的作品,建站類型包括:成都做網(wǎng)站、網(wǎng)站制作、企業(yè)官網(wǎng)、英文網(wǎng)站、手機端網(wǎng)站、網(wǎng)站推廣、申請域名、網(wǎng)絡(luò)空間、企業(yè)郵箱。業(yè)務(wù)覆蓋固陽地區(qū)。

1. 數(shù)據(jù)存儲區(qū)

String是一個比較特殊的類,除了new之外,還可以用字面常量來定義。為了弄清楚這二者間的區(qū)別,首先我們得明白JVM運行時數(shù)據(jù)存儲區(qū),這里有一張圖對此有清晰的描述:
分析JDK中String的存儲區(qū)與不可變性

非共享數(shù)據(jù)存儲區(qū)

非共享數(shù)據(jù)存儲區(qū)是在線程啟動時被創(chuàng)建的,包括:

  • 程序計數(shù)器(program counter register)控制線程的執(zhí)行;

  • 棧(JVM Stack, Native Method Stack)存儲方法調(diào)用與對象的引用等。

  • 共享數(shù)據(jù)存儲區(qū)

    該存儲區(qū)被所有線程所共享,可分為:

  • 堆(Heap)存儲所有的Java對象,當(dāng)執(zhí)行new對象時,會在堆里自動進(jìn)行內(nèi)存分配。

  • 方法區(qū)(Method Area)存儲常量池(run-time constant pool)、字段與方法的數(shù)據(jù)、方法與構(gòu)造器的代碼。

  • 2. 兩種實例化

    實例化String對象:

    public class StringLiterals {
        public static void main(String[] args) {
            String one = "Test";
            String two = "Test";
            String three = "T" + "e" + "s" + "t";
            String four = new String("Test");
        }
    }

    javap -c StringLiterals反編譯生成字節(jié)碼,我們選取感興趣的部分如下:

      public static void main(java.lang.String[]);
        Code:
           0: ldc           #2                  // String Test
           2: astore_1
           3: ldc           #2                  // String Test
           5: astore_2
           6: ldc           #2                  // String Test
           8: astore_3
           9: new           #3                  // class java/lang/String
          12: dup
          13: ldc           #2                  // String Test
          15: invokespecial #4                  // Method java/lang/String."": (Ljava/lang/String;)V
          18: astore        4
          20: return
    }

    ldc #2表示從常量池中取#2的常量入棧,astore_1表示將引用存在本地變量1中。因此,我們可以看出:對象one、two、three均指向常量池中的字面常量"Test";對象four是在堆中new的新對象;如下圖所示:
    分析JDK中String的存儲區(qū)與不可變性
    總結(jié)如下:

  • 當(dāng)用字面常量實例化時,String對象存儲在常量池;

  • 當(dāng)用new實例化時,String對象存儲在堆中;

  • 操作符==比較的是對象的引用,當(dāng)其指向的對象不同時,則為false。因此,開篇中的代碼會出現(xiàn)通過new所創(chuàng)建String對象不一樣。

    3. 不可變String

    String源碼

    JDK7的String類:

    public final class String
        implements java.io.Serializable, Comparable, CharSequence {
        /** The value is used for character storage. */
        private final char value[];
        /** Cache the hash code for the string */
        private int hash; // Default to 0
    }

    String類被聲明為final,不可以被繼承,所有的方法隱式地指定為final,因為無法被覆蓋。字段char value[]表示String類所對應(yīng)的字符串,被聲明為private final;即初始化后不能被修改。常用的new實例化對象String s1 = new String("abcd");的構(gòu)造器:

    public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }

    只需將value與hash的字段值進(jìn)行傳遞即可。

    不可變性

    所謂不可變性(immutability)指類不可以通過常用的API被修改。為了更好地理解不可變性,我們先來看《Thinking in Java》中的一段代碼:

    //: operators/Assignment.java
    // Assignment with objects is a bit tricky.
    import static net.mindview.util.Print.*;
    class Tank {
      int level;
    }   
    public class Assignment {
      public static void main(String[] args) {
        Tank t1 = new Tank();
        Tank t2 = new Tank();
        t1.level = 9;
        t2.level = 47;
        print("1: t1.level: " + t1.level +
              ", t2.level: " + t2.level);
        t1 = t2;
        print("2: t1.level: " + t1.level +
              ", t2.level: " + t2.level);
        t1.level = 27;
        print("3: t1.level: " + t1.level +
              ", t2.level: " + t2.level);
      }
    } /* Output:
    1: t1.level: 9, t2.level: 47
    2: t1.level: 47, t2.level: 47
    3: t1.level: 27, t2.level: 27
    *///:~

    上述代碼中,在賦值操作t1 = t2;之后,t1、t2包含的是相同的引用,指向同一個對象。因此對t1對象的修改,直接影響了t2對象的字段改變。顯然,Tank類是可變的。

    也許,有人會說s = s.concat("ef");不是修改了對象s么?而事實上,我們?nèi)タ碿oncat的實現(xiàn),會發(fā)現(xiàn)其返回的是新String對象(return new String(buf, true););改變的只是s1引用所指向的對象,如下圖所示:
    分析JDK中String的存儲區(qū)與不可變性

    4. 反射

    String的value字段是final的,可不可以通過過某種方式修改呢?答案是反射。在stackoverflow上有這樣一段修改value字段的代碼:

    String s1 = "Hello World";  
    String s2 = "Hello World";  
    String s3 = s1.substring(6);  
    System.out.println(s1); // Hello World  
    System.out.println(s2); // Hello World  
    System.out.println(s3); // World  
    Field field = String.class.getDeclaredField("value");  
    field.setAccessible(true);  
    char[] value = (char[])field.get(s1);  
    value[6] = 'J';  
    value[7] = 'a';  
    value[8] = 'v';  
    value[9] = 'a';  
    value[10] = '!';  
    System.out.println(s1); // Hello Java!  
    System.out.println(s2); // Hello Java!  
    System.out.println(s3); // World

    在上述代碼中,為什么對象s2的值也會被修改,而對象s3的值卻不會呢?根據(jù)前面的介紹,s1與s2指向同一個對象;所以當(dāng)s1被修改后,s2也會對應(yīng)地被修改。至于s3對象為什么不會?我們來看看substring()的實現(xiàn):

    public String substring(int beginIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        int subLen = value.length - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
    }

    當(dāng)beginIndex不為0時,返回的是new的String對象;當(dāng)beginIndex為0時,返回的是原對象本身。如果將String s3 = s1.substring(6);改為String s3 = s1.substring(0);,那么對象s3也會被修改了。

    如果仔細(xì)看java.lang.String.java,我們會發(fā)現(xiàn):當(dāng)需要改變字符串內(nèi)容時,String類的方法返回的是新String對象;如果沒有改變,String類的方法則返回原對象引用。這節(jié)省了存儲空間與額外的開銷。

到此,關(guān)于“分析JDK中String的存儲區(qū)與不可變性”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>
分享標(biāo)題:分析JDK中String的存儲區(qū)與不可變性
URL鏈接:http://weahome.cn/article/pchhgh.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部