使用 InetAddress.getAddress() 獲取 IP 地址時,返回 byte[] 數組。直接打印會發現部分段落顯示為負數(如 61.135.169.105 變為 61.-121.-87.105),這是 Java 無無符號類型導致的經典陷阱。本文深度解析原理並提供通用修正方案。
InetAddress addr = InetAddress.getByName("61.135.169.105");
byte[] bytes = addr.getAddress();
// 直接打印 bytes[1] → -121
// 直接打印 bytes[2] → -87
byte:8 位,有符號,範圍 -128 ~ 127以 135 為例:
1000 0111(最高位為 1)byte 有符號數 → 符號位 = 1 → 負數1000 0111 → 取反+1 → 1111 1001 → 十進制 -121135 + 121 = 256(模 256 同餘)以 65 為例:
0100 0001(最高位 0)將 byte 提升為 int,與 0xFF(二進制 24 個 0 + 8 個 1)按位與,清除符號擴展位,保留低 8 位真實值。
public static String formatIp(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
// 關鍵:byte 自動提升為 int,& 0xFF 保留低 8 位
int unsignedByte = bytes[i] & 0xFF;
sb.append(unsignedByte);
if (i < bytes.length - 1) sb.append(".");
}
return sb.toString();
}
// 測試
byte[] ipBytes = {(byte) 135, (byte) 169};
System.out.println(formatIp(ipBytes)); // 輸出: 135.169
輔助調試位運算、理解補碼表示:
/**
* 打印整數二進制表示
* @param n 目標數值
* @param size 位寬度(如 Byte.SIZE=8, Integer.SIZE=32)
* @return 格式化二進制字符串(每 4 位分組)
*/
public static String printBinary(long n, int size) {
StringBuilder sb = new StringBuilder();
for (int i = size - 1; i >= 0; i--) {
// 無符號右移 >>> 關鍵:補 0,避免負數符號擴展
sb.append((n >>> i) & 0x01);
if (i % 4 == 0) sb.append(" ");
}
return sb.toString();
}
// 使用示例
System.out.println(printBinary(135, Byte.SIZE)); // 1000 0111
System.out.println(printBinary(-121, Byte.SIZE)); // 1000 0111 (相同位模式)
System.out.println(printBinary(135 & 0xFF, Byte.SIZE)); // 0000 0000 ... 1000 0111 (int 32位)
InetAddress.getHostAddress() 直接返回標準點分十進制字符串,無需手動解析b & 0xFF 轉無符號值ByteBuffer、DataOutputStream 寫入 byte 時無需轉換,網絡傳輸本質是無符號字節流Java 設計哲學「簡單性」犧牲了無符號類型,開發者需自行處理無符號語義。掌握 & 0xFF 慣用語,是 Java 程序員處理二進制數據的必備技能。