日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

编写高质量代码:改善Java程序的151个建议(第4章:字符串___建议52~55)

發(fā)布時(shí)間:2024/8/26 java 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 编写高质量代码:改善Java程序的151个建议(第4章:字符串___建议52~55) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

建議52:推薦使用String直接量賦值

  一般對(duì)象都是通過(guò)new關(guān)鍵字生成的,但是String還有第二種生成方式,也就是我們經(jīng)常使用的直接聲明方式,這種方式是極力推薦的,但不建議使用new String("A")的方式賦值。為什么呢?我們看如下代碼:

public class Client58 {public static void main(String[] args) {String str1 = "詹姆斯";String str2 = "詹姆斯";String str3 = new String("詹姆斯");String str4 = str3.intern();// 兩個(gè)直接量是否相等System.out.println(str1 == str2);// 直接量和對(duì)象是否相等System.out.println(str1 == str3);// 經(jīng)過(guò)intern處理后的對(duì)象與直接量是否相等System.out.println(str1 == str4);} }

  注意看上面的程序,我們使用"=="判斷的是兩個(gè)對(duì)象的引用地址是否相同,也就是判斷是否為同一個(gè)對(duì)象,打印的結(jié)果是true,false,true。即有兩個(gè)直接量是同一個(gè)對(duì)象(進(jìn)過(guò)intern處理后的String與直接量是同一個(gè)對(duì)象),但直接通過(guò)new生成的對(duì)象卻與之不等,原因何在?

  原因是Java為了避免在一個(gè)系統(tǒng)中大量產(chǎn)生String對(duì)象(為什么會(huì)大量產(chǎn)生,因?yàn)镾tring字符串是程序中最經(jīng)常使用的類型),于是就設(shè)計(jì)了一個(gè)字符串池(也叫作字符串常量池,String pool或String Constant Pool或String Literal Pool),在字符串池中容納的都是String字符串對(duì)象,它的創(chuàng)建機(jī)制是這樣的:創(chuàng)建一個(gè)字符串時(shí),首先檢查池中是否有字面值相等的字符串,如果有,則不再創(chuàng)建,直接返回池中該對(duì)象的引用,若沒(méi)有則創(chuàng)建之,然后放到池中,并返回新建對(duì)象的引用,這個(gè)池和我們平常說(shuō)的池非常接近。對(duì)于此例子來(lái)說(shuō),就是創(chuàng)建第一個(gè)"詹姆斯"字符串時(shí),先檢查字符串池中有沒(méi)有該對(duì)象,發(fā)現(xiàn)沒(méi)有,于是就創(chuàng)建了"詹姆斯"這個(gè)字符串并放到池中,待創(chuàng)建str2字符串時(shí),由于池中已經(jīng)有了該字符串,于是就直接返回了該對(duì)象的引用,此時(shí),str1和str2指向的是同一個(gè)地址,所以使用"=="來(lái)判斷那當(dāng)然是相等的了。

  那為什么使用new String("詹姆斯")就不相等了呢?因?yàn)橹苯勇暶饕粋€(gè)String對(duì)象是不檢查字符串池的,也不會(huì)把對(duì)象放到字符串池中,那當(dāng)然"=="為false了。

  那為什么intern方法處理后即又相等了呢?因?yàn)閕ntern會(huì)檢查當(dāng)前對(duì)象在對(duì)象池中是否存在字面值相同的引用對(duì)象,如果有則返回池中的對(duì)象,如果沒(méi)有則放置到對(duì)象池中,并返回當(dāng)前對(duì)象。

  可能有人要問(wèn)了,放到池中,是不是要考慮垃圾回收問(wèn)題呀?不用考慮了,雖然Java的每個(gè)對(duì)象都保存在堆內(nèi)存中但是字符串非常特殊,它在編譯期已經(jīng)決定了其存在JVM的常量池(Constant Pool),垃圾回收不會(huì)對(duì)它進(jìn)行回收的。

  通過(guò)上面的介紹,我們發(fā)現(xiàn)Java在字符串的創(chuàng)建方面確實(shí)提供了非常好的機(jī)制,利用對(duì)象池不僅可以提高效率,同時(shí)減少了內(nèi)存空間的占用,建議大家在開(kāi)發(fā)中使用直接量賦值方式,除非必要才建立一個(gè)String對(duì)象。

建議53:注意方法中傳遞的參數(shù)要求

?  有這樣的一個(gè)簡(jiǎn)單需求,寫(xiě)一個(gè)方法,實(shí)現(xiàn)從原始字符串中刪除與之匹配的所有字符串,比如在"好是好"中,刪除"好",代碼如下:

public class StringUtils {//刪除字符串public static String remove(String source, String sub) {return source.replaceAll(sub, "");} }

  StringUtils工具類很簡(jiǎn)單,它采用了String的replaceAll方法,該方法是做字符串替換的,我們編寫(xiě)一個(gè)測(cè)試用例,檢查remove方法是否正確,如下所示:

import static org.junit.Assert.*; import org.junit.Test; public class TestStringUtils {@Testpublic void test() {assertTrue(StringUtils.remove("好是好","好").equals("是"));assertTrue(StringUtils.remove("$是$","$").equals("是"));} }

  單獨(dú)運(yùn)行第一個(gè)是綠條,單獨(dú)運(yùn)行第二個(gè)是紅條,為什么第二個(gè)(assertTrue(StringUtils.remove("$是$","$").equals("是")))不通過(guò)呢?

  問(wèn)題就出在replaceAll方法上,該方法確實(shí)需要傳遞兩個(gè)String類型的參數(shù),也確實(shí)進(jìn)行了字符串替換,但是它要求第一個(gè)參數(shù)是正則表達(dá)式,符合正則表達(dá)式的字符串才會(huì)被替換。對(duì)上面的例子來(lái)說(shuō),第一個(gè)測(cè)試案例傳遞進(jìn)來(lái)的是一個(gè)字符串"好",這是一個(gè)全匹配查找替換,處理的非常正確,第二個(gè)測(cè)試案例傳遞進(jìn)來(lái)的是一個(gè)"$"符號(hào),"$"符號(hào)在正則表達(dá)式中表示的是字符串的結(jié)束位置,也就是執(zhí)行完replaceAll后在字符串結(jié)尾的地方加上了空字符串,其結(jié)果還是"$"是"$",所以測(cè)試失敗也就再所難免了。問(wèn)題清楚了,解決方案也就出來(lái)了:使用replace方法替換即可,它是replaceAll的方法的簡(jiǎn)化版,可傳遞兩個(gè)String參數(shù),與我們的編碼意圖是吻合的。

  大家如果注意看JDK文檔,會(huì)發(fā)現(xiàn)replace(CharSequence target,CharSequence replacement)方法是1.5版本以后才開(kāi)始提供的, 在此之前如果要對(duì)一個(gè)字符串進(jìn)行全體換,只能使用replaceAll方法,不過(guò)由于replaceAll方法的第二個(gè)參數(shù)使用了正則表達(dá)式,而且參數(shù)類型只要是CharSequence就可以(String的父類),所以很容易使使用者誤解,稍有不慎就會(huì)導(dǎo)致嚴(yán)重的替換錯(cuò)誤。

  注意:replaceAll傳遞的第一個(gè)參數(shù)是正則表達(dá)式  

建議54:正確使用String、StringBuffer、StringBuilder

?  CharSequence接口有三個(gè)實(shí)現(xiàn)類與字符串有關(guān),String、StringBuffer、StringBuilder,雖然它們都與字符串有關(guān),但其處理機(jī)制是不同的。

  String類是不可變的量,也就是創(chuàng)建后就不能再修改了,比如創(chuàng)建了一個(gè)"abc"這樣的字符串對(duì)象,那么它在內(nèi)存中永遠(yuǎn)都會(huì)是"abc"這樣具有固定表面值的一個(gè)對(duì)象,不能被修改,即使想通過(guò)String提供的方法來(lái)嘗試修改,也是要么創(chuàng)建一個(gè)新的字符串對(duì)象,要么返回自己,比如:

String str = "abc"; String str1 = str.substring(1);

  其中str是一個(gè)字符串對(duì)象,其值是"abc",通過(guò)substring方法又重新生成了一個(gè)字符串str1,它的值是"bc",也就是說(shuō)str引用的對(duì)象一但產(chǎn)生就永遠(yuǎn)不會(huì)變。為什么上面還說(shuō)有可能不創(chuàng)建對(duì)象而返回自己呢?那是因?yàn)椴捎胹ubstring(0)就不會(huì)創(chuàng)建對(duì)象。JVM從字符串池中返回str的引用,也就是自身的引用。

  StringBuffer是一個(gè)可變字符串,它與String一樣,在內(nèi)存中保存的都是一個(gè)有序的字符序列(char 類型的數(shù)組),不同點(diǎn)是StringBuffer對(duì)象的值是可改變的,例如:

StringBuffer sb = new StringBuffer("a"); sb.append("b");

  從上面的代碼可以看出sb的值在改變,初始化的時(shí)候是"a" ,經(jīng)過(guò)append方法后,其值變成了"ab"。可能有人會(huì)問(wèn)了,這與String類通過(guò) "+" 連接有什么區(qū)別呢?例如

String s = "a"; s = s + "b";

  有區(qū)別,字符串變量s初始化時(shí)是 "a" 對(duì)象的引用,經(jīng)過(guò)加號(hào)計(jì)算后,s變量就修改為了 “ab” 的引用,但是初始化的 “a” 對(duì)象還沒(méi)有改變,只是變量s指向了新的引用地址,再看看StringBuffer的對(duì)象,它的引用地址雖不變,但值在改變。

  StringBuffer和StringBuilder基本相同,都是可變字符序列,不同點(diǎn)是:StringBuffer是線程安全的,StringBuilder是線程不安全的,翻翻兩者的源代碼,就會(huì)發(fā)現(xiàn)在StringBuffer的方法前都有關(guān)鍵字syschronized,這也是StringBuffer在性能上遠(yuǎn)遠(yuǎn)低于StringBuffer的原因。

  在性能方面,由于String類的操作都是產(chǎn)生String的對(duì)象,而StringBuilder和StringBuffer只是一個(gè)字符數(shù)組的再擴(kuò)容而已,所以String類的操作要遠(yuǎn)慢于StringBuffer 和 StringBuilder。

  弄清楚了三者之間的原理,我們就可以在不同的場(chǎng)景下使用不同的字符序列了:

  • 使用String類的場(chǎng)景:在字符串不經(jīng)常變化的場(chǎng)景中可以使用String類,例如常量的聲明、少量的變量運(yùn)算等;
  • 使用StringBuffer的場(chǎng)景:在頻繁進(jìn)行字符串的運(yùn)算(如拼接、替換、刪除等),并且運(yùn)行在多線程的環(huán)境中,則可以考慮使用StringBuffer,例如XML解析、HTTP參數(shù)解析和封裝等;
  • 使用StringBuilder的場(chǎng)景:在頻繁進(jìn)行字符串的運(yùn)算(如拼接、替換、刪除等),并且運(yùn)行在單線程的環(huán)境中,則可以考慮使用StringBuilder,如SQL語(yǔ)句的拼接,JSON封裝等。
  •   注意:在適當(dāng)?shù)膱?chǎng)景選用字符串類型?

    建議55:注意字符串的位置

    ?  看下面一段程序:

    public class Client55 {public static void main(String[] args) {String str1 = 1 + 2 + "apples";String str2 = "apples" + 1 + 2;System.out.println(str1);System.out.println(str2);} }

      想想兩個(gè)字符串輸出的結(jié)果的蘋(píng)果數(shù)量是否一致,如果一致,會(huì)是幾呢?

      答案是不一致,str1的值是"3apples" ,str2的值是“apples12”,這中間懸殊很大,只是把“apples” 調(diào)換了一下位置,為何會(huì)發(fā)生如此大的變化呢?

      這都源于java對(duì)于加號(hào)的處理機(jī)制:在使用加號(hào)進(jìn)行計(jì)算的表達(dá)式中,只要遇到String字符串,則所有的數(shù)據(jù)都會(huì)轉(zhuǎn)換為String類型進(jìn)行拼接,如果是原始數(shù)據(jù),則直接拼接,如是是對(duì)象,則調(diào)用toString方法的返回值然后拼接,如:

      str =? str + new ArrayList();

      上面就是調(diào)用ArrayList對(duì)象的toString方法返回值進(jìn)行拼接的。再回到前面的問(wèn)題上,對(duì)與str1 字符串,Java的執(zhí)行順序是從左到右,先執(zhí)行1+2,也就是算術(shù)加法運(yùn)算,結(jié)果等于3,然后再與字符串進(jìn)行拼接,結(jié)果就是 "3 apples",其它形式類似于如下計(jì)算:

      String str1 = (1 + 2 ) + "apples" ;

      而對(duì)于str2字符串,由于第一個(gè)參與運(yùn)算的是String類型,加1后的結(jié)果是“apples 1” ,這仍然是一個(gè)字符串,然后再與2相加,結(jié)果還是一個(gè)字符串,也就是“apples12”。這說(shuō)明如果第一個(gè)參數(shù)是String,則后續(xù)的所有計(jì)算都會(huì)轉(zhuǎn)變?yōu)镾tring類型,誰(shuí)讓字符串是老大呢!

      注意: 在“+” 表達(dá)式中,String字符串具有最高優(yōu)先級(jí)。

    轉(zhuǎn)載于:https://www.cnblogs.com/selene/p/5868160.html

    總結(jié)

    以上是生活随笔為你收集整理的编写高质量代码:改善Java程序的151个建议(第4章:字符串___建议52~55)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。