作者:禪樓望月(http://m.survivalescaperooms.com/yaoyinglong/)
字符串的陷阱java程序創建對象常見的方式有:
此外,基本類型以及基本類的包裝類、字符串還可以以直接量的方式來創建Java對象。如:
String str="hello world";Integer in=5;
對于Java程序中的字符直接量,JVM會使用一個字符串池來保護他們:當第一次使用某個字符串直接時,JVM會將它們放入字符串池進行緩存。在一般情況下,字符串緩沖池中字符串對象不會被垃圾回收,當程序再次需要使用該字符串時,無需重新創建一個新的字符串,而是直接讓引用變量指向字符串池中已有的字符串:
String str1="hello";String str2="hello";System.out.PRintln(str1==str2);//-->true
雖然String是引用類型。但是由于str1和str2兩個字符串的值都是直接量,它們都指向JVM的字符串池里的“hello”字符串,因此判斷str1==str2時輸出true。
注意:
也可以通過字符串連接表達式來創建字符串對象。如果這個字符串連接表達式的值可以在編譯時確定下來,那么JVM會在編譯時計算該字符串變量的值,并讓它指向字符串池中對應的字符串。但是,如果字符串連接表達式中使用了變量或者調用了方法,那就只能到運行時才可確定該字符串連接表達式的值,也就無法在編譯時確定該字符串變量的值,因此無法利用JVM的字符串池。

輸出的結果為:


但是也有例外:字符串連接運算中的所有變量都可執行“宏替換”
如果字符串連接運算中的所有變量都可以執行“宏替換”,那么JVM一樣可以在編譯時就確定字符串連接表達式的值,一樣會使字符串變量指向JVM字符串池中的對應字符串。
String str="I love Java";final String str1="I love ";String str2=str1+"Java";System.out.println(str==str2);//-->true
我們用反編譯工具看看:
不可變的字符串String str = "I love Java";String str1 = "I love ";String str2 = "I love Java"; //這里執行了宏替換。編譯時將str1用相應的值代替了。這是因為str1是final的而final變量的值在初始化之后不會在變化了。所以可以執行宏替換。System.out.println(str == str2);
String在創建之后是不可變的。只能改變字符串變量的引用,不能改變字符串對象。
String str="I love Java";str="I love C#";
第二行代碼并沒有將“I love Java”字符串對象變為“I love C#”字符串對象,只是改變str的指向,使其指向“I love C#”字符串對象,但是“I love Java”字符串對象仍然存在內存中。但是該“I love Java”字符串對象以后也許永遠也不會被使用了,但是它并不會被垃圾回收器所回收,它將一直存在于字符串池中------>這也是Java內存泄漏的一個原因。字符串比較
使用“==”比較的是兩個字符串所引用的是否是同一個字符串對象。但是如果要比較兩個字符串序列是否相同則須用String的 equals 方法,如下是String類的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; }表達式類型的陷阱還有String的compareTo系列方法。
表達式類型的自動提升。
當表達式中出現不同的數據類型是,表達式的結果以表達中最高等級操作數的類型為準。
復合賦值運算符的陷阱
諸如:+=、-=、*=……等復合賦值運算符都隱式包含了類型轉換。

如上圖所示,a=a+5出現了編譯錯誤,是因為,5為int類型,所以會將整個表達式的值提升為int,而a又是short不能進行類型的自動轉換,所以出現了編譯錯誤。而a+=5沒有出現編譯錯誤的原因為,我們看看Java底層怎么處理的:

是不是很清楚了,底層默默的進行了隱式的類型轉換。怎么轉的呢?是以等號左邊的類型作為等號后邊表達式計算結果的類型。這下可危險了:萬一等號右邊表達式的計算結果的范圍比等號左邊的大怎么辦,那只能悲催了,如:

所以我們在使用復合賦值運算符是要特別小心,特別是:
不要使用Thread類來創建線程。
不要調用run方法。
當同步一段代碼塊時,程序必須顯式的為它指定同步監視器;對于非靜態方法同步方法而言,該方法的同步監視器是this—即調用該方法的Java對象;對于靜態同步方法而言,該方法的同步監視器不是this而是當前類本身。
public class Test implements Runnable{ static boolean staticFLag=true; public static synchronized void test0(){ for(int i=0; i<100; i++){ System.out.println("test0:"+Thread.currentThread().getName()+" "+i); } } public void test1(){ synchronized (this) { for(int i=0; i<100; i++){ System.out.println("test1:"+Thread.currentThread().getName()+" "+i); } } } @Override public void run() { if(staticFLag){ staticFLag=false; test0(); }else { staticFLag=true; test1(); } } public static void main(String[] args) throws InterruptedException { Runnable runnable=new Test(); new Thread(runnable).start(); new Thread(runnable).start(); }}打印的結果為:
test0:Thread-0 0
test1:Thread-1 0
test0:Thread-0 1
test1:Thread-1 1
test0:Thread-0 2
test1:Thread-1 2
test0:Thread-0 3
test1:Thread-1 3
……
我們可以看到,由于兩個方法 沒有使用相同的同步監視器,所以它們可以同時并發執行,相互之間不會有影響。但是如果我們這樣變一下:
public class Test implements Runnable{ static boolean staticFLag=true; public static synchronized void test0(){ for(int i=0; i<100; i++){ System.out.println("test0:"+Thread.currentThread().getName()+" "+i); } } public void test1(){ synchronized (Test.class) { for(int i=0; i<100; i++){ System.out.println("test1:"+Thread.currentThread().getName()+" "+i); } } } @Override public void run() { if(staticFLag){ staticFLag=false; test0(); }else { staticFLag=true; test1(); } } public static void main(String[] args) throws InterruptedException { Runnable runnable=new Test(); new Thread(runnable).start(); new Thread(runnable).start(); }}打印:
test0:Thread-0 0
test0:Thread-0 1
test0:Thread-0 2
……
test0:Thread-0 98
test0:Thread-0 99
test1:Thread-1 0
test1:Thread-1 1
……
test1:Thread-1 99
我們看到,當我們顯示的指定test1方法內的代碼塊的同步監視器為Test.class時,由于test0方法和test1方法是用了相同的同步監視器,所以必須在test0方法釋放了該同步監視器的鎖定,test1才能使用。
本文鏈接:http://m.survivalescaperooms.com/yaoyinglong/p/java%E8%A1%A8%E8%BE%BE%E5%BC%8F%E4%B8%AD%E7%9A%84%E9%99%B7%E9%98%B1.html
新聞熱點
疑難解答