首先我們先懂得一下字符串,字符串在天生時就寫進物理地址,定為不可更改,我們可以把它當作是final形的變量,既然是不可更改的那麼我們對於字符串的任何修正將不是原對象,而是產生新的對象。Sun這樣設計也許是為了在其性能有所提高。既然每次對字符串把持其成果將是新的字符串,那麼頻繁調用,既不是很浪費。Java設計者可能想到這一點,所以加了字符串池。也許有的人對於字符串池並不感到熟悉,那你就把它當成是一個ArrayList吧!它用來存儲你應用到的所有字符串,追加字符串時,假如有則返加字串符池中的字符串引用,沒有則創立一個。這樣做是為了避免大批的字符串把持而產生浪費。
好了,我們來看一下下面幾個程序:
String s = "abc";//這裡用了幾個字符串對象
當程序碰到"abc"時,會首先查找字符串池有沒有"abc",沒有則在字符串池中創立"abc",然後將引用賦給了s。
我們再來看這一句:
String s = new String("abc");//這裡用了幾個字符串對象
當程序碰到"abc"後,在字符串池創立並返回了引用。但是這裡又用到了new要害字,new是在內存當中創立一個對象,並將內存地址賦給了s。也就是在這裡一共創立了兩個對象,一個是字符串池的引用,另一個是內存地址的引用。
那麼這兩句話有什麼差別嗎!當然有差別了,一個是字符串池引用,一個是內存地址。從速度上說當然是拜訪內存快了。但是它是在內存中創立了一個對象。於利於弊,個人看著應用。
關於字符串重載:
在Java中是不答應應用符號重載的,(在C++就可以)似乎是Java的設計者說這是一個不好的設計。於是在Java中往了這個功效。但是在 String中卻應用了+的重載符。由於字符串在天生時都是定為不可轉變,所以在重載多個字符串時將會產生大批的字符,所以在後來的虛擬機中對於字符串重載應用了StringBuider進行優化。(至於什麼版本的進行了優化,嘿嘿,偶不知道。怎麼優化,下面有先容。)
重載時還分有三種情況:
第一:變量重載
第二,字符串常量重載。變量是指字符串所賦值的對象。常量就是指以""的未分配的字符串。而常量重載時虛擬機會為你新建一個 StringBuilder作為臨時的字符鏈接。把所有字符串鏈接完成,用toString()方法返回新的字符串。而常量字符串則是直接交由字符串池處理。一切把持只在字符串池中,也就是說新天生的字符串也是在字符串池中的引用。
第三種就是對象與常量一起用了。那虛擬會以第一種方法處理。
關於==與equals():
==是檢測兩個對象的地址是否相等。equals()則是檢測兩個對象值是否相等。
另有一點,""也是一個字符串,跟new String("")一樣。可以用new String("").intern()==""來檢測是否同一個引用。
我們再來做一下練習:
首先,定義一堆變量:
String _abc = "abc";String _2_abc = "abc";String _def = "def";String _abcdef = "abcdef";String _new_abc = new String("abc");String _new_abcdef = new String("abcdef");String _abc_def = _abc + _def;String _abc_add_def = "abc" + "def";String _new_abc_def = _new_abc + _def; System.out.println("_abc == _2_abc -> " + (_abc == _2_abc)); // trueSystem.out.println("_abc==_new_abc -> " + (_abc == _new_abc)); // falseSystem.out.println("_abc==_new_abc.intern() -> " + (_abc == _new_abc.intern())); // true
第一個:第一個數在字符串池中創立了"abc",第二個數天生時,先在字符串池中查找有沒有"abc",有則返回這個字符串的引用,所用兩個都是同一個引用。
第二個:_new_abc在天生時先查找字符串池是否有"abc",之後又在內存中創立了"abc",並把內存地址給_new_abc,所以這兩個對象當然不雷同,由於一個是內存地址,一個是字符串池引用。
第三個:用到了intern()此方法返回的是一個新字符串,其對象是指向字符串池的引用。由於先前_abc在字符串池中也創立了"abc"字符串,所以返回的是與_abc雷同的引用。
好了,小試了牛刀,再看下面的程序:
System.out.println("_abc_def==_new_abc_def -> " + (_abc_def == _new_abc_def)); // falseSystem.out.println("_abcdef==abc add def -> " + (_abcdef == "abc" + "def")); // trueSystem.out.println("_abcdef==_abc_def -> " + (_abcdef == _abc_def)); // false
第一個由於是變量與常量重載,所以虛擬為你開了一個StringBuilder作為臨時鏈接字符串。其返回的是一個new出來的字符串,也就是其地址是內存地址。所認為false。
第二個由於是常量重載,所以天生的對象是字符串池中的引用。與本來的_abcdef是同一個地址。
第三個跟第一個同理。
再看以下這一句:
System.out.println("_abcdef==_abc_def.intern() -> " + (_abcdef == _abc_def.intern())); // true
_abcdef是字符池的引用,讓_abc_def也指向字符池。所以兩個對象都是同一個引用。
再看以下:
System.out.println("_abcdef==_abc_add_def -> " + (_abcdef == _
println("_new_abcdef==_abc_add_def -> " + (_new_abcdef == _abc_add_def)); //falseSystem.out.println("_new_abcdef==_new_abc_def -> " + (_new_abcdef == _new_abc_def)); //false
第一個:_abc_add_def是字符串池"abc"與"def"的常量重載。返回其成果也是字符串池的引用。
第二個:_new_abcdef是new出來的內存地址與_abc_add_def的字符串池引用當然是不同的對象。
第三個:_new_abcdef是new出來的對象,而_new_abc_def也是重載後new出來的對象。但它們之間卻是不雷同的。也就是說內存不會像字符串池那樣給我們找字符串引用。(呵呵,弱智闡明)
我們來做一下測試:int time = 60;System.out.println("游戲時間" + time + "幀數" + 60);這裡用幾個字符串4個5個6個7個,也許你會感到是7個,但是這裡共用了5個字符串,為什麼,由於虛擬機給你做了優化,在字符串重載時應用了StringBuilder,也就是說上面的程序創立了四個字符串,並用append()方法將四個字符串鏈接,最後toString()返回一個新的字符串,共是五個字符串。
也許你會想,既然虛擬機會主動為我們優化,那不是怎麼用都行。實在不然,虛擬機沒有想像中的那麼聰慧,甚至說它很笨拙。看下面程序:String s = "";for (int i = 0; i < 10; i++) { s += i + " ";}這裡首先將i轉化為String,再把s+i鏈接成一個新的字符串。也許你會說那虛擬機不就天生了StringBuilder將s與i鏈接起來再 toString()返回給s。說得沒錯。但有一點不對,那就是不止一個Stringbuilder,而是10個,在每一次的循環中都會創立一次 StringBuilder,處理完後又開釋了。
既是這樣。那怎樣優化呢。可以看下面這個例子:
假如你想用sb.append(i+" ");那虛擬就會認為你需要StringBuilder鏈接,於是又為你創立了一個StringBuilder。
最後做一個小測試。public class Test { public static void main(String[] args) { Hello();"ts = " + ts); String string = "123"; System.out.println("string = " + string); setString(string); System.out.println("string = " + string); } private static void setString(TestString ts) { ts.string = "321"; } private static void setString(String s) { s = "321"; }}輸出成果是多少呢?就留給各位自己解決了。嘿嘿。。
假如有什麼處所不對,歡迎拍磚。。。