前置知识: Java

Java try-with-resources 与异常链语法速查手册

1 min入门

Java try-with-resources 与异常链语法速查手册 的完整教学讲解。

0. 本节阅读指引(先读这一节)

本篇是「try-with-resources 与异常链」语法速查手册,按需查阅。

零基础第一遍只读:try-with-resources 与异常捕获两个小节,掌握自动关闭资源的写法;异常链、finally 块、StackWalker 遇到再查。

前置:017 异常处理机制。

try-with-resources

基本写法:自动关闭资源

try (<资源声明>) { <语句> }
// 自动关闭实现了 AutoCloseable 的资源
try (FileInputStream in = new FileInputStream("a.txt")) {
    int b = in.read();
}

基本写法:多个资源

try (<资源1>; <资源2>) { <语句> }
// 多个资源用分号分隔,关闭顺序与声明相反
try (FileInputStream in = new FileInputStream("in.txt");
     FileOutputStream out = new FileOutputStream("out.txt")) {
    out.write(in.readAllBytes());
}

基本写法:引用外部变量

<AutoCloseable 变量> = ...;
try (<变量>) { <语句> }
// Java 9+ 支持使用 effectively final 的外部资源
BufferedReader r = Files.newBufferedReader(Path.of("a.txt"));
try (r) {
    System.out.println(r.readLine());
}

异常捕获

基本写法:多异常捕获

try { <语句> } catch (<类型1> | <类型2> <变量>) { <处理> }
// 一个 catch 块处理多种异常
try {
    Files.readAllBytes(Path.of("a.txt"));
} catch (IOException | SecurityException e) {
    log.error(e);
}

基本写法:异常重新抛出

catch (<类型> <变量>) { throw <变量>; }
// 处理后再抛出
try { risky(); }
catch (IOException e) { log.error(e); throw e; }

异常链

基本写法:包装异常 throw new <异常>(<消息>, <原因>);

// 把底层异常包装成业务异常
try {
    Files.readString(Path.of("a.txt"));
} catch (IOException e) {
    throw new BusinessException("读取配置失败", e);
}

基本写法:获取根因 <throwable>.getCause();

// 获取异常的根本原因
Throwable root = e.getCause();

基本写法:添加受抑制异常 <throwable>.addSuppressed(<异常>);

// 主异常抛出后关闭资源时的异常被抑制
try (Resource r = new Resource()) {
    throw new IOException("main");
} catch (IOException e) {
    for (Throwable s : e.getSuppressed()) {
        System.out.println(s);
    }
}

finally 块

基本写法:finally 执行清理

try { <语句> } catch (<类型> <变量>) { <处理> } finally { <清理> }
// finally 块无论是否抛异常都会执行
try {
    return risky();
} finally {
    cleanup();
}

StackWalker 栈遍历

基本写法:遍历调用栈 StackWalker.getInstance().forEach(<消费者>);

// 打印调用栈
StackWalker.getInstance().forEach(f -> System.out.println(f.getClassName() + "#" + f.getMethodName()));

基本写法:获取调用者 StackWalker.getInstance().walk(<函数>);

// 获取直接调用者的栈帧
StackTraceElement caller = StackWalker.getInstance()
    .walk(s -> s.skip(1).findFirst())
    .map(StackWalker.StackFrame::toStackTraceElement)
    .orElse(null);