Java 输出透过 InetAddress 获得的 IP 地址数组

Java 输出通过 InetAddress 获得的 IP 地址数组使用 InetAddress 获取 IP 地址会得到一个 byte 数组如果你

Java 输出通过 InetAddress 获得的 IP 地址数组

使用 InetAddress 获取 IP 地址会得到一个 byte 数组

如果你直接输出这个数组,你会发现 IP 地址中的某些位变成了负数

比如 61.135.169.105 会输出成 61.-121.-87.105

仔细看一看,会发现 135 + 121 = 256,169 + 87 = 256


-_-! 怎么个情况!


我首先想到的是 byte 类型向 int 类型转换过程中出现了问题,后来发现,实际不然

因为 Java 中没有 unsigned 类型,所以byte、short、int、long 都是有符号的,所以根本就不存在隐式类型转换出错的问题。


既然说到了 Java 没有 unsigned 类型,那么 byte 是 8 位,所以表示范围为 -127 - 128,而 IP 一个段的表示范围为 0 - 255,终于找到了不对劲的地方了

IP 的一个段是一个 unsigned byte,这样一个 unsigned byte 存入一个 signed byte 中当然会导致一些问题出现


分析一下:

35 的二进制编码为 1000 0111,最高位置为 1

由于 byte 被认为是 unsigned byte,所以最高位的 1 将会被解释为符号位,另外 Java 中存储是按照补码存储,所以 1000 0111 会被认为是补码形式,转换成原码便是 1111 0001,转换成十进制数便是 -121。

再看,65 的二进制编码为 0100 0001,由于小于 128,所以没有将最高位置 1,0100 0001 的补码还是 0100 0001,所以 65 不变。


分析这么多,这个问题的解决方法其实很简单,将 byte 变量与 0xFF 按位与即可,过程中 byte 会隐式类型转换为 int,当与 0xFF 按位与的时候,会将除了低 8 位的其他位全部置 0,这样一来便将符号扩展的那些高位清除掉了。


最后附上一个自己写的通用函数,用来将整型变量的二进制编码输出

/** * 将整型变量的二进制编码形式输出 * @param n 整型变量 * @param size 整型的二进制长度,可使用类似 Short.SIZE 获得 * @return 二进制编码字符串 */public static String printBinary(long n, int size) {StringBuilder sb = new StringBuilder();for (int i = size - 1; i >= 0; i--) {sb.append(n >>> i & 0x01);if (i % 4 == 0) {sb.append(" ");}}return sb.toString();}