字节的游戏
業(yè)務(wù)處理上,有時會直接對字節(jié)進行操作。例如實現(xiàn)私有協(xié)議,對校驗位進行檢測,敏感數(shù)據(jù)加密等。博主查了 一下網(wǎng)上的資料,發(fā)現(xiàn)有不少都是錯誤的。甚至連《Thinking in Java》的解釋都很令人困惑,以下是從書中摘錄的原文:
如果對char、byte或者short類型的數(shù)值驚醒移位處理,那么在移位之前,他們會被轉(zhuǎn)換為int類型,并且得到的結(jié)果也是一個int類型。只有數(shù)值右端的低5位才有用。
當(dāng)時讀到這一句的時候,我理解了很久,至今沒有明白“只有數(shù)值右端的低5位才有用”的含義。理解字節(jié)處理的基本方法就是動手操作,下面我會結(jié)合用例進行解釋。
首先,我們需要理解幾個基礎(chǔ)概念。一般來說,字節(jié)是我們可以用語言處理的最小對象,無論是C/C++還是Java都沒有直接提供bit類型。1 byte = 8 bit,除去最左側(cè)的符號位1byte可以描述的范圍是:-128 ~ 127。但是在大多數(shù)的業(yè)務(wù)處理中,我們通常會采用無符號位,即用1byte表示:0 ~ 255。其次,常見的移位操作符有左移(<<) 和右移 (>>),比較容易忽視的是右移操作,如果最左側(cè)的符號位為1則右移是在高位插入的是——1。因此Java中增加了一種“無符號”右位移操作符>>>,通常用不上了解即可。最后,如果我們采用byte[]來表示一種數(shù)據(jù)類型,數(shù)組下標(biāo)從小到大即內(nèi)存地址的從低位到高位。記住這個概念非常重要,后面我會引入大端模式與小端模式。
為了讓大家理解以上概念,下面看兩個例子:
1. 假設(shè)byte x = 127,對它執(zhí)行左移1位的操作 x = ?
byte x = 127; x <<= 1; System.out.println(Integer.toHexString(x));在代碼執(zhí)行之前我們先使用計算器計算一下:BIN(1111 1110) HEX(FE),代碼的執(zhí)行結(jié)果為:FFFFFFFE。原因是對x左移1位超出了byte的表示范圍,Java自動在左側(cè)補位,由于最高位是1,因此我們獲得了一個怪異的結(jié)果。那么有什么辦法得到一個正確的結(jié)果呢?
byte x = 127; x <<= 1; System.out.println(Integer.toHexString(x & 0xFF));2. 假設(shè)byte x = 1,對它執(zhí)行左移32位的操作 x = ?
byte x = 1; System.out.println(x << 32);答案是1。這個結(jié)論比較怪異而且確實是一個坑,大家只需要記住:對一個int值來說,左移32位等于它的原始值;對于一個long值來說,左移64位等于它的原始值。
在理解了這些基本概念以后,我們已經(jīng)做好了進入字節(jié)世界的準(zhǔn)備。
我們?nèi)绾斡?個字節(jié)的大端模式表示一個整型變量?
對大端模式的定義為:數(shù)據(jù)的高字節(jié)保存在內(nèi)存的低地址中,而數(shù)據(jù)的低字節(jié)保存在內(nèi)存的高地址中。這個說法很繞而且也不利于理解,對于數(shù)字常量來說0x1234,1即為高位,4即為低位。而對于byte[4]來說,bs[0]即為地址低位,bs[3]即為地址高位。這樣看來就很清楚了。大端模式符合人們的閱讀模式。
int i = 0x1234; byte[] bs = new byte[4]; bs[3] = (byte) (i & 0xFF); bs[2] = (byte) (i >> 8 & 0xFF); bs[1] = (byte) (i >> 16 & 0xFF); bs[0] = (byte) (i >> 24 & 0xFF);for(byte b : bs) {System.out.println(Integer.toHexString(b)); }更抽象的算法,大家可以在理解了上面的例子以后自己封裝。
反過來我們將以大端模式生成的4個字節(jié)還原為一個整型數(shù)?
int x = bs[3] & 0xFF; x |= bs[2] & 0xFF << 8; x |= bs[1] & 0xFF << 16; x |= bs[0] & 0xFF << 24;System.out.println(Integer.toHexString(x));注意:為了得到正確的結(jié)果,我們在對byte進行移位前一定要先做位與(&)操作。
接下來我們需要升級問題,將一個8個字節(jié)寬度的符合大端模式的字節(jié)數(shù)組還原為一個長整型數(shù)。
long x = bs[7] & 0xFF; x |= (bs[6] & 0xFF) << 8; x |= (bs[5] & 0xFF) << 16; x |= (bs[4] & 0xFF) << 24; x |= (bs[3] & 0xFF) << 32; x |= (bs[2] & 0xFF) << 40; x |= (bs[1] & 0xFF) << 48; x |= (bs[0] & 0xFF) << 56; System.out.println(Long.toHexString(x));?
似乎我們很容易按照整型的轉(zhuǎn)換方式得到以上算法。不幸的是,這樣做是錯誤的。如果這個byte[]表示的數(shù)字范圍超過整型數(shù)的上限,我們將無法獲得正確的長整型數(shù)。原因是Java默認在對byte進行移位操作前會轉(zhuǎn)換為int類型,還記得上面我們讓大家記住“對一個int值來說,左移32位等于它的原始值”嗎?正確的做法應(yīng)該是這樣:
long x = bs[7] & 0xFF; x |= ((long)bs[6] & 0xFF) << 8; x |= ((long)bs[5] & 0xFF) << 16; x |= ((long)bs[4] & 0xFF) << 24; x |= ((long)bs[3] & 0xFF) << 32; x |= ((long)bs[2] & 0xFF) << 40; x |= ((long)bs[1] & 0xFF) << 48; x |= ((long)bs[0] & 0xFF) << 56; System.out.println(Long.toHexString(x));?
至此我們應(yīng)該可以很輕松的解決有關(guān)字節(jié)轉(zhuǎn)換的各種難題了,但是上面的這些算法未免顯得太不優(yōu)美,幸虧Java早就為我們想到了這一點。本著不要重復(fù)造輪子的觀點,我提供了一套工具。
/*** 任意字節(jié)寬度轉(zhuǎn)換為標(biāo)準(zhǔn)整型數(shù)*/ public static int bytesToInt(byte[] bytes, int byteNum, ByteOrder order) {ByteBuffer buffer = ByteBuffer.allocate(4);buffer.order(order);buffer.put(bytes, 0, bytes.length);buffer.put(new byte[buffer.limit() - byteNum], 0, buffer.limit() - byteNum);buffer.flip();return buffer.getInt(); }/*** 長整型數(shù)轉(zhuǎn)換為指定字節(jié)寬度*/ public static byte[] longToBytes(long x, int byteNum, ByteOrder order) {ByteBuffer buffer = ByteBuffer.allocate(8);buffer.order(order);buffer.putLong(0, x);return Arrays.copyOfRange(buffer.array(), 0, byteNum); }/*** 任意字節(jié)寬度轉(zhuǎn)換為長整型*/ public static long bytesToLong(byte[] bytes, int byteNum, ByteOrder order) {ByteBuffer buffer = ByteBuffer.allocate(8);buffer.order(order);buffer.put(bytes, 0, bytes.length);buffer.put(new byte[buffer.limit() - byteNum], 0, buffer.limit() - byteNum);buffer.flip();return buffer.getLong(); }/*** 長整型數(shù)轉(zhuǎn)換為標(biāo)準(zhǔn)的8字節(jié)寬度*/ public static byte[] longToBytes(long x, ByteOrder order) {ByteBuffer buffer = ByteBuffer.allocate(8);buffer.order(order);buffer.putLong(0, x);return buffer.array(); }/*** 標(biāo)準(zhǔn)8字節(jié)寬度轉(zhuǎn)換為長整型數(shù)*/ public static long bytesToLong(byte[] bytes, ByteOrder order) {ByteBuffer buffer = ByteBuffer.allocate(8);buffer.order(order);buffer.put(bytes, 0, bytes.length);buffer.flip();return buffer.getLong(); }/*** 整型數(shù)轉(zhuǎn)換為標(biāo)準(zhǔn)4字節(jié)寬度*/ public static byte[] intToBytes(int x, ByteOrder order) {ByteBuffer buffer = ByteBuffer.allocate(4);buffer.order(order);buffer.putInt(0, x);return buffer.array(); }/*** 標(biāo)準(zhǔn)4字節(jié)寬度轉(zhuǎn)換為整型數(shù)*/ public static int bytesToInt(byte[] bytes, ByteOrder order) {ByteBuffer buffer = ByteBuffer.allocate(4);buffer.order(order);buffer.put(bytes, 0, bytes.length);buffer.flip();return buffer.getInt(); }轉(zhuǎn)載于:https://www.cnblogs.com/learnhow/p/10800153.html
總結(jié)
                            
                        - 上一篇: 分辨率与栅格系统的对应关系:
 - 下一篇: 生物-脑-脑容量:脑容量