在網絡通訊、文件存儲、數據庫 Blob 字段操作等場景中,Java 基本類型與 byte[] 互轉是高頻剛需。本文提供一個無依賴、高性能、覆蓋全類型的 ByteUtil 工具類,採用位運算實現零拷貝轉換,並解決浮點數精度、字符編碼等易踩坑點。
& 0xFF 取低 8 位,>> 8 右移 8 位,避免符號擴展問題getBytes()/getXxx() 方法,調用鏈路清晰public class ByteUtil {
/** short (2 bytes) */
public static byte[] getBytes(short data) {
return new byte[] {
(byte) (data & 0xFF),
(byte) ((data >> 8) & 0xFF)
};
}
/** char (2 bytes) - UTF-16BE */
public static byte[] getBytes(char data) {
return new byte[] {
(byte) (data & 0xFF),
(byte) ((data >> 8) & 0xFF)
};
}
/** int (4 bytes) */
public static byte[] getBytes(int data) {
return new byte[] {
(byte) (data & 0xFF),
(byte) ((data >> 8) & 0xFF),
(byte) ((data >> 16) & 0xFF),
(byte) ((data >> 24) & 0xFF)
};
}
/** long (8 bytes) */
public static byte[] getBytes(long data) {
byte[] bytes = new byte[8];
for (int i = 0; i < 8; i++) {
bytes[i] = (byte) ((data >> (i * 8)) & 0xFF);
}
return bytes;
}
/** float (4 bytes) - IEEE 754 */
public static byte[] getBytes(float data) {
return getBytes(Float.floatToIntBits(data));
}
/** double (8 bytes) - IEEE 754 */
public static byte[] getBytes(double data) {
return getBytes(Double.doubleToLongBits(data));
}
/** String → byte[] (指定編碼) */
public static byte[] getBytes(String data, String charsetName) {
try {
return data.getBytes(Charset.forName(charsetName));
} catch (UnsupportedEncodingException e) {
throw new IllegalArgumentException("Unsupported charset: " + charsetName, e);
}
}
/** String → byte[] (默認 GBK) */
public static byte[] getBytes(String data) {
return getBytes(data, "GBK");
}
}
/** byte[] → short */
public static short getShort(byte[] bytes) {
if (bytes.length < 2) throw new IllegalArgumentException("Need 2 bytes");
return (short) ((bytes[0] & 0xFF) | ((bytes[1] & 0xFF) << 8));
}
/** byte[] → char */
public static char getChar(byte[] bytes) {
if (bytes.length < 2) throw new IllegalArgumentException("Need 2 bytes");
return (char) ((bytes[0] & 0xFF) | ((bytes[1] & 0xFF) << 8));
}
/** byte[] → int */
public static int getInt(byte[] bytes) {
if (bytes.length < 4) throw new IllegalArgumentException("Need 4 bytes");
return (bytes[0] & 0xFF)
| ((bytes[1] & 0xFF) << 8)
| ((bytes[2] & 0xFF) << 16)
| ((bytes[3] & 0xFF) << 24);
}
/** byte[] → long */
public static long getLong(byte[] bytes) {
if (bytes.length < 8) throw new IllegalArgumentException("Need 8 bytes");
long val = 0;
for (int i = 0; i < 8; i++) {
val |= ((long) bytes[i] & 0xFF) << (i * 8);
}
return val;
}
/** byte[] → float */
public static float getFloat(byte[] bytes) {
return Float.intBitsToFloat(getInt(bytes));
}
/** byte[] → double */
public static double getDouble(byte[] bytes) {
return Double.longBitsToDouble(getLong(bytes));
}
/** byte[] → String (指定編碼) */
public static String getString(byte[] bytes, String charsetName) {
try {
return new String(bytes, Charset.forName(charsetName));
} catch (UnsupportedEncodingException e) {
throw new IllegalArgumentException("Unsupported charset: " + charsetName, e);
}
}
/** byte[] → String (默認 GBK) */
public static String getString(byte[] bytes) {
return getString(bytes, "GBK");
}
}
// 錯誤寫法:byte 有符號,直接左移會符號擴展
byte b = -1; // 0xFF
int wrong = b << 8; // 0xFFFF0000 (錯誤!)
// 正確寫法:& 0xFF 先轉無符號
int correct = (b & 0xFF) << 8; // 0x0000FF00 (正確!)
// float 32 位 IEEE 754 標準
float f = 1.5f;
int bits = Float.floatToIntBits(f); // 獲取位模式 0x3FC00000
byte[] bytes = getBytes(bits); // 作為 int 寫入
// 反向還原
float restored = Float.intBitsToFloat(getInt(bytes)); // 精確還原 1.5f
public class ByteUtilBuffer {
// 線程不安全,高併發下需 ThreadLocal 或對象池
private static final ThreadLocal BUFFER = ThreadLocal.withInitial(() -> ByteBuffer.allocate(8).order(ByteOrder.BIG_ENDIAN));
public static byte[] getBytesInt(int value) {
ByteBuffer bb = BUFFER.get();
bb.clear();
bb.putInt(value);
return Arrays.copyOfRange(bb.array(), 0, 4);
}
public static int getInt(byte[] bytes, int offset) {
ByteBuffer bb = BUFFER.get();
bb.clear();
bb.put(bytes, offset, 4);
bb.flip();
return bb.getInt();
}
// 其他類型同理...
}
@Test
public void testRoundTrip() {
// 基本類型
assertEquals((short) 122, ByteUtil.getShort(ByteUtil.getBytes((short) 122)));
assertEquals(122, ByteUtil.getInt(ByteUtil.getBytes(122)));
assertEquals(1222222L, ByteUtil.getLong(ByteUtil.getBytes(1222222L)));
assertEquals('a', ByteUtil.getChar(ByteUtil.getBytes('a')));
// 浮點數精度驗證
assertEquals(122.22f, ByteUtil.getFloat(ByteUtil.getBytes(122.22f)), 0.0001f);
assertEquals(122.22, ByteUtil.getDouble(ByteUtil.getBytes(122.22)), 0.0001);
// 字符串編碼驗證
String cn = "我是好孩子";
assertEquals(cn, ByteUtil.getString(ByteUtil.getBytes(cn, "UTF-8"), "UTF-8"));
assertEquals(cn, ByteUtil.getString(ByteUtil.getBytes(cn), "GBK")); // 默認 GBK
}
ByteUtil (位運算) | 無依賴、可控、教學價值高 | 手寫易出錯、維護成本 | 學習理解、輕量項目、特殊協議 |
| ByteBuffer | 高性能、支持大小端、鏈式調用 | 線程不安全、對象創建開銷 | 高性能網絡編程、Netty 等框架 |
| DataOutputStream | 標準 API、自動擴容 | 僅支持 OutputStream、性能一般 | 文件寫入、簡單序列化 |
| ObjectOutputStream | 支持任意對象圖 | 序列化體積大、版本敏感、不安全 | 緩存存儲、RMI 遠程調用 |
ByteUtil 通過位運算實現了基本類型與字節數組的精確、高效雙向轉換。理解其核心原理——「大端序存儲、位掩碼去符號、IEEE 754 浮點數位模式轉換」,能幫助開發者在網絡協議解析、二進制文件處理、嵌入式通訊等場景中游刃有餘。生產環境建議優先使用 ByteBuffer 或成熟序列化框架(Protobuf、Kryo),僅在極簡依賴或教學場景下直接使用本工具類。