异常的捕获及处理
异常的捕获及处理
认识异常
异常是导致程序中断执行的一种指令流,一旦产生异常没有正常处理的话,那么程序江湖中断执行。
范例:观察产生异常的代码
public class Hello{
public static void main(String args[]){
System.out.println("AAAAAA 计算开始 AAAAAA");
System.out.println("BBBBBB 除法计算:"+(10/0)+" BBBBBB");
System.out.println("CCCCCC 计算结束 CCCCCC");
}
}
出现异常,程序并未执行完,而是打印了一条信息。
处理异常
如果要在Java中进行异常的处理可以使用三个关键字的组合完成:try、catch、finally。有如下语法:
而此时给出的语法也有三种组合模式:try…catch、try…catch…finally、try…finally。
范例:实现异常的处理操作
public class Hello{
public static void main(String args[]){
System.out.println("AAAAAA 计算开始 AAAAAA");
try{
System.out.println("BBBBBB 除法计算:"+(10/0)+" BBBBBB");
}catch(ArithmeticException e){//e是一个对象
System.out.println(e);
}
System.out.println("CCCCCC 计算结束 CCCCCC");
}
}
此时程序出现异常仍可以正常执行完毕。
要想完整的输出异常信息,则可以使用printStackTrace()方法完成。
public class Hello{
public static void main(String args[]){
System.out.println("AAAAAA 计算开始 AAAAAA");
try{
System.out.println("BBBBBB 除法计算:"+(10/0)+" BBBBBB");
}catch(ArithmeticException e){//e是一个对象
e.printStackTrace();
}
System.out.println("CCCCCC 计算结束 CCCCCC");
}
}
范例:使用try…catch…finally来处理异常
public class Hello{
public static void main(String args[]){
System.out.println("AAAAAA 计算开始 AAAAAA");
try{
System.out.println("BBBBBB 除法计算:"+(10/0)+" BBBBBB");
}catch(ArithmeticException e){//e是一个对象
e.printStackTrace();
}finally{
System.out.println("*************************");
}
System.out.println("CCCCCC 计算结束 CCCCCC");
}
}
异常产生之后找到了相应的catch语句执行,而后处理异常完毕后继续执行finally的代码。
public class Hello{
public static void main(String args[]){
System.out.println("AAAAAA 计算开始 AAAAAA");
try{
System.out.println("BBBBBB 除法计算:"+(10/2)+" BBBBBB");
}catch(ArithmeticException e){//e是一个对象
e.printStackTrace();
}finally{
System.out.println("*************************");
}
System.out.println("CCCCCC 计算结束 CCCCCC");
}
}
多个异常的处理
在一个try语句之后可以编写多个catch进行处理,模拟一个输入的数字计算操作,假设要计算的数字使用过初始化参数设置的。
范例:修改程序代码
public class Hello{
public static void main(String args[]){
System.out.println("AAAAAA 计算开始 AAAAAA");
try{
int x = Integer.parseInt(args[0]);
int y = Integer.parseInt(args[1]);
int result=x/y;
System.out.println("BBBBBB 除法计算:"+result+" BBBBBB");
}catch(ArithmeticException e){//e是一个对象
e.printStackTrace();
}finally{
System.out.println("*************************");
}
System.out.println("CCCCCC 计算结束 CCCCCC");
}
}
下面就有了这样几种执行情况:
- 执行程序没有设置初始化参数:ArrayIndexOutOfBoundsException;
- 执行程序的时候输入的内容不是数字:NumberFormatException;
- 执行的时候被除数为0:ArithmeticException。
只有在异常处理之后,异常之后的代码才能执行,finally代码永远会出现。
为了保证程序出现错误之后仍然可以正常执行完,可以采用多个catch。
范例:修改代码,处理多个异常
public class Hello{
public static void main(String args[]){
System.out.println("AAAAAA 计算开始 AAAAAA");
try{
int x = Integer.parseInt(args[0]);
int y = Integer.parseInt(args[1]);
int result=x/y;
System.out.println("BBBBBB 除法计算:"+result+" BBBBBB");
}catch(ArithmeticException e){//e是一个对象
e.printStackTrace();
}catch(NumberFormatException e){//e是一个对象
e.printStackTrace();
}catch(ArrayIndexOutOfBoundsException e){//e是一个对象
e.printStackTrace();
}finally{
System.out.println("*************************");
}
System.out.println("CCCCCC 计算结束 CCCCCC");
}
}
异常处理流程
两个异常类的继承结构
java.lang.ArithmeticException | java.lang.NumberFormatException |
---|---|
可以发现所有的异常类都是java.lang.Throwable的子类,此类定义如下:
public class Throwable extends Object implements Serializable
可以发现Throwable直接是Object子类。从JDK1.0开始提供。但是在Throwable下有两个子类,因此在开发中几乎不会考虑Throwable处理。
面试题:请解释Throwable下的Error和Exception子类的区别。
- Error:在程序还未执行时出现的错误,一般指的是JVM出错,用户无法处理;
- Exception:在程序运行中出现的异常,异常处理都是针对此类型完成的。
因此,以后开发之中异常处理的最大父类就是Exception。
- 当程序出现异常时会由JVM自动的根据异常类型实例化一个指定的异常类对象;
- 程序需要判断当前的代码之中是否存在有异常处理逻辑,如果没有,则由JVM默认处理,处理方式输出异常信息,而后中断程序执行。
- 如果程序中存在异常处理,则try语句会捕获该异常类的实例化对象(想象为引用传递)
- 捕获到的异常类的实例化对象要与catch中的异常类型进行依次匹配;
- 如果catch匹配了异常,则用相应的代码进行处理,随后执行finally语句,如果没有任何catch匹配,则交由finally进行处理。
- 执行完finally代码之后要判断该异常是否已经处理过,若处理过,则执行后续代码,若没有,则交由JVM进行默认处理。
catch匹配跟参数传递类似,实例化对象有类型匹配,则进行接收,所有的异常都可以由Exception处理。
如果try语句后有多个异常处理,则大的异常处理要放在小的异常处理之后,否则出错。
范例:利用Exception处理异常。
public class Hello{
public static void main(String args[]){
System.out.println("AAAAAA 计算开始 AAAAAA");
try{
int x = Integer.parseInt(args[0]);
int y = Integer.parseInt(args[1]);
int result=x/y;
System.out.println("BBBBBB 除法计算:"+result+" BBBBBB");
}catch(Exception e){//e是一个对象
e.printStackTrace();
}finally{
System.out.println("*************************");
}
System.out.println("CCCCCC 计算结束 CCCCCC");
}
}
在实际开发中如果有明确要求,就分开处理。其他情况采用Exception是最方便的。
throws关键字
用户如何知道那些代码会产生怎样的异常?Java中提供throws关键字,其主要目的是明确告诉用户执行某一个方法有可能会产生哪些异常。所以throws主要是用于方法的声明处。
范例:观察throws关键字的使用
class MyMath{
public static int div(int x,int y)throws Exception{
return x/y;
}
}
表示如果想执行本语句就必须进行异常处理。
范例:不处理异常直接调用
class MyMath{
public static int div(int x,int y)throws Exception{
return x/y;
}
}
public class Hello{
public static void main(String args[]){
System.out.println(MyMath.div(10, 2));
}
}
代码中出现throws声明,表示必须强制性进行异常处理操作。
范例:正常调用形式
class MyMath{
public static int div(int x,int y)throws Exception{
return x/y;
}
}
public class Hello{
public static void main(String args[]){
try{
System.out.println(MyMath.div(10, 2));
}catch(Exception e) {
e.printStackTrace();
}
}
}
但是使用了throws之后还有一个传递的问题,主方法也是一个方法,所以主方法也可以使用throws,那么就表示此时的主方法不进行异常的处理,而交给被调用处处理。
class MyMath{
public static int div(int x,int y)throws Exception{
return x/y;
}
}
public class Hello{
public static void main(String args[]) throws Exception{
System.out.println(MyMath.div(10, 0));
}
}
主方法之上就由JVM进行默认处理,一般主方法中应该把异常处理完成。
throw关键字
所有异常对象都是Java负责实例化,我们只能对异常进行捕获。Java中允许用户自己实例化异常对象,要抛出这个异常,就必须使用throw关键字。
范例:自己手工抛出异常
public class Hello{
public static void main(String args[]) {//throws Exception
throw new Exception("这是一个异常。");
}
}
范例:异常处理
public class Hello{
public static void main(String args[]) {//throws Exception
try{
throw new Exception("这是一个异常。");
}catch(Exception e) {
e.printStackTrace();
}
}
}
异常处理的模型(核心)
设计除法计算方法,有如下要求:
范例:程序基本实现
class MyMath{
public static int div(int x,int y)throws Exception{//交给被调用处处理
int result=0;//保存计算结果
System.out.println("************begin*************");
result = x/y;
System.out.println("*************end*************");
return result;
}
}
public class Hello{
public static void main(String args[]) {//throws Exception
try{
System.out.println(MyMath.div(10, 2));
}catch(Exception e) {
e.printStackTrace();
}
}
}
一旦程序出现错误,这个时候的运行结果如下。
范例:加入异常控制
class MyMath{
public static int div(int x,int y)throws Exception{//交给被调用处处理
int result=0;//保存计算结果
System.out.println("************begin*************");
try{
result = x/y;
}catch(Exception e){
throw e;//继续向上抛
}finally{
System.out.println("*************end*************");
}
return result;
}
}
public class Hello{
public static void main(String args[]) {//throws Exception
try{
System.out.println(MyMath.div(10, 0));
}catch(Exception e) {
e.printStackTrace();
}
}
}
在以后的开发之中,一定会牵扯到资源的使用,例如文件、数据库操作前一定要打开,操作后一定要关闭。但是以上给出的异常处理模型可以简化:try…finally。
class MyMath{
public static int div(int x,int y)throws Exception{//交给被调用处处理
int result=0;//保存计算结果
System.out.println("************begin*************");
try{
result = x/y;
}finally{
System.out.println("*************end*************");
}
return result;
}
}
public class Hello{
public static void main(String args[]) {//throws Exception
try{
System.out.println(MyMath.div(10, 0));
}catch(Exception e) {
e.printStackTrace();
}
}
}
RuntimeException类
看段代码:
public class Hello{
public static void main(String args[]) {
int num = Integer.parseInt("123");
System.out.println(num*num);
}
}
方法中出现throws就要进行异常处理,但此时并没有,观察NumberFormat类的继承结构。
因为NumberFormat类属于RuntimeException的子类,所以该异常属于选择性处理,即使不处理,编译的时候也不会出错,但是运行的时候会出错。
面试题:请解释Exception与RuntimeException的区别?列举出几个常见的RuntimeException。
- RuntimeException是Exception的子类;
- Exception定义的异常都需要强制性的处理,而RuntimeException的子类在编写代码时异常不需要强制性处理,由用户自己选择,如果不处理并且产生异常将交由JVM负责处理。
- 常见的RuntimeException:ArithmeticException,NullPointerException、ClassCastException、NumberFormatException。
断言:assert(了解)
断言指的是在程序编写的过程之中,确定代码执行到某行之后数据一定是某个期待的内容。
范例:观察断言
断言默认情况不会启用,只有在程序运行时增加了配置参数才可以使用。
java -ea Hello
自定义异常类
Java异常类之中提供了大量类型,但是这些类型之中几乎都是与语法有关的异常,缺少业务有关类型,例如:输入某人成绩,如果超过100,那么就应该产生对应的异常,这些往往需要开发者自己定义,正因为如此才需要自定义异常类的概念。
异常类型主要有两个:Exception、RuntimeException。
范例:设计一个成绩异常