1. 程式人生 > >String求求你別秀了

String求求你別秀了

  小魯班今年計算機專業大四了,在學校可學了不少軟體開發的東西,也自學了一些JAVA的後臺框架,躊躇滿志,一心想著找個好單位實習。當投遞了無數份簡歷後,終於收到了一個公司發來的面試通知,小魯班欣喜若狂。

  到了人家單位後,前臺小姐姐給了小魯班一份筆試題目,要求在一個小時內完成,小魯班雙手接過題目後,粗略的看了一下題目,心裡暗喜,嘻嘻這個還不簡單。一頓操作猛如虎,做完了感覺也沒什麼錯誤。就交卷了,等待片刻後,小姐姐親切的說需要一週內等通知哦。於是呢,小魯班就回去耐心的等待了。可是半個月都快過去了,什麼訊息都沒有,小魯班就納悶了,明明我做的挺好的呀,為什麼連面試的機會都不給我。

  小魯班於是找到了他表哥魯班大師,把一些當時面試的題目重現了一些,並把自己對題目的理解也說了遍,魯班大師一看他填的答案就知道為什麼了,前5道題目關於String類的判斷題可真是完全避開了正確答案呀,而且後邊的題目也是大部分都是錯了,人家當然不給你機會呀。

  小魯班你可要虛心學習了,就拿下邊最簡單的一題來說,你怎麼連==對於非基本資料型別是比較引用而不是比較值的都不知道呀

String str1 = new String("AA");
String str2 = new String("AA");
System.out.println(str1 == str2);
這裡的正確答案是false

  魯班大師:感覺你的JAVA基礎不咋地呀,你說說你在學校學習你所掌握的關於String類的知識點,你表哥今天有空幫你惡補一波吧。

  小魯班低聲的說到:

  • String類有如下這些特點

  1. String類是final類,也即意味著String類不能被繼承,並且它的成員方法都預設為final方法。
  2. String類其實是通過char陣列來儲存字串的。
  3. String物件一旦被建立就是固定不變的了,對String物件的任何改變都不影響到原物件,相關的任何change操作都會生成新的物件。

 

  魯班大師:嗯,不錯嘛,那有沒有深入一點的理解呢,比如關於字串常量池

  小魯班:這個我~~忘記了!

  魯班大師:沒關係,那你得認真聽講了

  小魯班:em

  • 字串常量池

  1. 我們知道字串的分配和其他物件分配一樣,是需要消耗高昂的時間和空間的,而且字串我們使用的非常多。JVM為了提高效能和減少記憶體的開銷,在例項化字串的時候進行了一些優化:使用字串常量池。每當我們建立字串常量時,JVM會首先檢查字串常量池,如果該字串已經存在常量池中,那麼就直接返回常量池中的例項引用。如果字串不存在常量池中,就會例項化該字串並且將其放到常量池中。由於String字串的不可變性我們可以十分肯定常量池中一定不存在兩個相同的字串。
  2. 字串池的出現避免了相同內容的字串的建立,節省了記憶體,省去了建立相同字串的時間,同時提升了效能;另一方面,字串池的缺點就是犧牲了JVM在常量池中遍歷物件所需要的時間,不過其時間成本相比而言比較低。

  String a="AA";

  String b="AA";

  String c=new String("AA");

  a、b和字面上的AA都是指向JVM字串常量池中的"AA"物件,他們指向同一個物件。

  new關鍵字一定會產生一個物件AA,同時這個物件是儲存在堆中。所以上面這一句應該產生了兩個物件:儲存在方法區中字串常量池的AA和儲存堆中AA。但是在Java中根本就不存在兩個完全一模一樣的字串物件。故堆中的AA應該是引用字串常量池中AA。所以c、堆AA、池AA的關係應該是:c--->堆AA--->池AA。

  雖然a、b、c、c是不同的引用,但是從String的內部結構我們是可以理解上面的。String c = new String("AA");雖然c的內容是建立在堆中,但是他的內部value還是指向JVM常量池的AA的value,它構造AA時所用的引數依然是AA字串常量。所以a==b是ture,因為記憶體地址是一樣的 a==c是false,因為c的記憶體地址是在堆中new的是新的地址

  魯班大師又問了:我看你還挺懵的,你知道==和equals嗎

  小魯班:這個我知道。

  

  • 對於==,如果作用於基本資料型別的變數(byte,short,char,int,long,float,double,boolean ),則直接比較其儲存的"值"是否相等;如果作用於引用型別的變數(String),則比較的是所指向的物件的地址(即是否指向同一個物件)。
  • 對於equals方法,注意:equals方法不能作用於基本資料型別的變數。如果沒有對equals方法進行重寫,則比較的是引用型別的變數所指向的物件的地址;而String類對equals方法進行了重寫,用來比較指向的字串物件所儲存的字串是否相等。其他的一些類諸如Double,Date,Integer等,都對equals方法進行了重寫用來比較指向的物件所儲存的內容是否相等。

  魯班大師:嗯,答的不錯,但是要應付一些面試題,你還要知道這些。

  1. 單獨使用""引號建立的字串都是常量,編譯期就已經確定儲存到String Pool中;
  2. 使用new String("")建立的物件會儲存到heap中,是執行期新建立的;
  3. 使用只包含常量的字串連線符如"aa" + "aa"建立的也是常量,編譯期就能確定,已經確定儲存到String Pool中;
  4. 使用包含變數(引用)的字串連線符如"aa" + s1建立的物件是執行期才建立的,儲存在heap中;
  5. 但是如果s1是被final修飾的話,則s1是屬於常量。結果存在String Pool,但是 final修飾的是一個方法返回的值也是在編譯器確定。

  好了,這些你都知道了,那你把剛那份題目在做一些看看

  

String str1 = "aaa";
String str2 = "aaa";
System.out.println(str1 == str2);// true 因為String有常量池

String str3 = new String("aaa");
String str4 = new String("aaa");
System.out.println(str3 == str4);// false 可以看出用new的方式是生成不同的物件,比較堆上的
		
String s0="helloworld";  
String s1="helloworld";  
String s2="hello"+"world";  
System.out.println(s0==s1); //true 可以看出s0跟s1是指向同一個物件   
System.out.println(s0==s2); //true 可以看出s0跟s2是指向同一個物件   
	    
String st0="helloworld";   
String st1=new String("helloworld");   
String st2="hello" + new String("world");   
System.out.println( st0==st1 ); //false    用new String() 建立的字串不是常量,不能在編譯期就確定
System.out.println( st0==st2 ); //false   st2地址存在堆中,不可能相同
System.out.println( st1==st2 ); //false  
	    
	    
String stri1="abc";     
String stri2="def";     
String stri3=stri1+stri2;  
System.out.println(stri3=="abcdef"); //false    變數相+是在的堆記憶體中建立
	    
String strin0 = "a1";   
String strin1 = "a" + 1;   //這種不是變數,是常量
System.out.println((strin0 == strin1)); //result = true    

String strin2 = "atrue";   
String strin3= "a" + "true";   
System.out.println((strin2 == strin3)); //result = true    

String strin4 = "a3.4";   
String strin5 = "a" + 3.4;   
System.out.println((strin4 == strin5)); //result = true  
	    
	    
String string0 = "ab";   
String string1 = "b";   
String string2 = "a" + string1;   
System.out.println((string0 == string2)); //result = false  在字串的"+"連線中,有字串引用存在,而引用的值在程式編譯期是無法確定的
	    
	    
String test="javalanguagespecification";  
String test2="java";  
String test3="language";  
String test4="specification";  	   
System.out.println(test == "java" + "language" + "specification");  //true 字串字面量拼接操作是在Java編譯器編譯期間就執行了
System.out.println(test == test2 + test3 + test4);  //false  字串引用的"+"運算是在Java執行期間執行的
	    
	    
String ss0 = "ab";   
final String ss1 = "b";   
String ss2 = "a" + ss1;    	  
System.out.println((ss0 == ss2)); //result = true  對於final修飾的變數,它在編譯時被解析為常量值的一個本地拷貝儲存到自己的常量池中或嵌入到它的位元組碼流中。所以此時的"a" + s1和"a" + "b"效果是一樣的
	    
String ss10 = "ab";   
final String ss11 = getS1();   
String ss12 = "a" + ss11;   	  
System.out.println((ss10 == ss12)); //result = false   這裡面雖然將s1用final修飾了,但是由於其賦值是通過方法呼叫返回的,那麼它的值只能在執行期間確定
	
public static String getS1(){
	 return "b";     
}    		

  

  魯班大師:優秀呀,小魯班!不過呢我們既然都研究了String,那麼關於StringBuffer和StringBuilder也得知道,給你佈置個作業,把他們3者的區別寫一下自己的簡介,發到我的郵件,今天就到此為止了。

  小魯班:謝謝表哥,我一定好好整理的

  send to 魯班大師@qq.com 

  String、StringBuffer、StringBuilder的區別?

  • 可變與不可變:String是不可變字串物件,StringBuilder和StringBuffer是可變字串物件(其內部的字元陣列長度可變)。
  • 是否多執行緒安全:String中的物件是不可變的,也就可以理解為常量,顯然執行緒安全。StringBuffer 與 StringBuilder 中的方法和功能完全是等價的,只是StringBuffer 中的方法大都採用了synchronized 關鍵字進行修飾,因此是執行緒安全的,而 StringBuilder 沒有這個修飾,可以被認為是非執行緒安全的。
  • String、StringBuilder、StringBuffer三者的執行效率如下:
  • StringBuilder > StringBuffer > String 當然這個是相對的,不一定在所有情況下都是這樣。比如String str = "hello"+ "world"的效率就比 StringBuilder st  = new   StringBuilder().append("hello").append("world")要高。因此,這三個類是各有利弊,應當根據不同的情況來進行選擇使用:
  • 當字串相加操作或者改動較少的情況下,建議使用 String str="hello"這種形式;
  • 當字串相加操作較多的情況下,建議使用StringBuilder,如果採用了多執行緒,則使用StringBuffer。

 

  虛心的小魯班除了整理了這些之外,同時自己繼續學習封裝類的比較,因為同樣是被坑慘了呀!

1.兩個基本型別的只能用 ==
2.基本型和封裝型用==,封裝型將會自動拆箱變為基本型後再進行比較
3.用==來比較兩個封裝類的話,比較的是地址。(其中-127到127之間的Integer地址相同)
4.至少有一個封裝型的建議使用.equals。用==對基本型和封裝性比較必須保證封裝型不為null。如果為null則不能轉化為基本型就會報錯。
5.兩個封裝型進行equals()比較,首先equals()會比較型別,如果型別相同,則繼續比較值,如果值也相同,返回true
6.封裝型別呼叫equals(),但是引數是基本型別,這時候,先會進行自動裝箱,基本型轉換為其封裝型別,若型別不同返回false,若裝箱後型別相同,則比較值,如果值相同,則返回true

  

int a=128;
int a2=127;
Integer b=128;
Integer b2=127;
Integer c=128;
Integer c2=127;
Integer d=new Integer(a);
Integer d2=new Integer(a);

Integer b2=57;
Integer c2=57;

System.out.println(a==b);//true	    

System.out.println(b==c);//false
System.out.println(b2==c2);//3true

System.out.println(a==d);//true
System.out.println(b==d);//false
System.out.println(d==d2);//false

//由強型別向弱型別轉換需要強制轉換,而由弱型別向強型別轉換則系統自動轉換。
//double 型別相比int型別是屬於強型別,則由double型別的資料向int型別資料轉換就需要強制轉換,反之則自動轉換。資料型別的強弱關係如下:byte<short=char<int<long<float<double,同級之間相互轉換也需要強制轉換。   
//對於未宣告資料型別的整形,其預設型別為int型。
//在浮點型別(float/double)中,對於未宣告資料型別的浮點型,預設為double型。
System.out.println(b.equals(128.0));//false

  

  寫完這些後小魯班,小魯班關了檯燈,休息一下準備明天的面試~