异常
异常
Exception 和 Error 有什么区别?
在 Java 中,所有的异常都有一个共同的祖先 java.lang
包中的 Throwable
类。Throwable
类有两个重要的子类:
Exception
:程序本身可以处理的异常,可以通过catch
来进行捕获。Exception
又可以分为 Checked Exception (受检查异常,必须处理) 和 Unchecked Exception (不受检查异常,可以不处理)。- **
Error
**:Error
属于程序无法处理的错误 ,我们没办法通过catch
来进行捕获不建议通过catch
捕获 。例如 Java 虚拟机运行错误(Virtual MachineError
)、虚拟机内存不够错误(OutOfMemoryError
)、类定义错误(NoClassDefFoundError
)等 。这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。
Exception 分为两大类
编译时期异常: checked异常。在编译时期,就会检查,如果没有处理异常,则编译失败。
常见的编译异常有:
- SQLException //操作数据库时,查询表可能发生异常
- IOException //操作文件时,发生的异常
- FileNotFoundException //当操作一个不存在的文件时,发生异常
- ClassNotFoundException //加载类,而该类不存在时,异常
- EOFException // 操作文件,到文件未尾,发生异常
- ParseException //日期格式化异常
运行时期异常: runtime异常(uncheck异常)。在运行时期,检查异常,在编译时期,运行异常不会编译器检测(不报错)。
RuntimeException
及其子类都统称为非受检查异常,常见的有(建议记下来,日常开发中会经常用到):NullPointerException
(空指针错误)IllegalArgumentException
(参数错误比如方法入参类型错误)NumberFormatException
(字符串转换为数字格式错误,IllegalArgumentException
的子类)ArrayIndexOutOfBoundsException
(数组越界错误)ClassCastException
(类型转换错误)ArithmeticException
(算术错误)SecurityException
(安全错误比如权限不够)UnsupportedOperationException
(不支持的操作错误比如重复创建同一用户)
注意事项
- 对于编译异常,程序中必须处理,比如 try-catch 或者 throws
- 对于运行时异常,程序中如果没有处理,默认就是throws的方式处理
- 在throws 过程中,如果有方法 try-catch,就相当于处理异常,就可以不必throws
异常处理的两种方式
try-catch-finally程序员在代码中捕获发生的异常,自行处理
throws将发生的异常抛出,交给调用者(方法)来处理,最顶级的处理者就是JVM
捕获异常try…catch…finally
异常处理的第一种方式,自己处理异常
- 如果异常发生了,则异常发生后面的代码不会执行,直接进入到catch块
- 如果异常没有发生,则顺序执行try的代码块,不会进入到catch
- 如果希望不管是否发生异常,都执行某段代码(比如关闭连接,释放资源等),则使用finally
- try中可能会抛出多个异常对象,那么就可以使用多个catch来处理这些异常对象
- 执行完try…catch后,后续代码继续执行

Throwable 类常用方法
String getMessage()
: 返回异常发生时的简要描述
String toString()
: 返回异常发生时的详细信息
String getLocalizedMessage()
: 返回异常对象的本地化信息。使用 Throwable
的子类覆盖这个方法,可以生成本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与 getMessage()
返回的结果相同
void printStackTrace()
: 在控制台上打印 Throwable
对象封装的异常信息
1 |
|
一次捕获多次处理
1 |
|
注意事项:catch里边定义的异常变量,如果有子父类关系,那么子类的异常变量必须写在上边,否则就会报错ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException。
多个异常一次处理
1 |
|
注意:不管是多个异常一次处理还是一次捕获多次处理,当捕获异常后程序会跳转到与抛出的异常类型匹配的 catch
块。
声明异常throws
异常处理的第二种方式,交给调用者处理,会把异常对象声明抛出给方法的调用者处理(自己不处理,给别人处理),最终交给JVM处理–>中断处理
如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理
在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。
如果抛出的多个异常对象有子父类关系,那么直接声明父类异常即可
方法内部如果抛出了多个异常对象,那么throws后边必须也声明多个异常
throws关键字后边声明的异常必须是Exception或者是Exception的子类
注意事项
- 对于编译异常,程序中必须处理,比如 try-catch 或者 throws
- 对于运行时异常,程序中如果没有处理,默认就是throws的方式处理
- 在throws 过程中,如果有方法 try-catch,就相当于处理异常,就可以不必throws
1 |
|
编译异常必须处理,要么try-catch 要么 throws
1 |
|
子父类的异常
- 如果父类抛出了多个异常,子类重写父类方法时,可以抛出和父类相同的异常,或者抛出父类异常的子类,或者不抛出异常。
- 父类方法没有抛出异常,子类重写父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出
1 |
|
throw关键字
可以使用throw关键字在指定的方法内部抛出指定的异常
抛出异常的过程通常用于表示出现了错误或异常情况,并将异常传递给调用者或上层代码进行处理。
- throw关键字必须写在方法的内部
- throw关键字后边new的对象必须是Exception或者Exception的子类对象
- throw关键字后边创建的是RuntimeException或者是RuntimeException的子类对象,我们可以不处理,默认交给JVM处理(打印异常对象,中断程序)
- throw关键字后边创建的是编译异常(写代码的时候报错),我们就必须处理这个异常,要么throws,要么try…catch
- throw关键字抛出指定的异常对象,我们就必须处理这个异常对象
1 |
|
编译型异常,必须自己处理,此处使用throws交给调用者处理
FileNotFoundException extends IOException extends Excepiton,如果抛出的多个异常对象有子父类关系,那么直接声明父类异常即可
1 |
|
NullPointerException是一个运行期异常,我们不用处理,默认交给JVM处理
ArrayIndexOutOfBoundsException是一个运行期异常,我们不用处理,默认交给JVM处理
自定义异常
当程序中出现了某些“错误”,但该错误信息并没有在Throwable子类中描述处理,这个时候可以自己设计异常类,用于描述该错误信息。
注意:
- 自定义异常类一般都是以Exception结尾,说明该类是一个异常类
- 自定义异常类,必须的继承Exception或者RuntimeException
- 继承Exception:那么自定义的异常类就是一个编译期异常,如果方法内部抛出了编译期异常,就必须处理这个异常,要么throws,要么try…catch
- 继承RuntimeException:那么自定义的异常类就是一个运行期异常,无需处理,交给虚拟机处理(中断处理)
自定义异常的练习
要求:我们模拟注册操作,如果用户名已存在,则抛出异常并提示:亲,该用户名已经被注册。
首先定义一个登陆异常类RegisterException
1 |
|
Exception源码

测试
1 |
|
finally 中的代码一定会执行吗?
不一定的,在某些情况下,finally 中的代码不会被执行。
就比如说 finally 之前虚拟机被终止运行的话,finally 中的代码就不会被执行。
1 |
|
输出:
1 |
|
另外,在以下 2 种特殊情况下,finally
块的代码也不会被执行:
- 程序所在的线程死亡。
- 关闭 CPU。
如何使用 try-with-resources 代替try-catch-finally?
try-with-resources
是 Java 7 引入的一种自动资源管理机制,主要用于简化对实现了 AutoCloseable
或 Closeable
接口的资源的处理。它通过在 try
语句中声明资源,自动确保在语句结束时资源被正确关闭,不再需要显式地在 finally
块中关闭资源。
- 适用范围(资源的定义): 任何实现
java.lang.AutoCloseable
或者java.io.Closeable
的对象 - 关闭资源和 finally 块的执行顺序: 在
try-with-resources
语句中,任何 catch 或 finally 块在声明的资源关闭后运行
《Effective Java》中明确指出:
面对必须要关闭的资源,我们总是应该优先使用
try-with-resources
而不是try-finally
。随之产生的代码更简短,更清晰,产生的异常对我们也更有用。try-with-resources
语句让我们更容易编写必须要关闭的资源的代码,
Java 中类似于InputStream
、OutputStream
、Scanner
、PrintWriter
等的资源都需要我们调用close()
方法来手动关闭,一般情况下我们都是通过try-catch-finally
语句来实现这个需求,如下:
1 |
|
使用 Java 7 之后的 try-with-resources
语句改造上面的代码
1 |
|