Checked 异常和 Runtime 异常体系
《Java学习笔记》
Java 的异常被分为两大类:
Checked
异常和Runtime
异常(运行时异常)
- 所有的 RuntimeException 类及其子类的实例被称为 Runtime 异常;
- 不是 RuntimeException 类及其子类的异常实例则被称为 Checked 异常
Java 语言提供了 Checked 异常,程序必须显示的处理 Checked 异常,如果程序没有处理 Checked 异常,该程序在编译时就会发生错误,无法编译通过。 - 对于 Checked 异常的处理方式有如下两种:
- 当前方法明确知道该如何处理异常,程序应该使用 try … catch 块来捕获该异常,然后在对应的 catch 块中打印对用户的提示信息,重新开始下一次循环
- 当前方法不知道如何处理这种异常,应该在定义该方法时抛出该异常
Runtime 异常则更加灵活,Runtime 异常无需显示声明抛出,如果程序需要捕获 Runtime 异常,也可以使用 try … catch 块来实现
使用 throws 声明抛出异常
- 使用 throws 声明抛出异常的思路是,当前方法不知道如何处理这种类型的异常,该异常由上一级调用者处理;如果 main 方法也不知道如何处理这种类型的异常,也可以使用 throws 声明抛出异常,该异常交给 JVM 处理。JVM 对异常的处理方式是,打印异常的跟踪栈信息,并终止程序运行。
- throws 声明抛出只能在方法签名中使用,throws 可以抛出多个异常类,多个异常类之间以逗号隔开。
throws ExceptionClass1 , ExceptionClass2 ...
一旦使用 throws 语句声明抛出该异常,程序就无需使用 try … catch 块来捕获该异常了
class ThrowsTest {
public static void main(String[] args) throws IOException {
//a.txt不存在,会引发 java.io.FileNotFoundException: a.txt (系统找不到指定的文件。) 异常
FileInputStream in = new FileInputStream("a.txt");
}
}
如果某段代码中调用了一个 throws声明的方法,该方法声明抛出了 Checked 异常,则表明该方法希望调用者来处理该异常。也就是说,调用该方法时要么放在 try 块中显示捕获该异常,要么放在里一个带 throws 声明抛出的方法中。
class ThrowsTest2 {
public static void main(String[] args) throws IOException {
//因为 test() 方法声明抛出 IOException 异常
//所以调用该方法的代码要么放在 try... cache 块中
//要么处于另一个带 throws 声明抛出的方法中
test();
}
public static void test() throws IOException {
//因为 FileInputStream 的构造器声明抛出 IOException 异常
//所以调用 FileInputStream 的代码要么放在 try... cache 块中
//要么处于另一个带 throws 声明抛出的方法中
FileInputStream in = new FileInputStream("a.txt");
}
}
使用 throws 声明抛出异常时有一个限制,就是方法重写时“两小”中的一条规则;
- 子类抛出的异常类型应该是父类方法声明抛出的异常类型的子类或相同,子类方法声明抛出的异常不允许比父类声明抛出的异常多。
使用 throw
抛出异常
- 当程序出现错误时,系统会自动抛出异常;除此之外,Java 也允许程序自行抛出异常,自行抛出异常使用
throw
语句来完成 - 如果需要在程序中自行抛出异常,则使用 throw 语句,throw 语句可以单独使用,throw 语句抛出的不是异常类,而是一个异常实例,而且每次只能抛出一个异常实例。
throw new Exception("手动抛出异常");
- 如果 throw 语句抛出的异常时 Checked 异常,则该 throw 语句要么处于 try 块里,显示捕获该异常,要么放在一个带 throws 声明抛出的方法中,即把该异常交给该方法的调用者处理;
- 如果 throw 语句抛出的异常是 Runtime 异常,则该语句无须放在带 throws 声明抛出的方法中;程序即可以显示使用 try … catch 来捕获并处理该异常,也可以完全不理会该异常,把该异常交给该方法的调用者来处理。
class ThrowTest {
public static void main(String[] args) {
try {
//调用声明抛出 Checked 异常的方法,要么显示捕获该异常
//要么在main 方法中再次声明抛出
throwChecked(-3);
} catch (Exception e) {
//打印异常跟踪栈
e.printStackTrace();
}
//调用声明抛出 Runtime 异常的方法即可以显示捕获该异常
//也可不必理会该异常
throwRuntime(3);
}
public static void throwChecked(int a) throws Exception {
if (a > 0) {
//自行抛出 Exception 异常
// 该代码必须放在 try 块中,或处于带 throws 声明的方法中
throw new Exception("a的值大于0,不符合要求");
}
}
public static void throwRuntime(int a) {
if (a > 0) {
//自行抛出 RuntimeException 异常,既可以显示捕获该异常
//也可以完全不理会该异常,把该异常交给该方法调用者处理
throw new RuntimeException("a 的值大于0,不符合要求");
}
}
}
自行抛出 Runtime 异常比自行抛出 Checked 异常的灵活性更好。同样,抛出 Checked 异常则可以让编译器提醒程序员必须处理该异常
自定义异常类
自定义异常类应该继承 Exception 基类,如果希望自定义 Runtime 异常,则应该继承 RuntimeException 基类。定义异常类通常需要提供两个构造器:一个无参构造器;另一个是带字符串参数的构造器,这个字符串将作为该异常对象的描述信息(也就是异常对象的 getMessage()方法返回的字符串)
//大部分情况下创建自定义异常类,都可以采用如下方式
//并让异常类的类名可以准确的描述该异常
class CustomeException extends Exception {
public CustomeException() {
}
//带字符串的构造器
public CustomeException(String msg) {
super(msg);
}
}
Java 7 增强的 throw 语句
class ThrowTest2 {
public static void main(String[] args) throws FileNotFoundException {
try {
FileInputStream in = new FileInputStream("a.txt");
} catch (Exception e) {
//再次抛出该异常,Java7会检查该异常的实际类型,因此在main方法中只抛出 FileNotFoundException 即可
throw e;
}
}
}
异常链
程序先捕获原始异常,然后抛出一个新的业务异常,新的业务异常中包含了对用户的提示信息,这种方式被称为异常异常转译
public calSal() throws SalException{
try{
//实现结算工资的业务逻辑
}catch(SQLException sqle){
//把原始异常记录下来,留给管理员
...
//下面异常中的 message 就是对用户的提示
throw new SalException("访问底层数据库出现异常");
}
}
- 这种把原始异常信息隐藏起来,仅向上提供必要的异常提示信息的处理方式,可以保证底层异常不会扩算到表现层,可以避免向上暴露太多的实现细节
- 这种把捕获一个异常然后接着抛出另一个异常,并把原始异常信息保存下来是一种典型的链式处理,也被称为“异常链”
- 在 JDK 1.4 以前,程序员必须自己编写代码保持原始异常信息。从 JDK 1.4 以后,所有 Throwable 的子类在构造器中都可以接收一个 cause 对象作为参数。这个 cause 就用来表示原始异常。
public calSal() throws SalException{
try{
//实现结算工资的业务逻辑
}catch(SQLException sqle){
//把原始异常记录下来,留给管理员
...
//下面异常中的 sqle就是原始异常
throw new SalException(sqle);
}
}
再定义自定义异常类的时候应该在原来的基础上添加一个带 Throwable 类型参数的构造器
//大部分情况下创建自定义异常类,都可以采用如下方式
//并让异常类的类名可以准确的描述该异常
class CustomeException extends Exception {
public CustomeException() {
}
//带字符串的构造器
public CustomeException(String msg) {
super(msg);
}
//创建一个接受 Throwable 参数的构造器
public CustomeException(Throwable cause) {
//调用 Throwable 类中的
super(cause);
}
}