共计 2049 个字符,预计需要花费 6 分钟才能阅读完成。
[toc]
Java 语言在设计之初就提供了相对完善的异常处理机制,大大提高了程序的可靠性。
异常的分类
我们编写的程序都会经过编译、运行这两个阶段,在这两个阶段都会出现异常。Java 将这两个阶段的异常分别称为 Checked 异常和 Runtime 异常:
checked 异常:在编译阶段被强制处理的异常(IDE 自动提示的异常)
Runtime 异常:在运行阶段出现的异常,这些异常是非强制处理的(也有称之为非 checked 异常)
异常类的结构体系
图片中 RuntimeException 类的子类全部为 Runtime 异常,而继承至 Exception 的其他子类为 checked 异常
图片中还有另一个分支 Error,Error 错误与虚拟机相关 (OutOfMemoryError 之类的),大部分的 Error 错误会导致程序或 JVM 处于非正常、不可恢复的状态,因此没有捕获的必要。
异常处理机制
当程序运行期间出现了意外情况,系统会自动生成一个 Exception 对象来通过程序。从而将“业务实现代码”和“错误处理代码”分离,提高程序的可读性
Java 的异常处理机制主要依赖于 try、catch、finally、throw、throws 五个关键字。
try…catch…finally
把程序的业务实现代码放在 try 块中,异常处理逻辑放在 catch 块中进行处理。
两个基本概念
抛出异常:执行 try 块里的业务实现代码时出现异常,系统将自动生成一个异常对象,并将其提交给运行时环境
捕获异常:当运行时环境接收到异常对象时,会自上而下依次判断该异常对象是否是 catch 块后异常类或其子类的实例。如果找到合适的 catch 块,则把该异常对象交给 catch 块处理(在程序进入 catch 块时,赋值于 catch 块的形参)。否则,将终止运行时环境并退出程序。
多异常捕获
捕获多种类型的异常时,使用“|”将其隔开。
捕获的多种类型的异常变量是不可变的的 (隐式的 final 修饰)
物理资源的两种回收方式
Java 的垃圾回收机制只能回收堆内存中的对象所占用的资源,不会回收任何物理资源
物理资源像数据库连接、网络连接、IO 操作等都需要显示回收, 显示回收的两种方式为添加 finally 块或使用自动关闭资源的 try 语句
finally 块的使用规则
除非在 try、catch 块中调用了退出虚拟机的方法 (System.exit(0)),否则无论 try、catch 块中出现什么情况,异常处理的 finally 块总会被执行
finally 块中使用了 return 或 throw 语句,方法最终将得到 finally 块中的 return 或 throw 的结果,无论 try、catch 块中是否有 return 或 throw 语句
自动关闭回收资源
//() 中是可关闭的资源的声明
// 这些资源类必须实现 AutoCloseable 或 Closeable 接口,从而实现 close() 方法
try(BufferReader reader=new BufferReader(…)){
//do something
}
//Closeable 是 AutoCloseable 的子接口,只包含 close 一个方法
// 区别是 Closeable 中的 close 抛出 IOException 异常,而 AutoCloseable 抛出 Exception 异常
public interface Closeable extends AutoCloseable {
public void close() throws IOException;
}
throws 声明抛出异常的规则
当前方法不知道如何处理该类型的异常,应该将异常向上级调用者抛出。直到 main 方法也无法处理时,该异常将交由 jvm 处理,jvm 的处理方式是——打印异常的 StackTrace 信息,并中止程序运行。
throws 声明抛出异常只能在方法签名中使用
可以抛出多个异常类并以 ”,” 号隔开
子类方法声明抛出的异常类型应该是父类方法声明抛出的异常类型的子类或相同,并且异常类型的数量不能比父类的多
throw 主动抛出异常对象
有一类异常大多数是根据业务需求来决定的,由于与业务需求不符合而产生的异常,必须由开发人员来决定抛出
throw new IllegalArgumentException(“ 非法参数 ”);
异常处理的原则
捕获体现代码逻辑相关的特定异常,不要捕获像 Exception 这样的通用异常。程序除了被计算机运行之外最多的就是被程序员阅读的,所以我们有义务将自己的代码体现出尽量多的信息。
所有父类异常的 catch 块都应该排在子类异常的 catch 块之后(先处理小异常,再处理大异常),否则将出现编译错误。
尽量避免在 finally 块中使用 return 或 throw 语句,这样导致 try、catch 块中的终止语句失效,进而影响业务逻辑。
不要忽略异常,忽略异常可能导致非常难以诊断的情况出现。
一些博客或文档
官方 Exception 文档
Java 内置异常类
异常常见面试题
ClassNotFoundException 和 NoClassDefFoundError 的区别