聊聊String,StringBuffer,StringBuilder
一個蘿蔔一個坑
(面試遇到的坑)
1.String 能不能被繼承呢?
從沒見過String被繼承過,那String就是不能被繼承了,為什麼不能被繼承呢,因為String是由final關鍵字修飾的,final 關鍵字宣告類可以把類定義為不能繼承的,即最終類;或者用於修飾方法,該方法不能被子類重寫;
注:例項變數也可以定義為final,被定義為final的變數不能被修改。被宣告為final類的方法自動宣告為final方法,但是例項變數並不是final。
2.String、StringBuffer、StringBuilder之間有什麼不同呢?
String是字串常量,StringBuffer和StringBuilder是字串變數。
String物件一旦建立就不可改,而StringBuffer和StringBuilder是可以改的。
public class StringTest { public static void main(String[] args) { String str = "abc"; System.out.println(str+" str hashCode:"+str.hashCode()); str = str+"de"; System.out.println(str+" str hashCode:"+str.hashCode()); System.out.println("----------"); StringBuffer stringBuffer = new StringBuffer("ABC"); System.out.println(stringBuffer+" stringBuffer hashCode:"+stringBuffer.hashCode()); stringBuffer.append("DE"); System.out.println(stringBuffer+" stringBuffer hashCode:"+stringBuffer.hashCode()); System.out.println("----------"); StringBuilder stringBuilder = new StringBuilder("ABC"); System.out.println(stringBuilder+" stringBuilder hashCode:"+stringBuilder.hashCode()); stringBuilder.append("DE"); System.out.println(stringBuilder+" stringBuilder hashCode:"+stringBuilder.hashCode()); } }
寫段程式碼看一下執行結果:
發現對String的物件進行操作出現了兩個不同的hashCode,說明這裡建立了兩個物件,所以String型別的物件一旦建立完之後就不可以修改。對StringBuilder和StringBuffer進行操作則出現相同的hashCode,所以StringBuilder和StringBuffer是字串變數,是可變的。
實際上,在JAVA虛擬機器(JVM)中存在著一個字串池,其中儲存著很多String物件,並且可以被共享使用,因此它提高了效率。由於String類是final的,它的值一經建立就不可改變,因此我們不用擔心String物件共享而帶來程式的混亂。字串池由String類維護,我們可以呼叫intern()方法來訪問字串池。
我們再回頭看看String str = "abc";,這行程式碼被執行的時候,JAVA虛擬機器首先在字串池中查詢是否已經存在了值為"abc"的這麼一個物件,它的判斷依據是String類equals(Object obj)方法的返回值。如果有,則不再建立新的物件,直接返回已存在物件的引用;如果沒有,則先建立這個物件,然後把它加入到字串池中,再將它的引用返回。
為了驗證我們的理論,我們對上面的程式碼稍作修改,新建了一個str1的物件,它的值為"abc",然後檢視str1和str的兩個hashCode有沒有一樣。
public class StringTest {
public static void main(String[] args) {
String str = "abc";
System.out.println(str+" str hashCode:"+str.hashCode());
str = str+"de";
System.out.println(str+" str hashCode:"+str.hashCode());
String str1 = "abc";
System.out.println(str1+" str1 hashCode:"+str1.hashCode());
}
}
我們發現第一個str的hashCode和str1的hashCode是一樣的,所以證實了上面的結論。
從執行緒安不安全性上面來說:
StringBuilder是執行緒不安全的,而StringBuffer是執行緒安全的。
因為StringBuffer的方法都是新增Synchronized關鍵字的,所以是執行緒安全的,而StringBuilder沒有新增Synchronized關鍵字,所以是執行緒不安全的。因此,如果是在單執行緒的情況下可以使用StringBuilder,沒有加鎖,執行速度比較快,而在多執行緒的情況下可以使用StringBuffer,執行緒安全。
總結下這三個類的用法:
String:適用於少量的字串操作的情況
StringBuilder:適用於單執行緒下在字元緩衝區進行大量操作的情況StringBuffer:適用多執行緒下在字元緩衝區進行大量操作的情況
3.String str=new String("abc"); 這行程式碼究竟建立了幾個String物件呢?
我們可以把上面這行程式碼分成String str、=、"abc"和new String()四部分來看待。String str只是定義了一個名為str的String型別的變數,因此它並沒有建立物件;=是對變數str進行初始化,將某個物件的引用(或者叫控制代碼)賦值給它,顯然也沒有建立物件;現在只剩下new String("abc")了。那麼,new String("abc")為什麼又能被看成"abc"和new String()呢?
我們來看一下被我們呼叫了的String的構造器:
public String(String original) { //other code ... } 大家都知道,我們常用的建立一個類的例項(物件)的方法有以下兩種:
一、使用new建立物件。
二、呼叫Class類的newInstance方法,利用反射機制建立物件。
我們正是使用new呼叫了String類的上面那個構造器方法建立了一個物件,並將它的引用賦值給了str變數。同時我們注意到,被呼叫的構造器方法接受的引數也是一個String物件,這個物件正是"abc"。由此我們又要引入另外一種建立String物件的方式的討論——引號內包含文字。
這種方式是String特有的,並且它與new的方式存在很大區別。
String str="abc";
毫無疑問,這行程式碼建立了一個String物件。
String a="abc"; String b="abc"; 那這裡呢?
答案還是一個。
String a="ab"+"cd"; 再看看這裡呢?
答案是三個。
棧(stack):主要儲存基本型別(或者叫內建型別)(char、byte、short、int、long、float、double、boolean)和物件的引用,資料可以共享,速度僅次於暫存器(register),快於堆。
堆(heap):用於儲存物件