Java基礎:String不可變性和final修飾
阿新 • • 發佈:2017-07-22
可能 blank 內存區域 什麽 這就是 重寫方法 以及 name ati
String類型變量時,其不能表現出其他行為。這就是為什麽String類須要用finalkeyword來進行修飾的原因,設想一下String不用final修飾。則其能夠被繼承並重寫方法:
轉載請註明出處: jiq?欽‘s technical Blog - 季義欽
String的不可變性
Java規定String是不可變的(immutable)。事實上這個不可變具備兩層含義:
1 內容不可變
不論什麽看起來改動它們的操作。實際上都是又一次new出一個對象。
String s = new String("111"); String newS = s; newS = "dsfsd"; System.out.println(s); //111
假設不能做到內容不可變。在兩個引用同一時候指向一個內存的時候,改動當中一個也會影響到另外一個。
2 實現方式不可變
使用
/** * 模擬非final的String * @author jiyiqin * */ class MyString{ public char value[] = new char[10]; MyStringtoLowerCase(MyString s) { System.out.println("將s轉換為小寫,但不改變原有s"); char newValue[] = new char[s.value.length]; for(int i = 0; i < s.value.length; i++) newValue[i]= LowerUtils.Lower(s.value[i]); MyStringnewString = newMyString(); newString.value = newValue; return newString; } } /** * 繼承MyString,將其toLowerCase重寫 * @author jiyiqin * */ class OverrideString extends MyString{ MyStringtoLowerCase(MyString s) { System.out.println("直接改動傳遞進來的s的內存。將s轉換為小寫"); for(int i = 0; i < s.value.length; i++) s.value[i] = LowerUtils.Lower(s.value[i]); return s; } } public classFinalStringTest { /** * 測試函數,java設計String是不可變的 * 所以必須將String修飾為final。否則其 * 一旦被繼承。相似以下這樣的調用時。若傳遞 * 進來的參數是其被繼承的子類。調用的就是 * 被重寫的方法,這個重寫的方法可能會破壞 * String的不可變特性。 * @param s * @return */ static MyStringLowerCusString(MyString s)//多態 { return s.toLowerCase(s); } public static void main(String[] args) { OverrideStringss = newOverrideString(); LowerCusString(ss);//傳入重寫後的字符串類 LowerUtils.testFinalClass(); } }
能夠看到LowerCusString方法想要的是MyString對象,可是其子類能夠傳遞進來使用,調用之後發現傳遞進來的對象s的內容被改動了,表現出和父類不同的行為!!
!
同樣地。應用程序能夠編寫新的String類,改動hasCode方法,讓內容同樣的String對象返回的hashCode不同,這樣HashMap在使用String類型變量作為Key的時候,發現同樣內容的Key居然哈希到不同的位置。
除了實現方法不可改動這個原因。將String修飾為final另一個優點就是效率。
String其他特性
除了上面講的不可變性。String還其具備以下特性:
特性1:編譯時和執行時差別
u 編譯時能確定的String對象都放入常量池
u 執行時才幹確定或者new出的String對象都放入堆
特性2:hasCode唯一性
兩個內容同樣的String對象hashCode返回值一定同樣
以下通過一個樣例總結一下這兩個特性:
final String tmp = "ji"; //常量池、編譯時確定 String tmp2 = "ji"; //常量池、編譯時確定 String s1 = "jiyiqin"; //常量池、編譯時確定 String s2 = "jiyiqin"; //常量池、編譯時確定 String s3 = "ji" + "yiqin"; //常量池、編譯時確定 String s4 = tmp + "yiqin"; //常量池、編譯時確定(final一旦初始化就不可變) String s5 = tmp2 + "yiqin";//堆、執行時確定 String s6 = new String("jiyiqin");//堆、執行時確定 System.out.println(s1.hashCode()); System.out.println(s2.hashCode()); System.out.println(s3.hashCode()); System.out.println(s4.hashCode()); System.out.println(s5.hashCode()); System.out.println(s6.hashCode()); //所有同樣 System.out.println(s1 == s2); //true。指向常量池中同樣內存 System.out.println(s1 == s3); //true,指向常量池中同樣內存 System.out.println(s1 == s4); //true,指向常量池中同樣內存 System.out.println(s1 == s5); //false,一個指向堆一個指向常量池 System.out.println(s1 == s6); //false,一個指向堆一個指向常量池 System.out.println(s5 == s6); //false,指向堆中不同內存區域
Double、Long、Integer
對於String的不可變性(包含內容不可變和實現方式不可變),以及hashCode唯一性,Double、Long、Integer也是一樣的。
不同的是它們沒有執行時和編譯時的差別,都是在堆上分配內存。
Java基礎:String不可變性和final修飾