文章目录
Java的异常机制主要依赖于 try、catch、finally、throw、throws 五个关键字。
Java异常分为两种: Checked异常 和 Runtime异常。
异常类的继承关系
异常(Exception)和 错误(Error),它们都继承Throwable父类。
在Java中只有Exception类型的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制的基本组成类型。
Exception和Error体现了Java平台设计者对不同异常情况的分类:
- Exception是程序正常运行中,可以预料的意外情况,可能并且应该被捕获,进行相应处理。
- Error是指在正常情况下,不大可能出现的情况,绝大部分的Error都会导致程序(比如JVM自身)处于非正常的、不可恢复状态。既然是非正常情况,所以不便于也不需要捕获,常见的比如OutOfMemoryError之类,都是Error的子类。
整体分类
通常,Java的异常(包括Exception和Error)分为:检查异常(checked exceptions)和 非检查异常(unchecked exceptions)。
检查异常(checked exceptions)
什么是检查异常?
可检查异常在源代码里必须显式地进行捕获处理,这是编译期检查的一部分,就是编译器要求你必须处置的异常。编译器要求你必须要对这段代码try…catch,或者throws exception,要求你对可能出现的异常必须做出相应的处理。
对检查异常(checked exception)的几种处理方式:
- 继续抛出,消极的方法,一直可以抛到java虚拟机来处理,就是通过throws exception抛出
- 用try…catch捕获
注意,对于检查的异常必须处理,或者必须捕获或者必须抛出
如何区分什么是检查异常呢?
除了RuntimeException与其子类,以及错误(Error),其他的都是检查异常(绝对的大家族)。
非检查异常(unchecked exceptions)
什么是非检查异常?
编译器不要求强制处置的异常,虽然你有可能出现错误,但是我不会在编译的时候检查,没必要,也不可能。为什么呢?你想想非检查异常都有哪些?NullPointerException,IndexOutOfBoundsException,VirtualMachineError等,这些异常你编译的时候检查吗?那我还要不要运行了,等死人啊。再说了,明明可以运行时检查,都在编译的时候检查,你写的代码还能看吗?而且有些异常只能在运行时才能检查出来,比如空指针,堆溢出等。
对未检查的异常(unchecked exception )的几种处理方式:
1、捕获
2、继续抛出
3、不处理
一般我们是不处理的,因为你很难判断会出什么问题,而且有些异常你也无法运行时处理,比如空指针,需要人手动的去查找。而且,捕捉异常并处理的代价远远大于直接抛出。
如何区分什么是非检查异常呢?
RuntimeException与其子类,以及错误(Error)。
Exception异常分类
对Exception异常进行划分,它可分为运行时异常和非运行时异常。
运行时异常
都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是非检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。
非运行时异常
是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不要自定义检查异常。
常见异常类
类别 | 常用异常类 |
---|---|
Error | AssertionError、OutOfMemoryError、StackOverflowError |
RuntimeException | AlreadyBoundException、ClassCastException、ConcurrentModificationException、IllegalArgumentException、IllegalStateException、IndexOutOfBoundsException、JSONException、NullPointerException、SecurityException、UnsupportedOperationException |
CheckedException | ClassNotFoundException、CloneNotSupportedException、FileAlreadyExistsException、FileNotFoundException、InterruptedException、IOException、SQLException、TimeoutException、UnknownHostException |
异常处理机制
如果执行try块里的业务逻辑代码时出现异常,系统自动生成一个异常对象,该异常对象被提交给Java运行时环境。这个过程被称为 抛出(throw)异常
当Java运行时环境收到异常对象时,会寻找能处理该异常对象的catch块,如果找到合适的catch块,则把该异常对象交给该catch块处理。这个过程被称为捕获(catch)异常
注意:
不管程序代码块是否处于try块中,甚至包括catch块中的代码,只要执行该代码块时出现异常,系统总会自动生成一个异常对象。
如果程序没有为这段代码定义任何的catch块,则Java运行时环境无法找到处理该异常的catch块,程序就此退出。
机制和注意事项
-
当Java运行时环境接收到异常对象后,会依次判断该异常对象是否是catch块后异常类或其子类的实例。
如果是,Java运行时环境将调用该catch块来处理异常;
否则再次拿该异常对象和下一个catch块里的异常类进行比较。 -
在通常情况下,如果try块被执行一次,则try块后只有一个catch块会被执行,绝不可能有多个catch块被执行。
除非在循环中使用了continue 开始下一次循环,下一次循环又重新运行try块,这才会导致多次catch块被执行。 -
注意:
try 和 if 语句不一样。
try后的花括号 ({…})不可省略,即使try块中只有一行代码。
与之类似的是,catch块后的 {} 也不可以省略。
try 块里声明的变量是代码块内局部变量,它只在try块内有效,在catch块中不能访问该变量。 -
注意: 异常捕获时,一定要记住,先捕获小异常,再捕获大异常。
Java 7 提供的多异常捕获
- 在Java 7 之前,每个catch块只能捕获一种类型的异常;但从Java 7之后,一个catch块可以捕获多种类型的异常。
注意:
捕获多种类型的异常时,多个异常类型之间用“|” 隔开。
捕获多种类型的异常时,异常变量有隐式的final修饰,因此程序不能对异常变量赋值。
public static void main(String[] args) {
try {
int a = Integer.parseInt(args[0]);
int b = Integer.parseInt(args[1]);
int c = a / b;
System.out.println("Result = " + c);
} catch (NumberFormatException | IndexOutOfBoundsException | ArithmeticException e) {
System.out.println("The program occur NumberFormatException, IndexOutOfBoundsException and ArithmeticException");
//捕获多异常时,异常变量默认有final修饰。
//下面代码报错。
//e = new ArithmeticException("Test");
} catch (Exception e) {
System.out.println("The unknow exception");
//捕获一种异常时,异常变量没有final修饰。
//下面代码正确。
e = new RuntimeException("Test");
}
}
访问异常信息
所有的异常对象,都包含如下几个常用方法:
getMessage(); 返回该异常的详细描述字符串。
printStackTrace(); 将该异常的跟踪栈信息输出到标准错误输出。
printStackTrace(PrintStream s); 将该异常的跟踪栈信息输出到指定输出流。
getStackTrace(); 返回该异常的跟踪栈信息
使用finally回收资源
注意:
除非在try块, catch块中调用了退出虚拟机的方法。否则不管在try块, catch块中执行怎样的代码,出现怎样的情况,异常处理的finally块总会被执行
final、finally、finalize 有什么区别?
- final可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表示该变量是一个常量不能被重新赋值。
- finally一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码方法finally代码块中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。
- finalize是一个方法,属于Object类的一个方法,而Object类是所有类的父类,该方法一般由垃圾回收器来调用,当我们调用System的gc()方法的时候,由垃圾回收器调用finalize(),回收垃圾。
Java7 的自动关闭资源的try语句
Java7增强了try语句的功能——它允许在try关键字后紧跟一对圆括号,圆括号可以声明、初始化一个或多个资源。此处的资源,指的是那些必须在程序结束时显示关闭的资源(比如数据库连接,网络连接等),try语句在该语句结束时自动关闭这些资源。
注意:
为了保证try语句可以正常关闭资源,这些资源实现类必须实现 AutoCloseable或 Closeable接口,实现这两个接口就必须实现close()方法。
throws
throws是用来声明一个方法可能抛出的所有异常信息,throws是将异常声明但是不处理,而是将异常往上传,谁调用我就交给谁处理
使用throws声明抛出异常的思路是:
当前方法不知道如何处理这种类型的异常,该异常应该由上一级调用者处理; 如果mian方法也不知道如何处理这种类型的异常,也可以使用throws声明抛出异常,该异常将交给JVM处理。
JVM对异常的处理方法是,打印异常的跟踪栈信息,并终止程序运行。
注意:
-
throws声明抛出只能在方法签名中使用,throws可以声明抛出多个异常类,多个异常类之间以逗号隔开。
-
限制: 方法重写时,两小中的一条。
子类方法声明抛出的异常类型应该是父类方法声明抛出的异常类型的子类或相同,子类方法声明抛出的异常不允许比父类方法声明抛出的异常多。
throw
throw则是指抛出的一个具体的异常类型
-
throw 语句可以单独使用,throw语句抛出的不是异常类,而是一个异常实例,而且每次只能抛出一个异常实例。
-
注意下面例子中的注释:
public class ThrowTest {
public static void main(String[] args) {
try {
// 调用声明抛出 Checked异常的方法,要么显示捕获该异常
// 要么在main()方法中再次声明抛出
throwChecked(3);
} catch (Exception e) {
System.out.println(e.getMessage());
}
// 调用声明抛出Runtime异常的方法,可以显示捕获异常,也可以不理会该异常
throwRuntime(3);
}
public static void throwChecked(int a) throws Exception {
if (a > 0) {
// 自行抛出Exception异常
// 该代码必须处于 try块中,或者处于带有throws声明的方法中
throw new Exception("a 的值大于零,不符合要求");
}
}
public static void throwRuntime(int a) {
if (a > 0) {
// 自行抛出RuntimeException异常
// 既可以显示捕获异常,也可以完全不理会该异常,把该异常交给调用者处理
throw new RuntimeException("a 的值大于零,不符合要求");
}
}
}
自定义异常
为了明确地描述异常情况,我们可以自定义异常。
用户自定义异常都应该继承Exception基类。
如果希望自定义Runtime异常,则应该继承RuntimeException基类。
定义异常时,通常需要提供两个构造器:
- 一个无参数的构造器
- 一个带一个字符串参数的构造器,这个字符串作为该异常对象的描述信息。
可以 找下NullPointerException、FileNotFoundException异常的写法。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/155773.html