Java异常详解

来源:互联网 发布:那些违规毁了的淘宝店 编辑:程序博客网 时间:2024/06/05 15:41

一、异常

1、异常的概述

  1)异常:就是程序在运行时出现不正常情况。
  2)异常由来:问题也是现实生活中一个具体的事物,也可以通过java类的形式进行描述。并封装成对象。其实就是java对不正常情况进行描述后的对象体现。

2、异常的体系

  对于问题的划分有两种:一种是严重的问题,java通过Error类对其进行描述,对于Error一般不编写针对性代码进行处理;一种是非严重的问题,java通过Exception类对其进行描述,对于Exception可以使用针对性的处理方式进行处理。无论Error或者Exception都具有一些共性内容。比如:不正常情况的信息,引发原因等。将它们向上提取,可以得到Java中所以异常的父类Throwable。
  Throwable
    |–Error
    |–Exception
      |–RuntimeException//特殊异常类,抛时不需要声明
      |–其他异常类
  异常体系的特点:
    1、异常体系中的所有类以及建立的对象都具备可抛性。
    2、也就是说可以被throw和throws关键字所操作。
    3、只有异常体系具备这个特点

3、异常的分类

  unchecked Exception(未检查异常):任何Error的子类以及RuntimeException的子类都称为未检查异常,编译时不会报错。
  checked Exception(已检查异常):其他的异常都被称为已检查异常。该异常如果没有被处理(没有抛也,没有try),编译时会报错。
  所有运行时异常均继承自RuntimeException类,常见的有:

NullPointerException - 空指针引用异常  ClassCastException - 类型强制转换异常。  IllegalArgumentException - 传递非法参数异常。  ArithmeticException - 算术运算异常  ArrayStoreException - 向数组中存放与声明类型不兼容对象异常  IndexOutOfBoundsException - 下标越界异常  NegativeArraySizeException - 创建一个大小为负数的数组错误异常  NumberFormatException - 数字格式异常  SecurityException - 安全异常  UnsupportedOperationException - 不支持的操作异常

4、声明并抛出异常

  Java中使用throws关键字用来声明异常,自己并不处理异常,而是将该异常传递给调用者,让调用者去处理这个异常。throw关键字用来抛出异常。throws用在方法头上,后面跟的是异常的类;throw用在方法体中,后面跟的是一个异常的对象。

1)声明异常

  Java方法使用throws关键字在其方法头中声明可能抛出的异常。例如,Java中的BufferrdReader类中的readLine方法。
  public String readLine() throws IOException
  该方法头表明方法会返回一个字符串,同时也可能抛出一个IOException异常对象。如果一个方法要抛出多个异常,可以用逗号将它们分开。
  public void method() throws IOException,MalformedURIException
  
  什么时候需要在方法头中使用throws关键字呢?
  a、调用了一个会抛出checked Exception的方法,如readLine方法。
  b、程序运行中发生错误,并且用throw(不是throws)关键字抛出一个checked Exception。
  
  注意:a、不需要声明Java的内部错误,也就是那些从Error类继承来的错误。
  b、不应该声明从RuntimeException继承来的异常。
  
  总之,一个方法必须声明它可能抛出的全部checked Exception,而unchecked Exception要么是不可控制的(Error),要么是开发人员应该避免的(RuntimeException)。如果方法没有声明所有checked Exception,则编译器会给出错误信息。

2)抛出异常

  假设有一个ShenXian类,类中有一个shiFa方法。正常情况下,每个神仙都可以变法术,但法术也有偶尔失灵的情况(异常)。此时,可以有shiFa这个方法抛出一个Exception异常对象。

class ShenXian{    //连续五次施法则法术失灵    public  void shiFa(int i) throws Exception{//使用throw关键字在shiFa这个方法头上声明异常        if(i == 5){            throw new Exception();//使用throw关键字抛出自定义异常对象。        }        System.out.println("第"+i+"次施法成功");    }    public static void main(String [] args) throws Exception{//使用throws关键字声明异常并将异常传给JVM        ShenXian shenxian = new ShenXian();//建立一个ShenXian类的对象        for (int i = 1; i < 7;i++){            shenxian.shiFa(i);        }    }}

  运行结果:

1次施法成功第2次施法成功第3次施法成功第4次施法成功Exception in thread "main" java.lang.Exception        at ShenXian.shiFa(ShenXian.java:5)        at ShenXian.main(ShenXian.java:12)

  shiFa这个方法头上用throws声明了可能发生的异常,并将该异常交给它的调用者去处理,它的调用者是主函数,也就是说让主函数去处理。但是主函数也用throws关键字声明了异常,并将该异常交给它的调用者去处理,它的调用者是JVM。也就是说,如果shiFa这个方法运行时发生了异常,它会用throw关键字抛出一个异常对象,这个对象最后会被传给虚拟机,交由虚拟机去处理。从打印出来的代码来看,前四次施法都成功了,打印出了代表施法成功的语句,当准备进行第5次时施法时,发现i == 5 这个条件满足,然后开始执行throw new Exception();这条语句,抛出了一个异常对象,交给了主函数,主函数又将该异常对象交给了JVM,JVM调用默认处理异常的方法,在屏幕上打印出相关的异常信息。

5、处理异常

  如果一个Java方法碰到异常,有两种处理方式:一是在方法内捕获(try-catch)这个异常,二是将异常声明(throws),由调用该方法的上级方法处理。对于第一种处理方式,一般采用try-catch-finally三段论的方式,代码格式如下:

try{    需要被检测的代码;}catch(异常类 变量){    处理异常的代码(处理方式)}finally{    无论是否发生异常都会执行的代码    释放资源,如关闭I/O流,断开数据库连接。}

结合下面代码进行分析:

class Demo{    int div(int a,int b) throws ArithmeticException{        return a/b;                         }}class Test{    public static void main(String [] args){        Demo d = new Demo();        System.out.println("除法运算开始");        try{            int x = d.div(4,0);            System.out.println("运算结果x="+x);        }        catch(Exception e){            System.out.println("Exception:除数不能为零");        }        finally{            System.out.println("清理除法运算后的垃圾");        }    }}

运行结果:

除法运算开始Exception:除数不能为零清理除法运算后的垃圾

  Demo类中的div方法头中用throws声明了ArithmeticException异常,也就是说如果div方法运行时出现了异常,会将该异常交给它的调用者(主函数)去处理,主函数处理的方式就是使用try-catch-finally三段论方式。结合打印结果来分析,虚拟机执行完System.out.println("除法运算开始");这条语句后,开始执行try代码块中的被检测语句,首先执行的是int x = d.div(4,0);这条语句,因为要调用div方法,且div方法有throws声明了异常,所以div方法会抛出一个ArithmeticException异常对象给try代码块,try代码块会结合该异常对象来分析被检测代码是否属于这类异常,由于Java中的ArithmeticException异常类定义了除数不能为零,所以检测结果是int x = d.div(4,0);这条语句确实属于ArithmeticException异常,这时候JVM就不在继续执行异常代码下面的代码了,也就是不执行System.out.println("运算结果x="+x);这条语句了,并且会将由int x = d.div(4,0);引起的异常交给相对应参数的catch代码块,然后JVM就会执行catch代码块中的处理该异常的方法。
  处理语句的其他格式:
  a、

try{}catch{}

b、

try{}finally{}

6、自定义异常类

  虽然Java语言提供了许多异常种类,但是有些时候这些异常仍然不够用。例如,假设要设计一个给小学低年级学生使用的计算器,该计算器在执行减法运算时要保证减数不能大于被减数,如果发生了这种错误,则要抛出一个异常。但是Java异常体系中没有定义这样一个异常,此时就有必要建立一个自己的异常类。
  建立一个自己的异常类很简单,步骤是新建一个类,并使该类继承Exception(一般都会选择直接继承Exception,或者继承RuntimeException以及它的子类这两种情况)。先来看下IOException源码是如何继承Exception的?

public class IOException extends Exception{    static final long serialVersionUID = 7818375828146090155L;    public IOException(){        super();    }    public IOException(String message){        super(message);    }    public IOException(String message,Throwable cause){        super(message,cause);    }    public IOException(Throwable cause){        super(cause);    }}

  类IOException直接继承了Exception,然后覆盖了Exception中的各个构造方法。我们可以参照它来建立我们自己的异常类。

public class BasicCalculator{    //减法运算    //使用throws关键字声明SubstractException异常,并将异常传给调用者处理。    public int substract(int i,int j) throws SubstractException{        if(i < j){            SubstractException e = new SubstractException();            throw e;//使用throw关键字抛出一个SubstractException异常对象。        }        else{            return i-j;        }    }    //加法运算    //使用throws关键字声明ArithmeticException异常,并将异常传给调用者处理。    public int add(int i,int j) throws NegativeException{        if(i < 0 || j < 0){            throw  new ArithmeticException();        }        return i+j;    }    //减法异常    class SubstractException extends ArithmeticException{        public SubstractException(){            super("减数大于被减数");        }    }    //负数异常    class NegativeException extends ArithmeticException{        public NegativeException(){            super("出现负数的异常");        }    }    public static void main(String [] args){        BasicCalculator cal = new BasicCalculator();        cal.add(2,5);        cal.add(-2,-3);        cal.substract(2,3);    }}

运行结果:

Exception in thread "main" java.lang.ArithmeticException        at BasicCalculator.add(BasicCalculator.java:17)        at BasicCalculator.main(BasicCalculator.java:37)

  从程序运行的结果看,其报错机制与Java语言自带的异常类没有什么不同。

7、异常的其他事项

1)异常的好处:

    a、将问题进行封装。
    b、将正常流程代码和问题处理代码相分离,方便于阅读

2)异常的处理原则:

  a、处理方式有两种:try或者 throws。
  b、调用到抛出异常的功能时,抛出几个,就处理几个。一个try对应多个catch。
  c、多个catch时,父类的catch放到最下面。否则编译会报错,因为其余的catch语句执行不到。
  d、catch内,需要定义针对性的处理方式。不要简单的定义printStackTrace,输出语句。也不要不写。当捕获到的异常,本功能处理不了时,可以继续在catch中抛出。例如,

try{    throw new AException();}catch (AException e){    throw e;}

  如果该异常处理不了,但并不属于该功能出现的异常。可以将异常转换后,在抛出和该功能相关的异常。或者异常可以处理,当需要将异常产生后和本功能相关的问题提供出去,让调用者知道并处理。也可以将捕获异常处理后,转换新的异常。这样就好比在给别人转账时,如果ATM机出现故障,这时可以另外找地方去转,也可以告诉对方,转账不成功。

try{         throw new AException();}catch (AException e){         对AException处理;         throw new BException();}

2)异常的注意事项:

  A、异常在子父类覆盖中的体现:
    a,子类抛出的异常必须是父类的异常的子类或者子集。
    b,如果父类或者接口没有异常抛出时,子类覆盖出现异常,只能try不能抛。
    

/*Exception     |--AException        |--BException    |--CException*/class AException extends Exception{}class BException extends AException{}class CException extends Exception{}class Fu{    void show()throws AException{    }}class Test{    void function(Fu f){        try{            f.show();        }        catch (AException e){        }    }}class Zi extends Fu{    void show()throws CException{        //如果这里子类抛出CException,父类中的catch就无法处理,        //这样就会导致编译失败,所以子类只能抛出父类中的异常或子集    }}

  B、问题在内部被解决就不需要声明。
  C、catch是用于处理异常。如果没有catch就代表异常没有被处理,如果该异常是checked Exception,那么必须声明。

0 0
原创粉丝点击