數(shù)據(jù)的傳遞
1.?實(shí)參的數(shù)目命斧、數(shù)據(jù)類型和次序必須和所調(diào)用的方法聲明的形式參數(shù)列表匹配寡壮。
? ? ? 2.?return 語句終止方法的運(yùn)行并指定要返回的數(shù)據(jù)和敬。
? ? ? 3.?Java中進(jìn)行方法調(diào)用中傳遞參數(shù)時裳瘪,遵循值傳遞的原則(傳遞的都是數(shù)據(jù)的副本):
? ? ? 4.?基本類型傳遞的是該數(shù)據(jù)值的copy值。
? ? ? 5.?引用類型傳遞的是該對象引用的copy值橙凳,但指向的是同一個對象蕾殴。
遞歸的調(diào)用:
自己調(diào)用自己”,一個使用遞歸技術(shù)的方法將會直接或者間接的調(diào)用自己岛啸。
遞歸結(jié)構(gòu)包括兩個部分:
? ? ??1.定義遞歸頭钓觉。解答:什么時候不調(diào)用自身方法。如果沒有頭坚踩,將陷入死循環(huán)荡灾,也就是遞歸的結(jié)束條件。
? ? ??2.遞歸體瞬铸。解答:什么時候需要調(diào)用自身方法卧晓。
任何能用遞歸解決的問題也能使用迭代解決。當(dāng)遞歸方法可以更加自然地反映問題赴捞,并且易于理解和調(diào)試逼裆,并且不強(qiáng)調(diào)效率問題時,可以采用遞歸;
? ? ??在要求高性能的情況下盡量避免使用遞歸赦政,遞歸調(diào)用既花時間又耗內(nèi)存胜宇。
面向?qū)ο蟮膬?nèi)存:
java虛擬機(jī)的內(nèi)存可以分為三個區(qū)域: 棧stack ,堆heap,方法區(qū)method area
棧stack:
1, 棧描述的是方法執(zhí)行的內(nèi)存模型,每個方法被調(diào)用都會創(chuàng)建一個棧幀(存儲局部變量恢着,操作數(shù)桐愉,方法出口等)
2. JVM為每個線程創(chuàng)建一個棧,用于存放該線程執(zhí)行方法的信息(實(shí)際參數(shù)掰派,局部變量等)
3. 棧的存儲特性私有从诲,不能實(shí)現(xiàn)線程間共享
4.棧的存儲特性是“先進(jìn)后出,后進(jìn)先出”
5. 棧是由系統(tǒng)自動分配靡羡,速度快系洛, 棧是一個連續(xù)的內(nèi)存空間?
堆:
1 堆是用于存儲創(chuàng)建好的對象和數(shù)組(數(shù)組也是對象)
2. JVM只有一個堆俊性,被所有的線程共享
3. 堆是一個不連續(xù)的內(nèi)存空間,分配靈活描扯,速度慢
方法區(qū)(靜態(tài)區(qū)):
1. JVM只有一個方法區(qū)定页,被所有線程共享,
2.方法區(qū)實(shí)際也是堆绽诚,只是用于存儲類典徊,常量相關(guān)信息
3.用來存放程序中永遠(yuǎn)是不變或者唯一的內(nèi)容(類信息,class對象恩够,靜態(tài)變量卒落,字符串常量等)
思考:本人思考如何實(shí)現(xiàn)一個100的階乘 100!因為java的基本類型是長度是確定的蜂桶,必定會出現(xiàn)溢出情況导绷。
基本數(shù)據(jù)類型是放在棧中還是放在堆中,這取決于基本類型聲明的位置屎飘。
?一:在方法中聲明的變量妥曲,即該變量是局部變量,每當(dāng)程序調(diào)用方法時钦购,系統(tǒng)都會為該方法建立一個方法棧檐盟,其所在方法中聲明的變量就放在方法棧中,當(dāng)方法結(jié)束系統(tǒng)會釋放方法棧押桃,其對應(yīng)在該方法中聲明的變量隨著棧的銷毀而結(jié)束葵萎,這就局部變量只能在方法中有效的原因
????? 在方法中聲明的變量可以是基本類型的變量,也可以是引用類型的變量唱凯。
?????? ? (1)當(dāng)聲明是基本類型的變量的時羡忘,其變量名及值(變量名及值是兩個概念)是放在方法棧中
??????? ?(2)當(dāng)聲明的是引用變量時,所聲明的變量(該變量實(shí)際上是在方法中存儲的是內(nèi)存地址值)是放在方法的棧中磕昼,該變量所指向的對象是放在堆類存中的卷雕。
?? 二:在類中聲明的變量是成員變量,也叫全局變量票从,放在堆中的(因為全局變量不會隨著某個方法執(zhí)行結(jié)束而銷毀)漫雕。
?????? 同樣在類中聲明的變量即可是基本類型的變量 也可是引用類型的變量
???????(1)當(dāng)聲明的是基本類型的變量其變量名及其值放在堆內(nèi)存中的
???????(2)引用類型時,其聲明的變量仍然會存儲一個內(nèi)存地址值峰鄙,該內(nèi)存地址值指向所引用的對象浸间。引用變量名和對應(yīng)的對象仍然存儲在相應(yīng)的堆中
根據(jù)這個,我將基本棧中的數(shù)據(jù)存放在堆中吟榴,用Array 來存儲就可以解決這個問題
public class MulTest {
public static void main(String[] args) {
// System.out.println(capacity(9000));
// int num[] = numToarray(980);
// for(int i =0 ; i<num.length;i++){
// System.out.println("打印的第"+i+"位"+num[i]);
// }
int fnum[] = fn(4096);
printNum(fnum);
}
/**
* 確定一個數(shù)多少位魁蒜,為方后邊能給一個數(shù)組大小的空間
* @param n
* @return
*/
static int capacity(int num) {
int count = 0;
while (num > 0) {
num = num / 10;? //比如說240 240/10 =24 count=1 ;24/10=2 count=2; 2/10=0 count=3
count++;
}
return count;
}
/**
* 將整型數(shù)字轉(zhuǎn)換為數(shù)組
* numtoarray[0]= 0 ;第二次 num=240/10=24? ;numtoarray[1]=24%10=4;numtoarray[2]=2%10=2
* @param num
* @return
*/
static int [] numToarray(int num){
int len = capacity(num);
int numtoarray[] = new int [len];
for(int i=0;i<len;i++){
numtoarray[i] = num %10;?
num = num/10;
}
return numtoarray;
}
/**
*
* 數(shù)組相乘,每一位數(shù)組與另一位數(shù)組相乘
*
*/
public static int[] mul(int []a, int []b){
int temp = 0;
int alength =a.length;
int blength = b.length;
int retult[] = new int[alength+blength] ;
for (int i = 0; i <= alength+blength - 1; i++) {
retult[i] = 0;
}
for (int i = 0; i <= alength - 1; i++) {
for (int j = 0; j <= blength - 1; j++) {
temp = a[i] * b[j];
retult[j + i] += temp;
}
}
int len = retult.length;
int temp2;
for (int i = 0; i <= retult.length-2; i++) {
temp2 = retult[i];
if (temp2 >= 10) {
retult[i] = temp2 % 10;
retult[i + 1] = retult[i + 1] + temp2 / 10;
}
}
return retult;
}
/**
* 階乘
*/
public static int[] fn(int n){
int retult[] = {1};
int bit = capacity(n);
// System.out.println("返回多少位:"+n);
System.out.println("開始:"+n+"!? = ");
int temp[] = new int[bit];
for(int i = 1;i<=n;i++){
temp? =numToarray(i);
retult = mul(temp,retult);
}
return retult;
}
static void printNum(int a[]) {
int len = a.length;
int f = 0;
int count = 0;
for (int i = len - 1; i >= 0; i--) {
if (a[i] > 0)
f = 1;
if (f > 0) {
System.out.print(a[i]);
++count;
}
}
System.out.println();
System.out.println("----------------------------");
System.out.println("一共多少位:"+count);
}
}
構(gòu)造器:
1. 通過New關(guān)鍵字調(diào)用,
2.構(gòu)造器雖然沒有返回值兜看,但是不能定義返回值類型(返回值類型肯定是本類)锥咸,不能在構(gòu)造器里使用return返回某個值,但可以使用return結(jié)束
3.如果我們沒有定義構(gòu)造器铣减,編譯器會幫我們自動定義一個無參的構(gòu)造器
如果已定義則編譯器不會自動創(chuàng)建
4. 構(gòu)造器的方法名必須和類名一致她君。
內(nèi)存泄漏:
· 創(chuàng)建大量無用對象
比如脚作,我們在需要大量拼接字符串時葫哗,使用了String而不是StringBuilder。
String?str?=?"";
for?(int?i?=?0;?i?<?10000;?i++)?{???
????str?+=?i;?????//相當(dāng)于產(chǎn)生了10000個String對象
}
靜態(tài)集合類的使用
像HashMap球涛、Vector劣针、List等的使用最容易出現(xiàn)內(nèi)存泄露,這些靜態(tài)變量的生命周期和應(yīng)用程序一致亿扁,所有的對象Object也不能被釋放捺典。
· 各種連接對象(IO流對象、數(shù)據(jù)庫連接對象从祝、網(wǎng)絡(luò)連接對象)未關(guān)閉
IO流對象襟己、數(shù)據(jù)庫連接對象、網(wǎng)絡(luò)連接對象等連接對象屬于物理連接牍陌,和硬盤或者網(wǎng)絡(luò)連接擎浴,不使用的時候一定要關(guān)閉。
· 監(jiān)聽器的使用
釋放對象時毒涧,沒有刪除相應(yīng)的監(jiān)聽器贮预。
要點(diǎn):
1. 程序員無權(quán)調(diào)用垃圾回收器。
2. 程序員可以調(diào)用System.gc()契讲,該方法只是通知JVM仿吞,并不是運(yùn)行垃圾回收器。盡量少用捡偏,會申請啟動Full GC唤冈,成本高,影響系統(tǒng)性能银伟。
3. finalize方法务傲,是Java提供給程序員用來釋放對象或資源的方法,但是盡量少用枣申。
垃圾回收過程:
? ? 1售葡、新創(chuàng)建的對象,絕大多數(shù)都會存儲在Eden中忠藤,
? ? 2挟伙、當(dāng)Eden滿了(達(dá)到一定比例)不能創(chuàng)建新對象,則觸發(fā)垃圾回收(GC),將無用對象清理掉尖阔,
???????????然后剩余對象復(fù)制到某個Survivor中贮缅,如S1,同時清空Eden區(qū)
? ? 3介却、當(dāng)Eden區(qū)再次滿了谴供,會將S1中的不能清空的對象存到另外一個Survivor中,如S2齿坷,
? ? ? ? ??同時將Eden區(qū)中的不能清空的對象桂肌,也復(fù)制到S1中,保證Eden和S1永淌,均被清空崎场。
? ? 4、重復(fù)多次(默認(rèn)15次)Survivor中沒有被清理的對象遂蛀,則會復(fù)制到老年代Old(Tenured)區(qū)中谭跨,
? ? 5、當(dāng)Old區(qū)滿了李滴,則會觸發(fā)一個一次完整地垃圾回收(FullGC)螃宙,之前新生代的垃圾回收稱為(minorGC)
在對JVM調(diào)優(yōu)的過程中,很大一部分工作就是對于Full GC的調(diào)節(jié)所坯。有如下原因可能導(dǎo)致Full GC:
1.年老代(Tenured)被寫滿
2.持久代(Perm)被寫滿
3.System.gc()被顯式調(diào)用(程序建議GC啟動谆扎,不是調(diào)用GC)
4.上一次GC之后Heap的各域分配策略動態(tài)變化
多態(tài):
1.多態(tài)是方法的多態(tài),不是屬性的多態(tài)(多態(tài)與屬性無關(guān))
2.多態(tài)的存在要有3個必要的條件:繼承包竹,方法重寫燕酷,父類引用指向子類對象。
3.父類引用指向子類重寫的方法周瞎,此時多態(tài)就出現(xiàn)了苗缩。
package com.test;
class Animal {
? ? public? void shout() {
? ? ? ? System.out.println("叫了一聲!");
? ? }
}
class Dog extends Animal {
? ? public void shout() {
? ? ? ? System.out.println("旺旺旺声诸!");
? ? }
? ? public void seeDoor() {
? ? ? ? System.out.println("看門中....");
? ? }
}
class Cat extends Animal {
? ? public void shout() {
? ? ? ? System.out.println("喵喵喵喵酱讶!");
? ? }
}
public class TestPolym {
? ? public static void main(String[] args) {
? ? ? ? Animal a1 = new Cat(); // 向上可以自動轉(zhuǎn)型
? ? ? ? //傳的具體是哪一個類就調(diào)用哪一個類的方法。大大提高了程序的可擴(kuò)展性彼乌。
? ? ? ? animalCry(a1);
? ? ? ? Animal a2 = new Dog();
? ? ? ? animalCry(a2);//a2為編譯類型泻肯,Dog對象才是運(yùn)行時類型。
? ? ? ? //編寫程序時慰照,如果想調(diào)用運(yùn)行時類型的方法灶挟,只能進(jìn)行強(qiáng)制類型轉(zhuǎn)換。
? ? ? ? // 否則通不過編譯器的檢查毒租。
? ? ? ? Dog dog = (Dog)a2;//向下需要強(qiáng)制類型轉(zhuǎn)換
? ? ? ? dog.seeDoor();
? ? }
? ? // 有了多態(tài)稚铣,只需要讓增加的這個類繼承Animal類就可以了。
? ? static void animalCry(Animal a) {
? ? ? ? a.shout();
? ? }
? ? /* 如果沒有多態(tài),我們這里需要寫很多重載的方法惕医。
? ? * 每增加一種動物耕漱,就需要重載一種動物的喊叫方法。非常麻煩抬伺。
? ? static void animalCry(Dog d) {
? ? ? ? d.shout();
? ? }
? ? static void animalCry(Cat c) {
? ? ? ? c.shout();
? ? }*/
}
package com.test;
public class HttpServer {
public void service(){
System.out.println("httpserver service");
doGet();
}
public void doGet(){
System.out.println("httpserver doGet");
}
}
package com.test;
public class MyServlet extends HttpServer{//繼承
//方法重寫
@Override
public void doGet() {
System.out.println("MyServlet doGet");
}
public static void main(String[] args) {
? ? ? ? ? //父類引用指向子類重寫的方法螟够,此時多態(tài)就出現(xiàn)了
HttpServer MyServlet = new MyServlet();//父類引用指向子類對象
MyServlet.service();
}
}
final關(guān)鍵字的作用:
? ? ? 1. 修飾變量: 被他修飾的變量不可改變。一旦賦了初值峡钓,就不能被重新賦值妓笙。
final??int???MAX_SPEED?=?120;
? ? ??2. 修飾方法:該方法不可被子類重寫。但是可以被重載!
final??void??study(){}
? ? ??3. 修飾類: 修飾的類不能被繼承椒楣。比如:Math给郊、String等。
抽象類:
抽象類是一種模板,抽象類為所有的子類提供一個通用的模板芜壁,子類可以在這個模板的基礎(chǔ)上進(jìn)行擴(kuò)展
通過抽象類梳庆,可以避免子類設(shè)計的隨意性,通過抽象類奔则,我們就可以做到嚴(yán)格限制子類餓設(shè)計,是子類之間更加通用。
要點(diǎn):
1. 有抽象方法的類只能定義成抽象類
2. 抽象類不能實(shí)例化煌寇,及不能用new來實(shí)例化抽象類
3. 抽象方法可以包含屬性、方法逾雄、構(gòu)造方法阀溶。但是構(gòu)造方法不能用來new實(shí)例,只能被子類調(diào)用鸦泳。
4. 抽象方法只能用來繼承
5. 抽象方法必須被子類實(shí)現(xiàn)
接口:
[訪問修飾符]??interface?接口名???[extends??父接口1银锻,父接口2…]??{
常量定義;??
方法定義做鹰;
}
interface MyInterface4extends MyInterface,MyInterface2,MyInterface3{
}
定義接口的詳細(xì)說明:
? ? ? 1. 訪問修飾符:只能是public或默認(rèn)击纬。
? ? ??2. 接口名:和類名采用相同命名機(jī)制。
? ? ??3. extends:接口可以多繼承钾麸。
? ? ??4. 常量:接口中的屬性只能是常量更振,總是:public static final 修飾。不寫也是饭尝。
? ? ??5. 方法:接口中的方法只能是:public abstract肯腕。 省略的話,也是public abstract钥平。
要點(diǎn)
? ? ??1. 子類通過implements來實(shí)現(xiàn)接口中的規(guī)范实撒。
? ? ??2. 接口不能創(chuàng)建實(shí)例,但是可用于聲明引用變量類型。
? ? ??3. 一個類實(shí)現(xiàn)了接口奈惑,必須實(shí)現(xiàn)接口中所有的方法吭净,并且這些方法只能是public的。
? ? ??4. JDK1.7之前肴甸,接口中只能包含靜態(tài)常量寂殉、抽象方法,不能有普通屬性原在、構(gòu)造方法友扰、普通方法。
? ? ??5. JDK1.8后庶柿,接口中包含普通的靜態(tài)方法村怪。
內(nèi)部內(nèi):
?1. 內(nèi)部類提供了更好的封裝。只能讓外部類直接訪問浮庐,不允許同一個包中的其他類直接訪問甚负。
? ? ??2. 內(nèi)部類可以直接訪問外部類的私有屬性,內(nèi)部類被當(dāng)成其外部類的成員审残。 但外部類不能訪問內(nèi)部類的內(nèi)部屬性梭域。
? ? ??3. 接口只是解決了多重繼承的部分問題,而內(nèi)部類使得多重繼承的解決方案變得更加完整搅轿。
內(nèi)部類的使用場合:
? ? ??1. 由于內(nèi)部類提供了更好的封裝特性病涨,并且可以很方便的訪問外部類的屬性。所以璧坟,在只為外部類提供服務(wù)的情況下可以優(yōu)先考慮使用內(nèi)部類既穆。
? ? ??2. ?使用內(nèi)部類間接實(shí)現(xiàn)多繼承:每個內(nèi)部類都能獨(dú)立地繼承一個類或者實(shí)現(xiàn)某些接口,所以無論外部類是否已經(jīng)繼承了某個類或者實(shí)現(xiàn)了某些接口雀鹃,對于內(nèi)部類沒有任何影響幻工。
在Java中內(nèi)部類主要分為成員內(nèi)部類(非靜態(tài)內(nèi)部類、靜態(tài)內(nèi)部類)褐澎、匿名內(nèi)部類会钝、局部內(nèi)部類。
. 成員內(nèi)部類(可以使用private工三、default迁酸、protected、public任意進(jìn)行修飾俭正。 類文件:外部類$內(nèi)部類.class)
a) 非靜態(tài)內(nèi)部類(外部類里使用非靜態(tài)內(nèi)部類和平時使用其他類沒什么不同)
? ? ? i. 非靜態(tài)內(nèi)部類必須寄存在一個外部類對象里奸鬓。因此,如果有一個非靜態(tài)內(nèi)部類對象那么一定存在對應(yīng)的外部類對象掸读。非靜態(tài)內(nèi)部類對象單獨(dú)屬于外部類的某個對象串远。
? ? ??ii. 非靜態(tài)內(nèi)部類可以直接訪問外部類的成員宏多,但是外部類不能直接訪問非靜態(tài)內(nèi)部類成員。
? ? ??iii. 非靜態(tài)內(nèi)部類不能有靜態(tài)方法澡罚、靜態(tài)屬性和靜態(tài)初始化塊伸但。
? ? ??iv. 外部類的靜態(tài)方法、靜態(tài)代碼塊不能訪問非靜態(tài)內(nèi)部類留搔,包括不能使用非靜態(tài)內(nèi)部類定義變量更胖、創(chuàng)建實(shí)例。
? ? ??v. 成員變量訪問要點(diǎn):
? ? ? ??1. 內(nèi)部類里方法的局部變量:變量名隔显。
? ? ? ??2. 內(nèi)部類屬性:this.變量名却妨。
? ? ? ??3. 外部類屬性:外部類名.this.變量名。
注意
? ? ??內(nèi)部類只是一個編譯時概念括眠,一旦我們編譯成功彪标,就會成為完全不同的兩個類。對于一個名為Outer的外部類和其內(nèi)部定義的名為Inner的內(nèi)部類掷豺。編譯完成后會出現(xiàn)Outer.class和Outer$Inner.class兩個類的字節(jié)碼文件捞烟。所以內(nèi)部類是相對獨(dú)立的一種存在,其成員變量/方法名可以和外部類的相同萌业。
String:
String 類是不可改變的坷襟,所以你一旦創(chuàng)建了 String 對象奸柬,那它的值就無法改變了(詳看筆記部分解析)生年。
如果需要對字符串做很多修改,那么應(yīng)該選擇使用 StringBuffer & StringBuilder 類廓奕。
StringBuffer & StringBuilder 類
StringBuilder:線程不安全抱婉,效率高,一般用它桌粉。
StrungBuffer:線程安全蒸绩,但是效率低,一般不用铃肯。
? String一經(jīng)初始化后患亿,就不會再改變其內(nèi)容了。對String字符串的操作實(shí)際上是對其副本(原始拷貝)的操作押逼,原來的字符串一點(diǎn)都沒有改變步藕。比如:
? ? ??String s ="a"; 創(chuàng)建了一個字符串
? ? ??s = s+"b"; 實(shí)際上原來的"a"字符串對象已經(jīng)丟棄了,現(xiàn)在又產(chǎn)生了另一個字符串s+"b"(也就是"ab")挑格。 如果多次執(zhí)行這些改變串內(nèi)容的操作咙冗,會導(dǎo)致大量副本字符串對象存留在內(nèi)存中,降低效率漂彤。如果這樣的操作放到循環(huán)中雾消,會極大影響程序的時間和空間性能灾搏,甚至?xí)斐煞?wù)器的崩潰。
? ? ??相反立润,StringBuilder和StringBuffer類是對原字符串本身操作的狂窑,可以對字符串進(jìn)行修改而不產(chǎn)生副本拷貝或者產(chǎn)生少量的副本。因此可以在循環(huán)中使用桑腮。
String源碼中數(shù)組是final固定的
StringBuilder源碼中繼承了抽象類AbstractStringBuilder蕾域,數(shù)組是可變的
無值的時候初始長度是16
有值的時候,默認(rèn)是參數(shù)的長度+16
超過默認(rèn)長度后到旦,會增加
使用append()方法在字符串后面追加?xùn)|西的時候旨巷,如果長度超過了該字符串存儲空間大小了就需要進(jìn)行擴(kuò)容:構(gòu)建新的存儲空間更大的字符串,將舊的的復(fù)制過去添忘;
再進(jìn)行字符串a(chǎn)ppend添加的時候采呐,會先計算添加后字符串大小,傳入一個方法:ensureCapacityInternal 這個方法進(jìn)行是否擴(kuò)容的判斷搁骑,需要擴(kuò)容就調(diào)用expandCapacity方法進(jìn)行擴(kuò)容:
? ? char[] value;
? ? int count;
嘗試將新容量擴(kuò)為 大懈隆:變成2倍+2,容量如果還不夠仲器,直接擴(kuò)充到需要的容量大忻郝省;
1.String
(1)類定義
public final class String
??? implements java.io.Serializable, Comparable<String>, CharSequence
String類是個final類乏冀,實(shí)現(xiàn)了序列化接口蝶糯,比較大小接口和只讀字符序列接口。String和其他八個基本數(shù)據(jù)類型的包裝類共同為不可變類辆沦。
(2)主要變量
private final char value[];
String類的底層基本是char類型數(shù)組昼捍,String的一些基本方法也是調(diào)用char數(shù)組的基本方法。
(3)主要構(gòu)造方法
public String() {
this.value = "".value;
}
String的默認(rèn)值為"";
(4)String常量池
jvm在啟動時就會加載的一塊空間肢扯,符串常量池的特點(diǎn)就是有且只有一份相同的字面量妒茬,如果有其它相同的字面量,jvm則返回這個字面量的引用蔚晨,如果沒有相同的字面量乍钻,則在字符串常量池創(chuàng)建這個字面量并返回它的引用。通過使用new關(guān)鍵字得對象會放在堆里铭腕,而不會加載到字符串常量池银择,intern()方法能使一個位于堆中的字符串在運(yùn)行期間動態(tài)地加入到字符串常量池中。
(5)字符串拼接
String字符串拼接一般通過用+號實(shí)現(xiàn)谨履,正常情況下有兩種形式:
String a = "ab" + "cd";
此時在JVM在編譯時就認(rèn)為這個加號是沒有用處的欢摄,編譯的時候就直接變成abcd,即使是后面添加多個字符串效率不會比StringBuiler或者StringBuffer慢笋粟。
String a = "ab";
String b = "cd";
String c = a + b;
此時字符串拼接系統(tǒng)會優(yōu)化成通過new StringBuiler怀挠,進(jìn)行兩次append操作析蝴,該操作是在堆中進(jìn)行,如果是進(jìn)行多次拼接绿淋,會產(chǎn)生多個StringBuiler對象闷畸,效率自然會降低,但是如果是在同一行代碼里做拼接操作吞滞,只是額外new了一個StringBuiler對象佑菩,效率也不會慢。
2.StringBuilder
(3)主要構(gòu)造方法
public StringBuilder() {
super(16);
}
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
StringBuiler類的基本構(gòu)造方法是一個容量為16的字符串?dāng)?shù)組
(4)字符串拼接
public StringBuilder append(String str) {
??? super.append(str);
??? return this;
}
public AbstractStringBuilder append(String str) {
??? if (str == null)
??????? return appendNull();
??? int len = str.length();
??? ensureCapacityInternal(count + len);
??? str.getChars(0, len, value, count);
??? count += len;
??? return this;
}
private void ensureCapacityInternal(int minimumCapacity) {
??? // overflow-conscious code
??? if (minimumCapacity - value.length > 0) {
????????? value = Arrays.copyOf(value,
??????????????? newCapacity(minimumCapacity));
??? }
}
內(nèi)部的實(shí)現(xiàn)是通過char數(shù)組操作的裁赠,如果超過容量殿漠,會發(fā)生通過數(shù)組拷貝方式的擴(kuò)容操作。
(5)線程安全性
從上面的源碼解析佩捞,count += len;并非原子操作绞幌,如果兩個線程同時訪問到這個方法,那么AbstractStringBuilder中的count是不是就是相同的一忱,所以這兩個線程都是在底層char數(shù)組的count位置開始append添加莲蜘,那么最終的結(jié)果肯定就是在后執(zhí)行的那個線程append進(jìn)去的數(shù)據(jù)會將前面一個覆蓋掉,所以StringBuilder在字符串拼接操作是線程不安全的帘营。由于String的拼接也是通過StringBuiler來實(shí)現(xiàn)票渠,所以String的字符拼接也不是線程安全的
3.StringBuffer
(1)類定義
public final class StringBuffer
??? extends AbstractStringBuilder
??? implements java.io.Serializable, CharSequence
StringBuffer其父類實(shí)現(xiàn)了只讀字符序列接口和拼接接口,與StringBuilder有公共的父類AbstractStringBuilder芬迄,該類是可變類问顷。
(2)主要變量
StringBuffer的底層基本實(shí)現(xiàn)和StringBuilder是一致的。
(3)構(gòu)造方法
StringBuffer的構(gòu)造方法和StringBuilder是一致的薯鼠。
(4)字符串拼接
public synchronized StringBuffer append(String str) {
??? toStringCache = null;
??? super.append(str);
??? return this;
}
StringBuffer字符串拼接基本與StringBuilder內(nèi)部實(shí)現(xiàn)是一致的择诈,主要是區(qū)別是在方法體上通過synchronized關(guān)鍵字加鎖。
(5)線程安全性
從上面的源碼解析出皇,由于在方法加上synchronize關(guān)鍵字,所以字符串拼接操作是線程安全哗戈。
備注:在正式面試時郊艘,基本回答三者的區(qū)別是:String是不可變類,StringBuffer和StringBuilder是可變類唯咬;String在多次字符串拼接時效率低纱注,且線程不安全,StringBuilsder效率最高胆胰,但是線程不安全狞贱,StringBuffer效率在前兩者其中,但是線程安全蜀涨。