Apache工具包方法——Hex.encodeHexString(byte[] data)源码浅析
【2019-07-02 注:標題是Hex.encodeHexString(byte[] data) 的源碼解析,但在實際測試過程中,改了方法名稱,內部實現還是完全一樣的。】
最近正在研究加密的相關方法和思想,有時候會用到byte類型的數組密鑰或者密文,由于經常會遇到要將這鬼東西與數據庫進行存取操作的情況,并且大多數情況都是將這樣的byte數組轉化為字符串進行存儲的,因此用到了題目中的API,現對其源碼實現進行總結和思考。
public static final char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };public static String byte2HexString(final byte[] data) {final int l = data.length;final char[] out = new char[l << 1];// two characters form the hex value.for (int i = 0, j = 0; i < l; i++) {out[j++] = DIGITS_UPPER[(0xF0 & data[i]) >>> 4];out[j++] = DIGITS_UPPER[0x0F & data[i]];}return new String(out); }源碼中我們注意到了DIGITS_UPPER這個char[] 數組,事實上這個byte2HexString的實現原理是:將data中的每個byte元素拆分為兩個十六進制數,作為DIGITS_UPPER數組的下標,然后找到其對應的char字符,組裝成一個char[]數組。
測試用例
public static void main(String[] args) {byte[] org = {1, 2, 3, 15, 10, 16, 17, 32, 0, 8};// 01 02 03 0F 0A 10 11 20 00 08String hexstr = DataConvertUtil.byte2HexString(org);System.out.println("轉化結果:" + hexstr);}執行結果:
轉化結果:0102030F0A1011200008?結果是完全沒有問題的,下面是圖解(20190702追加圖解):
首先data.length獲取到data數組的元素個數(數組長度也就是元素的個數),然后再初始化一個新的char數組,out = new char[l << 1];這里值得注意的是char數組的長度,源碼中用移位運算來表示,事實上在我上一篇文章中已經寫得很明確,將一個數左移一位等價于將這個數 ×2,也就是說這個char數組的長度是data數組的二倍!為什么?原因如下:data中的每個元素都是byte類型,每個元素8個bit,分為高四位低四位,而每四位就可以表示一個0x0到0x15的十六進制數字,因此我們想將data中的每個元素用兩個十六進制的數字表示就必須初始化一個是data長度兩倍的char數組,這就是out對象的長度是[l << 1]的原因。
我們在源碼匯中也可以看到這樣的注釋// two characters form the hex value.
緊接著,for循環。我們可以在腦海中創建這樣一幅景象,兩個并列的數組byte[] data和char[] out我們從data中取第一個元素,將其以二進制的數字表示出來,然后拆分高四位低四位,分別找到對應的兩個十六進制的字符表示,然后再塞入char[]數組中,這就是這個for循環的功能!
0xF0 & data[i]根據“與運算”的邏輯規則,我們得知其目的是為了使得data[i]的低四位都變成0;然后>>>4無符號右移4位,取得高四位所表示的數值,我們可以換一種理解,“與”的目的是讓低四位變成0,然后右移是為了去掉高四位后面的四個0,這樣我們就拿到了高四位表示的值(我們已經知道二進制的四位數的取值范圍就是0到15)。然后將這個高四位表示的值作為index找到對應的DIGITS_LOWER數組中的字符,并賦值給out[j],然后++兩次,代表高位一次和低位一次,這樣,我們就成功的完成了一組高四位低四位的拆分、替換、重組,然后只要循環data數組的長度就可以對每個byte元素進行相同的拆分重組操作了。
最后返回char[] out 。
這是一個非常簡單實用的byte[]數組轉化為String的小方法,API的開發者巧妙的利用'0'到'f'這十六個字符“代表” 0 到 15這十六個十六進制數,然后將byte元素逐一拆分并重組成一個新的char[]數組。
以上是本人經過實踐之后對源碼的分析和理解,喜歡的朋友記得點贊哦,作為一個工作僅兩年的小菜鳥來說誠然是不小的鼓勵,如果同行的朋友有什么問題和建議,還請不吝賜教!
【2019-07-02 追加更新
這里需要注意一個問題,即byte的取值范圍問題,原來之前的知識有漏洞,忽視了下面這句代碼的重要性:
out[j++] = DIGITS_UPPER[(0xF0 & data[i]) >>> 4];這句代碼的信息量很大,首先, >>> 是為了取byte元素的高四位,這個不難理解,但是為什么一定要先進行(0xF0 & data[i]) 運算?這個 & 運算是不是可有可無?
一開始我也認為是可有可無的,但實際上并不是!byte 變量的取值范圍很小,是 [-128? ~? 127],同樣是 1000 0000,int類型所表示的是128, 而byte類型鎖表示的數是 -128 ,因此,必須先將 byte 變量進行擴容轉正,那么實現方法就是 & 0xF0,否則的話,如果 一個 byte類型的F0 ,表示的數代表 -16,再 >>> 4 的話,就會得到 -1 ,很明顯不符合要求,因此& 0xF0 這句代碼必不可少。
另外一個問題是 j++ ,++ 在后代表先取值,在加 1,那么out[j++] = DIGITS_UPPER[(0xF0 & data[i]) >>> 4]; 就相當于下面這句代碼:
out[j] = DIGITS_UPPER[(0xF0 & data[i]) >>> 4]; j++;?總之,千萬別小看這些細節,搞不明白還真容易翻船!不得不佩服這些寫底層API “輪子”的大神,能把各種語法和知識用到極致!
如果老鐵們有幸看到這篇文章,而且也玩頭條的話,歡迎關注我的頭條號,不得不說,以前寫博客是兩三天一更,已經感覺很累,現在頭條號要日更,還要篇篇精品,真的非常累,希望大家多多關注!頭條號搜索 “Java圣斗士” ,謝謝老鐵了!
】
總結
以上是生活随笔為你收集整理的Apache工具包方法——Hex.encodeHexString(byte[] data)源码浅析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MongoDB学习手记
- 下一篇: Git初学札记(四)————Git Pu