前言
Java 編程中常會遇到需要進行字節處理的地方,本篇文章就來探討編程中會遇到的字節處理問題。
字節序
字節序(endianness)是對於多字節數據來說的,它描述了多字節數據存儲的順序,分為大端字節序(big endian)和小端字節序(little endian)兩種。
大端序即高位字節位於低地址,而小端字節序則與之相反。例如 4 字節的數據 0x01234567,其 大、小端字節序存儲順序如下。
Big Endian
address |0x100|0x101|0x102|0x103|
content |01 |23 |45 |67 |
Little Endian
address |0x100|0x101|0x102|0x103|
content |67 |45 |23 |01 |
為保證數據在不同設備進行傳輸時能被正確解析,規定了使用大端字節序作為網絡字節序。
字節序轉換
由於 Java 使用大端字節序在內存中進行數據存儲,所以進行網絡傳輸時不需要進行字節序轉換。不過除了網絡編程,還是有需要小端字節序的情況,下面就來討論如何進行字節序的轉換。
基本類型字節數
下表給出了 Java 八種基本類型的所占的字節數,可以看到里面六種類型都是多字節的,而這六種在轉換時都是需要處理的。
| 類型 | 字節數 |
|---|---|
| byte | 1 |
| boolean | 1 |
| char | 2 |
| short | 2 |
| int | 4 |
| float | 4 |
| long | 8 |
| double | 8 |
這里需要強調一點,基本類型的數組形式雖是多字節的,但其字節序只和數組元素有關,而與數組元素的順序無關。例如存在短整形數組 {0x0001, 0x0002} (Java 默認的大端字節序),則其小端字節序是 {0x0100, 0x0200} 。
字節緩沖區
字節緩沖區(ByteBuffer)常用於數據的字節級處理,可以利用靜態方法 ByteBuffer allocate(int) 來申請一塊固定大小的緩沖區,或者使用 ByteBuffer wrap(byte[]) 包裝一個現有的字節數組。
字節緩沖區提供了 void order(ByteOrder) 方法來設置該字節緩沖區的字節序,提供了 asTypeBuffer() (其中 Type 為多字節的基本類型)來將字節緩沖區作為其他基本類型緩沖區,以便於插入其他基本類型,利用這些特性便可實現基本類型的字節序轉換。
基本類型及其數組的字節序轉換
下面例子利用了字節緩沖區來進行整型的字節序轉換,而只需將 asIntBuffer() 改成相應基本類型的 asTypeBuffer() 方法,就可以進行其它基本類型的字節序轉換。
int data = 0x01020304;
ByteBuffer buf = ByteBuffer.allocate(Integer.BYTES);
buf.asIntBuffer().put(data);
// [1, 2, 3, 4]
System.out.println(Arrays.toString(buf.array()));
buf.order(ByteOrder.LITTLE_ENDIAN);
buf.asIntBuffer().put(data);
// [4, 3, 2, 1]
System.out.println(Arrays.toString(buf.array()));
對於基本類型的數組,只需將 data 數據改成數組即可,如下面給出的例子。
int[] data = new int[]{0x01020304, 0x05060708};
ByteBuffer buf = ByteBuffer.allocate(Integer.BYTES * data.length);
buf.asIntBuffer().put(data);
// [1, 2, 3, 4, 5, 6, 7, 8]
System.out.println(Arrays.toString(buf.array()));
buf.order(ByteOrder.LITTLE_ENDIAN);
buf.asIntBuffer().put(data);
// [4, 3, 2, 1, 8, 7, 6, 5]
System.out.println(Arrays.toString(buf.array()));
字符串的字節序轉換
Java 中字符類型使用 Unicode 字符集,所以占用兩個字節,而字符串只需要將其轉成字符數組即可進行字節序轉換。
char[] data = new String("example").toCharArray();
ByteBuffer buf = ByteBuffer.allocate(Character.BYTES * data.length);
buf.order(ByteOrder.LITTLE_ENDIAN);
buf.asCharBuffer().put(data);
字符串編碼與字節數組
編程中通常需要將字符串轉字節數組,而這就涉及到字符串編解碼,但 Java 提供了很便捷的方式來進行轉換,所以無需關心如何字符串編解碼。
byte[] buf = new String("example").getBytes(); // use platform's default charset
注意上面使用了平台的默認的字符集(可利用 Charset.defaultCharset() 獲取平台默認字符集)進行解碼,而不是上面提到的 Unicode 字符集,但可以如下面例子一樣指定字符集。
byte[] buf = new String("example").getBytes("UTF-8"); // use specify charset 'UTF-8'
