先點贊再看,養成好習慣
本文正在參加「Java主題月 - Java Debug筆記活動」,詳情查看 活動連接java
相信你們都用過 Fastjson,阿里的一個開源 JSON 庫,在阿里系的開源項目裏應用的很是普遍。雖然有時候也被戲稱「沉的快」,但 Fastjson 從功能豐富度、易用性、源碼設計角度來看,都是一款十分優秀的工具庫。json
在使用 Fastjson 時,常常會配置一些枚舉參數,好比日期格式、格式化輸出、NULL值格式啊等等,就像下面這樣配置:數組
String jsonStr = JSON.toJSONString(obj, SerializerFeature.WriteDateUseDateFormat, SerializerFeature.PrettyFormat, SerializerFeature.WriteNullStringAsEmpty); // JSON.toJSONString public static String toJSONString(Object object, SerializerFeature... features);
這種配置方式用起來那是至關爽,要什麼輸出配置,利用 JAVA 的可變長度參數(varargs)直接在方法後面動態追加就行;要是再加上 import static
,那麼連 SerializerFeature 都不用寫了。工具
不過細想一下,這種動態傳的參數數組,Fastjson 在接受後怎麼知道咱們具體傳了哪些參數?接受時遍歷數組,每次 equals 對比嗎?好比這樣:post
// 寫三個 for 循環的緣由是大機率下,不一樣 feature 處理時機不一樣,因此不能在一個 for 循環內處理 for (SerializerFeature feature : features) { if(feature.equals(SerializerFeature.WriteDateUseDateFormat)){ // solve WriteDateUseDateFormat } } for (SerializerFeature feature : features) { if(feature.equals(SerializerFeature.PrettyFormat)){ // solve PrettyFormat } } for (SerializerFeature feature : features) { if(feature.equals(SerializerFeature.WriteNullStringAsEmpty)){ // solve WriteNullStringAsEmpty } }
這樣也太不「優雅」了,每次還須要遍歷,白白浪費性能!或者只用一個循環,弄幾個變量存儲這幾個 boolean 值呢:性能
boolean writeDateUseDateFormatEnable = false; boolean PrettyFormatEnable = false; boolean WriteNullStringAsEmptyEnable = false; for (SerializerFeature feature : features) { if(feature.equals(SerializerFeature.WriteDateUseDateFormat)){ writeDateUseDateFormatEnable = true; } if(feature.equals(SerializerFeature.PrettyFormat)){ PrettyFormatEnable = true; } if(feature.equals(SerializerFeature.WriteNullStringAsEmpty)){ WriteNullStringAsEmptyEnable = true; } }
這樣比上面要好一點了,但仍是須要循環判斷,並且對每一個 Feature 都要額外增長一個變量來存儲,同樣不太「優雅」。spa
在 Fastjson 中使用了一種很巧妙的方式來處理這個動態的枚舉參數設計
在正式介紹以前,須要先了解枚舉中的一個概念- 序數(ordinal),每一個枚舉類都會有一個 ordinal 屬性,這個ordinal 表明的是當前枚舉值在枚舉類中的序號。好比下面這個枚舉類裏,F_A
/F_B
/F_C
/F_D 四個枚舉值的序數依次爲 0/1/2/3code
public enum Feature { F_A, // ordinal 0 F_B, // ordinal 1 F_C, // ordinal 2 F_D, // ordinal 3 ; }
經過 ordinal() 方法,就能夠獲取枚舉實例的序數值,好比Feature._F_A_.ordinal()
orm
瞭解了枚舉序數以後,如今來看看 Fastjson 中是怎麼個玩法。在 SerializerFeature
的源碼中有一個 mask(掩碼) ,這個 mask 的值爲 1 << ordinal
public enum SerializerFeature { WriteDateUseDateFormat, PrettyFormat, WriteNullStringAsEmpty ... SerializerFeature(){ mask = (1 << ordinal()); } public final int mask; ... }
在位運算中掩碼 mask 的做用通常是爲了保持/更改/刪除某(些)位的值,有張圖很是形象(這個圖能夠簡單的理解爲,白色像素表明1,黑色像素表明0,按爲與後,爲1的像素位纔會顯示):
那在 SerializerFeature
中,WriteDateUseDateFormat,
PrettyFormat,
WriteNullStringAsEmpty 三個值得序數分別爲 0/1/2,左移後他們對應的二進制位以下:
0 0 0 1 WriteDateUseDateFormat 0 0 1 0 PrettyFormat 0 1 0 0 WriteNullStringAsEmpty ... 1 0 0 0
這裏的作法很巧妙,用 1 左移序數個位,就能夠獲得一個序數位爲 1 的數字,好比序數爲 1 ,那麼第0位就是1,序數爲3,那麼第4爲就是1,以此類推,這樣枚舉中每一個值的 mask 裏爲 1 的位都會不一樣
單看這個位掩碼仍是以爲沒啥用,來看看實戰吧。如今定義一個初始爲 0 的 features 變量,用來存儲全部的 feature
int features = 0;
利用位或(OR)對 features 和 mask 進行運算
features |= SerializerFeature.WriteDateUseDateFormat.getMask(); 0 0 0 0 [input(features)] (|) 0 0 0 1 [mask(feature mask)] ------------- 0 0 0 1 [output(new features)]
位或運算後的 features 爲 0 0 0 1
,第 0 位上變成了 1 ,表明第 1 位的枚舉值(WriteDateUseDateFormat)被啓用了,接着繼續對 PrettyFormat 也執行位或,
features |= SerializerFeature.PrettyFormat.getMask();
0 0 0 1 [input(features)] (|) 0 0 1 0 [mask(feature mask)] ------------- 0 0 1 1 [output(new features)]
此時 features 爲 0 0 1 1
,第 2 位 上也變成了 1,表明第 2 位的枚舉值(PrettyFormat)也被啓用了
有了 features 的值,仍是須要一個簡單的判斷方法,來檢查某個枚舉值是否被設置:
public static boolean isEnabled(int features, SerializerFeature feature) { return (features & feature.mask) != 0; }
用 features 和 某個 Feature 的掩碼作位與後,就能夠得出一個某位爲 1 的數字。位與運算中只有上下兩個位都爲 1 ,返回的位纔會爲 1,那麼只要返回的結果位內包含任何一個 1 ,這個數就不會爲 0 ;因此只要這個結果不爲 0 ,就能夠說明這個 Feature 已經被設置了。
0 0 1 1 [input(features)] (&) 0 0 1 0 [mask(PrettyFormat)] ------------- 0 0 1 0 [output(new features)]
好比上面這個例子中,當前 features 爲 0 0 1 1
,和 PrettyFormat 的 mask 作位與後,就能夠得出 0 0 1 0
,結果不爲 0 ,因此 PrettyFormat 已經被設置了
// 存儲全部配置的 Feature int features = 0; // 每添加一個 Feature, 就拿 features 和 當前 Feature 的掩碼作位或運算 features |= SerializerFeature.WriteDateUseDateFormat.getMask(); features |= SerializerFeature.PrettyFormat.getMask(); // 再經過位與運算的結果,就能夠判斷某個 Feature 是否配置 boolean writeDateUseDateFormatEnabled = SerializerFeature.isEnabled(features,SerializerFeature.WriteDateUseDateFormat); boolean prettyFormatEnabled = SerializerFeature.isEnabled(features,SerializerFeature.PrettyFormat); boolean writeNullStringAsEmpty = SerializerFeature.isEnabled(features,SerializerFeature.WriteNullStringAsEmpty); System.out.println("writeDateUseDateFormatEnabled: "+writeDateUseDateFormatEnabled); System.out.println("prettyFormatEnabled: "+prettyFormatEnabled); System.out.println("writeNullStringAsEmpty: "+writeNullStringAsEmpty); //output writeDateUseDateFormatEnabled: true prettyFormatEnabled: true writeNullStringAsEmpty: false
不止是 Fastjson,Jackson 中對 Feature 的處理也是基於這個枚舉序數+位掩碼的邏輯,兩者實現如出一轍,算是一種主流的作法吧。
原創不易,禁止未受權的轉載。若是個人文章對您有幫助,就請點贊/收藏/關注鼓勵支持一下吧❤❤❤❤❤❤