java基礎解析系列(九)---String不可變性分析

java基礎解析系列(九)---String不可變性分析

目錄

什麼是不可變

  • 一個對象,在它建立完成以後,不能再改變它的狀態,那麼這個對象就是不可變的。不能改變狀態的意思是,不能改變對象內的成員變量,包括基本數據類型的值不能改變,引用類型的變量不能指向其餘的對象,引用類型指向的對象的狀態也不能改變

先看一個例子

public static void main(String[] args) throws Exception {
        String s=new String("jia");
        String s2=s.concat("jun");
        System.out.println(s);
        StringBuffer sb=new StringBuffer("jia");
        sb.append("jun");
        System.out.println(sb);
    }
輸出jia和jiajun
  • 對字符串s的操做並無改變他,而對StringBuffer sb進行apped,輸出的時候卻改變了,這就說明了String一個不可變性。

也許你會說這是可變的

public static void main(String[] args) {
        String s1="jiajun";
        String s2=s1;
        s1="666";
        System.out.println(s1);
    }
輸出:666
  • 實際上,"jiajun"字符串並無改變,能夠經過一個例子來證實
String s3="jiajun";
        System.out.println(s2==s3);
        輸出:true
  • 爲何會這樣,由於實際上"jiajun"字符串存放在了常量池,此時s2和s3都指向了這個這個字符串,因此能夠證實這個字符串是不改變的並存在的
  • 之因此會輸出666,是由於此時s1指向的字符串是另外一個了
  • 其實最本質的是這個改變是改變s1的引用

也許你會說這是可變的

public static void main(String[] args) {
        String s1="jiajun";
        s1=s1.replace("j","J");
        System.out.println(s1);
        s1=s1.toLowerCase();
        System.out.println(s1);
    }
    JiaJun
    jiajun
  • 實際上jiajun字符串仍是沒有改變的,看一下方法的源碼
2047    public String More ...replace(char oldChar, char newChar) {
2048        if (oldChar != newChar) {
                ...
2069                return new String(0, len, buf);
2070            }
2071        }
2072        return this;
2073    }
  • 能夠看到返回的時候是建立一個新的字符串
  • 實際上String的一些方法substring, replace, replaceAll, toLowerCase,返回的時候是建立一個新的String

分析源碼

111 public final class String
112     implements java.io.Serializable, Comparable<String>, CharSequence {
    
The value is used for character storage.
113 
114     private final char value[];

    
Cache the hash code for the string
116 
117     private int hash; // Default to 0
118 
    
  private static final long serialVersionUID = -6849794470754667710L;

136 
137     public String() {
138         this.value = new char[0];
139     }
151     public String(String original) {
152         this.value = original.value;
153         this.hash = original.hash;
154     }
1913    public String substring(int beginIndex) {
1914        if (beginIndex < 0) {
1915            throw new StringIndexOutOfBoundsException(beginIndex);
1916        }
1917        int subLen = value.length - beginIndex;
1918        if (subLen < 0) {
1919            throw new StringIndexOutOfBoundsException(subLen);
1920        }
1921        return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
1922    }
  • 111行能夠看到,String類是用final修飾的,說明這個類是沒法被繼承的
  • 114行能夠String類裏面維護一個value的char數組,這個數組是用final修飾的,說明這個value不能指向別的數組,可是並不說明這個value數組的內容不可變,而這個value是用private修飾的,說明只有在類裏面能夠修改訪問他,在外部不能改變他,這是關鍵
  • 從1913行能夠看到substring方法實際上返回的數組是新建立的數組

怎麼實現不可變

  • String裏面維護的value數組是用private final修飾的,沒法改變引用,也沒法訪問這個數組修改數組的值,最關鍵的是private
  • 對Sting的操做,並無修改數組的值,而是建立新的String
  • 類用final修飾,方法沒法被子類重寫,避免被其餘人破壞

不可變的好處

  • 節省空間,大量使用相同的字符串,同時指向常量池的字符串就行,若是字符串是可變的話,那麼常量池就沒意義了
String s1="jiajun";
        String s2="jiajun";
        System.out.println(s1==s2);
  • 線程安全,出現線程安全的是在對共享變量寫的時候,而由於不可變,因此Strig是線程安全的html

  • 最重要的是安全,若是當一個String已經傳給別人了,這個時候若是是可變,那麼能夠在後面進行修改,那麼這是麻煩並不安全的。並且在hashmap中,若是做爲key的String s1是可變的,那麼這樣是很危險的,好比說可能出現兩個一樣的鍵。java

真的不可變嗎

public static void main(String[] args) throws Exception {
        String s1="jiajun";
        Field field=String.class.getDeclaredField("value");
        field.setAccessible(true);
        char [] value=(char[])field.get(s1);
        value[0]='Jiajun';
  • 實際上,經過反射能夠修改value數組

爲何設置爲不可變

  • 調用其餘方法,好比調用一些系統級操做以前,可能會有一系列校驗,若是是可變類的話,可能在你校驗事後,其內部的值被改變了,可能引發嚴重的系統崩潰問題
  • 當你在傳參的時候,使用不可變類不須要去考慮誰可能會修改其內部的值

我以爲分享是一種精神,分享是個人樂趣所在,不是說我以爲我講得必定是對的,我講得可能不少是不對的,可是我但願我講的東西是我人生的體驗和思考,是給不少人反思,也許給你一秒鐘、半秒鐘,哪怕說一句話有點道理,引起本身心裏的感觸,這就是我最大的價值。(這是我喜歡的一句話,也是我寫博客的初衷)

做者:jiajun 出處: http://www.cnblogs.com/-new/
本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然保留追究法律責任的權利。若是以爲還有幫助的話,能夠點一下右下角的【推薦】,但願可以持續的爲你們帶來好的技術文章!想跟我一塊兒進步麼?那就【關注】我吧。算法

相關文章
相關標籤/搜索