下载工作台
Java 编程实战

异常处理与 IO 流

试读上半部分 · 解锁后可读全文

第 7 章 · 异常处理与 IO 流

本章目标:掌握 try-catch-finallytry-with-resources;理解受检异常与未受检异常;能定义自定义异常;熟练使用 FileBufferedReader/BufferedWriter 读写文本;完成 toolkit-demo 的配置文件读取日志行解析;对照 python-devtry/exceptopen()

学时建议:4~5 小时(含 2 小时跟练)

前置:完成 java-dev ch06;建议已学 python-dev 中异常与文件读写章节。


7.1 场景说明:配置与日志

toolkit-demo 启动时需读取虚构配置文件 app.properties(键值对),并解析 access.log 中的访问行;格式错误时抛出自定义异常,避免静默失败。

learn-java/toolkit-demo/
├── config/
│   └── app.properties      # 教学虚构配置
├── data/
│   └── access.log
└── src/main/java/demo/io/
    ├── ConfigLoader.java
    ├── LogParser.java
    ├── ToolkitConfigException.java
    └── IoDemo.java

示例 app.properties

app.name=toolkit-demo
app.version=1.0
pricing.member.rate=0.90

示例 data/access.log(与 §7.7 LogParser 格式一致:时间|路径|状态码):

2026-08-19T10:00:01Z|/books|200
2026-08-19T10:00:02Z|/api/cart|404
2026-08-19T10:00:03Z|/checkout|500
2026-08-19T10:00:04Z|/books|200
2026-08-19T10:00:05Z|/login|302
ch09/ch17 毕业项目会使用更详细的日志格式(含 ms= 耗时);本章先掌握 管道分隔 基础解析,后续章节会升级格式。

7.2 异常体系

  • 受检异常(如 IOException):编译器强制 catchthrows
  • 未受检 RuntimeException:如 NullPointerExceptionIllegalArgumentException
PythonJava
raise ValueErrorthrow new IllegalArgumentException
except Exceptioncatch (Exception e)

7.3 try-catch-finally

public static int parsePort(String text) {
    try {
        int port = Integer.parseInt(text);
        if (port < 1 || port > 65535) throw new IllegalArgumentException("端口 1-65535");
        return port;
    } catch (NumberFormatException e) {
        System.err.println("格式错误:" + e.getMessage());
        return -1;
    }
}
作用
try可能抛出异常的代码
catch捕获并处理
finally清理资源(现代优先 try-with-resources)

多 catchcatch (IOException | ToolkitConfigException e)(Java 7+)。


7.4 try-with-resources

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

public static void readLines(String path) throws IOException {
    try (BufferedReader reader = new BufferedReader(
            new FileReader(path, StandardCharsets.UTF_8))) {
        String line;
        while ((line = reader.readLine()) != null) {
            System.out.println(line);
        }
    } // 自动 close,即使异常
}
PythonJava
with open(...) as ftry (BufferedReader r = ...) { }
上下文管理器AutoCloseable 接口

7.5 自定义异常

package demo.io;

/**
 * toolkit-demo 配置相关异常
 */
public class ToolkitConfigException extends Exception {

    private final String configKey;

    public ToolkitConfigException(String message, String configKey) {
        super(message);
        this.configKey = configKey;
    }

    public ToolkitConfigException(String message, String configKey, Throwable cause) {
        super(message, cause);
        this.configKey = configKey;
    }

    public String getConfigKey() {
        return configKey;
    }
}
选型说明
extends Exception受检,调用方必须处理
extends RuntimeException未受检,参数错误等

业务配置错误本课程用受检异常。路径使用 Paths.get("config", "app.properties") 等相对路径,运行于 toolkit-demo 根目录。


7.6 ConfigLoader 完整代码

package demo.io;

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;

/**
 * 简单 properties 风格配置加载(不依赖 java.util.Properties 教学演示)
 */
public class ConfigLoader {

    public Map<String, String> load(Path path) throws ToolkitConfigException {
        if (!Files.isRegularFile(path)) {
            throw new ToolkitConfigException("配置文件不存在", path.toString());
        }

        Map<String, String> map = new HashMap<>();

        try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
            String line;
            int lineNo = 0;
            while ((line = reader.readLine()) != null) {
                lineNo++;
                line = line.strip();
                if (line.isEmpty() || line.startsWith("#")) {
                    continue;
                }
                int eq = line.indexOf('=');
                if (eq <= 0) {
                    throw new ToolkitConfigException(
                        "第 " + lineNo + " 行格式错误(缺少 =)", path.toString()
                    );
                }
                String key = line.substring(0, eq).strip();
                String value = line.substring(eq + 1).strip();
                map.put(key, value);
            }
        } catch (IOException e) {
            throw new ToolkitConfigException("读取失败", path.toString(), e);
        }

        return map;
    }

以下内容需解锁后阅读

试读已结束。解锁本章 ¥5.00,或开通年度会员畅读全部教程。
年度会员 ¥199.00/年; 小紫 AI 工作台有效会员 ¥99.00/年

正文仅在服务端鉴权后下发,未付费无法获取下半部分内容。