异常与异常的处理
一、 异常概述:
异常指的是程序在运行时产生的问题,在java中,把异常信息封装成一个类,出现问题的时候会创建异常类对象,将问题的详细信息封装在对象中并且抛出。我们可以根据这些信息判断问题产生的原因,位置。从而修改代码。
异常的继承体系:
根据此图我们可以看出:
Throwable
类是 Java 语言中所有错误或异常的超类,他有2大子类:Error 和 Exception
Error: (JVM层次的问题,程序员无法处理)
表示由JVM所侦测到的无法预期的错误,由于这是属于JVM层次的严重错误,导致JVM无法继续执行,因此,这是不可捕捉到的,无法采取任何恢复的操作,顶多只能显示错误信息。Error类体系描述了Java运行系统中的内部错误以及资源耗尽的情形。应用程序不应该抛出这种类型的对象(一般是由虚拟机抛出).假如出现这种错误,除了尽力使程序安全退出外,在其他方面是无能为力的。
Exception:(程序员可以处理的问题,一般需要显示地声明或捕获)
所有异常的父类,其子类对应了各种各样可能出现的异常事件,一般需要显示地声明或捕获。Exception中包含2个子类:runtime exception和checked exception。
checked exception:( 必须要对其进行处理,否则无法通过编译)
Java 中凡是继承自 Exception,而不继承自 RuntimeException 类的异常都是非运行时异常,也叫检查时异常。 如:IOException。 必须要对其进行处理,否则无法通过编译。
这类异常的产生不是程序本身的问题,通常由外界因素造成的。 为了预防这些异常产生时,造成程序的中断或得到不正确的结果,Java 要求编写可能产生这类异常的程序代码时,一定要去做异常的处理。
Runtime Exception:(通过程序的健壮性来处理,不推荐使用异常处理机制来处理)
Runtime Exception类是 Exception 类的子类,叫做运行时异常,Java 中的所有运行时异常都会直接或者间接地继承自 RuntimeException 类。
这一类特殊的异常,如被0除、数组下标超范围等,其产生比较频繁,处理麻烦,如果显示的声明或捕获将会对程序可读性可运行效率影响很大。因此由系统自动检测
并将它们交给缺省的异常处理程序(用户可不必对其处理)。我们可以通过程序的健壮性来处理,不推荐使用异常处理机制来处理。
例如:
NullPointerException:当程序访问只有引用没有对象的成员属性或成员方法。怎么处理?
ArithmeticException:除数为 0
ClassCastException:多态时类型转换错误
ArrayIndexOutOfBoundsException:访问的元素下表超过数组长度
NumberFormatException:数字格式异常
二、 异常产生
之所以出现异常,是因为内部抛出了异常对象,这个异常对象的产生分为系统内部产生,或程序员手动抛出异常。
- 系统内部产生
1 //工具类 2 class ArrayTools{ 3 //对给定的数组通过给定的角标获取元素。 4 public static int getElement(int[] arr,int index) { 5 int element = arr[index]; 6 return element; 7 } 8 } 9 //测试类 10 class ExceptionDemo2 { 11 public static void main(String[] args) { 12 int[] arr = {34,12,67}; 13 int num = ArrayTools.getElement(arr,4) 14 System.out.println("num="+num); 15 System.out.println("over"); 16 } 17 }
- 程序员手动抛出
1 public class ExceptionDemo { 2 3 public static void main(String[] args) { 4 5 int arr[] = null; 6 try{ 7 getElement(arr, 5); 8 }catch(ArrayIndexOutOfBoundsException e){ 9 System.out.println(e+"www"); 10 }catch (RuntimeException exception) { //越大的父类越往下放 不然抓不到 11 System.out.println("RuntimeException"+exception); 12 }finally { 13 arr = null; 14 } 15 16 } 17 /* 18 * 通过给定的数组,返回给定的索引对应的元素值。 19 */ 20 public static int getElement (int[] arr, int idx) throws RuntimeException{//在方法声明处抛出异常 21 if(arr == null){ 22 throw new NullPointerException("数组为null"); 23 } 24 if(idx < 0 || idx >= arr.length){ 25 throw new ArrayIndexOutOfBoundsException("错误的索引 :" + idx); 26 } 27 28 int ele = arr[idx]; 29 return ele; 30 31 } 32 }
三、 异常处理和捕获
对于编译(非运行)时异常( checked exception ),必须要对其进行处理,否则无法通过编译。处理方式有两种:
1 、异常捕获 try catch finally
1 try { 2 //需要被检测的语句。 3 } 4 catch(异常类 变量) { //参数。 5 //异常的处理语句。 6 } 7 finally { 8 //一定会被执行的语句。 9 }
说明:
try代码段包含可能产生例外的代码
try代码段后跟有一个或多个catch代码段
每个catch代码段声明其能处理的一种特定类型的异常并提供处理的方法
当异常发生时,程序会中止当前的流程,根据获取异常的类型去执行相应的catch代码段
一个 try 后面可以跟多个 catch,但不管多少个, 最多只会有一个catch块被执行。
finally段的代码无论是否发生异常都有执行
try语句
try{…}语句制定了一段代码,该段代码就是一次捕获并处理意外的范围。
在执行过程中,该段代码可能会产生并抛出一种或几种类型的异常对象,它后面的catch语句要分别对这些异常做相应的处理。
如果没有意外产生,所有的catch代码段都被略过不执行。
catch语句
在catch语句块中是对异常进行处理的代码,每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常对象。
在catch中声明的异常对象封装了异常事件发生的信息,在catch语句块中可以使用这个对象的一些方法获取这些信息
例如:
getMessage()方法,用来得到有关异常事件的信息
printStackTrace()方法,用来跟踪异常事件发生时执行堆栈的内容
finally语句
finally语句为异常处理提供一个统一的出口,使得在控制流程转到程序的其它部分以前,能够对程序的状态作统一的管理。
无论try所制定的程序块中是否抛出异常,finally所指定的代码都要被执行。
通常在finally语句中可以进行资源的清除工作,如:
关闭打开的文件
删除临时文件
2 、 向外 声明( 抛出) 异常 throws
在产生异常的方法声明后面写上 throws 某一个 Exception 类型,如 throws Exception,将异常抛出到外面一层去
1 /* 2 如果定义功能时有问题发生需要报告给调用者。可以通过在方法上使用throws关键字进行声明。 3 */ 4 public void show(int x)throws Exception { 5 if(x>0){ 6 throw new Exception(); 7 } else { 8 System.out.println("show run"); 9 } 10 } 11 }
异常与重写
- 子类覆盖父类方法时,如果父类的方法声明异常,子类只能声明父类异常或者该异常的子类,或者不声明。
1 class Fu { 2 public void method () throws RuntimeException { 3 } 4 } 5 class Zi extends Fu { 6 public void method() throws RuntimeException { } //抛出父类一样的异常 7 //public void method() throws NullPointerException{ } //抛出父类子异常 8 }
- 当父类方法声明多个异常时,子类覆盖时只能声明多个异常的子集。
1 class Fu { 2 3 public void method () throws NullPointerException, ClassCastException{ 4 5 } 6 7 } 8 9 class Zi extends Fu { 10 11 public void method()throws NullPointerException, ClassCastException { }
public void method() throws NullPointerException{ } //抛出父类异常中的一部分 12 13 public void method() throws ClassCastException { } //抛出父类异常中的一部分 14 15 }
- 当被覆盖的方法没有异常声明时,子类覆盖时无法声明异常的。
1 class Fu { 2 3 public void method (){ 4 5 } 6 7 } 8 9 class Zi extends Fu { 10 11 public void method() throws Exception { }//错误的方式 12 13 }
举例:父类中会存在下列这种情况,接口也有这种情况
问题:接口中没有声明异常,而实现的子类覆盖方法时发生了异常,怎么办?
答:无法进行throws声明,只能catch的捕获。万一问题处理不了呢?catch中继续throw抛出,但是只能将异常转换成RuntimeException子类抛出。
interface Inter { public abstract void method(); } class Zi implements Inter { public void method(){ //无法声明 throws Exception int[] arr = null; if (arr == null) { //只能捕获处理 try{ throw new Exception(“哥们,你定义的数组arr是空的!”); } catch(Exception e){ System.out.println(“父方法中没有异常抛出,子类中不能抛出Exception异常”); //我们把异常对象e,采用RuntimeException异常方式抛出 throw new RuntimeException(e); } } } }
四、 使用自定义的异常
所谓自定义异常,通常就是定义一个类,去继承 Exception 类或者它的子类。因为异常必须直接或者间接地继承自 Exception 类。通常情况下,会直接继承自 Exception 类,一般不会继承某个运行时的异常类。
1 class MyException extends Exception{ 2 /* 3 为什么要定义构造函数,因为看到Java中的异常描述类中有提供对异常对象的初始化方法。 4 */ 5 public MyException(){ 6 super(); 7 } 8 public MyException(String message) { 9 super(message);// 如果自定义异常需要异常信息,可以通过调用父类的带有字符串参数的构造函数即可。 10 } 11 }
使用自定义异常一般有如下步骤:
1、 通过继承java.lang.Exception类声明自己的异常类
2、 在方法适当的位置生成自定义异常的实例,并用throw语句抛出
3、 在方法声明部分用throws语句声明该方法可能抛出的异常
五、重点总结
一个图: 异常体系
五个关键字: try catch finally throws throw
先逮小的,再逮大的
异常和重写的关系