改變Java String內容 1. 前言
從事Java開發的同學,或多或少都聽說過這樣一句話,String是不可變的,可你是否想過為什麼。還有人認為String是可變的,因為我們在實際開發中,經常寫出下面這樣的代碼,String的輸出內容確是是改變了,這又是為什麼呢。
// 代碼0String str = "hello"str = "world";System.out.println(str); // 輸出world
在我們進入正題下,先解答下這個疑惑,這個清楚了,本篇的內容就很容易理解了。
在代碼0中,輸出結果確實是顯示了,我們改變了str的內容,可是String不是不可變的嗎。首先我們要先弄清楚String的內部結構,String是一個final類,內部有一個private final char[] value;,這個value就是String儲存核心(之前說過,詳情請看深入淺出Java String)。而String是參考型別的,所以不可變的是value,而不是str引用,看下圖就明白了:
通過上圖可以看到,我們只是改變來str的引用指向,並未改變源String的內容,所以我們常常所說的String不變,是指它的內容,即那個char value[]。 2. 改變String
接下來進入本文,我們知道了String不可變是因為它內部的final char value[],那麼現在就開始改變這個value[]。
大家都知道final修飾的變數,一經賦值,就不允許在改變,但是在修飾數組時,我們卻可以改變它的數群組成員,看代碼:
// 代碼1final int a = 1;a = 2; // 編譯錯誤final char[] arr = {'h','e','l','l','o'};char[] temp = {'w','o','r','l','d'};arr = temp; // 編譯錯誤arr[0] = 't'; // 正確arr[1] = 't'; // 正確
為什麼會這樣呢,因為final修飾的是char[] arr,所以這裡的arr是個引用,即這個引用不能改變,而char[]的成員修改,不會影響到arr,所以是正確的。
講到這裡,我們就應該知道了如何修改String內部的value[]了,但是還面臨一個問題,即value[]是private的,這意味著只有它自己才能訪問,但是我們可以用反射機制來打破它,強制修改私人成員。
// 代碼2String str = "hello";Field value = String.class.getDeclaredField("value");// 擷取String的value屬性value.setAccessible(true); //設定存取權限為truechar[] valueOfStr = (char[])value.get(str);//擷取str上的value屬性valueOfStr[0] = 'Y'; //改變第一個字元System.out.println(str); //輸出Yello
在代碼2中,我們通過反射機制,打破來私人變數的訪問規則,強制修改了str的內容。 3. 總結
瞭解了String的內部結構,通過反射機制我們也可以改變String的內容。但是,這裡僅僅作為實驗用,實際上不建議這樣做,因為這樣違背了封裝性。