Java知識——基礎知識

????????本文是我自己在秋招復習時的讀書筆記,整理的知識點负蠕,也是為了防止忘記蛙埂,尊重勞動成果,轉載注明出處哦遮糖!如果你也喜歡绣的,那就點個小心心,文末贊賞一杯豆奶吧,嘻嘻屡江。 讓我們共同成長吧……


一芭概、字符串

????????Java中的字符串處理主要有下面三個類來處理的:StringStringBuffer惩嘉、StringBuilder

1罢洲、String

? ??1、String簡介

? ??初始化:

????一般由String聲明的字符串文黎,長度是不可變的惹苗,這也是它與StringBuffer和StringBuilder最直觀的一個區(qū)別。一般初始化方式:String s = "hello world";經過這條語句耸峭,JVM的棧內存中產生一個s變量桩蓉,堆內存中產生hello world字符串對象。s指向了hello world的地址劳闹。像上面這種方式產生的字符串屬于直接量字符串對象院究,JVM在處理這類字符串的時候,會進行緩存玷或,產生時放入字符串池儡首,當程序需要再次使用的時候,無需重新創(chuàng)建一個新的字符串蔬胯,而是直接指向已存在的字符串氛濒∥韪停看下面程序:

public? class? String? Test4{

????public? static? void? main(String[] args){

????????String s ="hello world";

????????String s2 ="hello world";

????????System.out.println(s == s2);

????}

}

????該程序輸出:true 因為s和s2都指向了hello world字符串,它們的地址是同一個执桌。 我們常說仰挣,String的一個很大的特點错蝴,就是它是一個“不可變的字符串”顷锰,就是說馍惹,當一個String對象完成創(chuàng)建后,該對象的內容就固定下來了慎框,但是為什么還會有下面這種情況呢?

public? class? String? Init{

????public? static? void? main(String[] args){

????????String str ="I like";//---------1--------

????????System.out.println(System.identityHashCode(str));

????????str = str +"java";//--------2---------

????????System.out.println(System.identityHashCode(str));

????}

}

該程序輸出:

14576877

12677476

????說明:str似乎是變了馅精,這是為什么呢洲敢?其實是這樣的:str只是一個引用變量,當程序執(zhí)行完1后壮不,str指向“I like”询一。當程序執(zhí)行完2之后,連接運算符會將兩個字符串連在一起绊诲,并且讓str指向新的串:"I like java"掂之,所以动雹,從這里應該可以看得出來胰蝠,最初的對象確實沒有改變茸塞,只是str所指向的對象在不斷改變钾虐。

????String對象的另一種初始化方式效扫,就是采用String類提供的構造方法進行初始化。String類提供了16種構造方法济丘,常用的有五種:

? ??String() ---------?初始化一個String對象,表示一個空字符序列

? ??String(String value)?---------?利用一個直接量創(chuàng)建一個新串

? ??String(char[] value)?---------?利用一個字符數組創(chuàng)建

? ??String(char[] value,int offset,int count)?---------?截取字符數組泪掀,從offset開始count個字符創(chuàng)建

? ??String(StringBuffer buffer)?---------?利用StringBuffer創(chuàng)建

形如:

????String s = new String();

????String s1 = new String(“hello”);

????char[] c = {'h','e','l','l','o'};

????String s2 = new String(c);

????String s3 = new String(c,1,3);

????以上就是String類的基本初始化方法。

2塔拳、String類的一些常用方法

????字符串是最常用的對象,所以颂碧,我們有必要徹底的了解下它肌似,下面我會列舉常用的字符串里的方法川队,因為有很多,就不一一列舉对雪。

????-------public int length()--------

該方法用于獲取字符串的長度,實現如下:

????int? length(){

????????return count;

? ? }

????這是JDK種的原始實現栅干,count在String類里被定義為一個整型常量:private final int count;并且不論采用哪種構造方法,最終都會為count賦值窿给。

使用方法:

????String s ="hello world";

????int length = s.length();

這個比較簡單。

-----------public boolean equals(Object anObject)-----------

該方法用于比較給定對象是否與String相等角撞。

JDK里是這樣實現的:

public? boolean? equals(Object anObject){

????????if(this== anObject) {

????????return? true;

}

if(anObjectinstanceofString) {

? ? String anotherString = (String)anObject;

int n = count;

if(n == anotherString.count) {

char? v1[] = value;//---------1---------

char? v2[] = anotherString.value;//-------2----------

int? i = offset;

int? j = anotherString.offset;

while(n-- !=0) {

if(v1[i++] != v2[j++])

return? false;

}

return? true;

? ? }

}

return? false;

? ? }

從1和2處也看出來劣领,String的底層是基于字符數組的尖淘。我們可以像下面這種方式使用equals():

String s1 =newString("hello world");

String s2 =newString("hello world");

String s3 =newString("hello");

System.out.println(s1.equals(s2));;

System.out.println(s1.equals(s3));

結果輸出:

true

false

此處插入一個很重要的知識點,重寫equals()的一般步驟及注意事項:

1. 使用==操作符檢查“實參是否為指向對象的一個引用”缴守。

2. 使用instanceof操作符檢查“實參是否為正確的類型”。?

3. 把實參轉換到正確的類型村砂。?

4. 對于該類中每一個“關鍵”域罕模,檢查實參中的域與當前對象中對應的域值是否匹配蒿讥。

a.對于既不是float也不是double類型的基本類型的域,可以使用==操作符進行比較

b.對于對象引用類型的域摔敛,可以遞歸地調用所引用的對象的equals方法

?c.對于float類型的域,先使用Float.floatToIntBits轉換成int類型的值售貌,然后使用==操作符比較int類型的值

d.對于double類型的域给猾,先使用Double.doubleToLongBits轉換成long類型的值,然后使用==操作符比較long類型的值颂跨。

5. 當你編寫完成了equals方法之后敢伸,應該問自己三個問題:它是否是對稱的、傳遞的恒削、一致的池颈?(其他兩個特性通常會自行滿足)如果答案是否定的躯砰,那么請找到這些特性未能滿足的原因揭保,再修改equals方法的代碼。

稍后我再做說明匕得,請先再看個例子:

public? class? String? Init{

public? static? void? main(String[] args){

String s ="hello world";

String s1 =newString("hello world");

String s2 =newString("hello world");

String s3 =newString("hello");

String s4 ="hello world";

System.out.println(s.equals(s1));;

System.out.println(s1.equals(s2));

System.out.println(s1.equals(s3));

System.out.println("------------------");

System.out.println(s == s1);

System.out.println(s == s3);

System.out.println(s == s4);

}

}

輸出:

true

true

false

------------------

false

false

true

此處驗證了一個問題,就是比較方法equals()和==的區(qū)別,一句話:equals()比較的是對象的內容冀惭,也就是JVM堆內存中的內容科吭,==比較的是地址宜狐,也就是棧內存中的內容俭驮。

如上述代碼中,s司光、s1、s2掀虎、s4他們四個String對象的內容都是"hello world"继效,所以翁逞,用equals()比較他們肉拓,返回的都是true毫痕。但是蛉加,當s和s1用==比較時,卻返回false七婴,因為二者在堆中開辟的地址不一樣饲化,所以棚品,返回的肯定是false。而為什么s和s4用==比較時嚼锄,返回的是true呢减拭,因為上文中提到過,直接量的字符串會產生緩存池区丑,所以拧粪,當聲明s4的時候,編譯器檢測到緩存池中存在相同的字符串沧侥,所以就直接使用可霎,只要將s4指向s所指向的字符串就行了,二者指向同一字符串宴杀,所以地址當然相等癣朗!

注意:此處隱藏著一個比較細的編程習慣,尤其是用==進行比較的時候旺罢,盡量將常量放在==的左邊旷余,因為我們有的時候,會不小心將==寫成=扁达,這樣的話正卧,如果將常量放在左邊,編譯器會報錯跪解,提醒你炉旷,但是,如果將變量放在左邊叉讥,常量放右邊砾跃,即使你寫成了=,編譯器默認為變量賦值了节吮,因此也不會報錯。

因為String類實現了public interface Comparable判耕,而Comparable接口里有唯一的方法:public int compareTo(T o)透绩。所以,String類還有另一個字符串比較方法:compareTo()

-----------------public int compareTo(String anotherString)---------------

compareTo()可實現比較兩個字符串的大小蜈首,源碼如下:

publicintcompareTo(String anotherString){

intlen1 = count;

intlen2 = anotherString.count;

intn = Math.min(len1, len2);

charv1[] = value;

charv2[] = anotherString.value;

inti = offset;

intj = anotherString.offset;

if(i == j) {

intk = i;

intlim = n + i;

while(k < lim) {

charc1 = v1[k];

charc2 = v2[k];

if(c1 != c2) {

returnc1 - c2;

}

k++;

? ? }

}else{

while(n-- !=0) {

charc1 = v1[i++];

charc2 = v2[j++];

if(c1 != c2) {

returnc1 - c2;

}

? ? }

}

returnlen1 - len2;

? ? }

compareTo是怎么實現的呢呈驶?

首先拱雏,會對兩個字符串左對齊悔据,然后從左到右一次比較望艺,如果相同顽耳,繼續(xù)扼菠,如果不同尼荆,則計算不同的兩個字符的ASCII值的差烛亦,返回就行了诈泼。與后面的其他字符沒關系。

舉個例子:

public? class? ?CompareToTest{

public? static? void? main(String[] args){

String s ="hallo";

String s2 ="ha";

String s3 ="haeeo";

inta = s.compareTo(s2);

System.out.println("a:"+a);

intb = s.compareTo(s3);

System.out.println("b:"+b);

intc = s2.compareTo(s3);

System.out.println("c:"+c);

}

}

程序輸出:

a:3

b:7

c:-3

s和s2相比煤禽,前兩個相同铐达,如果是這種情況,則直接返回length1-length2

s和s3相比檬果,前兩個相同瓮孙,不用管,直接用第三個字符的ASCII碼做差就行了选脊。所以'l'-'a'=7

此處網友“handsomeman_wei”問我源碼中的c1-c2理解不了杭抠,就是上面紅字部分的解釋。

s2和s3相比恳啥,同第一種情況一樣偏灿,只是length1比length2小,因此值為負數角寸。

-----------public char charAt(int index)-----------

獲取指定位置的字符菩混,比較容易理解,源碼為:

public? char? ?charAt(intindex){

if((index <0) || (index >= count)) {

throw? new? StringIndexOutOfBoundsException(index);

? ? ? ? }

return? value[index + offset];

? ? }

String s = "hallo";

char a = s.charAt(2);

System.out.println(a);

輸出:l

注意:參數index的值從0到字符串的長度-1扁藕,所以沮峡,如果值不在這個范圍內,如下:

String s = "hallo";

char a = s.charAt(8);

System.out.println(a);

則報錯:

Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 8

at java.lang.String.charAt(String.java:686)

at com.xtfggef.string.CompareToTest.main(CompareToTest.java:20)

與charAt()相對應的是indexOf():根據給定的字符串亿柑,返回他的位置邢疙。

indexOf()有多個參數:

public int indexOf(int ch)

public int indexOf(int ch, int fromIndex)

public int indexOf(String str)

public int indexOf(String str, int fromIndex)

static int indexOf(char[] source, int sourceOffset, int sourceCount,

char[] target, int targetOffset, int targetCount,

int fromIndex)

有興趣的自己去試試,這兒就不多闡述了望薄。

-----------substring()------------

publicStringsubstring(intbeginIndex){

returnsubstring(beginIndex, count);

? ? }

用于截取字符串疟游,此處與另一個方法對比:

publicStringsubstring(intbeginIndex,intendIndex){

if(beginIndex <0) {

thrownewStringIndexOutOfBoundsException(beginIndex);

}

if(endIndex > count) {

thrownewStringIndexOutOfBoundsException(endIndex);

}

if(beginIndex > endIndex) {

thrownewStringIndexOutOfBoundsException(endIndex - beginIndex);

}

return((beginIndex ==0) && (endIndex == count)) ?this:

newString(offset + beginIndex, endIndex - beginIndex, value);

? ? }

前者調用后者來實現,前者截取從指定位置到字符串結束的子字符串痕支,后者截取從指定位置開始颁虐,到endIndex-1位置的子字符串。

publicclassCompareToTest{

publicstaticvoidmain(String[] args){

String s ="helloworld";

String s1 = s.substring(2);

String s2 = s.substring(2,7);

String s3 = (String) s.subSequence(2,7);

System.out.print("s1:"+s1+"\n"+"s2:"+s2+"\n"+"s3:"+s3);

}

}

輸出:

s1:lloworld

s2:llowo

s3:llowo

細心的讀者應該看出來卧须,該類里面包含一個subSequence()另绩,而且該方法與substring(int,int)返回的結果一樣儒陨,觀察下源碼,不難發(fā)現的區(qū)別:

publicCharSequencesubSequence(intbeginIndex,intendIndex){

returnthis.substring(beginIndex, endIndex);

? ? }

? ? }

其實subSequence()內部就是調用的substring(beginIndex, endIndex)笋籽,只是返回值不同蹦漠。

subString返回的是String,subSequence返回的是實現了CharSequence接口的類车海,也就是說使用subSequence得到的結果笛园,只能使用CharSequence接口中的方法。不過在String類中已經重寫了subSequence侍芝,調用subSequence方法研铆,可以直接轉為String對象,如我們例子中的做法竭贩。

-----------------public String replace(char oldChar, char newChar)和public String replaceAll(String regex, String replacement)-------------------

publicStringreplace(charoldChar,charnewChar){

if(oldChar != newChar) {

intlen = count;

inti = -1;

char[] val = value;/* avoid getfield opcode */

intoff = offset;/* avoid getfield opcode */

while(++i < len) {

if(val[off + i] == oldChar) {

break;

}

? ? }

if(i < len) {

charbuf[] =newchar[len];

for(intj =0; j < i ; j++) {

? ? buf[j] = val[off+j];

}

while(i < len) {

charc = val[off + i];

? ? buf[i] = (c == oldChar) ? newChar : c;

? ? i++;

}

returnnewString(0, len, buf);

? ? }

}

returnthis;

? ? }

publicStringreplaceAll(String regex, String replacement){

returnPattern.compile(regex).matcher(this).replaceAll(replacement);

? ? }

前者參數為兩個字符串蚜印,用newChar替換原串里的所有oldChar。

后者從第一個參數可以看出留量,需要替換的東西可以用正則表達式描述窄赋。例子如下:

packagecom.xtfggef.string;

publicclassReplaceTest{

publicstaticvoidmain(String[] args){

String s ="hello world";

String s1 = s.replace("l","d");

System.out.println(s1);

String s2 ="a78e5opx587";

String s3 = s2.replaceAll("[0-9]","");//用空串替換原串里所有的0-9的數字

System.out.println(s3);

}

}

輸出:

heddo wordd

aeopx

-------------public String[] split(String regex)-----------

該方法用于分割字符串,得到一個String類型的數組楼熄,根據regex可知忆绰,參數是個正則表達式。請看下面的例子:

packagecom.xtfggef.string;

publicclassSpiltTest{

publicstaticvoidmain(String[] args){

String s ="hello world";

String s1 ="hello.worldd";

String[] s2 = s.split(" ");

String[] s3 = s1.split("\\.");

for(inti=0; i

System.out.print(s2[i]+" ");

}

System.out.println();

for(intj=0; j

System.out.print(s3[j]+" ");

}

}

}

輸出:

hello world?

hello worldd

關于spilt()的其他重載方法可岂,可參見JDK的String類的實現错敢。

spilt()需要注意的事項,就是當分隔符為 . 的話缕粹,處理起來不一樣稚茅,必須寫成\\.因為.是正則表達式里的一個特殊符號,必須進行轉義

--------------------public native String intern();--------------------(補充知識點:經網友java2000_wl提醒平斩,特此補充亚享,歡迎廣大讀者及時提出建議,我必將虛心接受;婷妗)

intern()方法和前面說的equals()方法關系密切欺税,從public native String intern()看出,它是Java的本地方法揭璃,我們先來看看Java文檔里的描述:

Returns a canonical representationforthe string object.

? ? A pool of strings, initially empty, is maintained privately by the

classString.Whentheinternmethodisinvoked,ifthepoolalreadycontainsa

stringequaltothisStringobjectasdeterminedby

theequals(Object)method,thenthestringfromthepoolis

returned.Otherwise,thisStringobjectisaddedtothe

poolandareferencetothisStringobjectisreturned.

Itfollowsthatforanytwostringssandt,

s.intern()==t.intern() istrueifand onlyifs.equals(t) istrue.

? ? All literal strings and string-valued constant expressions are interned.

@returna string that has the same contents asthisstring, but is

? ? guaranteed to be from a pool of unique strings.

意思就是說晚凿,返回字符串一個規(guī)范的表示。進一步解釋:有兩個字符串s和t瘦馍,s.equals(t),則s.intern()==t.intern().舉個例子:

publicclassStringTest{

publicstaticvoidmain(String[] args){

String s =newString("abc");

String s1 ="abc";

String s2 ="abc";

? ? String s3 = s.intern();

System.out.println(s == s1);//false

System.out.println(s == s2);//false

System.out.println(s == s3);//false

System.out.println(s1 == s3);//true? ? ?

}

}

輸出結果如注釋所示歼秽,前兩個結果前面已經說的很清楚了,現在拿最后一個說明情组,首先看看s3 = s.intern()這句哲银,當調用s.intern()這句的時候扛吞,先去字符串常量池中找,是否有abc這個串荆责,如果沒有,則新增亚脆,同時返回引用做院,如果有,則返回已經存在的引用濒持,此處s1和s2都指向常量池中的abc對象键耕,所以此處是存在的,調用s.intern()后柑营,s3和s1屈雄、s2指向同一個對象,所以s1==s3返回的是true官套。

intern()做到了一個很不尋常的行為:在運行期動態(tài)的在方法區(qū)創(chuàng)建對象酒奶,一般只有像new關鍵字可以在運行期在堆上面創(chuàng)建對象,所以此處比較特殊奶赔。屬于及時編譯的概念惋嚎。

一般常見的字符串處理函數就這些,其它的還有很多站刑,就不一一列舉另伍。

3、一些常見的問題绞旅,處理結果

在我們日常的開發(fā)中摆尝,總會遇到一些問題,在此我總結一下:

String s = "123" + "456"內存中產生幾個字符串對象因悲?

這是個比較有爭議的問題堕汞,面試的時候,老師還挺喜歡問囤捻,論壇上大家說幾個的也有臼朗,我給大家分析一下,因為我們前面有提到Java字符串的緩存機制蝎土,編譯器在編譯的時候會進行優(yōu)化视哑,所以在編譯的過程中123和456被合成了一個字符串"123456",因此誊涯,如果緩存池中目前沒有123456這個對象挡毅,那么會產生一個,即""123456"暴构,且棧中產生一個引用s指向它跪呈,如果緩存池中已經存在"123456"段磨,那么將產生0個對象,直接用s指向它耗绿。

如果spilt()函數的參數在要分割的字符串中沒有怎么辦苹支?如String s = "helloworld" ,我現在調用String[] s2 = s.spilt("abc"),返回什么误阻?

這個問題是我曾經參加紅帽軟件面試的時候遇到的相關題债蜜,當時懵了,像這樣的題目究反,如果不親自遇到過寻定,或者看過源代碼,很難準確的寫出來精耐。

做一個簡單的測試狼速,就可以看得出來:

packagecom.xtfggef.string;

publicclassStringSpilt{

publicstaticvoidmain(String[] args){

String s ="helloworld";

String[] s2 = s.split("abc");

for(inti =0; i < s2.length; i++) {

System.out.println(s2[i] +" "+ i);

}

}

}

輸出:helloworld 0

說明當遇到源字符串中沒有的字符時,會把它整個串放入到數組中卦停。spilt()的內部實現還是挺復雜的向胡,多層嵌套,不便于放到這兒分析沫浆。

關于字符串自動類型轉換分析

首先看一下題的類型:

inti =2;

intj =3;

String s ="9";

System.out.println(i+j+s);

System.out.println("-----------------------");

System.out.println(i+s+j);

以上運算各輸出什么捷枯?不妨猜猜

59

-----------------------

293

首先i+j=5,然后5和9自然連接专执,這里涉及到java的自動類型轉換淮捆,此處int型的直接轉成String類型的。第二個依次連接本股,都轉化為String類型的了攀痊。

補充(細節(jié)):看下面的程序:

String s ="ab";

String s1 ="a";

String s2 = s1 +"b";

String s3 ="ab";

System.out.println(s == s2);//false

System.out.println(s2 == s3);//false

System.out.println(s2.hashCode() == s3.hashCode());

String s4 ="ad";

String s5 ="a"+"d";

String s6 ="ad";

System.out.println(s4 == s5);//true

System.out.println(s4 == s6);//true

此處主要是想說明:s1+"b"和"a"+"b"的不同,再看一段代碼:

? ? ? ? ? ? ? ? ? System.out.println(s1.hashCode());

System.out.println(s2.hashCode());

System.out.println(s3.hashCode());

System.out.println(s4.hashCode());

System.out.println(s5.hashCode());

輸出:

97

3105

3105

3107

3107

說明s1+"b"的過程創(chuàng)建了新的對象拄显,所以地址不一樣了苟径。所以用==比較的話,返回的是false躬审。

此處繼續(xù)補充:為什么s1+"b"會產生新的對象棘街?而沒有去常量池查找是否已經存在ab對象,以致于s==s2返回false承边。因為我們說過常量池(下文會講常量池)是在編譯期確定好的遭殉,所以如果我們的語句時String s5 = "ab"的話,這個是在編譯期確定的博助,會去常量池查找险污,而此處我們的語句時s2 = s1+"b",s2的值只有在運行期才能確定,所以不會去常量池查找蛔糯,也就是產生新串拯腮。再次提問:那么這里s2的值是在哪兒分配的呢?堆蚁飒、JVM棧還是運行時常量池动壤?正確回答:s2在堆上分配,因為+的內部實現是用StringBuilder來實現的飒箭。String s2 =?s1+"b" 內部是這樣實現的:String s2 = new StringBuilder(s1).append("b").toString();所以是在堆上來分配的

此處網友cowmich補充:調用s2.hashCode() == s3.hashCode()返回true狼电。我解釋下:

==比較的是他們的地址,s1+"b"會產生一個新的串弦蹂,所以和s和s2用==比,返回false强窖,如果用equals的話凸椿,返回肯定是true,因為equals()比較的是對象的內容(String類是這樣的)翅溺。至于hashCode脑漫,是這樣的:如果沒有重寫Object的hashCode(),那么如果對象調用equals()放回true咙崎,則這兩個對象調用hashCode()后返回的整數一定相等优幸。此處繼續(xù)補充:對于Object類而言,原生的equals()方法褪猛,必須兩個對象的地址和內容都一樣才返回true网杆,同時Object類原生的hashCode()是參照對象的地址和內容根據一定的算法生產的。所以原生的hashCode()只有調用equals()返回true才相等伊滋。而String類不同碳却,String類重寫了Object的equals(),放松了條件笑旺,只要對象地址或者內容相等就返回true昼浦,我們看看源碼:

publicbooleanequals(Object anObject){

if(this== anObject) {

returntrue;

}

if(anObjectinstanceofString) {

? ? String anotherString = (String)anObject;

intn = count;

if(n == anotherString.count) {

charv1[] = value;

charv2[] = anotherString.value;

inti = offset;

intj = anotherString.offset;

while(n-- !=0) {

if(v1[i++] != v2[j++])

returnfalse;

}

returntrue;

? ? }

}

returnfalse;

? ? }

同時,String類重寫了hashCode()方法筒主,只要內容相等关噪,則調用hashCode返回的整數值也相等,所以此處:s3和s2雖然地址不等乌妙,但是內容相等使兔,所以會有:s2.hashCode() == s3.hashCode()返回true。但是這句話反過來講就不一定成立了冠胯,因為畢竟hashCode()只是一種算法火诸。繼續(xù)補充:剛剛說了Object類和String類,此處補充下Integer類:Integer類荠察,返回的哈希碼就是Integer對象里所包含的那個整數的數值置蜀,例如Integer a=new Integer(50),則a.hashCode的值就是50 奈搜。由此可見,2個一樣大小的Integer對象盯荤,返回的哈希碼也一樣馋吗。

補充:應網友 ?KingBoxing ?的要求,我做下關于常量池秋秤、字符串常量池宏粤、運行時常量池的介紹:

常量池一般就是指字符串常量池,是用來做字符串緩存的一種機制灼卢,當我們在程序中寫了形如String s = "abc"這樣的語句后绍哎,JVM會在棧上為我們分配空間,存放變量s和對象”abc“鞋真,當我們再次需要abc對象時崇堰,如果我們寫下:String s1 = "abc"的語句時,JVM會先去常量池中找涩咖,如果不存在海诲,則新創(chuàng)建一個對象。如果存在檩互,則直接將s1指向之前的對象”abc“特幔,此時,如果我們用==來判斷的話闸昨,返回的true蚯斯。這樣做的好處就是節(jié)省內存,系統(tǒng)響應的速度加快零院,(因為省去了對象的創(chuàng)建時間)這也是緩存系統(tǒng)存在的原因溉跃。常量池是針對在編譯期間就確定下來的常量而言的,如上所說的String類的一些對象告抄。但是撰茎,當類被加載后,常量池會被搬到方法區(qū)的運行時常量池打洼,此時就不再是靜態(tài)的了龄糊,那么是不是就不能向常量池中添加新的內容了呢(因為我們剛剛說過,常量池是在編譯期確定好的)募疮?答案是否定的炫惩,我們依然可以在運行時向常量池添加內容!這就是我們說過的String類有個方法叫intern()阿浓,它可以在運行時將新的常量放于常量池他嚷。因為我在上文中已經詳細介紹過intern(),此處不再贅述!

2筋蓖、StringBuffer卸耘、StringBuilder

?????1、初始化

????????StringBuffer和StringBuilder就是所謂的可變字符串類粘咖,共四個構造方法:

? ??????StringBuffer()

? ??????public StringBuffer(int paramInt)

? ??????public StringBuffer(String paramString)

? ??????public StringBuffer(CharSequence paramCharSequence)

????????觀察其源碼發(fā)現蚣抗,使用StringBuffer()時,默認開辟16個字符的長度的空間瓮下,使用public StringBuffer(int paramInt)時開辟指定大小的空間翰铡,使用public StringBuffer(String paramString)時,開辟paramString.length+16大小的空間讽坏。都是調用父類的構造器super()來開辟內存锭魔。這方面StringBuffer和StringBuilder都一樣,且都實現AbstractStringBuilder類路呜。

?????2赂毯、主要方法

????????二者幾乎沒什么區(qū)別,基本都是在調用父類的各個方法拣宰,一個重要的區(qū)別就是StringBuffer是線程安全的,內部的大多數方法前面都有關鍵字synchronized烦感,這樣就會有一定的性能消耗巡社,StringBuilder是非線程安全的,所以效率要高些手趣。

publicstaticvoidmain(String[] args)throwsException{

String string ="0";

intn =10000;

longbegin = System.currentTimeMillis();

for(inti =1; i < n; i++) {

string += i;

}

longend = System.currentTimeMillis();

longbetween = end - begin;

System.out.println("使用String類耗時:"+ between+"ms");

intn1 =10000;

StringBuffer sb =newStringBuffer("0");

longbegin1 = System.currentTimeMillis();

for(intj =1; j < n1; j++) {

sb.append(j);

}

longend1 = System.currentTimeMillis();

longbetween1 = end1 - begin1;

System.out.println("使用StringBuffer類耗時:"+ between1+"ms");

intn2 =10000;

StringBuilder sb2 =newStringBuilder("0");

longbegin2 = System.currentTimeMillis();

for(intk =1; k < n2; k++) {

sb2.append(k);

}

longend2 = System.currentTimeMillis();

longbetween2 = end2 - begin2;

System.out.println("使用StringBuilder類耗時:"+ between2+"ms");

}

輸出:

使用String類耗時:982ms

使用StringBuffer類耗時:2ms

使用StringBuilder類耗時:1ms

雖然這個數字每次執(zhí)行都不一樣晌该,而且每個機子的情況也不一樣,但是有幾點是確定的绿渣,String類消耗的明顯比另外兩個多得多朝群。還有一點就是,StringBuffer要比StringBuilder消耗的多中符,盡管相差不明顯姜胖。

接下來介紹一些常用的方法。

-----------------------public synchronized int length()--------------------------

-------------------------public synchronized int capacity()---------------------------

二者都是獲取字符串的長度淀散,length()獲取的是當前字符串的長度右莱,capacity()獲取的是當前緩沖區(qū)的大小。舉個簡單的例子:

StringBuffer sb =newStringBuffer();

System.out.println(sb.length());;

System.out.println(sb.capacity());

輸出:

0

16

StringBuffer sb =newStringBuffer("hello");

System.out.println(sb.length());;

System.out.println(sb.capacity());

輸出:

5

21

因為默認分配16個字符大小的空間档插,所以不難解釋上面的結果慢蜓。

------------------public boolean equals(Object paramObject)---------------------

StringBuffer sb =newStringBuffer("hello");

StringBuffer sb2 =newStringBuffer("hello");

System.out.println(sb.equals(sb2));

以上程序輸出false,是不是有點驚訝郭膛?記得之前我們的文章說過晨抡,equals()比較的是字符串的內容,按理說此處應該輸出的是true才對。

究其原因耘柱,String類重寫了Object的equals()如捅,所以只需要看內容是否相等即可,但是StringBuffer沒有重寫equals()帆谍,此處的equals()仍然是調用的Object類的伪朽,所以,調用StringBuffer類的equals()汛蝙,只有地址和內容都相等的字符串烈涮,結果才會返回true。

另外StringBuffer有一系列追加窖剑、插入坚洽、刪除字符串的方法,首先append()西土,就是在原來的字符串后面直接追加一個新的串讶舰,和String類相比有明顯的好處:

String類在追加的時候,源字符串不變(這就是為什么說String是不可變的字符串類型)需了,和新串連接后跳昼,重新開辟一個內存。這樣就會造成每次連接一個新串后肋乍,都會讓之前的串報廢鹅颊,因此也造成了不可避免的內存泄露。

//append()

StringBuffer sb =newStringBuffer("helloworld, ");

sb.append("I'm ").append("erqing ").append("who ").append("are you ?");

System.out.println(sb);

//public synchronized StringBuffer insert(int paramInt, Object paramObject)

sb.insert(12,/*9*/"nice! ");

System.out.println(sb);

//public synchronized StringBuffer reverse()

sb.reverse();

System.out.println(sb);

sb.reverse();

System.out.println(sb);

//public synchronized StringBuffer delete(int paramInt1, int paramInt2)

//public synchronized StringBuffer deleteCharAt(int paramInt)

sb.delete(12,18);

System.out.println(sb);

sb.deleteCharAt(5);

System.out.println(sb);

輸出:

helloworld, I'm erqing who are you ?

helloworld, nice! I'm erqing who are you ?

? uoy era ohw gniqre m'I !ecin ,dlrowolleh

helloworld, nice! I'm erqing who are you ?

helloworld, I'm erqing who are you ?

helloorld, I'm erqing who are you ?

-----------------public synchronized void trimToSize()---------------------

該方法用于將多余的緩沖區(qū)空間釋放出來墓造。

StringBuffer sb =newStringBuffer("hello erqing");

System.out.println("length:"+sb.length());

System.out.println("capacity:"+sb.capacity());

sb.trimToSize();

System.out.println("trimTosize:"+sb.capacity());

輸出:

length:12

capacity:28

trimTosize:12

StringBuffer類還有很多方法堪伍,關于字符查找,截取觅闽,替換方面的方法帝雇,有興趣的童鞋可以去研究研究源碼,定會學到不少知識蛉拙!

三尸闸、字符串處理類StringTokenizer

StringTokenizer是java.util包下的一個類,用來對字符串做簡單的處理刘离。

舉個簡單的例子:

String s ="Tonight is the answer !";

StringTokenizer st =newStringTokenizer(s," ");

intcount = st.countTokens();

System.out.println("個數為:"+count);

while(st.hasMoreTokens()) {

String token = st.nextToken();

System.out.println(token);

}

輸出:

個數為:5

Tonight

is

the

answer

!


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末室叉,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子硫惕,更是在濱河造成了極大的恐慌茧痕,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件恼除,死亡現場離奇詭異踪旷,居然都是意外死亡曼氛,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進店門令野,熙熙樓的掌柜王于貴愁眉苦臉地迎上來舀患,“玉大人,你說我怎么就攤上這事气破×那常” “怎么了?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵现使,是天一觀的道長低匙。 經常有香客問我,道長碳锈,這世上最難降的妖魔是什么顽冶? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮售碳,結果婚禮上强重,老公的妹妹穿的比我還像新娘。我一直安慰自己贸人,他們只是感情好间景,可當我...
    茶點故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著艺智,像睡著了一般拱燃。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上力惯,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天,我揣著相機與錄音召嘶,去河邊找鬼父晶。 笑死,一個胖子當著我的面吹牛弄跌,可吹牛的內容都是我干的甲喝。 我是一名探鬼主播,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼铛只,長吁一口氣:“原來是場噩夢啊……” “哼埠胖!你這毒婦竟也來了?” 一聲冷哼從身側響起淳玩,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤直撤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蜕着,有當地人在樹林里發(fā)現了一具尸體谋竖,經...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡红柱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了蓖乘。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锤悄。...
    茶點故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖嘉抒,靈堂內的尸體忽然破棺而出零聚,到底是詐尸還是另有隱情,我是刑警寧澤些侍,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布隶症,位于F島的核電站,受9級特大地震影響娩梨,放射性物質發(fā)生泄漏沿腰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一狈定、第九天 我趴在偏房一處隱蔽的房頂上張望颂龙。 院中可真熱鬧,春花似錦纽什、人聲如沸措嵌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽企巢。三九已至,卻和暖如春让蕾,著一層夾襖步出監(jiān)牢的瞬間浪规,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工探孝, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留笋婿,地道東北人。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓顿颅,卻偏偏與公主長得像缸濒,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子粱腻,可洞房花燭夜當晚...
    茶點故事閱讀 45,573評論 2 359

推薦閱讀更多精彩內容

  • package cn.itcast_01;/* 字符串:就是由多個字符組成的一串數據庇配。也可以看成是一個字符數組。 ...
    蛋炒飯_By閱讀 590評論 0 0
  • 1. Java基礎部分 基礎部分的順序:基本語法绍些,類相關的語法捞慌,內部類的語法,繼承相關的語法柬批,異常的語法卿闹,線程的語...
    子非魚_t_閱讀 31,664評論 18 399
  • java中String的常用方法 1揭糕、length()字符串的長度 例:char chars[]={'a','b'...
    赤赤有名閱讀 2,063評論 0 10
  • 從網上復制的,看別人的比較全面锻霎,自己搬過來著角,方便以后查找。原鏈接:https://www.cnblogs.com/...
    lxtyp閱讀 1,348評論 0 9
  • 今天過得很充實旋恼,早上卸了一車的電視機吏口,有6000/8000/610的,看到我們康佳的大倉庫冰更,都是滿滿的錢啊 下午去...
    阿立立哥閱讀 76評論 0 0