《On Java 8》第15章 异常

本文详细探讨了Java异常处理机制,包括基本理念、异常体系结构、自定义异常、异常声明及重新抛出等核心概念。同时,介绍了Try-With-Resources用法及如何获取线程堆栈信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java 的基本理念是“结构不佳的代码不能运行”。

Java 使用异常来提供一致的错误报告模型,使得构件能够与客户端代码可靠地沟通问题。

Java 异常体系结构图

在这里插入图片描述

异常概念

C 以及其他早期语言常常具有多种错误处理模式,这些模式往往建立在约定成俗的基础上,而并不是语言的一部分。

Java 引入异常?

在异常处理程序中,不仅节省代码,而且把“描述在正常执行过程中做什么事”的代码和“出了问题怎么办”的代码相分离。与之前的错误处理方法相比,异常机制使代码的阅读、编写和调试工作更加井井有条。

基本异常

异常抛出后:

1、使用 new 在堆上创建异常对象

2、当前的执行路径被终止

3、从当前环境中弹出对异常对象的引用

4、异常处理机制接管程序,在异常处理程序中继续执行程序

自定义异常

class SimpleException extends Exception {
    SimpleException(String message) {
        super(message);
    }
}

public class Test {
    public void method() throws SimpleException {
        throw new SimpleException("hehe");
    }

    public static void main(String[] args) {
        Test test = new Test();
        try {
            test.method();
        } catch (Exception e) {
            e.printStackTrace();
            // e.printStackTrace(System.out); // 标准输出流
            // e.printStackTrace(System.err); // 标准错误流
        }
    }
}

// 运行结果
SimpleException: hehe

异常声明

Java 强制使用异常声明,使你能以礼貌的方式告知客户端程序员某个方法可能会抛出的异常类型,然后客户端程序员就可以进行相应的处理。

异常声明属于方法声明的一部分,紧跟在形式参数列表之后。

void method() throws MyException, MyException1, MyException2 {}

可以声明方法将抛出异常,实际上却不抛出。这样做的好处是,为异常先占个位子,以后就可以抛出这种异常而不用修改已有的代码,尤其在定义抽象基类和接口时会用到。

重新抛出异常

调用 fillInStackTrace() 的那一行会成为异常的新发生地。

public class Rethrowing {
    public static void method() throws Exception {
        throw new Exception();
    }

    public static void main(String[] args) {
        try {
            method();
        } catch (Exception e) {
            // e.fillInStackTrace();
            e.printStackTrace();
        }
    }
}

// 运行结果1:
java.lang.Exception
	at Rethrowing.method(Rethrowing.java:3)
	at Rethrowing.main(Rethrowing.java:8)
    
// 运行结果2:
java.lang.Exception
	at Rethrowing.main(Rethrowing.java:10)

异常链

常常会想要在捕获一个异常后抛出另一个异常,并且希望把原始异常的信息保存下来,这被称为异常链。

JDK1.4 之后,所有的 Throwable 的子类在构造器中都可以接受一个 cause 对象作为参数,这个 cause 就用来表示原始异常。

// 方法一:
RuntimeException runtimeException = new RuntimeException(new NullPointerException());

// 方法二:
RuntimeException runtimeException = new RuntimeException().initCause(new NullPointerException());

Try-With-Resources 用法

// 方法一:
public class Test {
    public static void main(String[] args) {
        InputStream in = null;
        try {
            in = new FileInputStream(new File("haha.txt"));
            int contents = in.read();
            // Process contents
        } catch (IOException e) {
            // Handle the error
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    // Handle the close() error
                }
            }
        }
    }
}

// 方法二:
public class Test {
    public static void main(String[] args) {
        // 资源规范头
        try (InputStream in = new FileInputStream(new File("haha.txt"))) {
            int contents = in.read();
            // Process contents
        } catch (IOException e) {
            // Handle the error
        }
    }
}

1、在 try-with-resources 定义子句中创建的对象必须实现 java.lang.AutoCloseable 接口(Closeable 继承于 AutoCloseable),这个接口有一个方法:close()。否则,会编译器报错。

2、资源规范头中可以包含多个定义,并且通过分号进行分割(最后一个分号是可选的)。规范头中定义的每个对象都会在 try 语句块结束之后调用 close() 方法,关闭顺序与创建顺序相反。

3、try-with-resources 里面的 try 语句块可以不包含 catch 或者 finally 语句而独立存在。

class A implements AutoCloseable {
    String name = getClass().getSimpleName();

    A() {
        System.out.println("Creating " + name);
    }

    public void close() throws Exception {
        System.out.println("Closing " + name);
    }
}

class B extends A {
    @Override
    public void close() throws Exception {
        super.close();
        throw new Exception();
    }
}

public class CloseExceptions {
    public static void main(String[] args) {
        try (
                A a = new A();
                B b = new B();
        ) {
            System.out.println("In body");
        } catch (Exception e) {
            System.out.println("Caught: " + e);
        }
    }
}

// 运行结果:
Creating A
Creating B
In body
Closing B
Closing A
Caught: java.lang.Exception

获取线程堆栈信息

Thread.getAllStackTraces()
Thread.currentThread().getStackTrace()
new Exception().getStackTrace()
new Throwable().getStackTrace()
    
StackTraceElement[]
    
public final class StackTraceElement implements java.io.Serializable {
    // Normally initialized by VM (public constructor added in 1.5)
    private String declaringClass;
    private String methodName;
    private String fileName;
    private int    lineNumber;

异步调用堆栈信息跟踪

import java.util.Map;
import java.util.concurrent.CompletableFuture;

public class TurnOffChecking {
    public static void main(String[] args) {
        CompletableFuture<Void> run = CompletableFuture.runAsync(() -> {
            process();
        });

        new Thread() {
            @Override
            public void run() {
                Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces();
                for (Map.Entry<Thread, StackTraceElement[]> entry : allStackTraces.entrySet()) {
                    System.out.println("*****" + entry.getKey());
                    for (StackTraceElement element : entry.getValue()) {
                        System.out.println(element);
                    }
                }
            }
        }.run();
    }

    public static void process() {
        while (true) {
            try {
                Thread.sleep(100000);
                break;
            } catch (Exception e) {
            }
        }
    }
}

// 运行结果:
*****Thread[ForkJoinPool.commonPool-worker-1,5,main]
java.lang.Thread.sleep(Native Method)
TurnOffChecking.process(TurnOffChecking.java:27)
TurnOffChecking.lambda$main$0(TurnOffChecking.java:7)
TurnOffChecking$$Lambda$1/1324119927.run(Unknown Source)
java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1626)
java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1618)
java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
*****Thread[Reference Handler,10,system]
java.lang.Object.wait(Native Method)
java.lang.Object.wait(Object.java:502)
java.lang.ref.Reference.tryHandlePending(Reference.java:191)
java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
*****Thread[Signal Dispatcher,9,system]
*****Thread[Monitor Ctrl-Break,5,main]
java.net.SocketInputStream.socketRead0(Native Method)
java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
java.net.SocketInputStream.read(SocketInputStream.java:171)
java.net.SocketInputStream.read(SocketInputStream.java:141)
sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
java.io.InputStreamReader.read(InputStreamReader.java:184)
java.io.BufferedReader.fill(BufferedReader.java:161)
java.io.BufferedReader.readLine(BufferedReader.java:324)
java.io.BufferedReader.readLine(BufferedReader.java:389)
com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64)
*****Thread[Attach Listener,5,system]
*****Thread[main,5,main]
java.lang.Thread.dumpThreads(Native Method)
java.lang.Thread.getAllStackTraces(Thread.java:1610)
TurnOffChecking$1.run(TurnOffChecking.java:13)
TurnOffChecking.main(TurnOffChecking.java:21)
*****Thread[Finalizer,8,system]
java.lang.Object.wait(Native Method)
java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值