Java异常探秘:从错误中学习

image-20221021171559173

常用 API

image-20221021170448522

异常的分类

异常对象都是派生于 Throwable 类的一个实例。如果 Java 中内置的异常类不能够满足需求,用户可以创建自己的异常类。

image-20221021170709117

对于那些可能被他人使用的 Java 方法,应该根据异常规范,在方法的首部末尾来声明这个方法可能抛出的异常,如果可能有多个异常,那么异常名通过逗号分隔。

一个方法必须声明所有可能抛出的受查异常,而非受查异常要么不可控制( Error ),要么就应该避免发生( RuntimeException )。如果方法没有声明所有可能发生的受查异常,编译器就会发出一个错误消息。

除了声明异常之外,还可以捕获异常。这样会使异常不被抛到方法之外,也不需要 throws 规范。

Error

Error 类层次结构描述了 Java 运行时系统的内部错误和资源耗尽错误。 应用程序不应该抛出这种类型的对象。 如果出现了这样的内部错误,除了通告给用户,并尽力使程序安全地终止之外,再也无能为力了。这种情况很少出现。

Exception

Exception 又分解为两个分支: 一个分支派生 RuntimeException;另一个分支包含其他异常。划分两个分支的规则是:由程序错误导致的异常属于 RuntimeException ; 而程序本身没有问题,但由于像 I/O错误这类问题导致的异常属于其他异常。

RuntimeException 和 Error 是不会受到编译器检查的,叫作非检查异常。如果出现 RuntimeException 异常, 那么就一定是你的问题,属于Bug。

其他的异常类型是受检查异常,编译器将核查是否为所有的受査异常提供了异常处理器。

手动抛出异常

一旦方法抛出了异常,这个方法就不可能返回到调用者。也就是说,不必为返回的默认值或错误代码担忧。

手动抛出异常使用:throw new EOFException( );

自定义异常

定义一个派生于Exception 的类, 或者派生于 Exception 子类的类。

image-20221021170926578

捕获异常

捕获到异常后,打印详细信息可以使用:exception.printStackTrace( );

异常对象可能包含与异常本身有关的信息。可以使用 e.getMessage( ) 获得对象的更多信息,使用e.getClass().getName( ) 得到异常对象的实际类型。

try catch 语句

如果在 try 语句块中的任何代码抛出了一个在 catch 子句中说明的异常类,那么程序将跳过 try 语句块的其余代码,并且开始执行 catch 子句中的处理器代码。

如果在 try 语句块中的代码没有拋出任何异常,那么程序将跳过 catch 子句。

如果方法中的任何代码拋出了一个在 catch 子句中没有声明的异常类型,那么这个方法就会立刻退出。

image-20221021171051462

捕获多个异常

image-20221021171106478

捕获后再次抛出

在 catch 子句中可以抛出一个异常,这样做的目的是改变异常的类型。捕获到异常后进行包装或者修改后重新抛出。方便后续逻辑处理,注意一定要使用initCause方法将原始的异常信息保留下来方便日志定位。

强烈建议使用这种包装技术。这样可以让用户抛出子系统中的高级异常,而不会丢失原始异常的细节。

image-20221021171133106

finally子句

不管是否有异常被捕获,finally 子句中的代码都被执行。通常用来关闭资源。try 语句可以只有 finally 子句, 而没有 catch 子句。 强烈建议解耦合 try/catch和 try/finally语句块。

image-20221021171227086

内层的 try 语句块只有一个职责,就是确保关闭输入流。外层的 try 语句块也只有一个职责,就是确保报告出现的错误。这种设计方式不仅清楚,而且还具有一个功能,就是将会报告 finally 子句中出现的错误。

另外如果 finally 子句中也有一个 return 语句,这个返回值将会覆盖原始的返回值。

带资源的 try 语句

image-20221021171346553

这个块正常退出时,或者存在一个异常时,都会调用 in.close( ) 方法,就好像使用了 finally 块一样。而且 try( ) 的内容可以指定多个资源,用;分隔。

分析堆栈轨迹

可以调用 Throwable 类的 printStackTrace 方法访问堆栈轨迹的文本描述信息。

一种更灵活的方法是使用 getStackTrace 方法, 它会得到 StackTraceElement 对象的一个数组, 可以在你的程序中分析这个对象数组。StackTraceElement 类含有能够获得文件名和当前执行的代码行号的方法,同时,还含有能够获得类名和方法名的方法。 toString 方法将产生一个格式化的字符串, 其中包含所获得的信息。

异常使用技巧

  1. 只在异常情况下使用异常机制
  2. 捕获时不要过分地细化异常
  3. 利用异常层次结构,找到合适的异常
  4. 在检测错误时,苛刻要比放任更好
  5. 早抛出,晚捕获

断言

断言机制允许在测试期间向代码中插入一些检査语句。当代码发布时,这些插人的检测语句将会被自动地移走。

可以使用下面两种格式:

  • assert 表达式;
  • assert 表达式: 信息;

这两种形式都会对条件进行检测,如果结果为 false,则抛出一个 AssertionError 异常。在第二种形式中,表达式将被传人 AssertionError 的构造器,并转换成一个消息字符串。

在默认情况下,断言被禁用。可以在运行程序时用 -enableassertions-ea选项启用。

断言只应该用于在测试阶段确定程序内部的错误位置。断言是一种测试和调试阶段所使用的战术性工具,而日志记录是一种在程序的整个生命周期都可以使用的策略性工具。

笔记大部分摘录自《Java核心技术卷I》,含有少数本人修改补充痕迹。

  • 22
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值