Java InetAddress 详解:从 IP 地址表示到主机名解析

一、InetAddress 是什么

InetAddress 是 Java 网络编程中最基础的类之一,位于 java.net 包下,用于表示一个 IP 地址。它既可以表示 IPv4 地址,也可以表示 IPv6 地址,并且可以携带对应的主机名信息。

需要特别注意的是,InetAddress 本身是一个抽象类,不能直接通过 new 创建实例,只能通过它提供的静态工厂方法获取对象。这种设计把「地址解析」和「地址表示」解耦,屏蔽了底层 DNS 查询的细节。

java 复制代码
public abstract class InetAddress implements java.io.Serializable {
    // 抽象类,无法直接实例化
}

它的两个常用子类是 Inet4Address 和 Inet6Address,分别对应 IPv4 和 IPv6 地址。日常开发中我们几乎不需要直接使用子类,通过 InetAddress 的静态方法即可完成绝大多数操作。

二、创建 InetAddress 实例

InetAddress 没有公开的构造方法,常用的创建方式有以下几种。

2.1 根据主机名获取

java 复制代码
InetAddress address = InetAddress.getByName("www.example.com");
System.out.println(address);

getByName 会触发一次 DNS 解析,把主机名转换为 IP 地址。如果传入的是 IP 字符串(如 "192.168.1.1"),则不会进行 DNS 查询,直接构造地址对象。

2.2 获取本机地址

java 复制代码
InetAddress local = InetAddress.getLocalHost();
System.out.println(local.getHostName());
System.out.println(local.getHostAddress());

getLocalHost 返回本机的主机名和地址。在部分容器或云主机环境中,主机名可能无法正确解析,此时会抛出 UnknownHostException,需要做好异常处理。

2.3 获取全部地址

java 复制代码
InetAddress[] addresses = InetAddress.getAllByName("www.example.com");
for (InetAddress addr : addresses) {
    System.out.println(addr.getHostAddress());
}

一个域名可能对应多个 IP(例如负载均衡场景),getAllByName 会返回全部解析结果,适合需要遍历或做故障转移的场景。

2.4 直接由字节数组构造

java 复制代码
byte[] ip = new byte[] { (byte) 192, (byte) 168, 1, 1 };
InetAddress address = InetAddress.getByAddress(ip);
System.out.println(address.getHostAddress());

getByAddress 不会进行 DNS 查询,适合已知 IP 字节序列的场景。IPv4 需要 4 个字节,IPv6 需要 16 个字节,长度不合法会抛出 UnknownHostException。

三、常用方法

方法 说明
getHostAddress() 返回 IP 地址字符串,如 192.168.1.1
getHostName() 返回主机名,可能触发反向 DNS 查询
getCanonicalHostName() 返回完全限定域名(FQDN)
isReachable(int timeout) 判断地址是否可达,类似 ping
isLoopbackAddress() 是否为回环地址(127.0.0.1 或 ::1)
isAnyLocalAddress() 是否为通配地址(0.0.0.0 或 ::)
isMulticastAddress() 是否为组播地址
getAddress() 返回原始 IP 字节数组

其中 getHostName() 需要留意:如果构造时只传入了 IP,调用该方法会触发一次反向 DNS 查询,在网络不通时可能阻塞较长时间。若只需要 IP 字符串,应优先使用 getHostAddress()。

四、地址类型判断

Java 提供了若干便捷方法用于判断地址类型,避免手工解析字符串。

java 复制代码
InetAddress addr = InetAddress.getByName("127.0.0.1");
System.out.println(addr.isLoopbackAddress());   // true
System.out.println(addr.isAnyLocalAddress());   // false
System.out.println(addr instanceof Inet4Address); // true

在服务端开发中,判断请求来源是否为回环地址、是否为内网地址是常见需求。对于内网判断,Java 标准库没有直接方法,通常需要结合 getAddress() 返回的字节自行判断网段。

java 复制代码
public static boolean isPrivate(InetAddress addr) {
    byte[] bytes = addr.getAddress();
    if (bytes.length != 4) {
        return false;
    }
    int first = bytes[0] & 0xFF;
    int second = bytes[1] & 0xFF;
    return first == 10
            || (first == 172 && second >= 16 && second <= 31)
            || (first == 192 && second == 168);
}

五、可达性检测

isReachable 用于判断目标地址是否可达,底层会尝试使用 ICMP 或 TCP 连接。

java 复制代码
InetAddress target = InetAddress.getByName("www.example.com");
boolean reachable = target.isReachable(3000);
System.out.println("可达:" + reachable);

需要注意两点:一是该方法在部分系统上依赖 ICMP 权限,普通用户可能无法使用,此时会退化为 TCP 探测;二是超时时间单位为毫秒,设置过短容易误判。生产环境中更推荐使用专门的连接检测手段,而不是依赖该方法做健康检查。

六、常见异常与注意事项

  • UnknownHostException:主机名无法解析时抛出,属于受检异常,必须捕获或声明抛出。
  • DNS 缓存 :JVM 会缓存 DNS 解析结果,默认缓存时间受 networkaddress.cache.ttl 控制,修改域名解析后可能不会立即生效。
  • 阻塞风险 :getByName、getHostName 等操作可能阻塞,不建议在高并发主流程中直接调用,可考虑异步或加缓存。
  • IPv6 兼容 :优先使用 InetAddress 而非硬编码 IPv4 字符串,以便同时兼容 IPv6 环境。
  • 不可变性 :InetAddress 实例是不可变的,可以安全地在多线程间共享。

七、小结

InetAddress 是 Java 网络编程的入口类,负责 IP 地址的表示与主机名解析。掌握它的静态工厂方法、常用判断方法以及 DNS 缓存和阻塞特性,能够帮助我们在实际项目中更稳妥地处理地址相关逻辑。对于需要更细粒度控制的场景,可以进一步了解 InetSocketAddress 和 NetworkInterface 等配套类。

相关推荐
AI你一生一世9 小时前
不止于时序存储:DolphinDB 如何成为 AI 时代的工业智能底座
java·人工智能·struts·时序数据库·工业智能·dolphindb·ai基础设施
belldeep9 小时前
python:genesis-world, Genesis-Embodied-AI
人工智能·python·ai·genesis
布莱克6059 小时前
C/C++ 中变量的声明与定义:区别、联系与常见误区
c语言·开发语言·c++·声明·定义
云-缩影9 小时前
拆解 AI 图像生成应用的后端架构:ECS、OSS 和大模型 API 怎么配合
java·spring boot·后端
LabVIEW开发9 小时前
LabVIEW 局部变量为何引发竞争条件?四种同步机制
开发语言·labview
瑞克-桑切斯10 小时前
7、MySQL 慢查询排查与优化实战指南
java·服务器·数据库
benchmark_cc10 小时前
A 股、港股、美股如何建立统一的标的主数据格式?
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash
维克兜率天11 小时前
【维克】趋势型CTA:期货市场的“追涨杀跌“
人工智能·python
Zenova EdgeOS11 小时前
Go 工业边缘错误处理实战:用 errors 构建可追溯错误链
开发语言·数据库·golang