JAVA字符謎題8:字符串奶酪

字號(hào):

下面的程序從一個(gè)字節(jié)序列創(chuàng)建了一個(gè)字符串,然后迭代遍歷字符串中的字符,并將它們作為數(shù)字打印。請(qǐng)描述一下程序打印出來(lái)的數(shù)字序列:
    public class StringCheese {
     public static void main(String[] args) {
     byte bytes[] = new byte[256];
     for (int i = 0; i < 256; i++)
     bytes[i] = (byte)i;
     String str = new String(bytes);
     for (int i = 0, n = str.length(); i < n; i++)
     System.out.println((int)str.charAt(i) + " ");
     }
    }
     首先,byte數(shù)組用從0到255每一個(gè)可能的byte數(shù)值進(jìn)行了初始化,然后這些byte數(shù)值通過(guò)String構(gòu)造器被轉(zhuǎn)換成了char數(shù)值。最后,char數(shù)值被轉(zhuǎn)型為int數(shù)值并被打印。打印出來(lái)的數(shù)值肯定是非負(fù)整數(shù),因?yàn)閏har數(shù)值是無(wú)符號(hào)的,因此,你可能期望該程序?qū)错樞虼蛴〕?到255的整數(shù)。
    如果你運(yùn)行該程序,可能會(huì)看到這樣的序列。但是在運(yùn)行一次,可能看到的就不是這個(gè)序列了。我們?cè)谒呐_(tái)機(jī)器上運(yùn)行它,會(huì)看到四個(gè)不同的序列,包括前面描述的那個(gè)序列。這個(gè)程序甚至都不能保證會(huì)正常終止,比打印其他任何特定字符串都要缺乏這種保證。它的行為完全是不確定的。
    這里的罪魁禍?zhǔn)拙褪荢tring(byte[])構(gòu)造器。有關(guān)它的規(guī)范描述道:“在通過(guò)解碼使用平臺(tái)缺省字符集的指定byte數(shù)組來(lái)構(gòu)造一個(gè)新的String時(shí),該新String的長(zhǎng)度是字符集的一個(gè)函數(shù),因此,它可能不等于byte數(shù)組的長(zhǎng)度。當(dāng)給定的所有字節(jié)在缺省字符集中并非全部有效時(shí),這個(gè)構(gòu)造器的行為是不確定的”[Java-API]。
    到底什么是字符集?從技術(shù)角度上講,它是“被編碼的字符集合和字符編碼模式的結(jié)合物”[Java-API]。換句話說(shuō),字符集是一個(gè)包,包含了字符、表示字符的數(shù)字編碼以及在字符編碼序列和字節(jié)序列之間來(lái)回轉(zhuǎn)換的方式。轉(zhuǎn)換模式在字符集之間存在著很大的區(qū)別:某些是在字符和字節(jié)之間做一對(duì)一的映射,但是大多數(shù)都不是這樣。ISO-8859-1是能夠讓該程序按順序打印從0到255的整數(shù)的缺省字符集,它更為大家所熟知的名字是Latin-1[ISO-8859-1]。
     J2SE運(yùn)行期環(huán)境(JRE)的缺省字符集依賴于底層的操作系統(tǒng)和語(yǔ)言。如果你想知道你的JRE的缺省字符集,并且你使用的是5.0或更新的版本,那么你可以通過(guò)調(diào)用java.nio.charset.Charset.defaultCharset()來(lái)了解。如果你使用的是較早的版本,那么你可以通過(guò)閱讀系統(tǒng)屬性“file.encoding”來(lái)了解。
    幸運(yùn)的是,你沒(méi)有被強(qiáng)制要求必須去容忍各種稀奇古怪的缺省字符集。當(dāng)你在char序列和byte序列之間做轉(zhuǎn)換時(shí),你可以且通常是應(yīng)該顯式地指定字符集。除了接受byte數(shù)字之外,還可以接受一個(gè)字符集名稱的String構(gòu)造器就是專為此目的而設(shè)計(jì)的。如果你用下面的構(gòu)造器去替換在最初的程序中的String構(gòu)造器,那么不管缺省的字符集是什么,該程序都保證能夠按照順序打印從0到255的整數(shù):
    String str = new String(bytes, "ISO-8859-1");
     這個(gè)構(gòu)造器聲明會(huì)拋出UnsupportedEncodingException異常,因此你必須捕獲它,或者更適宜的方式是聲明main方法將拋出它,要不然程序不能通過(guò)編譯。盡管如此,該程序?qū)嶋H上不會(huì)拋出異常。Charset的規(guī)范要求Java平臺(tái)的每一種實(shí)現(xiàn)都要支持某些種類的字符集,ISO-8859-1就位列其中。
    這個(gè)謎題的教訓(xùn)是:每當(dāng)你要將一個(gè)byte序列轉(zhuǎn)換成一個(gè)String時(shí),你都在使用某一個(gè)字符集,不管你是否顯式地指定了它。如果你想讓你的程序的行為是可預(yù)知的,那么就請(qǐng)你在每次使用字符集時(shí)都明確地指定。對(duì)API的設(shè)計(jì)者來(lái)說(shuō),提供這么一個(gè)依賴于缺省字符集的String(byte[])構(gòu)造器可能并非是一個(gè)好主意。