字符串對象在JVM中可能有兩個存放的位置:字符串常量池或堆內存。
-
使用常量字符串初始化的字符串對象,它的值存放在字符串常量池中;
-
使用字符串構造方法創建的字符串對象,它的值存放在堆內存中;
String提供了一個API, java.lang.String.intern(),這個API可以手動將一個字符串對象的值轉移到字符串常量池中。
在1.7之前,字符串常量池是在PermGen區域,這個區域的大小是固定的——不能在運行時根據需要擴大,也不能被垃圾收集器回收,因此如果程序中有太多的字符串調用了intern方法的話,就可能造成OOM。
在1.7以後,字符串常量池移到了堆內存中,並且可以被垃圾收集器回收,這個改動降低了字符串常量池OOM的風險。
案例分析
1. String對象的兩種創建方式:
String s1="javaadu";
String s2="javaadu";
String s3=new String("javaadu");
System.out.println(s1==s2); //true
System.out.println(s1==s3); //false
String s4=s3.intern();
System.out.println(s1==s4); //true
System.out.println(s3==s4); //false
-
直接使用雙引號聲明出來的String對象會直接存儲在常量池中。
-
如果不是用雙引號聲明的String對象,可以使用String提供的intern方法。String.intern() 是一個Native方法,它的作用是:如果常量池中已經包含一個等於此String對象內容的字符串,則返回常量池中該字符串的引用;如果沒有,則在常量池中創建與此 String 內容相同的字符串,並返回常量池中創建的字符串的引用。
2. String字符串拼接
String str1 = "str";
String str2 = "ing";
String str3 ="str"+"ing"; //常量池中的對象
String str4 = str1 + str2; //在堆上創建的新對象
String str5 = "string";
System.out.println(str3 == str4); //flase
System.out.println(str3 == str5); //true
System.out.println(str4 == str5); //false
String s1 = new String("abc");這句話創建了幾個對象?
創建了兩個對象。或者一個
如果如果常量池沒有,就是兩個;如果常量池已經有,就是一個。
String str1=new String("abc");
String str2="abc";
System.out.println(str1==str2); //false
解釋:
先有字符串"abc"放入常量池,然後 new 了一份字符串"abc"放入Java堆(字符串常量"abc"在編譯期就已經確定放入常量池,而 Java 堆上的"abc"是在運行期初始化階段才確定),然後 Java 棧的 str1 指向Java堆上的"abc"。
3. 8種基本類型的包裝類和常量池
-
Java 基本類型的包裝類的大部分都實現了常量池技術,即Byte,Short,Integer,Long,Character,Boolean;這5種包裝類默認創建了數值[-128,127]的相應類型的緩存數據,但是超出此範圍仍然會去創建新的對象。
-
兩種浮點數類型的包裝類 Float,Double 並沒有實現常量池技術。
Integer aInteger=1;
Integer bInteger=2;
Integer cInteger=3;
Integer dInteger=3;
Integer eInteger=321;
Integer fInteger=321;
Long gLong=3L;
Long hLong=2L;
System.out.println(cInteger==dInteger); //true
System.out.println(eInteger==fInteger); //false
System.out.println(cInteger==(aInteger+bInteger)); //true
System.out.println(cInteger.equals(aInteger+bInteger)); //true
System.out.println(gLong==(aInteger+bInteger)); //true
System.out.println(gLong.equals(aInteger+bInteger)); //false
System.out.println(gLong.equals(aInteger+hLong)); //true
使用==:
①如果比較Integer變量,默認比較的是地址值。
②Java的Integer維護了從-128~127的緩存池。
③如果比較的某一邊有操作表達式(如a+b),那麼比較的是具體數值。
使用equals():
①無論Integer還是Long中的equals默認比較的是數值。
②Long的equals()方法,JDK的默認實現:會判斷是否是Long類型。
int a=59; //基本類型,存儲在棧中。
Integer bInteger=59; //會調用Integer的valueOf方法
/*
* valueOf這個方法就是返回一個Integer對象,返回之前,需要判斷當前值是否在[-128,127]區間。
* 如果在此區間,先看IntegerCache中是否存在此對象,若存在,直接返回引用,否則創建一個新的對象。
* 若不在此區間,return new Integer(i);
*/
Integer cInteger=Integer.valueOf(59); //因爲IntegerCache中存在此對象,所以直接返回引用。
Integer dInteger=new Integer(59); //直接創建一個新的對象。
System.out.println(a==bInteger); //true Integer會自動拆箱成int,然後進行值比較
System.out.println(bInteger==cInteger); //true
System.out.println(cInteger==dInteger); //false
System.out.println(a==dInteger); //true dInteger會自動拆箱,進行值比較
注意:
==比較的是地址。
但當爲基本類型時,比較的是值。
如果兩邊有包裝類型,則先將包裝類型轉換爲基本類型再比較值是否相等。