1. 程式人生 > >Java語言中的生僻知識

Java語言中的生僻知識

bsp 依賴 fix 字符 如果 怎麽 分享圖片 -c creat

技術分享圖片

最近有一首名叫《生僻字》的流行歌曲火遍大江南北,創作者給佶屈聱牙的生僻字,配上了優美明快的旋律,竟然讓歌曲變得瑯瑯上口、悅耳動聽起來,平時不太常見的拒人於千裏之外的這些漢字也不再那麽陌生,人們帶著一種獵奇和挑戰的心理,在街頭巷尾爭相傳唱。

同樣,在Java語言中,也有一些相對生僻的知識,平時用的機會可能不是很多,但如果不了解不掌握這些知識點的話,也可能會掉入陷阱之中,今天我們就來初步梳理一下:

1. goto是java語言中的關鍵字

“臭名昭著”、“十惡不赦”的goto竟然是java中的關鍵字!沒錯,參看下圖中的關鍵字列表,goto赫然在列:

技術分享圖片

雖然goto是java中的關鍵字,但它沒有在java中使用,如果我們需要類似跳轉的功能,可以使用break關鍵字,比如,如果要求在滿足某種條件時跳出整個兩重循環,可以用如下的代碼來實現:

label:
for(int i=0;i<10;i++){
 for(int j=0;j<10;j++){
 System.out.println("%i"+i+",j"+j);
 if(i>j)
 break label;
 }
}

2. Integer中,-128至127被緩存起來了

我們先來看看下面這段代碼:

public static void main(String[] args){
 Integer a = 100;
 Integer b = 100;
 Integer c = 200;
 Integer d = 200;
 System.out.println(a==b);
 System.out.println(c==d);
}

乍一看,我們可能會認為,輸出的結果要麽都是true,要麽都是false,但實際的情況卻讓人大跌眼鏡,正確的結果是true和false。

這是為什麽呢?原來Integer中有一個靜態內部類IntegerCache,在類加載的時候,它會把[-128, 127]之間的值緩存起來,而Integer a = 100這樣的賦值方式,會首先調用Integer類中的靜態valueOf方法,這個方法會嘗試從緩存裏取值,如果在這個範圍之類就不用重新new一個對象了,它的代碼如下:

public static Integer valueOf(int i) {
 if (i >= IntegerCache.low && i <= IntegerCache.high)
 return IntegerCache.cache[i + (-IntegerCache.low)];
 return new Integer(i);
}

所以上面的代碼就產生了這樣奇怪的輸出。

3. java註釋也能識別unicode

再看看這段代碼,應該輸出什麽呢?

public static void main(String[] args){
 // \u000d System.out.println("Hello World!");
}

如果你認為,註釋後面的代碼,當然不會執行,所以上面的代碼什麽都不會輸出,那你就錯了,結果恰恰相反,這段代碼就像打不死的小強,連註釋也不能阻擋它的綻放,最後還是輸出了我們最熟悉不過的“Hello World!”

為什麽呢?

原來,unicode解碼發生在代碼編譯之前,編譯器將\u樣式的代碼進行文本轉義,即使是註釋也是這樣,然後\u000a被轉換成\n換行符,所以println代碼得以正常執行。

4. 數組的定義方式靈活多變

定義一個數組,我們經常用如下的方式:

int[] arr;

可能是為了照顧從C語言轉到Java的程序員,下面的方式也是沒問題的:

int arr[];

大部分程序員會選擇第一種方式,畢竟它把數據類型和變量名稱分隔得非常清晰。但即使采用第二種方式,理解起來也問題不大,下面這種方式就有點奇怪了:

int[] arr[];

它其實等價於:

int[][] arr;

甚至還有一種更容易讓人混淆的方式。還記得變量定義的一種特殊形式嗎?就是在一行上定義多個同類型的變量,這個規則對於數組也是適用的,看看下面:

int[] arr, arr2[];

它等價於:

int[] arr;
int[][] arr2;

當然,不建議使用這樣的方式。

類似的定義方式也可以用在方法的返回值上面,比如

int[] fuction()[];

就等價於:

int[][] fuction();

5. new String("xyz")創建了兩個對象

下面的語句創建了幾個對象:

String str = new String("xyz");

這是面試時經常會問起的一個問題。跟其他普通的對象不一樣,上面的代碼創建了兩個對象,一個存放在堆中,一個存放在字符串常量池中。

當然,需要我們註意的是,如果之前常量池中已經存在"xyz"這個字符串,那麽,上面的語句就只會在堆中創建一個對象了。

另外,定義一個字符串的時候,我們還可以采用下列的方式:

String str = "xyz";

這樣,就只會創建一個存放在字符串常量池中的對象(如果池中不存在這個字符串的話)。

6. JVM指令重排序

在java代碼中有先後順序的代碼,在經過編譯器處理後,可能會對這些指令進行重排序,噢,聽起來有點匪夷所思。

來看一段代碼(來自於《Java並發編程實踐》):

public class PossibleReordering {
 static int x = 0, y = 0;
 static int a = 0, b = 0;
 public static void main(String[] args) throws InterruptedException {
 Thread one = new Thread(new Runnable() {
 public void run() {
 a = 1;
 x = b;
 }
 });
 Thread other = new Thread(new Runnable() {
 public void run() {
 b = 1;
 y = a;
 }
 });
 one.start();
 other.start();
 one.join();
 other.join();
 System.out.println("(" + x + "," + y + ")");
 }
}

由於線程執行的順序可能會有先後交叉的情況,所以上面的代碼可能會輸出(1,0),(0,1),(1,1),這不難理解,然而,它竟然也有可能輸出(0,0):

技術分享圖片

從上圖可以看出,編譯後的順序跟代碼的順序不一樣了,這看起來確實有些奇怪,背後的原因是,出於性能的考慮,JIT會對沒有數據依賴的指令進行重排,所以才會發生上面的情況。

可以學習Java內存模型(JMM),以及as-if-serial語義和happens-before等更多的知識來加深對指令重排的理解。

7. 95%的java代碼毫無價值

最後,來一個比較輕松一點(或許是沈重?)的冷知識。據一條網絡消息,加州大學戴維斯分校、中國東南大學和倫敦大學學院的研究人員發表了一篇研究報告(PDF),他們分析了1億行Java項目代碼,發現超過95%的代碼是沒什麽價值的。

怎麽樣,沒想到吧,是不是很冷?冷得讓人都打了個寒顫,日日夜夜攻堅,精心編寫的java代碼,竟然絕大部分是沒有價值的,著實讓人感覺不到溫暖了。不過,他們站的角度可能不同,分析的維度可能也有分別,就當是茶余飯後的一個談資吧,不用太往心裏去。

8. 結語

當然,上面提到的這些冷知識,對於基礎知識紮實,工作經驗豐富的人來講,一點都不冷,在實際工作中也是運用自如,手到擒來。就像歌曲《生僻字》中的歌詞:”煢煢孑立,沆瀣一氣,踽踽獨行,醍醐灌頂……”,對普通人來說可能會有一些難度,但對於飽讀經書、學富五車的學者,就像我們看到“一針見血、春色滿園、爭先恐後、興高采烈”這樣的成語一樣地簡單。

Java語言中的生僻知識