最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

java String 可變性的分析

 更新時間:2017年03月09日 09:03:28   作者:純-真  
這篇文章主要介紹了java String 可變性的分析的相關(guān)資料,通常大家都認(rèn)為java String 是不可變的,這里分析下源碼來說明它的可變性,需要的朋友可以參考下

前言

這兩天在看Java面試相關(guān)的一些問題,很偶然也很幸運的看到了下面這篇文章。

http://www.fzitv.net/article/105448.htm

這篇文章的作者有一系列關(guān)于Java深入學(xué)習(xí)的文章,很值得一看,個人覺得非常好,很有收獲。

起因

問答

正如我們所理解的,通過

String hello = "Hello World!";

String xx = new String("Hello World!");

得到的字符串對象是不一樣的,new方式是在堆空間中創(chuàng)建的,而直接的字符串則是先被放到常量池中。如果有新的與之一樣的對象被創(chuàng)建,則直接讓這個新對象引用常量池中的這個地址即可。

這樣的好處就是可以最大限度的節(jié)省內(nèi)存空間。

而使用new方式創(chuàng)建的則就不一樣了,只要是用了new創(chuàng)建字符串,就會在堆空間中開辟出一塊內(nèi)存,然后返回這個內(nèi)存地址的引用。所以這樣創(chuàng)建的對象,即使內(nèi)容一致,也不會是指向同一個內(nèi)存地址。

下面用幾個簡單的代碼做下測試。

/**
*字符串中對于內(nèi)容和地址的判定可以用下面兩種方式,但側(cè)重點不一樣。
*/
equals // 判斷 兩個字符串的內(nèi)容是否一致
==  // 判斷兩個字符串的內(nèi)存地址是否一致

且看下面的代碼:

public static void simple() {
  String s1 = "Hello World!";
  String s2 = "Hello World!";
  String s3 = new String("Hello World!");
  String s4 = new String("Hello World!");

  // 下面開始比較引用和內(nèi)容的比較
  System.out.println("字符串賦值方式:");
  System.out.println(s1==s2);
  System.out.println(s1.equals(s2));

  System.out.println("\n字符串賦值方式和new方式:");
  System.out.println(s1==s3);
  System.out.println(s1.equals(s3));

  System.out.println("\nnew 方式:");
  System.out.println(s3==s4);
  System.out.println(s3.equals(s4));
 }

得到的結(jié)果如下:

字符串賦值方式:
true
true

字符串賦值方式和new方式:
false
true

new 方式:
false
true

結(jié)果卻是和我們所說的那樣。

深入源碼

不出所料,String確實是“不可變的”,每次改變底層其實都是創(chuàng)建了一個心的字符串對象,然后賦予了新值。

為什么會這樣呢?我們也許可以在源碼中找到真相。

字符串源碼

哦,原來Java對于String類只是維護(hù)了一個final類型的字符數(shù)組啊。怪不得賦值之后就不能改變了呢。

但是也許你會有疑問,咦,不對啊,“我經(jīng)常使用String的什么replace方法改變字符串的內(nèi)容啊。你這則么解釋呢?”

其實答案還是那樣,它真的沒變,我們并沒有看到事情的真相,相信看完下面的源碼,你就明白了。

/**
  * Returns a string resulting from replacing all occurrences of
  * {@code oldChar} in this string with {@code newChar}.
  * <p>
  * If the character {@code oldChar} does not occur in the
  * character sequence represented by this {@code String} object,
  * then a reference to this {@code String} object is returned.
  * Otherwise, a {@code String} object is returned that
  * represents a character sequence identical to the character sequence
  * represented by this {@code String} object, except that every
  * occurrence of {@code oldChar} is replaced by an occurrence
  * of {@code newChar}.
  * <p>
  * Examples:
  * <blockquote><pre>
  * "mesquite in your cellar".replace('e', 'o')
  *   returns "mosquito in your collar"
  * "the war of baronets".replace('r', 'y')
  *   returns "the way of bayonets"
  * "sparring with a purple porpoise".replace('p', 't')
  *   returns "starring with a turtle tortoise"
  * "JonL".replace('q', 'x') returns "JonL" (no change)
  * </pre></blockquote>
  *
  * @param oldChar the old character.
  * @param newChar the new character.
  * @return a string derived from this string by replacing every
  *   occurrence of {@code oldChar} with {@code newChar}.
  */
 public String replace(char oldChar, char newChar) {
  if (oldChar != newChar) {
   int len = value.length;
   int i = -1;
   char[] val = value; /* avoid getfield opcode */

   while (++i < len) {
    if (val[i] == oldChar) {
     break;
    }
   }
   if (i < len) {
    char buf[] = new char[len];
    for (int j = 0; j < i; j++) {
     buf[j] = val[j];
    }
    while (i < len) {
     char c = val[i];
     buf[i] = (c == oldChar) ? newChar : c;
     i++;
    }
    return new String(buf, true);
   }
  }
  return this;
 }

源碼中很明確的使用了

new String(buf, true);

的方式返回給調(diào)用者新對象了。

真的不可變嗎?

讀到上面的內(nèi)容,其實基本上已經(jīng)夠了。但是了解一下更深層次的內(nèi)容,相信對我們以后編程來說會更好。

源碼中清楚的使用char[] value來盛裝外界的字符串?dāng)?shù)據(jù)。也就是說字符串對象的不可變的特性,其實是源自value數(shù)組的final特性。

那么我們可以這么想,我們不改變String的內(nèi)容,而是轉(zhuǎn)過頭來改變value數(shù)組的內(nèi)容(可以通過反射的方式來修改String對象中的private屬性的value),結(jié)果會怎樣呢?

答案是真的會變哦。

可以先看下下面的代碼

private static void deep() throws NoSuchFieldException, IllegalAccessException {
  String hello = "Hello World!";
  String xx = new String("Hello World!");
  String yy = "Hello World!";

  /**
   * 判斷字符串是否相等,默認(rèn)以內(nèi)存引用為標(biāo)準(zhǔn)
   */
  System.out.println(hello == xx);
  System.out.println(hello == yy);
  System.out.println(xx == yy);

  // 查看hello, xx, yy 三者所指向的value數(shù)組的真實位置
  Field hello_field = hello.getClass().getDeclaredField("value");
  hello_field.setAccessible(true);
  char[] hello_value = (char[]) hello_field.get(hello);
  System.out.println( hello_field.get(hello));

  Field xx_field = xx.getClass().getDeclaredField("value");
  xx_field.setAccessible(true);
  char[] xx_value = (char[]) xx_field.get(xx);
  System.out.println(xx_field.get(xx));

  Field yy_field = yy.getClass().getDeclaredField("value");
  yy_field.setAccessible(true);
  char[] yy_value = (char[]) yy_field.get(yy);
  System.out.println(yy_field.get(yy));
  /**
   * 經(jīng)過反射獲取到這三個字符串對象的最底層的引用數(shù)組value,發(fā)現(xiàn)如果一開始內(nèi)容一致的話,java底層會將創(chuàng)建的字符串對象指向同一個字符數(shù)組
   * 
   */

  // 通過反射修改字符串引用的value數(shù)組
  Field field = hello.getClass().getDeclaredField("value");
  field.setAccessible(true);
  char[] value = (char[]) field.get(hello);
  System.out.println(value);
  value[5] = '^';
  System.out.println(value);

  // 驗證xx是否被改變
  System.out.println(xx);
 }

結(jié)果呢?

false
true
false
[C@6d06d69c
[C@6d06d69c
[C@6d06d69c
Hello World!
Hello^World!
Hello^World!

真的改變了。

而我們也可以發(fā)現(xiàn),hello,xx, yy最終都指向了內(nèi)存中的同一個value字符數(shù)組。這也說明了Java在底層做了足夠強(qiáng)的優(yōu)化處理。

當(dāng)創(chuàng)建了一個字符串對象時,底層會對應(yīng)一個盛裝了相應(yīng)內(nèi)容的字符數(shù)組;此時如果又來了一個同樣的字符串,對于value數(shù)組直接獲取剛才的那個引用即可。(相信我們都知道,在Java中數(shù)組其實也是一個對象類型的數(shù)據(jù),這樣既不難理解了)。

不管是字符串直接引用方式,還是new一個新的字符串的方式,結(jié)果都是一樣的。它們內(nèi)部的字符數(shù)組都會指向內(nèi)存中同一個“對象”(value字符數(shù)組)。

總結(jié)

稍微有點亂,但是從這點我們也可以看出String的不可變性其實仍舊是對外界而言的。在最底層,Java把這一切都給透明化了。我們只需要知道String對象有這點特性,就夠了。

其他的,日常應(yīng)用來說,還是按照String對象不可變來使用即可。

感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!

相關(guān)文章

  • 設(shè)計模式之構(gòu)建(Builder)模式 建造房子實例分析

    設(shè)計模式之構(gòu)建(Builder)模式 建造房子實例分析

    構(gòu)建模式主要用來針對復(fù)雜產(chǎn)品生產(chǎn),分離部件構(gòu)建細(xì)節(jié),以達(dá)到良好的伸縮性,考慮到設(shè)計模式來源于建筑學(xué),因此舉一個建造房子的例子,需要的朋友可以參考下
    2012-12-12
  • Spring Task 動態(tài)修改任務(wù)執(zhí)行計劃cron方式

    Spring Task 動態(tài)修改任務(wù)執(zhí)行計劃cron方式

    這篇文章主要介紹了Spring Task 動態(tài)修改任務(wù)執(zhí)行計劃cron方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • SpringBoot中web模板渲染的實現(xiàn)

    SpringBoot中web模板渲染的實現(xiàn)

    本文主要介紹了SpringBoot中web模板渲染的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • java中的return關(guān)鍵字使用解讀

    java中的return關(guān)鍵字使用解讀

    這篇文章主要介紹了java中的return關(guān)鍵字使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • 深入了解final在java中的應(yīng)用

    深入了解final在java中的應(yīng)用

    談到final關(guān)鍵字,想必很多人都不陌生,在使用匿名內(nèi)部類的時候可能會經(jīng)常用到final關(guān)鍵字。另外,Java中的String類就是一個final類,那么今天我們就來了解final這個關(guān)鍵字的用法。
    2019-06-06
  • java中實現(xiàn)分頁的幾種常見方式總結(jié)

    java中實現(xiàn)分頁的幾種常見方式總結(jié)

    在項目中經(jīng)常會查詢大量數(shù)據(jù),這就要用到分頁展示,下面這篇文章主要給大家介紹了關(guān)于java中實現(xiàn)分頁的幾種常見方式,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-12-12
  • JAVA注解相關(guān)知識總結(jié)

    JAVA注解相關(guān)知識總結(jié)

    這篇文章主要介紹了JAVA注解相關(guān)知識,文中講解非常詳細(xì),代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-06-06
  • 如何動態(tài)修改JavaBean中注解的參數(shù)值

    如何動態(tài)修改JavaBean中注解的參數(shù)值

    這篇文章主要介紹了如何動態(tài)修改JavaBean中注解的參數(shù)值操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • 使用@CacheEvict?多參數(shù)如何匹配刪除

    使用@CacheEvict?多參數(shù)如何匹配刪除

    這篇文章主要介紹了使用@CacheEvict?多參數(shù)如何匹配刪除,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • SpringBoot多環(huán)境配置方式的新手教程

    SpringBoot多環(huán)境配置方式的新手教程

    我們平時做項目的時候,一般都會分幾套環(huán)境,每一套環(huán)境的配置都是不一樣的,所以這篇文章就來為大家詳細(xì)介紹一下SpringBoot多環(huán)境配置方式,希望對大家有所幫助
    2023-11-11

最新評論

鹤岗市| 阳朔县| 马龙县| 辽源市| 天柱县| 易门县| 遵化市| 丰宁| 晋州市| 临汾市| 射阳县| 漠河县| 松江区| 寿阳县| 五大连池市| 略阳县| 汉沽区| 治多县| 浪卡子县| 措勤县| 绥江县| 武宁县| 正蓝旗| 剑河县| 仙居县| 行唐县| 河西区| 玉山县| 定西市| 开化县| 鲁甸县| 安新县| 荔浦县| 甘肃省| 上饶市| 灵川县| 青龙| 哈尔滨市| 佛坪县| 江源县| 巩义市|