不再糾結(jié)Java中的String類
又是新的一月,又是各種總結(jié),先分享一下java中string的一些小專題吧,這部分比比較基礎(chǔ),但是也非常的有用。我發(fā)現(xiàn)很多面試官像中了邪一樣就愛問這個。。string的種種,糾結(jié),希望這篇文章讓大家不再糾結(jié)。。 |
String是我們經(jīng)常用到的一個類型,其實(shí)有時候覺得寫程序就是在反復(fù)的操作字符串,這是C的特點(diǎn),在java中,jdk很好的封裝了關(guān)于字符串的操作。今天主要講的是三個類String 、StringBuffer 、 StringBuilder .這三個類基本上滿足了我們在不同情景下使用字符串的需求。
先說,第一個String。
JDK的解釋是 “Strings are constant; their values cannot be changed after they are created”也就是說String對象一旦被創(chuàng)建就是固定不變的了(你一定有問題,但請先等一等,耐心讀下去),這樣的一點(diǎn)好處就是可以多線程之間訪問,因?yàn)橹蛔x不寫。
一般情況下我們以下面兩種方式創(chuàng)建一個String對象
兩種方式是有區(qū)別的,這和java的內(nèi)存管理有關(guān),前面已經(jīng)說過,string創(chuàng)建之后是不可變的,所以按照第一種方式創(chuàng)建的字符串會放在棧里,更確切的是常量池中,常量池就是用來保存在編譯階段確定好了大小的數(shù)據(jù),一般我們定義的int等基本數(shù)據(jù)類型就保存在這里。
其具體的一個流程就是,編譯器首先檢查常量池,看看有沒有一個“string”,如果沒有則創(chuàng)建。如果有的話,則則直接把str1指向那個位置。
第二種創(chuàng)建字符串的方法是通過new關(guān)鍵字,還是java的內(nèi)存分配,java會將new的對象放在堆中,這一部分對象是在運(yùn)行時創(chuàng)建的對象。所以我們每一次new的時候,都會創(chuàng)建不同的對象,即便是堆中已經(jīng)有了一個一模一樣的。
寫一個小例子
- String str1 = "string";
- String str4 = "string";
- String str2 = new String("string");
- String str3 = new String("string");
- /*用于測試兩種創(chuàng)建字符串方式的區(qū)別*/
- System.out.println(str1 == str4);
- System.out.println(str2 == str3);
- System.out.println(str3 == str1);
- str3 = str3.intern(); //一個不常見的方法
- System.out.println(str3 == str1);
這個的運(yùn)行結(jié)果是
true //解釋:兩個字符串的內(nèi)容完全相同,因而指向常量池中的同一個區(qū)域
false //解釋:每一次new都會創(chuàng)建一個新的對象
false // 解釋: 注意==比較的是地址,不僅僅是內(nèi)容
true //介紹一下intern方法,這個方法會返回一個字符串在常量池中的一個地址,如果常量池中有與str3內(nèi)容相同的string則返回那個地址,如果沒有,則在常量池中創(chuàng)建一個string后再返回。實(shí)際上,str3現(xiàn)在指向了str1的地址。
這就是讓人糾結(jié)的string了,現(xiàn)在你可以說話了。。。很多人有這樣的疑問就是既然string是不變的,那么為什么str1 + "some"是合法的,其實(shí),每次對string進(jìn)行修改,都會創(chuàng)建一個新的對象。
所以如果需要對一個字符串不斷的修改的話,效率是非常的低的,因?yàn)槎训暮锰幨强梢詣討B(tài)的增加空間,劣勢就是分配新的空間消耗是很大的,比如我們看下面的測試。
- long start = System.currentTimeMillis();
- for(int i = 0; i < 50000; i++)
- {
- str1+= " ";
- }
- long end = System.currentTimeMillis();
- System.out.println("the run time is "+(end -start)+" ms");
我的機(jī)器上運(yùn)行結(jié)果是the run time is 3538 ms 如果你把循環(huán)的次數(shù)后面再增加幾個0就會更慢。因?yàn)槊恳淮窝h(huán)都在創(chuàng)建心的對象,那么JDK如何解決這個問題?
下面就要說第二個類StringBuffer。
StringBuffer是一個線程安全的,就是多線程訪問的可靠保證,最重要的是他是可變的,也就是說我們要操作一個經(jīng)常變化的字符串,可以使用這個類,基本的方法就是append(與string的concat方法對應(yīng))和insert方法,至于怎么使用,就不多講了,大家可以自己查看API。
- StringBuilder sb = new StringBuilder("string builder");
- StringBuffer sf = new StringBuffer("string buffer");
- long start = System.currentTimeMillis();
- for(int i = 0; i < 50000; i++)
- {
- //str1+= " ";
- sb.append(" ");
- }
- long end = System.currentTimeMillis();
- System.out.println("the run time is "+(end -start)+" ms");
測試一下,這次只需要8ms,這就是效率。
那么接下來,就要問StringBuilder是干什么的,其實(shí)這個才是我們嘗使用的,這個就是在jdk 1.5版本后面添加的新的類,前面說StringBuffer是線程同步的,那么很多情況下,我們只是使用一個線程,那個同步勢必帶來一個效率的問題,StringBuilder就是StringBuffer的非線程同步的版本,二者的方法差不多,只是一個線程安全(適用于多線程)一個沒有線程安全(適用于單線程)。
其實(shí)看了一下jdk源代碼就會發(fā)現(xiàn),StringBuffer就是在各個方法上加上了關(guān)鍵字syncronized
以上就是對三個字符串類的一個總結(jié),總之不要在這上面糾結(jié)。。。。。。不想介紹太多的方法,總覺得那樣會把一篇博客弄成API文檔一樣,而且還非常的繁瑣。都是些體會,希望有所幫助。起碼不要再糾結(jié),尤其是面試。。。。
本文完整源代碼:https://github.com/octobershiner/Java-Taste/tree/master/StringDemo
歡迎關(guān)注JavaTaste項(xiàng)目 https://github.com/octobershiner/Java-Taste
系列文章:http://www.cnblogs.com/octobershiner/archive/2012/03/17/2404154.html
【編輯推薦】