java中的字符串相关知识整理
字符串為什么這么重要
寫了多年java的開發(fā)應(yīng)該對String不陌生,但是我卻越發(fā)覺得它陌生。每學(xué)一門編程語言就會與字符串這個關(guān)鍵詞打不少交道。看來它真的很重要。
字符串就是一系列的字符組合的串,如果寫過C/C++的應(yīng)該就了解,在字符串的操作上會有許多操作的函數(shù)與類,用于簡化代碼的開發(fā)。一方面是因?yàn)樽址诖a中會頻繁用到,另一方面是因?yàn)樽址牟僮鞣浅B闊?/p>
最初我知道String的特殊待遇就是在delphi中,因?yàn)镾tring在delphi里是一個關(guān)鍵字存在,與其他的基本類型是不一樣的。那時(shí)就了解到了許多相關(guān)的知識。在java/.net也都對string做了專門的處理,可見重要性。
正因?yàn)樽址诔绦蛑杏玫亩?#xff0c;而且操作也多這就會帶來內(nèi)存占用與性能的問題,所需要特殊的關(guān)照一下,想象一下一個日志記錄系統(tǒng)一天時(shí)間得用上多少字符串變量。
了解一下java中的String
java中提供了String類支持字符串的功能,畢竟字符串本質(zhì)就是一堆字符的組合,那么就來看看它有什么特點(diǎn)吧。
- String的特點(diǎn)
String把字符串還是存放在一個char數(shù)組中的,數(shù)據(jù)的操作圍繞它展開,但有點(diǎn)特別的地方,代碼如下
private final char value[];可以發(fā)現(xiàn)這個char value[]是加了final的,也就是說一旦創(chuàng)建了值就不可變。這樣就會導(dǎo)致每一次創(chuàng)建String只會有一個值,再對其進(jìn)行字符串操作也必須生成新的值。java對這個處理使用了字符串常量池的概念。就是把字符串丟到一個池里,如果相同就用相同的。當(dāng)然這也有個前提,就是要用下面的方式
String s = "abc";這樣做的時(shí)候jvm會在編譯期就確定了,在運(yùn)行時(shí)會先在常量池里查找是否有"abc",沒有就添加并返回,有的話返回常量池的對象。這樣做的好處是對于相同的字符串就不需要重復(fù)創(chuàng)建啦。 但是如果使用下面的代碼
String s1 = new String("abc");這個時(shí)候情景就變了,這里jvm會在堆棧里創(chuàng)建一個對象s1,只不過s1里的value也是指向"abc"的。后面在看字符串比較的時(shí)候會發(fā)現(xiàn)區(qū)別。
- 字符串比較?看一段代碼:
問:這時(shí)s==s1嗎?
答案是相等的,為什么呢?其實(shí)jvm會在s1創(chuàng)建時(shí)去常量區(qū)查找是否有相同值的字符串,如果有就返回給s1,這樣s1就和s指向了同一個字符串,所以是相等的。
但是還有一種情況就不一樣,
String s = "abc"; String s3 = new String("abc"); if (s == s3) { System.out.println("s == s3"); } else { System.out.println("s != s3"); }這個時(shí)候應(yīng)該print出s != s3,這是因?yàn)閚ew一個String對象后確實(shí)會創(chuàng)建一個新的變量。所以使用==比較的話自然就返回false了。
用到equals比較呢?
String s = "abc"; String s2 = new String("abc"); if (s.equals(s2)) { System.out.println("s = s2"); } else { System.out.println("s != s2"); }打印是s = s2,因?yàn)?#61;=是用于比較兩個地址,而equals是用于比較兩個變量的值。可以看一下equals的代碼
public boolean equals(Object anObject) {if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }在equals中,先是比較是否地址相同,如果不相同比較value,因?yàn)関alue都是"abc"自然就返回true。
- intern方法
String里有一個intern方法,我們可以先試一下面的代碼。
String s = "abc"; String s3 = new String("abc"); if (s.intern() == s3.intern()) { System.out.println("s.intern = s3.intern"); } else { System.out.println("s.intern != s3.intern"); }還是上面的s和s3,如果使用各自的intern方法返回的值比較則會輸出s.intern = s3.intern。找了找資料結(jié)合注釋了解到,這個intern方法其實(shí)是從字符串常量池里返回當(dāng)前字符串,如果當(dāng)前字符串已經(jīng)存在了則返回當(dāng)前字符串,如果當(dāng)前字符串不存在,則將當(dāng)前字符串放入常量池再返回。
有了這個解釋就明白了,s和s3都通過intern返回的那么都是常量池里的"abc"咯,所以intern比較時(shí)是相等的。
認(rèn)識一下StringBuffer和StringBuilder
- StringBuffer和StringBuilder哪一個是線程安全的?
面試時(shí)遇到的這個問題,我突然有點(diǎn)懵,沒太注意過這兩個類,而且印象中java里只有一個StringBuffer呀?回來看了一下代碼原來StringBuffer是線程安全的,也就是在字符串操作的方法上都有synchronized。
于是打開代碼注釋發(fā)現(xiàn)是Jdk1.5才開始有的StringBuilder,而且在后面版本加了個不加鎖的類,看樣子是解決非并發(fā)場景下的效率問題,不加鎖對于操作大字符串還是有性能提升的。
嗯,不錯,get了一個小知識。
出于好奇看了一下這兩個類的代碼,與String真有些類似,只不過這時(shí)的chat[] 已經(jīng)是不帶final的咯,這樣就避免了String類操作時(shí)產(chǎn)生一堆字符串對象的問題。
char[] value;- StringBuffer和StringBuilder的作用
既然已經(jīng)有了String,那這兩個家伙有什么用呢?其實(shí)問題還是要和String的原理有關(guān)系。因?yàn)镾tring是通過常量池管理的,這樣解決的是相同字符串重復(fù)創(chuàng)建的問題,但大部分字符串都是不一樣的,特別是在做字符串拼接操作時(shí),如果用String的+進(jìn)行拼接就會產(chǎn)生大量的字符串常量,非常的消耗性能與空間。
為解決這個問題就用到StringBuffer,本質(zhì)上也就是通過一個可變的字符序列,在字符串操作時(shí)不需要生成新的對象,從而提升內(nèi)存使用。
看看StringBuffer是怎么提升這個拼接性能的吧。 查看StringBuffer/StringBuilder的代碼(JDK1.5+)發(fā)現(xiàn)它們都繼承于AbstractStringBuilder。很多的代碼其實(shí)都是在AbstractStringBuilder里完成的。因?yàn)檫@個問題由拼接引出的,在此我們就主要關(guān)注一下append方法吧。
public AbstractStringBuilder append(String str) {if (str == null) return appendNull(); int len = str.length(); ensureCapacityInternal(count + len);//確定容量 str.getChars(0, len, value, count);//取出str的字符放入到value數(shù)組中 count += len;//count累加 return this; }代碼還是比較清楚的,整個過程最重要的就是使用String的getChars方法將str的值寫入到當(dāng)前對象的value中。而String的getChars方法如下:
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) { if (srcBegin < 0) { throw new StringIndexOutOfBoundsException(srcBegin); } if (srcEnd > value.length) { throw new StringIndexOutOfBoundsException(srcEnd); } if (srcBegin > srcEnd) { throw new StringIndexOutOfBoundsException(srcEnd - srcBegin); } System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin); }可以看出最終是做了一個數(shù)組的復(fù)制,因?yàn)樵贏bstractStringBuilder中的value是個可變的char數(shù)組,這樣的話對于字符串操作只需要在char數(shù)組上進(jìn)行即可。不會像String那樣生成新對象,所以說自然就變的高效了。
?
注:此文章為原創(chuàng),歡迎轉(zhuǎn)載,請?jiān)谖恼马撁婷黠@位置給出此文鏈接! 若您覺得這篇文章還不錯請點(diǎn)擊下右下角的推薦,非常感謝! http://www.cnblogs.com/5207/轉(zhuǎn)載于:https://www.cnblogs.com/5207/p/5892583.html
總結(jié)
以上是生活随笔為你收集整理的java中的字符串相关知识整理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【蓝鸥Unity开发基础三】课时14 刚
- 下一篇: String.prototype.sub