尋找字符集常量

源碼尋找字符集常量過程

做開發也有很多年了,時常會遇到使用字符集的地方,有時候就會想,使用硬編碼指定字符集也太不專業了,有沒有字符集的常量呢?與字符集相關的類有一個特別明顯的就是Charset,這個類就代表字符集,我們很少使用它的構造方法來創建字符集,它有一些獲取Charset實例對象的靜態方法,如下:
在這裏插入圖片描述
看方法名也很容易理解其中的含義,於是從這四個靜態方法中進入源碼查看,找到了一些字符字符集相關的常量,它們定義在sun.nio.cs包裏面,裏面有各種字符集類,如GBK、UTF_8、UTF_16、ISO_8859_1、Unicode等非常非常多的字符集類,應該是包含了所有的字符集了,正興高采烈地使用它們時,發現一運行就報錯了,大概意思是這些類是不允許使用的,暈!

一次偶然的機會,翻看Android中的Charset.defaultCharsets()的源碼,發現了新大陸,如下:

	public static Charset defaultCharset() {
        // Android-changed: Use UTF_8 unconditionally.
        synchronized (Charset.class) {
            if (defaultCharset == null) {
                defaultCharset = java.nio.charset.StandardCharsets.UTF_8;
            }

            return defaultCharset;
        }
    }

代碼註釋中有說明這是Android修改過的源碼,與標準JDK的源碼不一樣了哦。

StandardCharsets,翻譯過來就是標準字符集,但是沒有GBK呀,氣人,GBK不標準嗎?如下:
在這裏插入圖片描述
經實驗,這些常量可以使用。

在使用OkHttp的時候,也會涉及到字符集,所以我想OkHttp這麼專業不可能用硬編碼吧,於是進它源碼裏面找找他是用的什麼樣的字符集常量,關於字符集,在獲取String的時候肯定是用到了的,於是可以從獲取String的函數的源碼入手:

response.body().string()

通過源碼發現,在它的Util類裏面封裝了一些字符集常量,如下:

  public static final Charset UTF_8 = Charset.forName("UTF-8");
  public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
  private static final Charset UTF_16_BE = Charset.forName("UTF-16BE");
  private static final Charset UTF_16_LE = Charset.forName("UTF-16LE");
  private static final Charset UTF_32_BE = Charset.forName("UTF-32BE");
  private static final Charset UTF_32_LE = Charset.forName("UTF-32LE");

可惜,也沒有GBK,哎!

後來開發用了Kotlin,Kotlin是個好東西,可以少寫很多代碼,比如file.readText()就可以把一個文件對象關聯的文本內容全部讀取出來,這裏肯定也用到了字符集,點進去看源碼,發現Kotlin用的是自帶字符集常量,如下:
在這裏插入圖片描述
可惜,也沒有GBK。

總結

有如下三種字符集常量可以使用:

  1. StandardCharsets.UTF_8 (JDK自帶,這是JDK1.7的時候纔有的)
  2. Util.UTF_8 (OkHttp自帶)
  3. Charsets.UTF_8 (Kotlin自帶)

個人感覺使用Kotlin自帶的會比較方便,它的類名Charsets比較短也比較好記而且類名很專業。通過讀別人的源碼,我們也可以學到,其實可以自己定義一個類,裏面放一些字符集常量,不一定非要去JDK裏找,這樣的話我們自己定義,還可以增加GBK常量呢,說到這裏,Kotlin有擴展屬性功能,我們可以給Kotlin的Charsets類擴展一個GBK屬性,這樣就完美了,如下:

val Charsets.GBK: Charset
    get() = Charset.forName("GBK")

fun main() {
    println(Charsets.GBK)
}

這裏不用擔心每次調用Charsets.GBK時都會創建新的字符集對象,因爲Charset.forName()函數會緩存使用到的字符集,每次會先從緩存中取,取不到纔會去創建。

最後一點,有時候傳參時,可能函數接收的字符集就是一個String,而不是Charset對象,怎麼辦呢?Charset對象有如下三個方法可以返回它的名字:

    println(Charsets.GBK.displayName())
    println(Charsets.GBK.name())
    println(Charsets.GBK.toString())

輸出都是:GBK
點源碼進去看,發現toString返回的是name()函數,而name()函數和displayName()函數都是返回name屬性,似乎沒有區別,看看文檔說明,如下:

displayName() // 返回此 charset 用於默認語言環境的可讀名稱。
name()        // 返回此 charset 的規範名稱。

似乎name()函數比較好,因爲規範嘛,絕對是可用的,而displayName保不準哪天就改源碼了,如GBK編碼給你返回“漢字內碼擴展規範”,根據文檔描述返回默認語言環境的可讀名稱,如果在中國,很有可能 返回漢字的描述名稱嘛!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章