IOException 是网络IO异常的顶层检查型异常,覆盖读写中断、连接关闭等场景,必须显式处理;应分层捕获具体子类如 ConnectException、SocketTimeoutException、UnknownHostException 等,并按从具体到通用顺序排列;资源清理须独立于异常类型,优先使用 try-with-resources;避免统一吞异常、盲目重试等陷阱。

IOException 是网络IO异常的顶层检查型异常
它覆盖所有输入输出操作失败的通用场景,比如读写中断、连接意外关闭、流被提前终止等。作为 checked exception,Java强制要求你显式处理——不是用 try-catch 捕获,就得在方法签名中用 throws 声明。但直接只捕获 IOException 会掩盖问题本质,不利于定位和响应。
SocketException 及其常见子类需分层捕获
SocketException 本身是抽象基类,实际抛出的多为它的具体子类。捕获时应按“从具体到通用”顺序排列,否则更宽泛的 catch 会提前拦截掉子类异常:
- ConnectException:服务未启动、端口被防火墙屏蔽、目标主机拒绝连接 → 可提示用户检查服务状态或网络策略
-
SocketTimeoutException:连接超时(
setConnectTimeout)或读取超时(setReadTimeout)→ 建议重试,但需配合指数退避,避免雪崩 - UnknownHostException:DNS解析失败,非 SocketException 子类,但高频伴随出现 → 应单独捕获,提示“域名无效”或“网络不可达”,而非笼统报IO错误
- NoRouteToHostException:路由不可达,通常因本地网络配置或网关故障 → 属于底层连通性问题,一般不适合重试,应快速失败并记录
- EOFException:对方已关闭连接,但本端仍在尝试读取 → 表明通信已终结,可视为正常结束信号,无需告警
资源清理必须独立于异常类型
无论抛出哪种异常,Socket、InputStream、OutputStream 都必须确保关闭,否则引发句柄泄漏。推荐优先使用 try-with-resources(适用于实现了 AutoCloseable 的对象),例如:
try (Socket socket = new Socket();
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {
// 业务逻辑
} catch (UnknownHostException e) { ... }
catch (ConnectException e) { ... }
catch (SocketTimeoutException e) { ... }
catch (IOException e) { ... }
避免常见陷阱
不区分异常类型、统一打印日志并吞掉;在 catch 块里不做任何处理就 return;忽略 finally 或 try-with-resources 导致连接堆积;对 ConnectException 盲目重试(服务根本没启,重试无意义);用 Exception 大而化之捕获,丢失上下文。真正健壮的网络代码,靠的是分类响应 + 超时控制 + 资源自治 + 可观测日志。

















