InetAddress 与网络地址 | JavaSE

InetAddress 与网络地址

一、学习目标

学完本章,你应该能够:

  1. 能够解释 InetAddress 类在 Java 网络编程中的作用。
  2. 能够使用 InetAddress.getLocalHost() 获取本地主机地址对象。
  3. 能够使用 InetAddress.getByName() 根据域名或 IP 字符串获取网络地址对象。
  4. 能够使用 getHostName()getHostAddress() 获取主机名及文本形式 IP 地址。
  5. 能够使用 getLoopbackAddress() 正确获取回环地址。
  6. 能够理解域名解析可能返回多个 IP 地址,并使用 getAllByName() 获取它们。
  7. 能够正确理解 isReachable() 的能力边界,而不是把它简单等同于 ping

二、核心知识

2.1 InetAddress 是什么

Java 在:

java.net

包中提供了:

InetAddress

用于表示 Internet Protocol 地址相关信息。

导包:

import java.net.InetAddress;

可以把 InetAddress 理解为:

Java 对“主机名 + IP 地址”这类网络地址信息进行面向对象封装后的核心类型之一。

例如:

www.example.com
       ↓
 InetAddress
       ↓
93.xxx.xxx.xxx

或者:

当前计算机
    ↓
InetAddress
    ↓
主机名 + IP 地址

2.2 InetAddress 不是 Socket

这是一个非常重要的边界。

InetAddress

表示网络地址信息

而 Socket:

表示网络通信端点/连接相关能力

本章我们只是解决:

“目标在哪?”

后面学习 UDP、TCP 时才真正解决:

“怎么把数据发过去?”

知识顺序:

InetAddress
     ↓
得到目标地址
     ↓
DatagramSocket / Socket
     ↓
真正发送和接收数据

2.3 InetAddress 的创建方式

InetAddress 通常不是通过:

new InetAddress()

创建。

而是通过静态工厂方法获得对象。

常用方法:

| 方法 | 作用 | | --------------------------------------- | ------------------------------------ | | InetAddress.getLocalHost() | 获取本地主机地址对象 | | InetAddress.getByName(String host) | 根据主机名、域名或 IP 字符串解析地址 | | InetAddress.getAllByName(String host) | 获取某主机名对应的全部已解析 IP | | InetAddress.getLoopbackAddress() | 获取回环地址 |


2.4 获取本地主机地址

最基础的写法:

InetAddress localHost = InetAddress.getLocalHost();

完整示例:

import java.net.InetAddress;

public class InetAddressDemo01 {
    public static void main(String[] args) throws Exception {
        InetAddress localHost = InetAddress.getLocalHost();

        System.out.println(localHost);
    }
}

需要注意:

getLocalHost()

不是简单写死返回:

127.0.0.1

它会根据当前操作系统主机配置获取本地主机名,并尝试解析得到对应地址。

因此不同电脑输出可能完全不同。


2.5 获取主机名

可以使用:

getHostName()

例如:

InetAddress localHost = InetAddress.getLocalHost();

String hostName = localHost.getHostName();

System.out.println(hostName);

可能输出:

LingXi-PC

具体值由当前操作系统与网络配置决定。


2.6 获取 IP 地址字符串

使用:

getHostAddress()

例如:

InetAddress localHost = InetAddress.getLocalHost();

String address = localHost.getHostAddress();

System.out.println(address);

可能输出:

192.168.1.23

也可能是 IPv6 地址。

因此不要在代码中假定:

getHostAddress()
一定返回 IPv4

2.7 根据域名获取 InetAddress

可以使用:

InetAddress.getByName(...)

例如:

InetAddress address =
        InetAddress.getByName("www.example.com");

完整示例:

import java.net.InetAddress;

public class InetAddressDemo02 {
    public static void main(String[] args) throws Exception {
        InetAddress address =
                InetAddress.getByName("www.example.com");

        System.out.println("主机名:" + address.getHostName());
        System.out.println("IP地址:" + address.getHostAddress());
    }
}

这里发生了一个非常关键的过程:

www.example.com
        ↓
系统配置的名称解析机制
        ↓
      IP 地址
        ↓
   InetAddress

通常会涉及 DNS。


2.8 getByName 也可以接收 IP 字符串

例如:

InetAddress address =
        InetAddress.getByName("127.0.0.1");

甚至可以直接传一个合法的 IPv6 地址文本。

所以:

getByName(String host)

中的 host 并不仅仅表示“域名”。

它可以是:

主机名
域名
IP 地址文本

2.9 获取回环地址

如果真正想表示:

当前主机自身的回环接口

Java 提供了更明确的方法:

InetAddress.getLoopbackAddress()

例如:

import java.net.InetAddress;

public class InetAddressDemo03 {
    public static void main(String[] args) {
        InetAddress loopback =
                InetAddress.getLoopbackAddress();

        System.out.println(loopback.getHostAddress());
    }
}

返回的可能是:

127.0.0.1

也可能是 IPv6:

::1

具体取决于运行环境。

所以:

getLocalHost()

和:

getLoopbackAddress()

不要混为一谈。


2.10 一个域名可能对应多个 IP

大型网站通常不会只有一台服务器。

例如:

www.example.com

理论上可能解析出:

IP1
IP2
IP3
...

Java 提供:

InetAddress.getAllByName(host)

获取全部解析结果。

例如:

import java.net.InetAddress;

public class InetAddressDemo04 {
    public static void main(String[] args) throws Exception {
        InetAddress[] addresses =
                InetAddress.getAllByName("www.example.com");

        for (InetAddress address : addresses) {
            System.out.println(address.getHostAddress());
        }
    }
}

这段代码说明:

域名
≠
永远对应唯一 IP

2.11 UnknownHostException

如果 Java 无法确定目标主机地址,可能抛出:

UnknownHostException

它位于:

java.net

包中。

例如:

InetAddress.getByName("this-host-does-not-exist.invalid");

可能解析失败。

于是:

域名
 ↓
名称解析失败
 ↓
UnknownHostException

因此真实开发中需要考虑:

  • 域名输入错误
  • DNS 故障
  • 当前没有网络
  • 名称解析配置异常

而不能认为:

getByName()

永远成功。


三、使用方法

3.1 获取本地主机名和 IP

完整示例:

import java.net.InetAddress;
import java.net.UnknownHostException;

public class InetAddressLocalDemo {
    public static void main(String[] args) {
        try {
            InetAddress localHost =
                    InetAddress.getLocalHost();

            System.out.println(
                    "主机名:" + localHost.getHostName()
            );

            System.out.println(
                    "IP地址:" + localHost.getHostAddress()
            );
        } catch (UnknownHostException e) {
            System.out.println("无法解析本地主机地址");
            e.printStackTrace();
        }
    }
}

执行逻辑:

getLocalHost()
      ↓
获得 InetAddress
      ↓
getHostName()
      ↓
获取主机名

getHostAddress()
      ↓
获取 IP 文本

3.2 获取远程主机地址

import java.net.InetAddress;
import java.net.UnknownHostException;

public class InetAddressRemoteDemo {
    public static void main(String[] args) {
        try {
            InetAddress remote =
                    InetAddress.getByName("www.example.com");

            System.out.println(
                    "主机名:" + remote.getHostName()
            );

            System.out.println(
                    "IP地址:" + remote.getHostAddress()
            );
        } catch (UnknownHostException e) {
            System.out.println("无法解析目标主机");
        }
    }
}

这里的核心代码其实只有:

InetAddress remote =
        InetAddress.getByName("www.example.com");

之后就可以从对象中读取地址信息。


3.3 获取所有解析地址

import java.net.InetAddress;
import java.net.UnknownHostException;

public class InetAddressAllDemo {
    public static void main(String[] args) {
        try {
            InetAddress[] addresses =
                    InetAddress.getAllByName("www.example.com");

            for (InetAddress address : addresses) {
                System.out.println(
                        address.getHostAddress()
                );
            }
        } catch (UnknownHostException e) {
            System.out.println("域名解析失败");
        }
    }
}

这里再次出现之前学习过的:

数组
增强 for
异常处理

JavaSE 的知识已经开始组合使用。


3.4 判断是否可能到达目标主机

InetAddress 提供:

boolean isReachable(int timeout)

例如:

import java.net.InetAddress;

public class InetAddressReachableDemo {
    public static void main(String[] args) throws Exception {
        InetAddress address =
                InetAddress.getByName("www.example.com");

        boolean reachable =
                address.isReachable(3000);

        System.out.println(reachable);
    }
}

参数:

3000

单位是:

毫秒

也就是最多尝试:

3 秒

3.5 非常重要:isReachable 不等于 ping

很多初学代码会写:

System.out.println(address.isReachable(5000));

然后解释成:

“这就是 Java 在执行 ping。”

这种理解不够准确。

isReachable() 是一个:

best effort(尽力而为)的可达性测试。

Java 实现可能:

  • 尝试 ICMP Echo
  • 在权限不足等情况下采用其他方式

而:

  • 防火墙
  • 系统安全策略
  • 目标服务器配置

都可能影响结果。

所以:

isReachable(...) == false

不能严格推出:

目标服务器所有服务都无法访问

例如完全可能:

isReachable() → false

但是:

HTTPS 443 → 可以正常访问

因此实际网络诊断不能只依赖这一方法。


3.6 InetAddress 基础综合案例

import java.net.InetAddress;

public class InetAddressDemo {
    public static void main(String[] args) throws Exception {

        // 1. 获取本地主机地址
        InetAddress localHost =
                InetAddress.getLocalHost();

        System.out.println("=== 本地主机 ===");
        System.out.println(
                "主机名:" + localHost.getHostName()
        );
        System.out.println(
                "IP地址:" + localHost.getHostAddress()
        );

        // 2. 获取回环地址
        InetAddress loopback =
                InetAddress.getLoopbackAddress();

        System.out.println("=== 回环地址 ===");
        System.out.println(
                loopback.getHostAddress()
        );

        // 3. 解析远程主机
        InetAddress remote =
                InetAddress.getByName("www.example.com");

        System.out.println("=== 远程主机 ===");
        System.out.println(
                "主机名:" + remote.getHostName()
        );
        System.out.println(
                "IP地址:" + remote.getHostAddress()
        );

        // 4. 可达性尝试
        boolean reachable =
                remote.isReachable(3000);

        System.out.println(
                "可达性测试:" + reachable
        );
    }
}

这段代码已经覆盖本章最核心的 API。


四、原理与进阶

4.1 getLocalHost 到底做了什么

getLocalHost() 的含义不是:

永远返回 127.0.0.1

而是大致经历:

操作系统本地主机名
        ↓
名称解析
        ↓
InetAddress

因此电脑网络配置不同:

getLocalHost()

得到的结果也可能不同。

这也是为什么:

同一段 Java 程序

在不同机器上可能打印:

192.168.x.x

或者其他地址。


4.2 getHostName 可能涉及名称解析

对于:

getHostName()

不能简单理解为:

从对象内部读取一个永远现成的字符串

如果 InetAddress 对象创建时没有保存主机名,系统可能尝试进行:

反向名称解析
IP → Host Name

因此网络 API 中一些看似普通的方法,也可能依赖系统名称解析环境。


4.3 getHostAddress 更适合直接获取 IP 文本

如果你的真正需求只是:

我要把 IP 地址打印出来。

优先使用:

getHostAddress()

例如:

System.out.println(address.getHostAddress());

它表达的语义非常明确:

InetAddress
     ↓
IP 地址文本

4.4 Inet4Address 与 Inet6Address

InetAddress 是网络地址类体系中的基础类型。

Java 还提供:

Inet4Address
Inet6Address

分别用于:

IPv4
IPv6

因此:

InetAddress

并不意味着只能表示 IPv4。

例如:

InetAddress address =
        InetAddress.getByName("...");

最终返回的真实对象可能是:

Inet4Address

也可能是:

Inet6Address

这也是面向对象中:

父类引用
指向子类对象

思想在 Java 核心 API 中的真实应用。


4.5 DNS 与 Java 网络 API 的关系

当执行:

InetAddress.getByName("www.example.com");

时,可以简单理解为:

Java
 ↓
操作系统配置的名称解析机制
 ↓
DNS 等名称服务
 ↓
目标 IP
 ↓
InetAddress

Java 并不是凭空知道:

www.example.com

的 IP 地址。

背后仍然离不开网络名称解析体系。


五、实践应用

5.1 Socket 连接之前解析服务器地址

后续可能写:

Socket socket =
        new Socket("example.com", 9999);

其中域名最终仍然需要解析成 IP。

因此:

InetAddress

是理解 Socket 网络寻址的重要基础。


5.2 获取客户端 IP

后续服务器接受客户端连接后,经常会看到:

socket.getInetAddress()

最终获得的就是:

InetAddress

对象。

然后:

socket.getInetAddress()
      .getHostAddress()

就能获得客户端 IP。

例如服务器日志可能输出:

客户端上线:192.168.1.20

所以本章并不是一个孤立 API。

后续几乎整个网络编程阶段都会不断遇到:

InetAddress

5.3 局域网聊天程序

未来开发:

局域网即时通信系统

时,服务端需要识别:

客户端来自哪个 IP

例如:

Client A
192.168.1.20
        ↓

Client B
192.168.1.21
        ↓

      Server
192.168.1.100:9999

服务器可以通过 Socket 获取:

InetAddress

进一步获得客户端地址信息。


六、常见问题

6.1 InetAddress 可以 new 吗?

通常不会直接:

new InetAddress()

而是通过静态方法获取,例如:

InetAddress.getLocalHost();
InetAddress.getByName(...);
InetAddress.getLoopbackAddress();

6.2 getLocalHost 就是获取 127.0.0.1 吗?

不是。

如果需要明确获取回环地址,应理解:

InetAddress.getLoopbackAddress()

而:

getLocalHost()

会基于系统本地主机名及其解析结果获得地址。


6.3 getByName 一定访问互联网吗?

不一定。

例如:

InetAddress.getByName("127.0.0.1");

传入的就是 IP 文本。

而:

InetAddress.getByName("localhost");

也可能直接由本地 hosts 或系统名称解析机制处理。

只有需要解析远程域名时,才可能涉及外部 DNS 查询。


6.4 域名一定只对应一个 IP 吗?

不是。

因此 Java 提供:

getAllByName(...)

用于获取多个解析结果。


6.5 isReachable 返回 false 就说明网站挂了吗?

不能。

它只能说明:

当前 Java 可达性测试没有成功确认该主机可达。

可能原因包括:

  • 防火墙禁止 ICMP
  • 权限限制
  • 网络策略
  • 超时
  • 目标配置

网站具体端口仍可能正常工作。


6.6 为什么 getByName 会抛 UnknownHostException?

因为 Java 可能无法确定目标主机的 IP 地址。

典型原因:

域名不存在
DNS 失败
名称配置错误
网络异常

所以网络程序天然需要处理失败场景。


6.7 为什么我的运行结果和教程不同?

网络程序与普通:

int a = 10;

不同。

结果受到运行环境影响,例如:

  • 电脑主机名
  • Windows / Linux
  • Wi-Fi 网络
  • DNS
  • IPv4 / IPv6 配置
  • VPN
  • 防火墙
  • 当前网络是否连接

因此网络编程中:

结果不完全一致

是正常现象。

更重要的是理解 API 的语义。


七、练习与验收

7.1 知识问答

  1. InetAddress 位于哪个包?
  2. InetAddress 主要表示什么?
  3. getLocalHost() 有什么作用?
  4. getLoopbackAddress()getLocalHost() 有什么区别?
  5. getByName() 参数可以是什么?
  6. getHostName()getHostAddress() 分别返回什么?
  7. 为什么同一个域名可能对应多个 IP?
  8. getAllByName() 解决什么问题?
  9. UnknownHostException 通常在什么情况下出现?
  10. 为什么不能将 isReachable() 简单理解为系统 ping

7.2 代码阅读

阅读:

InetAddress address =
        InetAddress.getByName("www.example.com");

System.out.println(address.getHostName());
System.out.println(address.getHostAddress());

回答:

  1. 第一行代码主要进行了什么操作?
  2. address 保存的是字符串还是对象?
  3. getHostName() 返回什么信息?
  4. getHostAddress() 返回什么信息?
  5. 如果域名无法解析,可能出现什么异常?

7.3 手写代码

关闭 AI 自动补全,从零实现:

InetAddressDemo1

要求:

  1. 获取本地主机 InetAddress
  2. 打印本地主机名。
  3. 打印本地主机 IP。
  4. 获取回环地址。
  5. 根据一个域名获取远程主机对象。
  6. 打印远程 IP。
  7. 使用 isReachable(3000) 进行一次可达性尝试。

7.4 Debug

下面代码存在概念错误:

InetAddress ip = InetAddress.getLocalHost();

System.out.println(
        "我的回环地址:" + ip.getHostAddress()
);

问题:

getLocalHost() 得到的地址为什么不能直接称为回环地址?

请修改程序,使其真正获取:

Loopback Address

再观察:

InetAddress ip =
        InetAddress.getByName("abc.this-domain-does-not-exist.invalid");

System.out.println(ip.getHostAddress());

回答:

  1. 这段代码可能发生什么?
  2. 属于编译期错误还是运行期异常?
  3. 应该如何处理?

7.5 综合训练

编写:

NetworkAddressInspector

要求用户给程序一个:

域名 / 主机名 / IP 字符串

程序完成:

输入目标
   ↓
getAllByName()
   ↓
得到 InetAddress[]
   ↓
逐个打印 IP
   ↓
对每个地址尝试 isReachable()

输出格式自行设计。

暂时不要求真正建立 TCP 或 UDP 通信。


7.6 本章验收

如果关闭资料后,你能够从零写出:

InetAddress.getLocalHost();

InetAddress.getLoopbackAddress();

InetAddress.getByName(...);

InetAddress.getAllByName(...);

address.getHostName();

address.getHostAddress();

address.isReachable(...);

并且能够解释:

域名
 ↓
名称解析
 ↓
IP 地址
 ↓
InetAddress

同时知道:

InetAddress 只是在解决地址问题
Socket 才开始解决真正的数据通信问题

那么本章已经达到学习要求。

最终知识链:

              InetAddress
                   │
        ┌──────────┼──────────┐
        │          │          │
   LocalHost   Loopback    Remote Host
        │          │          │
        └──────────┼──────────┘
                   │
            HostName / IP
                   │
                   ▼
              Socket 编程