《String的特性》java
一、String類是final的,不可被繼承。
二、String類是的本質是字符數組char[], 而且其值不可改變。
三、Java運行時會維護一個String Pool(String池),JavaDoc翻譯很模糊「字符串緩衝區」。String池用來存放運行時中產生的各類字符串,而且池中的字符串的內容不重複。而通常對象不存在這個緩衝池,而且建立的對象僅僅存在於方法的堆棧區。編程
String str=new String("abc"); 緊接着這段代碼以後的每每是這個問題,那就是這行代碼究竟建立了幾個String對象呢?數組
相信你們對這道題並不陌生,答案也是衆所周知的,1個或2個。優化
1 首先在堆中(不是常量池)建立一個指定的對象"abc",並讓str引用指向該對象
2 在字符串常量池中查看,是否存在內容爲"abc"字符串對象
3 若存在,則將new出來的字符串對象與字符串常量池中的對象聯繫起來
4 若不存在,則在字符串常量池中建立一個內容爲"abc"的字符串對象,並將堆中的對象與之聯繫起來翻譯
接下來咱們就從這道題展開,一塊兒回顧一下與建立String對象相關的一些JAVA知識。 code
咱們能夠把上面這行代碼分紅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 ... } 你們都知道,咱們經常使用的建立一個類的實例(對象)的方法有如下兩種:
1、使用new建立對象。
2、調用Class類的newInstance方法,利用反射機制建立對象。
咱們正是使用new調用了String類的上面那個構造器方法建立了一個對象,並將它的引用賦值給了str變量。同時咱們注意到,被調用的構造器方法接受的參數也是一個String對象,這個對象正是"abc"。由此咱們又要引入另一種建立String對象的方式的討論——引號內包含文本。這種方式是String特有的,而且它與new的方式存在很大區別。blog
String str="abc";
毫無疑問,這行代碼建立了一個String對象。 繼承
String a="abc"; String b="abc"; 那這裏呢?
答案仍是一個。 內存
String a="ab"+"cd"; 再看看這裏呢?
經過編譯器優化後,獲得的效果是String a="abcd"
此時,若是字符串常量池中存在abcd,則該語句並不會建立對象,只是講字符串常量池中的引用返回而已。
若是字符串常量池中不存在abcd,則會建立並放入字符串常量池,並返回引用,此時會有一個對象進行建立。
說到這裏,咱們就須要引入對字符串池相關知識的回顧了。
在JAVA虛擬機(JVM)中存在着一個字符串池,其中保存着不少String對象,而且能夠被共享使用,所以它提升了效率。因爲String類是final的,它的值一經建立就不可改變,所以咱們不用擔憂String對象共享而帶來程序的混亂。字符串池由String類維護,咱們能夠調用intern()方法來訪問字符串池。
咱們再回頭看看String a="abc",這行代碼被執行的時候,JAVA虛擬機首先在字符串池中查找是否已經存在了值爲"abc"的這麼一個對象,它的判斷依據是String類equals(Object obj)方法的返回值。若是有,則再也不建立新的對象,將使用串池裏原來的那個內存,直接返回已存在對象的引用,而不會從新分配內存;若是沒有,則先建立這個對象,而後把它加入到字符串池中,再將它的引用返回。
而若是用String s=new String("abc"),無論串池裏有沒有"abc",它都會在堆中從新分配一塊內存,定義一個新的對象。
所以咱們提倡你們用引號包含文本的方式來建立String對象以提升效率,實際上這也是咱們在編程中常採用的。
再看下個例子:
String s="java"+"blog";//直接將javablog對象放入字符串池中。
System.out.println(s=="javablog");//結果是true;
String str1="java";//指向字符串池
String str2="blog";//指向字符串池
String s = str1+str2; // +運算符會在堆中創建起兩個String對象,這兩個對象的值分別是「java」,"blog",也就是說從字符串常量池中複製這兩個值,而後再堆中建立兩個對象。而後再創建對象s, 而後將「javablog」的堆地址賦給s. 這句話共建立了3個String對象。
System.out.println(s=="javablog");//由於內存地址不一樣,結果是false;
String s=str1+"blog";//不放在字符串池中,而是在堆中分分配。
System.out.println(s=="javablog");//結果是false;
JVM對形如String str="java"+"blog";根據編譯器合併已知量的優化功能,在池中開闢一塊空間,存放合併後的String常量"javablog"。而String s=str1+str2;是在運行時候才能知道的,也就是說str1+str2是在堆裏建立的因此結果爲false了。
總之,建立字符串有兩種方式:兩種內存區域(pool,heap) 1.""建立的字符串在字符串池中。 2.new 建立字符串時,首先查看池中是否有相同的字符串,若是有則拷貝一份放到堆中,而後返回堆中的地址;若是池中沒有則在堆中建立一份,而後返回堆中的地址, 3.在對字符串賦值時,若是右操做數含有一個或一個以上的字符串引用時,則在堆中再創建一個字符串對象,返回引用。如:String s= str1+"blog";