异常的捕获及处理
1.异常的产生分析以及所带来的影响
2.异常的处理的基本格式
3.异常的处理流程
4.异常的处理模式
5.自定义异常
java中最为优秀的设计就在于异常的处理上,而且很简单
认识异常
异常指的是导致程序中端执行的一种指令流。一旦产生异常并且没有正常处理的话,那么程序将会中断执行
范例:观察没有产生异常的代码
public class Abnormal{ public static void main(String aegs){ System.out.println("*****************************"); System.out.println("**************"+(10 + 2)+"***************"); System.out.println("*****************************"); } }
此时程序没有任何的问题,于是正常执行完毕
public class Abnormal{ public static void main(String aegs){ System.out.println("*****************************"); System.out.println("**************"+(10 / 0)+"***************"); System.out.println("*****************************"); } }// 结果:异常
此时的程序产生了异常,所以最终发现程序并没有正常执行完毕,而是打印了一行信息之后直接就进行了退出程序
处理异常
如果要在java中进行异常的处理可以使用三个关键字的组合完成:try , catch , finally 。
对于这三个关键字的组合可以有如下的使用语法
try{
// 有可能出现异常的语句
}[catch (异常类型对象){
异常处理
}catch (异常类型对象){
异常处理
}......][finally{
// 异常的统一出口代码
}]
而此时给出的语法也有三种组合模式: try...catch,try...catch..finally,try...finally
范例:实现异常的处理操作
public class Abnormal{ public static void main(String aegs){ System.out.println("*****************************"); try{ int result = 10/0; System.out.println("**************"+(10 / 0)+"***************"); }catch(ArithmeticException e){ // e 是一个对象 System.out.println(e); } System.out.println("*****************************"); } }
此时的程序出现了异常之后,那么可以进行正常的执行完毕。并没有出现程序退出。不过这个代码里面也会发现点问题
如果要想输出异常信息比较完整一些,则可以使用 printStackTrace();
public class Abnormal{ public static void main(String aegs){ System.out.println("*****************************"); try{ int result = 10/0; System.out.println("**************"+(10 / 0)+"***************"); }catch(ArithmeticException e){ // e 是一个对象 e.printStackTrace(); } System.out.println("*****************************"); } }
可以发现,此时异常出现的信息是非常完整的,会明确的告诉用户到底是哪行代码出现了问题
除了使用 try...catch 的结构之外也可以利用 try...catch..finally 结构进行异常的处理
范例:使用 try...catch..finally 类处理异常
public class Abnormal{ public static void main(String aegs){ System.out.println("*****************************"); try{ int result = 10/0; System.out.println("**************"+(10 / 0)+"***************"); }catch(ArithmeticException e){ // e 是一个对象 e.printStackTrace(); }finally{ System.out.println("ssssssssssssssssss"); } System.out.println("*****************************"); } }
异常产生之后找到了相应的 catch 语句执行,而后处理异常完毕后继续执行 finally 的代码
多个异常的处理
在一个 try 语句之后可以编写多个 catch 进行处理。
模拟一个输入的数字计算操作,假设现在要计算的两个数字通过初始化参数设置上的
范例:修改程序代码
public class Abnormal{ public static void main(String aegs){ System.out.println("*****************************"); try{ int x = lnteger.parselnt(aegs[0]); int y = lnteger.parselnt(aegs[1]); int result = x/y; // 进行计算 System.out.println("**************"+(10 / 0)+"***************"); }catch(ArithmeticException e){ // e 是一个对象 e.printStackTrace(); }finally{ System.out.println("ssssssssssssssssss"); } System.out.println("*****************************"); } }
于是下面就有了这样几种执行情况:
执行程序的时候没有设置初始化参数 : 数组越界异常
执行程序的时候输入的内容不是数字(java Abnormal a b);NumberFormatException
执行的时候输入的被除数为 0(java Abnormal 10 0):ArithmeticException
如果现在异常已经正常进行处理了,则最后的语句一定会执行,而如果没有处理,最后的语句将不执行,但是 finally 的代码永远都会出现
为了保证程序出现错误之后依然可以正常的执行完毕,那么可以采用多个 catch 处理
范例:修改代码,处理多个异常
public class Abnormal{ public static void main(String aegs){ System.out.println("*****************************"); try{ int x = lnteger.parselnt(aegs[0]); int y = lnteger.parselnt(aegs[1]); int result = x/y; // 进行计算 System.out.println("**************"+(10 / 0)+"***************"); }catch(ArithmeticException e){ // e 是一个对象 e.printStackTrace(); }catch(ArraylndexOutOfBoundsException e){ // e 是一个对象 e.printStackTrace(); }catch(NumberFormatException e){ // e 是一个对象 e.printStackTrace(); }finally{ System.out.println("ssssssssssssssssss"); } System.out.println("*****************************"); } }
这种代码使用if判断也行啊,折腾什么异常?
提示:如果在 try 语句之中真的需要进行多个异常处理,那么捕获范围大的异常要放在捕获范围小的异常之后,否则会出现语法错误
异常处理流程
对于整个程序的异常处理,以上只是针对于语法进行了基本的使用说明,但是从本质上来讲,java中的异常处理要比之前讲解的过程容易
为了分析这个流程,下面先来看两个异常类的继承结构
可以发现两个异常类都是 java.lang.Throwable 的子类,而现在来观察 Throwable 类
此类定义如下:
public class Throwable extends Object implements Serializable
可以发现 Throwable 直接是 Object 子类。从JDK 1.0开始提供了
但是在 Throwable 下有两个子类,因此在开发中几乎不会考虑 Throwable 处理
面试题:请解释 Throwable 下的 Error 和Exception 子类的区别
Error:是在程序还未执行时出现的错误,一般指的都是JVM 出错,用户无法处理
Exception:指的是在程序运行之中出现的异常,异常处理都是针对于此类型完成的
因此在以后的开发中,如果处理异常,能够处理的最大的父类就是 Exception
那么下面就可以根据以上给出的结论类分析异常处理流程(面试题)
1.当程序中产生异常之后,JVM 自动的根据异常类型实例化一个指定的异常类对象
2.程序需要判断当前的代码之中是否存在异常处理逻辑,如果没有,则交由JVM默认处理,处理方式输出异常信息,而后中断程序执行
3.如果程序存在有异常处理,则 try 语句会捕获该异常类的实例化对象(想象为引用传递)
4.捕获到的异常类的实例化对象要与 catch 中的异常类型进行依次匹配
5.如果 catch 匹配了该异常类型则使用相应代码进行处理,随后要执行 finally 代码,如果没有任何一个 catch匹配,则直接跳转到 finally 语句
6.执行完 finally 代码之后要判断该异常是否已经处理过了,如果已经处理过了,则继续执行后续代码,而如果没有处理,则程序将继续交由 JVM 默认处理
通过以上的分析,可以发现所谓的 catch 匹配异常,就跟方法的参数传递没有什么区别,有一个实例化对象了,如果发现类型匹配,则进行接收
那么按照对象向上转型的原则,所有的子类对象都可以向父类转换,那么也就证明所以的异常实际上都可以使用 Exception 进行处理
范例:直接使用 Exception 处理异常
public class Abnormal{ public static void main(String aegs){ System.out.println("*****************************"); try{ int x = lnteger.parselnt(aegs[0]); int y = lnteger.parselnt(aegs[1]); int result = x/y; // 进行计算 System.out.println("**************"+(10 / 0)+"***************"); }catch(Exception e){ // e 是一个对象 e.printStackTrace(); }finally{ System.out.println("ssssssssssssssssss"); } System.out.println("*****************************"); } }
在实际的开发过程之中,如果你的开发团队有明确的要求,那么就分开处理
但是如果要求不明确的情况下,使用 Exception 处理是最方便的
throws 关键字
异常的处理语句本身使用并不复杂,但是最为麻烦的是,执行到某段代码的时候,用户如何知道这个代码会产生什么异常?
所以在java中提供有一个 throws 关键字,这个关键字的主要目的是明确的告诉用户执行某一个方法中有可能会产生哪些异常
所以 throws 主要是用于方法的声明处
范例:观察 throws 关键字的使用
class MyMath{ public static int diy(int x,int y)throws Exception{ return x/y; } } public class Abnormal{ public static void main(String aegs){ } }
在这个 diy () 方法上那么就表示,如果要想执行本语句,那么就必须进行异常的处理
范例:不处理异常,而直接调用
class MyMath{ public static int diy(int x,int y)throws Exception{ return x/y; } } public class Abnormal{ public static void main(String aegs){ System.out.println(MyMath.diy(10,2)); } }// 结果出错
如果程序中使用了 throws 的声明,那么就表示必须强制性进行异常的处理操作
范例:正确的调用形式
class MyMath{ public static int diy(int x,int y)throws Exception{ return x/y; } } public class Abnormal{ public static void main(String aegs){ try{ System.out.println(MyMath.diy(10,2)); }catch(Exception e){ e.printStackTrace(); } } }
但是使用了 throws之后还存在有一个传递问题,主方法也是一个方法
所以主方法上也可以使用 throws ,那么就表示此时的主方法不进行异常的处理,而交给调用处处理
class MyMath{ public static int diy(int x,int y)throws Exception{ return x/y; } } public class Abnormal{ public static void main(String aegs)throws Exception{ System.out.println(MyMath.diy(10,2)); } }
主方法之上那么就由 JVM 进行默认的处理,所以一般而言,如果你直接写 java 代码,主方法中应该把异常全部处理完成
throw 关键字的使用
现在为止所有的异常对象都是由java负责进行实例化的,我们能够进行的处理只能够进行捕获
但是在java 中允许用户自己去实例化异常类对象,那么如果要抛出这个对象,就必须使用 throw 关键字
范例:自己手工抛出异常
public class Abnormal{ public static void main(String aegs){//throws Exception{ throw new Exception("*****"); } }
只要出现了 throw ,那么就表示代码中产生了异常,此时就两个选择:
方法上使用 throws 继续抛出
手工使用异常处理
范例:异常处理
public class Abnormal{ public static void main(String aegs)throws Exception{ try{ throw new Exception("*****"); }catch(Exception e){ e.printStackTrace(); } } }
面试题:请解释 throw 与 throws 的区别
throw 在方法体中使用,表示手工抛出一个异常类的实例化对象
throws 在方法的声明中使用,表示此方法调用时必须明确进行异常的处理
异常处理的实际应用
对于异常的处理已经学习过了: try,catch,finally,throw,throws, 其中感觉 finally,throw 存在的意义不大
但是实际上这些关键字如果真要使用,肯定一起使用
现在假设要定义一个实现除法计算的方法,但是此方法的设计有如下要求:
在进行除法计算开始要求可以打印提示信息,例如:“***计算开始***”;
在进行除法计算完成之后要求可以打印提示信息,例如:“***计算结束***”;
如果除法操作中出现了问题,那么必须交给调用处处理
class MyMath{ public static int diy(int x,int y)throws Exception{ // 交给被调用处处理 int result = 0; // 保存计算结果 System.out.println("*****除法计算开始*****"); return x/y; System.out.println("*****除法计算结束*****"); return result; } } public class Abnormal{ public static void main(String aegs){ try{ System.out.println(MyMath.diy(10,2)); }catch(Exception e){ e.printStackTrace(); } } }
一程序出现了错误,这个时候的执行结果.......
范例:加入异常控制
class MyMath{ public static int diy(int x,int y)throws Exception{ // 交给被调用处处理 int result = 0; // 保存计算结果 System.out.println("*****除法计算开始*****"); try{ return x/y; }catch(Exception e){ throw e; // 继续向上抛 }finally{ System.out.println("*****除法计算结束*****"); } return result; } } public class Abnormal{ public static void main(String aegs){ try{ System.out.println(MyMath.diy(10,2)); }catch(Exception e){ e.printStackTrace(); } } }
在以后的开发之中,一定会牵扯到资源的使用,例如:文件,数据库,数据库操作前一定要打开,操作后一定要关闭
但是以上给出的异常处理模型可以简化: try...finally
class MyMath{ public static int diy(int x,int y)throws Exception{ // 交给被调用处处理 int result = 0; // 保存计算结果 System.out.println("*****除法计算开始*****"); try{ return x/y; }finally{ System.out.println("*****除法计算结束*****"); } return result; } } public class Abnormal{ public static void main(String aegs){ try{ System.out.println(MyMath.diy(10,2)); }catch(Exception e){ e.printStackTrace(); } } }
RuntimeException
首先下段代码
public class Abnormal{ public static void main(String aegs){ int num = Integer.parseInt("123"); System.ou.println(num*num); } }
于是打开 Integer 类中的 parseInt() 方法定义来看
public static int parseInt(String s) throws NumberFormatException
从理论上来讲,方法中出现有 throws ,那么就需要进行异常处理,但是此时并没有处理,观察 NumderFormatExceptio 类的继承结构
java.lang.NumberFormatException
java.lang.Object
java.lang.Throwable
java.lang.Exception
java.lang.RuntimeException
java.lang.IllegalArgumentException
java.lang.NumberFormatException
因为 NumberFormatException 属于 RuntimeException 所以给异常属于选择性处理,用户即使不处理在程序编译的时候也不会发生错误,但是执行的时候会出错
面试题:请解释 Exception 与 RuntimeException 的区别?列举出几个你常见的 RuntimeException
RuntimeException 是 Exception 的子类
Exception 定义的异常都需要进行强制性的处理,而 RuntimeException 下的子类在编写代码异常不需要强制性的处理,由用户自己选择,如果不处理并且产生异常将交由JVM负责处理
常见的 RuntimeException :ArithmeticException,NullPointerException,ClassCastException,NumberFormatException
断言:assert (了解)
所谓的断言指的是在程序编写的过程之中,确定代码执行到某行之后数据一定是某个期待的内容
范例:观察断言
public class Abnormal{ public static void main(String aegs){ int num = 10; //中间可能经历过了3步进行num变量的处理 assert num == 100:"断言错误,num 的内容不是100"; System.ou.println(num); } }
对于断言,在默认的情况下是不会启用的,只有在程序运行时增加了一些配置参数才可以使用
java -ea (类名称)
自定义异常类
在java中的异常类之中提供有大量的类型,但是这些提供的类型几乎都是与语法有关的异常类型,缺少业务有关的类型
例如:现在输入某一个的成绩,成绩如果超过了100,那么就应该产生一个与之对应的异常处理。
但是这样的异常往往都需要开发者自己来进行设计,正因如此才需要使用到自定义异常类的概念
对于异常类型主要有两类:Exception, RuntimeException
范例:设计一个成绩的异常
class ScoreException extends Exception{ public ScoreException(String mag){ super(mag) } } public class Abnormal{ public static void main(String aegs){ double score = 101.0: if(score>100.0){ throw new ScoreException("成绩大于100分了"); } } }
总结:
1.几种组合: try...catch,try...catch...finally...throws...throw:
2. RuntimeException 与 Exception 的区别
3.异常的处理流程:引用对象的传递过程