1. 程式人生 > >《Java解惑》系列——02字元謎題——謎題14:轉義字元的潰敗

《Java解惑》系列——02字元謎題——謎題14:轉義字元的潰敗

謎題14至17,講的都是用16進位制程式碼表示unicode字元導致的問題。

知識點:

Java對在字串字面常量中的Unicode轉義字元(16進製表示)沒有提供任何特殊處理。

編譯器在將程式解析成各種符號之前,先將Unicode轉義字元轉換成為它們所表示的字元[JLS 3.2]。

普通的轉義字元序列和八進位制轉義字元都比Unicode 轉義字元要好得多,因為與Unicode 轉義字元不同,

轉義字元序列是在程式被解析為各種符號之後被處理的。


問題:

下面的程式會列印什麼?

public class EscapeRout{
    public static void main(String[] args){
        // \u0022 是雙引號的Unicode 轉義字元
        System.out.println("a\u0022.length() +\u0022b".length());
    }
}

// 期望結果:26或16
// 實際結果:2

結果是不是出乎大家的意料呢?的確如此。
產生這個結果的原因:

對該程式的一種很膚淺的分析會認為它應該打印出26,因為在由兩個雙引號
"a\u0022.length()+\u0022b"標識的字串之間總共有 26個字元。  
稍微深入一點的分析會認為該程式應該列印16,因為兩個Unicode 轉義字元每
一個在原始檔中都需要用6個字元來表示,但是它們只表示字串中的一個字
符。因此這個字串應該比它的外表看其來要短10 個字元。 如果你執行這個程
序,就會發現事情遠不是這麼回事。它列印的既不是 26也不是 16,而是 2。  
理解這個謎題的關鍵是要知道:Java 對在字串字面常量中的Unicode 轉義字
符沒有提供任何特殊處理。編譯器在將程式解析成各種符號之前,先將Unicode
轉義字元轉換成為它們所表示的字元[JLS 3.2]。因此,程式中的第一個Unicode
轉義字元將作為一個單字元字串字面常量("a")的結束引號,而第二個
Unicode 轉義字元將作為另一個單字元字串字面常量("b")的開始引號。程
序列印的是表示式"a".length()+"b".length(),即 2。  
如果該程式的作者確實希望得到這種行為,那麼下面的語句將要清楚得多:  
System.out.println("a".length()+"b".length());

解決方法:

你可以使用“普通的轉義字元序列(\x)”和“八進位制轉義序列\000”來替換16進製表示的unicode(\uXXXX)轉義字元。

從而來明確表達你的意圖。

1、System.out.println("a".length()+"b".length());  // 2


2、System.out.println("a\".length()+\"b".length());  // 16

總結:

1不要使用Unicode 轉義字元來表示ASCII字元;

2在字串和字元字面常量中要優先選擇的是轉義字元序列(普通轉義字元八進位制轉義字元),

而不是Unicode 轉義字元;

3ASCII是字符集的最小公共特性集,它只有 128(2^7)個字元,但是Unicode 有超65,000 (2^16-1)個字元。

      一個Unicode 轉義字元可以被用來在只使用 ASCII字元的程式中插入一個Unicode字元

      一個Unicode轉義字元精確地等價於它所表示的字元。Unicode 轉義字元被設計為用於

      在程式設計師需要插入一個不能用原始檔字符集表示的字元的情況。

實際上,你可以通過使用被稱為八進位制轉義字元的特殊型別的轉義字元序列,將
任何 ASCII字元置於一個字串字面常量或一個字元字面常量中,但是最好是盡
可能地使用普通的轉義字元序列。  
普通的轉義字元序列和八進位制轉義字元都比Unicode 轉義字元要好得多,因為與
Unicode 轉義字元不同,轉義字元序列是在程式被解析為各種符號之後被處理
的。  
ASCII是字符集的最小公共特性集,它只有 128個字元,但是Unicode 有超過
65,000 個字元。一個Unicode 轉義字元可以被用來在只使用 ASCII字元的程式
中插入一個Unicode字元。一個Unicode轉義字元精確地等價於它所表示的字元。  
Unicode 轉義字元被設計為用於在程式設計師需要插入一個不能用原始檔字符集表
示的字元的情況。它們主要用於將非 ASCII字元置於識別符號、字串字面常量、
字元字面常量以及註釋中。偶爾地,Unicode 轉義字元也被用來在看起來頗為相
似的數個字元中明確地標識其中的某一個,從而增加程式的清晰度。 

總之,在字串和字元字面常量中要優先選擇的是轉義字元序列,而不是
Unicode 轉義字元。Unicode 轉義字元可能會因為它們在編譯序列中被處理得過
早而引起混亂。不要使用Unicode 轉義字元來表示ASCII字元。在字串和字元
字面常量中,應該使用轉義字元序列;對於除這些字面常量之外的情況,應該直
接將 ASCII字元插入到原始檔中。