1. 程式人生 > >Java基礎:String不可變性和final修飾

Java基礎:String不可變性和final修飾

可能 blank 內存區域 什麽 這就是 重寫方法 以及 name ati

轉載請註明出處: 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 實現方式不可變

使用

String類型變量時,其不能表現出其他行為。這就是為什麽String類須要用finalkeyword來進行修飾的原因,設想一下String不用final修飾。則其能夠被繼承並重寫方法:

/**
 * 模擬非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修飾