异常
异常机制的概述
异常机制是指当程序出现错误后,程序如何处理。具体来说,异常机制提供了程序退出的安全通道。当出现错误后,程序执行的流程发生改变,程序的控制权转移到异常处理器。
程序错误分为三种:1、编译错误;2、运行时错误;3、逻辑错误。
(1)编译错误是因为程序没有遵循语法规则,编译程序能够自己发现并且提示我们错误的原因和位置;
(2)运行时错误是因为程序在执行时,运行环境发现了不能执行的操作;
(3)逻辑错误是因为程序没有按照预期的逻辑顺序执行,异常也就是指程序运行时发生错误,而异常处理就是对这些错误进行处理和控制。
异常的结构
Java 中所有的异常都有一个共同的祖先 Throwable。Throwable 允许代码可以通过异常传播机制将代码中的异常进行抛出。
下面是 JDK 中异常的结构(图来自网络,侵删):
从上图的结构中可以看出来,Throwable 有两个重要的子类:Exception(异常)和 Error(错误),二者都是 Java 异常处理的重要的类。Throwable 是 Exception 和 Error 的父类,而 Exception 和 Error 的区别就在于 Exception 能被程序本身处理(try...catch...),而 Error 是无法处理的(比如 StackOverflowError 和 OutOfMemoryError)。
Throwable
Throwable 是所有异常的父类,实现了 Serializable 接口。他有以下重要的属性和方法:
1、detailMessage(String)
描述当前 Throwable 的具体信息。在构造函数中赋值,在 toString()、getMessage()方法中都有用到
2、stackTrace(StackTraceElement[])
用于记录栈帧的数组,底层是用 native 方法 fillInStackTrace(int) 实现。
3、cause(Throwable)
可以看做原因,记录导致当前 Throwable 产生的那个 Throwable。没有的话就是本身
4、toString()
返回异常发生时的简要概述
5、getMessage()
返回 detailMessage
6、getLocalizedMessage()
返回异常对象的本地化信息。Throwable 实现的就是 getMessage() 方法。子类可以重写他。
7、printStackTrace()
控制台打印 stackTrace 中的栈帧信息。其中有控制台异常打印的锁的竞争。
8、readObject 和 writeObject
序列化和反序列化实现的方法
Error
Error 是程序中出现无法处理的情况导致的,表示运行程序中出现了严重的错误问题。Error 或者 Error 的子类,都代表这种异常不应该被捕获,如果遇到应该被捕获的异常,那么应该改用 Exception。网上说 Error 都是因为虚拟机引起的,倒也不全然是这样。Error 的子类众多,引起的情况也五花八门,但是相同点都是认为 Error 及其子类都应该是意料之外的异常产生的,是非受查异常,因此 Error 及其子类有且仅有构造方法。下面列举几个常见的Error:
1、AnnotationFormatError
从类文件中读取注解时报错产生的,通常是因为反射读取注解文件产生的
2、IOError
和 IOException 有区别,当有 IO 方面的错误发生时生成
3、ReflectionError
是 sun.nio.ch.Reflect 的内部类,不开源
4、VirtualMachineError
比较有名的是它的两个子类,OutOfMemoryError 和 StackOverFlowError
5、OutOfMemoryError
当堆空间无法分配内存的情况下出现该错误,原因是创建对象过多
6、StackOverFlowError
栈深达到上限时抛出错误,原因是记录的方法栈帧太多,超出栈的最大深度(常出现在递归中)
Exception
Exception 是程序本身可以处理的异常。Exception 可以简单分为 RuntimeException 和其他 Exception。前者是非受查异常,后者是受查异常。列举几个常见的Exception:
1、XMLParseException
非受查异常,XML 解析时产生
2、IndexOutOfBoundsException
非受查异常,下标越界时产生
3、ReflectionException
受查异常,MBeans 中使用反射时可能产生
4、SQLException
受查异常,和数据库交互时可能产生
异常常见的问题
Q1、什么是受查异常和非受查异常?
A:受查异常就是在代码中必须需要显式try...catch...的异常,非受查异常就是不需要在代码中显示捕获也能运行的异常。除了 Error 及其子类、RuntimeException 及其子类是非受查异常外,其他的都是受查异常。
Q2:在开发过程中,是应该抛出异常还是捕获异常?
A:遇到知道如何处理的异常时进行捕获并处理,遇到不知道如何处理时则抛出。