int a1= 1000;int a2=1000;if(a1==a2){System.out.println("yes");}
java
==的比較,基本數據類型看的是值是否相等,引用數據類型是看是不是一個對象(地址是否相等)程序員
Integer a=1377;
面試
Integer b=1377; if(a==b){ System.out.println("yes"); }
在裝箱的時候自動調用的是Integer的valueOf(int)方法。而在拆箱的時候自動調用的是Integer的intValue方法。數組
public static Integer valueOf(int i) {
緩存
if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i);
}
安全
由裝箱函數valueof可知,-128~127之間的整數有一個 IntegerCache緩存,不會建立新的對象。多線程
面試題:app
public class Main {
函數
public static void main(String[] args) { Double i1 = 100.0; Double i2 = 100.0; Double i3 = 200.0; Double i4 = 200.0; System.out.println(i1==i2); System.out.println(i3==i4); }
}
優化
若是是int型的,100不會建立新對象,200會,可是浮點型的就不同了,看一下double的valueof源碼:
public static Double valueOf(String s) throws NumberFormatException {
return new Double(parseDouble(s)); }
直接new了一個新對象了。(由於double的數表示範圍太大了,-128~127之間幾乎有無數的數字)
總結:
Integer、Short、Byte、Character、Long這幾個類的valueOf方法的實現是相似的。
Double、Float的valueOf方法的實現是相似的。
當 "=="運算符的兩個操做數都是 包裝器類型的引用,則是比較指向的是不是同一個對象,而若是其中有一個操做數是表達式(即包含算術運算)則比較的是數值(即會觸發自動拆箱的過程)。
public class Main {
public static void main(String[] args) { Integer a = 1; Integer b = 2; Integer c = 3; Integer d = 3; Integer e = 321; Integer f = 321; Long g = 3L; Long h = 2L; System.out.println(c==d); //在緩存的範圍內,true System.out.println(e==f);//不在緩存的範圍內,false System.out.println(c==(a+b));//a+b拆箱了,比較的是值,true System.out.println(c.equals(a+b));equals比較的是對象,因此拆箱後又裝箱了,可是在緩存以內,仍是同一個對象 true System.out.println(g==(a+b));true 比較的是值 System.out.println(g.equals(a+b));比較的是對象,兩者都不是同一個數據類型的天然new出來不是一個對象,false System.out.println(g.equals(a+h));兩者都是long型的,也就是longvalueof方法,也是有-128~127的緩存機制的,true }
}
(1)final char value[]; 由一個字符串數組構成,final修飾,因此不可變。
(2)裏面的一些字符串操做方法其實都是返回了一個新串:看一個源碼函數substring
public String substring(int beginIndex, int endIndex) {
if (beginIndex < 0) { throw new StringIndexOutOfBoundsException(beginIndex); } if (endIndex > value.length) { throw new StringIndexOutOfBoundsException(endIndex); } int subLen = endIndex - beginIndex; if (subLen < 0) { throw new StringIndexOutOfBoundsException(subLen); } return ((beginIndex == 0) && (endIndex == value.length)) ? this : new String(value, beginIndex, subLen);//new了一個新的string返回了 }
在這裏要永遠記住一點:
「對String對象的任何改變都不影響到原對象,相關的任何change操做都會生成新的對象」。
String s1="hello";
String s2="hello"; System.out.println(s1==s2);// true,hello是字符串常量,放在常量池裏 String s3=new String("hello"); System.out.println(s1==s3);//false//new後是一個新的對象。
public StringBuilder append(String str) {
super.append(str); return this;//在原有基礎上作變化的。 }
StringBuilder和StringBuffer類擁有的成員屬性以及成員方法基本相同,區別是StringBuffer類的成員方法前面多了一個關鍵字:synchronized,不用多說,這個關鍵字是在多線程訪問時起到安全保護做用的,也就是說StringBuffer是線程安全的。因此通常stringbuilder用的居多。
對於直接相加字符串,效率很高,由於在編譯器便肯定了它的值,也就是說形如」I」+」love」+」java」; 的字符串相加,在編譯期間便被優化成了」Ilovejava」。
對於間接相加(即包含字符串引用),形如s1+s2+s3; 效率要比直接相加低,由於在編譯器不會對引用變量進行優化。
String、StringBuilder、StringBuffer三者的執行效率:
StringBuilder > StringBuffer > String
固然這個是相對的,不必定在全部狀況下都是這樣。
好比String str = 「hello」+ 「world」的效率就比 StringBuilder st = new StringBuilder().append(「hello」).append(「world」)要高。
所以,這三個類是各有利弊,應當根據不一樣的狀況來進行選擇使用:
當字符串相加操做或者改動較少的狀況下,建議使用 String str=」hello」這種形式;
當字符串相加操做較多的狀況下,建議使用StringBuilder,若是採用了多線程,則使用StringBuffer。
幾個面試題:
1. 下面這段代碼的輸出結果是什麼?
String a = 「hello2″; String b = 「hello」 + 2; System.out.println((a == b));
輸出結果爲:true。緣由很簡單,」hello」+2在編譯期間就已經被優化成」hello2″,所以在運行期間,變量a和變量b指向的是同一個對象。
2.下面這段代碼的輸出結果是什麼?
String a = 「hello2″; String b = 「hello」; String c = b + 2; System.out.println((a == c));
輸出結果爲:false。因爲有符號引用的存在,因此 String c = b + 2;不會在編譯期間被優化,不會把b+2當作字面常量來處理的,所以這種方式生成的對象事實上是保存在堆上的。所以a和c指向的並非同一個對象。
3.下面這段代碼的輸出結果是什麼?
String a = 「hello2″; final String b = 「hello」; String c = b + 2; System.out.println((a == c));
輸出結果爲:true。對於被final修飾的變量,會在class文件常量池中保存一個副本,也就是說不會經過鏈接而進行訪問,對final變量的訪問在編譯期間都會直接被替代爲真實的值。那麼String c = b + 2;在編譯期間就會被優化成:String c = 「hello」 + 2
4.下面這段代碼輸出結果爲:
public class Main { static void main(String[] args) { String b = getHello(); |
|
---|---|
輸出結果爲false。這裏面雖然將b用final修飾了,可是因爲其賦值是經過方法調用返回的,那麼它的值只能在運行期間肯定,所以a和c指向的不是同一個對象。
5.下面這段代碼的輸出結果是什麼?
public class Main { static void main(String[] args) { String( |
|
---|---|
這裏面涉及到的是String.intern方法的使用。在String類中,intern方法是一個本地方法,在JAVA SE6以前,intern方法會在運行時常量池中查找是否存在內容相同的字符串,若是存在則返回指向該字符串的引用,若是不存在,則會將該字符串入池,並返回一個指向該字符串的引用。所以,a和d指向的是同一個對象,其他都是false
6.String str = new String(「abc」)建立了多少個對象?
這個問題在不少書籍上都有說到好比《Java程序員面試寶典》,包括不少國內大公司筆試面試題都會遇到,大部分網上流傳的以及一些面試書籍上都說是2個對象,這種說法是片面的。
若是有不懂得地方能夠參考這篇帖子:
http://rednaxelafx.iteye.com/...
首先必須弄清楚建立對象的含義,建立是何時建立的?這段代碼在運行期間會建立2個對象麼?毫無疑問不可能。
很顯然,new只調用了一次,也就是說只建立了一個對象。
而這道題目讓人混淆的地方就是這裏,這段代碼在運行期間確實只建立了一個對象,即在堆上建立了」abc」對象。而爲何你們都在說是2個對象呢,這裏面要澄清一個概念 該段代碼執行過程和類的加載過程是有區別的。在類加載的過程當中,確實在運行時常量池中建立了一個」abc」對象,而在代碼執行過程當中確實只建立了一個String對象。
所以,這個問題若是換成 String str = new String(「abc」)涉及到幾個String對象?合理的解釋是2個。
我的以爲在面試的時候若是遇到這個問題,能夠向面試官詢問清楚」是這段代碼執行過程當中建立了多少個對象仍是涉及到多少個對象「再根據具體的來進行回答。
7.下面這段代碼1)和2)的區別是什麼?
public class Main { static void main(String[] args) { |
|
---|---|
1)的效率比2)的效率要高,1)中的」love」+」java」在編譯期間會被優化成」lovejava」,而2)中的不會被優化。