String的Equals()和==比較函數
常量池(Constant Pool):指的是在編譯期被肯定,並被保存在已編譯的.class文件中的一些數據。JVM虛擬機爲每一個被裝載的類型維護一個常量池。常量池就是該類型所用到常量的一個有序集和,包括直接常量(String,Integer和 Floating point常量)和對其餘類型,字段和方法的符號引用。對於String常量,它的值是在常量池中的。而JVM中的常量池在內存當中是以表的形式存在的, 對於String類型,有一張固定長度的CONSTANT_String_info表用來存儲文字字符串值,注意:該表只存儲文字字符串值,不存儲符號引用。 優化
一、String s = "abc";
建立過程分析:在class文件被JVM裝載到內存中,JVM會建立一塊String Pool(String緩衝池)。當執行String s = 「abc」;時,JVM首先在String Pool中查看是否存在字符串對象「abc」(如何查看呢?用equals()方法判斷),若是已存在該對象,則不用建立新的字符串對象「abc」,而直接使用String Pool中已存在的對象「abc」,而後將引用s指向該對象;若是不存在該對象,則先在String Pool中建立一個新的字符串對象「abc」,而後將引用s指向String Pool中建立的新對象。
注意:使用「字符串常量」引號建立的字符串對象時,在編譯期就已經肯定將該對象存儲到String Pool中了。所以,String s = 「abc」只會在編譯期,在String Pool中建立一個對象。 spa
二、String s = new String("abc");
建立過程分析:當執行String s = new String(「abc」);時,JVM首先在String Pool中查看是否存在字符串對象「abc」,若是不存在該對象,則先在String Pool中建立一個新的字符串對象「abc」,而後執行new String(「abc」)構造方法,在Heap裏又建立一個新的字符串對象「abc」(new出來的對象都放在Heap裏面),並將引用s指向Heap中建立的新對象;若是已存在該對象,則不用建立新的字符串對象「abc」,而直接使用String Pool中已存在的對象「abc」, 而後執行new String(「abc」)構造方法,在Heap裏又建立一個新的字符串對象「abc」,並將引用s指向Heap中建立的新對象。
注意:使用new String(「」)建立的字符串對象時,會在運行期建立新對象存儲到Heap中。所以,new String(「abc」)建立字符串對象時,會建立2個對象,編譯期在String Pool中建立一個,運行時Heap中建立一個。 code
示例代碼:對象
String a="hello"; //編譯期間,以被存儲字符串常量池 String b=new String("hello"); //運行期間纔會建立 final String c="hello"; //編譯期間已被存儲字符串常量池 String d="hel"; String e="lo"; String f=new String("lo"); System.out.println(a==b); //false System.out.println(a.equals(b)); //true System.out.println(a==c); //true System.out.println(a.equals(c)); //true System.out.println(b==c); //false System.out.println(b.equals(c)); //true System.out.println("******split*****"); System.out.println(a==(d+f)); //false System.out.println(a.equals(d+f)); //true System.out.println(c==(d+f)); //false System.out.println(c.equals(d+f)); //true
注意代碼:blog
一、對於有+號,而且有引用變量:內存
String s1 = "abc"; String s2 = "def"; String s3 = "abcdef"; String s4 = "abc"+"def"; String s5 = s1 + "def"; String s6 = "abc"+s2; String s7 = s1 + s2; System.out.println(s3 == s4); System.out.println(s3 == s5); System.out.println(s3 == s6); System.out.println(s3 == s7); 運行結果以下: true false false false
結果說明:JVM對於有字符串引用存在的字符串"+"鏈接中,而引用的值在程序編譯期是沒法肯定的,即s1 + 「def」沒法被編譯器優化,只有在程序運行期來動態分配並將鏈接後的新地址賦給s5。 可是對於強制聲明爲final的除外,以下所示: final String s1 = "abc"; final String s2 = "def"; String s3 = "abcdef"; String s4 = "abc"+"def"; String s5 = s1 + "def"; String s6 = "abc"+s2; String s7 = s1 + s2; System.out.println(s3 == s4); System.out.println(s3 == s5); System.out.println(s3 == s6); System.out.println(s3 == s7);
運行結果以下:
true
true
true
true
結果說明:例程3和例程4與例程2的區別是,例程3在字符串s1前加了final修飾,例程4在字符串s1和s2前都加了final修飾。對於final修飾的變量,它在編譯時被解析爲常量值的一個本地拷貝存儲到本身的常量池中或嵌入到它的字節碼流中。因此此時的s1 + 「def」和"abc" + "def"效果是同樣的。接着後面兩個含引用的字符串鏈接,JVM會進行相同的處理。故上面程序後面三個的結果爲true。
二、對於方法調用的 public static void main(String args[]){ String s1 = "abc"; final String s2 = getDef(); String s3 = "abcdef"; String s4 = "abc"+s2; String s5 = s1 + s2; System.out.println(s3 == s4); System.out.println(s3 == s5); } private static String getDef(){ return "def"; } 程序運行結果以下: false false 結果說明:JVM對於方法調用給字符串引用賦值的狀況,引用指向字符串的值在編譯期是沒法肯定的,只有在程序運行調用方法後,將方法的返回值「def」和「abc」動態鏈接並分配新地址賦值給s4,因此上述程序的結果都爲false。
String s=new String("XYZ")+new String("XYZ");總共建立了四個對象字符串
對於第一個new String("XYZ"),須要在StringPool中存儲XYZ建立一個對象,構造函數建立一個對象get
對於第二個new String("XYZ"),構造函數須要建立一個對象編譯器
最後兩個字符串相加會再StringPool中再建立一個對象
所以總共建立了4個對象