HOWTO · Java
Java 中將位元組陣列轉換為整數
本教程演示如何在 Java 中將位元組陣列轉換為 int
本頁內容
在 Java 中處理網路通訊協定、檔案 I/O 或二進位資料格式時,將位元組陣列轉換為整數是常見的工作。正確做法取決於這些位元組代表的意義:有時多個位元組共同編碼成一個 int,有時每個位元組各自代表獨立的數值。本文介紹 Java 核心方法——ByteBuffer、DataInputStream、手動位元移位與 BigInteger——以及決定結果是否正確的位元組順序與正負號規則。
使用 ByteBuffer 將四個位元組轉換為 int
ByteBuffer(位於 java.nio)是進行此轉換最直接的 Java 核心工具。wrap 方法會建立與陣列共用儲存空間的緩衝區,而 getInt() 會從緩衝區目前位置開始讀取四個位元組,並將其解讀為一個 int。執行此範例:
本文所有範例的程式碼與輸出都保持位元組一致,因此其中顯示的標籤(例如 Converted Integer Value:)保留英文原文。
本節中的程式碼以及顯示的標籤或輸出仍為英文。
import java.nio.ByteBuffer;
public class ByteArrayToInt {
public static void main(String[] args) {
byte[] byteArray = {0x00, 0x01, 0x03, 0x10};
int intValue = ByteBuffer.wrap(byteArray).getInt();
System.out.println("Converted Integer Value: " + intValue);
}
}
輸出:
Converted Integer Value: 66320
ByteBuffer 在所有平台上預設都採用大端序:陣列中的第一個位元組會被視為最高有效位元組。如果資料是以小端序寫入,請在讀取前使用 ByteOrder 明確選擇該順序:
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class ByteArrayToIntOrder {
public static void main(String[] args) {
byte[] byteArray = {0x00, 0x01, 0x03, 0x10};
int bigEndianValue = ByteBuffer.wrap(byteArray).getInt();
int littleEndianValue =
ByteBuffer.wrap(byteArray).order(ByteOrder.LITTLE_ENDIAN).getInt();
System.out.println("big-endian: " + bigEndianValue);
System.out.println("little-endian: " + littleEndianValue);
}
}
輸出:
big-endian: 66320
little-endian: 268632320
這裡有兩個細節很重要。首先,getInt() 會在緩衝區目前位置讀取,並將位置向前移動四個位元組,因此對同一個緩衝區再次呼叫時會讀取接下來的四個位元組;新的 wrap 永遠從位置 0 開始。其次,當剩餘位元組不足四個時,getInt() 會拋出 BufferUnderflowException,因此讀取前應檢查輸入長度。
使用 DataInputStream 讀取四個位元組
當位元組透過串流傳入,或需要連續讀取多個原始型別的值時,DataInputStream 很適合。先將陣列包裝在 ByteArrayInputStream 中,再將其包裝在 DataInputStream 中,然後呼叫 readInt():
本節中的程式碼以及顯示的標籤或輸出仍為英文。
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
public class ByteArrayToIntWithDataInputStream {
public static void main(String[] args) throws IOException {
byte[] byteArray = {0x00, 0x01, 0x03, 0x10};
try (DataInputStream input =
new DataInputStream(new ByteArrayInputStream(byteArray))) {
int intValue = input.readInt();
System.out.println("Converted Integer Value: " + intValue);
}
}
}
輸出:
Converted Integer Value: 66320
readInt() 一律以大端序解讀接下來的四個位元組,與平台無關。當串流中剩餘位元組不足四個時,它會拋出 EOFException;你可以捕捉此例外來回報輸入過短,而不是讓程式崩潰:
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
public class ByteArrayToIntShortInput {
public static void main(String[] args) throws IOException {
byte[] byteArray = {0x03, 0x10};
try (DataInputStream input =
new DataInputStream(new ByteArrayInputStream(byteArray))) {
int intValue = input.readInt();
System.out.println("Converted Integer Value: " + intValue);
} catch (EOFException e) {
System.out.println("readInt() needs four bytes, but only " + byteArray.length + " remain");
}
}
}
輸出:
readInt() needs four bytes, but only 2 remain
透過位元組移位組成 int
自行移位位元組能清楚展示轉換的運作方式,並可完全控制順序與長度。針對每個位元組,將累積值向左移 8 位元,再用 OR 併入下一個位元組:
本節中的程式碼以及顯示的標籤或輸出仍為英文。
public class ByteArrayToIntManually {
public static void main(String[] args) {
byte[] byteArray = {0x00, 0x01, 0x03, 0x10};
int intValue = 0;
for (byte b : byteArray) {
intValue = (intValue << 8) | (b & 0xFF);
}
System.out.println("Converted Integer Value: " + intValue);
}
}
輸出:
Converted Integer Value: 66320
遮罩 b & 0xFF 是必要的,因為 Java 的 byte 型別帶有正負號:0x80 會儲存為 -128。若沒有遮罩,| 會合併 0xFFFFFF80 而非 0x00000080,導致所有較高位元組損毀。此迴圈依大端序組合位元組——第一個陣列元素是最高有效位元組——並假設陣列正好包含四個位元組。若要使用不同順序,請反向遍歷陣列;若要處理更多位元組,請改用 long 而非 int 累積。
使用 BigInteger 解讀位元組陣列
BigInteger 的建構子可直接接受位元組陣列,並將其解讀為任意長度、帶正負號且採用大端序的二補數數值。接著 intValue() 會將結果縮窄為 int:
本節中的程式碼以及顯示的標籤或輸出仍為英文。
import java.math.BigInteger;
public class ByteArrayToIntWithBigInteger {
public static void main(String[] args) {
byte[] byteArray = {0x00, 0x01, 0x03, 0x10};
int intValue = new BigInteger(byteArray).intValue();
System.out.println("Converted Integer Value: " + intValue);
}
}
輸出:
Converted Integer Value: 66320
由於此解讀帶有正負號,最高位元為 1 會產生小於 0 的值;若要使用無號解讀,請將 1 作為第一個建構子引數傳入 signum。請注意,intValue() 只保留低 32 位元——它會靜默截斷無法放入 int 的值:
import java.math.BigInteger;
public class BigIntegerSignAndRange {
public static void main(String[] args) {
byte[] allOnes = {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF};
System.out.println("signed: " + new BigInteger(allOnes));
System.out.println("unsigned: " + new BigInteger(1, allOnes));
System.out.println("narrowed to int: " + new BigInteger(1, allOnes).intValue());
}
}
輸出:
signed: -1
unsigned: 4294967295
narrowed to int: -1
當陣列長度超過四個位元組,或在縮窄前需要取得無號數值大小時,BigInteger 是自然的選擇。
分別轉換每個位元組
上述方法都是將多個位元組合併為一個數字。如果每個位元組都是獨立值——例如感測器讀數、色彩通道或通訊協定欄位——就應改為逐一轉換元素。請注意其中的差異:對兩個位元組使用 getShort() 或 readShort() 會產生一個合併的 short,而不是分別轉換每個位元組。
也請記得,Java 的 byte 型別帶有正負號,範圍是 -128 到 127。當目標是 0 到 255 的無號值時,請使用 Byte.toUnsignedInt(自 Java 8 起可用)或等效的 b & 0xFF 遮罩:
本節中的程式碼以及顯示的標籤或輸出仍為英文。
public class PrintEachByte {
public static void main(String[] args) {
byte[] byteArray = {0x03, 0x04, (byte) 0x80, (byte) 0xFF};
for (byte b : byteArray) {
System.out.println("signed: " + b + " unsigned: " + Byte.toUnsignedInt(b));
}
}
}
輸出:
signed: 3 unsigned: 3
signed: 4 unsigned: 4
signed: -128 unsigned: 128
signed: -1 unsigned: 255
選擇方法並處理邊界情況
每種方法適用於不同情境:
ByteBuffer是從陣列讀取四位元組int的首選:它很精簡,可透過ByteOrder支援兩種位元組順序,並提供getLong()、getShort()等方法處理其他寬度。DataInputStream適合已在使用串流,且需要連續讀取多個具型別值的程式碼;其讀取永遠採用大端序。- 手動移位需要多寫幾行程式碼,但能精確控制順序、偏移量與寬度,也不需要緩衝區或串流物件。
BigInteger可處理超過四個位元組的陣列,並能在縮窄前明確選擇有號或無號解讀。- 當每個元素本身就是獨立數值,而不是某個數字的一部分時,請使用
Byte.toUnsignedInt逐位元組轉換。
無論選擇哪種方法,轉換前都應檢查以下幾點:
- 輸入長度: 對長度不足的陣列執行四位元組讀取會失敗——
ByteBuffer會拋出BufferUnderflowException,DataInputStream會拋出EOFException,手動程式碼則可能拋出ArrayIndexOutOfBoundsException或產生不易察覺的錯誤結果。 - 位元組順序: 讀取端使用的順序必須與位元組寫入時的順序一致;
ByteBuffer預設採用大端序,並可使用ByteOrder切換,而DataInputStream的讀取永遠是大端序,沒有順序選項。 - 正負號與範圍:
getInt()和readInt()會回傳帶正負號的int,因此超過Integer.MAX_VALUE的無號 32 位元值會顯示為小於 0。當來源資料是無號值時,請使用Integer.toUnsignedLong(intValue)或以long累積。 - 緩衝區位置: 重複使用
ByteBuffer會從其目前位置繼續,而不是從頭開始;若要重新讀取陣列,請呼叫rewind()或再次使用wrap。