Java 與 C/C++、Delphi、C#、Go、Python 等語言進行網絡通訊、文件共享、共享內存交互時,必須解決**字節序 (Endianness)、基本類型內存佈局、字符串編碼、結構體對齊**四大核心差異。本文提供一個生產級 FormatTransfer 工具類,涵蓋 int/short/float/byte[]/String 相互轉換、大小端互轉、字節序反轉、打印調試等功能,並深度解析底層原理與使用陷阱。
| 維度 | Java (JVM 規範) | C/C++ (x86/x64 Windows/Linux) | 網絡傳輸標準 |
|---|---|---|---|
| 字節序 | Big-Endian (高位在前,固定) | Little-Endian (低位在前,Intel 架構) | Big-Endian (Network Byte Order) |
| int | 4 字節,有符號 | 4 字節,有符號 (通常) | 4 字節,Big-Endian |
| short | 2 字節,有符號 | 2 字節,有符號 | 2 字節,Big-Endian |
| float | 4 字節,IEEE 754 | 4 字節,IEEE 754 | 4 字節,Big-Endian |
| double | 8 字節,IEEE 754 | 8 字節,IEEE 754 | 8 字節,Big-Endian |
| char | 2 字節,UTF-16BE | 1 字節 (char) / 2 字節 (wchar_t) | 視協議定 |
| String | UTF-16 內存,getBytes() 可指定編碼 | char* (以 \0 結束) / Pascal String (長度前綴) | 通常 UTF-8 + 長度前綴或分隔符 |
// int → Little-Endian byte[4] (低位在前,符合 C/C++ 內存布局)
public static byte[] toLH(int n) {
byte[] b = new byte[4];
b[0] = (byte) (n & 0xFF);
b[1] = (byte) ((n >> 8) & 0xFF);
b[2] = (byte) ((n >> 16) & 0xFF);
b[3] = (byte) ((n >> 24) & 0xFF);
return b;
}
// int → Big-Endian byte[4] (高位在前,符合 Java/網絡標準)
public static byte[] toHH(int n) {
byte[] b = new byte[4];
b[3] = (byte) (n & 0xFF);
b[2] = (byte) ((n >> 8) & 0xFF);
b[1] = (byte) ((n >> 16) & 0xFF);
b[0] = (byte) ((n >> 24) & 0xFF);
return b;
}
// short 同理
public static byte[] toLH(short n) { ... }
public static byte[] toHH(short n) { ... }
// float → byte[] (通過 int 位模式轉換,保證 IEEE 754 位模式不變)
public static byte[] toLH(float f) { return toLH(Float.floatToRawIntBits(f)); }
public static byte[] toHH(float f) { return toHH(Float.floatToRawIntBits(f)); }
// Big-Endian byte[4] → int
public static int hBytesToInt(byte[] b) {
int s = 0;
for (int i = 0; i < 3; i++) {
s += (b[i] >= 0 ? b[i] : b[i] + 256);
s <<= 8;
}
s += (b[3] >= 0 ? b[3] : b[3] + 256);
return s;
}
// Little-Endian byte[4] → int
public static int lBytesToInt(byte[] b) {
int s = 0;
for (int i = 0; i < 3; i++) {
s += (b[3-i] >= 0 ? b[3-i] : b[3-i] + 256);
s <<= 8;
}
s += (b[0] >= 0 ? b[0] : b[0] + 256);
return s;
}
// short 同理
public static short hBytesToShort(byte[] b) { ... }
public static short lBytesToShort(byte[] b) { ... }
// Big-Endian byte[4] → float
public static float hBytesToFloat(byte[] b) {
int i = ((b[0]&0xFF)<<24) | ((b[1]&0xFF)<<16) | ((b[2]&0xFF)<<8) | (b[3]&0xFF);
return Float.intBitsToFloat(i);
}
// Little-Endian byte[4] → float
public static float lBytesToFloat(byte[] b) {
int i = ((b[3]&0xFF)<<24) | ((b[2]&0xFF)<<16) | ((b[1]&0xFF)<<8) | (b[0]&0xFF);
return Float.intBitsToFloat(i);
}
// String → byte[] (指定編碼,默認 UTF-8,長度不足右填充空格)
public static byte[] stringToBytes(String s, int length, String charset) {
byte[] src = s.getBytes(Charset.forName(charset));
if (src.length >= length) return Arrays.copyOf(src, length);
byte[] dst = new byte[length];
System.arraycopy(src, 0, dst, 0, src.length);
Arrays.fill(dst, src.length, length, (byte) ' ');
return dst;
}
// byte[] → String (指定編碼,去除尾部填充空格)
public static String bytesToString(byte[] b, String charset) {
int end = b.length;
while (end > 0 && b[end-1] == ' ') end--;
return new String(b, 0, end, Charset.forName(charset));
}
// 將 int 值按字節序反轉 (Big-Endian 值 ↔ Little-Endian 值)
public static int reverseInt(int i) {
return hBytesToInt(toLH(i)); // 等價於 ByteBuffer.wrap(toLH(i)).order(BIG_ENDIAN).getInt()
}
public static short reverseShort(short s) {
return hBytesToShort(toLH(s));
}
public static float reverseFloat(float f) {
return hBytesToFloat(toLH(f));
}
// 字節數組整體反序
public static byte[] bytesReverseOrder(byte[] b) {
byte[] r = new byte[b.length];
for (int i = 0; i < b.length; i++) r[b.length - 1 - i] = b[i];
return r;
}
public static void printBytes(byte[] bb) {
for (byte b : bb) System.out.printf("%02X ", b & 0xFF);
System.out.println();
}
// Java 發送請求 (協議:包頭 4 字節 Big-Endian 長度 + JSON Body UTF-8) ByteBuffer buf = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN); buf.putInt(jsonBytes.length); byte[] header = buf.array(); // toHH(jsonBytes.length) 等效 outputStream.write(header); outputStream.write(jsonBytes); // C++ 接收 (需手動處理網絡字節序) uint32_t netLen; recv(sock, &netLen, 4, MSG_WAITALL); int len = ntohl(netLen); // 網絡序(Big-Endian) → 主機序(Little-Endian) char* body = new char[len]; recv(sock, body, len, MSG_WAITALL);
// C 結構體 (packed, Little-Endian)
#pragma pack(1)
struct SensorData {
uint32_t timestamp; // 4 bytes
int16_t temperature; // 2 bytes
float humidity; // 4 bytes
};
// Java 解析
byte[] record = new byte[10];
raf.readFully(record);
long ts = FormatTransfer.lBytesToInt(Arrays.copyOfRange(record, 0, 4)) & 0xFFFFFFFFL;
short temp = FormatTransfer.lBytesToShort(Arrays.copyOfRange(record, 4, 2));
float hum = FormatTransfer.lBytesToFloat(Arrays.copyOfRange(record, 6, 4));
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
java.nio.ByteBuffer | JDK 原生、支援大小端切換、直接內存、視圖零拷貝 | API 冗長、無結構定義 | 高性能網絡編程、文件解析、Netty 基礎 |
Netty ByteBuf | 引用計數、池化、複合緩衝、內置編解碼器 | 依賴 Netty | 高並發網絡框架 |
| Protocol Buffers | Schema 驅動、跨語言、向前兼容、體積小、速度快 | 需編譯 .proto、學習成本 | 微服務 RPC、持久化、異構系統集成 |
| FlatBuffers | 零解析、內存映射直接訪問、無分配 | Schema 較複雜、工具鏈較少 | 遊戲、實時系統、嵌入式 |
| FormatTransfer (本工具) | 零依賴、輕量、API 直觀、適合簡單協議/舊系統對接 | 手動管理偏移、無 Schema 驗證、性能略低於 ByteBuffer | 輕量級集成、測試工具、遷移過渡期 |
byte 為有符號 (-128~127)。讀取 C uint8_t 需 b & 0xFF 擴展為 int。Float.floatToRawIntBits / Double.doubleToRawLongBits,不可用 Float.floatToIntBits (會標準化 NaN)。#pragma pack(1) 或 `__attribute__((packed))` 可禁用。Java 解析時必須知曉具體佈局(含填充字節)。\0 結尾,Pascal 風格長度前綴,或固定長度填充。雙方必須約定統一格式,解析時按約定截取/填充。HH=Big-Endian, LH=Little-Endian,切勿混淆。異構語言數據交互的核心是**精確控制內存字節佈局**。FormatTransfer 通過位運算實現基本類型與字節數組的雙向無損轉換,支援大小端自由切換,適合輕量級協議對接、舊系統集成、測試驗證場景。現代高性能系統建議遷移至 ByteBuffer/Netty/Protobuf,獲得 Schema 驅動、零拷貝、跨語言代碼生成等綜合優勢。無論選用何種工具,**字節序、類型大小、對齊、字符串約定**四大鐵律必須刻在腦海,代碼審查時逐項核對,方能避免「調試三天,字節序一行」的慘痛教訓。