博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java异常处理机制
阅读量:7143 次
发布时间:2019-06-29

本文共 7634 字,大约阅读时间需要 25 分钟。

  1. java异常是Java提供的用于处理程序中错误的一种机制。
  2. 所谓错误是指程序在运行过程中发生的一些异常事件(如除数为0、数组下标越界、操作的文件不存在等)。
  3. Java程序在执行过程中如果出现异常事件,可以生成一个异常类对象,该异常对象封装了异常事件的信息并将被提交给Java运行时系统,这个过程称为抛出(throw)异常。
  4. 当Java运行时系统收到异常对象时,会寻找能处理这一异常的代码并将对当前异常对象交给其处理,这一过程称为异常捕获(catch)。

JAVA中的异常类都继承自Throwable类,也就是说,这是异常类的根。Throwable类扩展了两个类Error类和Exception类,Exception类又扩展了一个RuntimeException类。如下图:

  • Error:称为错误,由Java虚拟机生成并抛出,这类错误一般是运行时系统内部的错误,无法被处理。
  • Exception:所有异常类的父类,其子类对应了各种各样可能出现的异常事件,一般需要用户显式地声明或捕获。如文件类异常:FileNotFoundException,IOExecption。
  • RuntimeException:一种特殊的异常类,继承自Exception类。如除数为0、数组下标越界等。这类异常产生比较频繁,用户一般不对其做捕获处理,否则对程序的可读性和运行效率影响很大,而是由系统自动检测并将它们交给默认的异常处理程序进行处理。如ArithmeticException,ArrayIndexOutOfBoundException。

一般来说,出现RuntimeException异常表示的是代码不合理而出现的问题。

  • 未检查异常:Error错误和RuntimeException类的异常;
  • 已检查异常:Exception类的异常但不包括RuntimeException类。

因此,在自定义异常类型时,大多数都直接继承Exception类,偶尔可能继承RuntimeException类,更偶尔的可能会继承这些类的某些子类。

try-catch-finally结构和处理流程

使用try-catch结构捕捉异常,并设置捕捉到后的处理方式。还可以加上finally结构,这是可选结构,但却表示try结构中必须会被执行的部分。

以下是try-catch-finally结构和处理过程的分析。

try {  // 待捕捉测试的代码1  // 待捕捉测试的代码2   (假设此为异常代码,将抛出名为异常名2的异常)  // 待捕捉测试的代码3} catch (异常名1 对象名e) {  // 捕捉到异常名1的异常后,该做的处理代码4} catch (异常名2 对象名e) {  // 捕捉到异常名2的异常后,该做的处理代码5} ... {  //...} finally {  //一定会执行的代码6}  //try结构外的代码7

前提假设,在各代码中没有return子句。执行过程为:首先代码1正常执行,到代码2处抛出异常名2的异常,通过异常名匹配,将选择第二个catch结构,于是将异常2封装到对象名e中,并执行代码5处理异常。catch部分处理完后,还有最后处理段finally,于是执行代码6。出了finally后,还将执行代码7。

注意,当代码2出现异常后,代码3不会执行。而finally则是无论是否真的捕捉到了异常、是否在catch段有return都会执行的代码段。换句话说,finally段的代码除了内部错误或外界影响都一定会执行就像下面的例子中,即使catch使用了return,但finally还是会执行,只不过这个catch终止了try结构外的代码。

例如,除数为0时会抛出ArithmeticException异常。try-catch捕捉它:

public class TEx {    public static void main(String[] args) {        try {            System.out.println("[start]");            System.out.println(2/0);            System.out.println("[end]");        } catch (ArithmeticException e) {            System.out.println("[Catching]: " + e);            return;        } finally {            System.out.println("[Finally]");        }        System.out.println("[out of try-catch]");    }}

在finally段中还可以继续try-catch-finally,防止该段落的代码再次抛出异常。

public class TEx {    public static void main(String[] args) {        try {            System.out.println("[start]");            System.out.println(2/0);            System.out.println("[end]");        } catch (ArithmeticException e) {            System.out.println("[Catching]: " + e);            return;        } finally {            try {                System.out.println("[Finally-try-start]");                System.out.println(3/0);            } catch (ArithmeticException e) {                System.out.println("[Finally-Catching]: " + e);            }        }        System.out.println("[out of try-catch]");    }}

输出异常信息

java中的异常都会封装到对象中。异常对象中有几个方法:

  • printStackTrace():输出最详细的信息,包括抛出异常的行号,异常信息以及异常原因。
  • getMessage():输出异常信息。
  • getCause():输出异常原因。

异常抛出过程和throw、throws关键字

throw关键字用于在某个语句处抛出一个异常,只要执行到这个语句就表示必定抛出异常。

throws关键字用于在方法处抛出一个或多个异常,这表示执行这个方法可能会抛出异常。

throw OBJECT;throw new EXCEPTION("Message");method() throws EXCEPTION1[,EXCEPTION2...] {}

对于Exception类(非RuntimeException)的异常即已检查异常类,在调用时要么进行捕捉,要么继续向上级抛出。这类错误产生和处理的过程为:

  1. 方法f()内部的方法体的throw向上抛出给方法f();
  2. 方法f()的throws向上抛出抛给f()调用者;
  3. 方法调用者必须捕捉处理,或者不想捕捉就继续向上抛出;
  4. 每一级的调用者都不想捕捉而是一直向上抛出,则最后由java虚拟机报错:"未报告的异常错误XXXX必须对其进行捕获或声明以便抛出"。

以下是抛出异常的一个简单示例,抛出的是ArithmeticException异常,因为是RuntimeException类异常,因此从方法体内部throw抛出后,无需在方法定义处使用throws继续抛出。

public class EX {    void f(int n) {          // 或void f(int n) throws ArithmeticException {}        if (n == 0) {            throw new ArithmeticException("hello Exception!");        } else {            System.out.println("right!");        }    }    public static void main(String[] args) {            EX m = new EX();            m.f(1);            m.f(0);   // throw Exception    }}

执行结果:

right!Exception in thread "main" java.lang.ArithmeticException: hello Exception!   //异常的信息        at EX.f(EX.java:4)      //真实产生异常的地方        at EX.main(EX.java:13)  //调用产生异常的地方

所以,对于RuntimeException类异常来说,是否使用throws关键字并无影响。一般来说,Exception类异常但非RuntimeException才需要使用throws关键字,因为Exception类异常必须要被捕获并处理,而RuntimeException异常则无所谓。

例如将上面的ArimeticException改为FileNotFoundException,前者为Runtime类异常,而后者为Exception但非Runtime类异常,因此使用throw抛出后,必须在定义方法处也使用throws抛出错误。这一过程是"向上级抛出"的过程:"方法体内部抛出异常-->抛给方法本身"

void f(int n) throws FileNotFoundException {    if (n == 0) {        throw new FileNotFoundException("hello Exception!");  //throw a new yichang duixiang    } else {        System.out.println("right!");    }}

如果不使用throws关键字抛出错误,则将报错:

EX.java:6: 错误: 未报告的异常错误FileNotFoundException; 必须对其进行捕获或声明以便抛出       throw new FileNotFoundException("hello Exception!");  //throw a new yichang duixiang

从方法f()抛出向上抛出给调用者后,调用者要么使用try-catch捕捉,要么继续向上抛出,否则报错。例如捕捉

import java.io.*;public class EX {    void f(int n) throws FileNotFoundException {        if (n == 0) {            throw new FileNotFoundException("hello Exception!");        } else {            System.out.println("right!");        }    }    public static void main(String[] args) {      try {          EX m = new EX();          m.f(0);      } catch (FileNotFoundException e) {         System.out.println(e);       }        System.out.println("out of try-catch");    }}

如果不捕捉,则可以继续在方法处向上抛出:

public static void main(String[] args) throws FileNotFoundException {          EX m = new EX();          m.f(0);    }

抛出异常时的注意事项

throw可以同时定义可能抛出的多种异常,尽管这些异常存在继承关系。这时在捕捉异常时,应该先捕捉子类,再捕捉父类。

例如FileNotFoundException是IOException的子类,可以同时:

throws FileNotFoundException,IOException

捕捉时应先捕捉FileNotFoundException,再IOException。

try {...} catch (FileNotFoundException e) {...} catch (IOException e) {...}

在重写有throws子句的方法时,需要注意:

  1. 子类重写父类方法要抛出与父类一致的异常,或者不抛出异常
  2. 子类重写父类方法所抛出的Exception类异常不能超过父类的范畴
  3. 子类重写父类方法抛出的异常可以超出父类范畴,但超出的部分必须是RuntimeException类的异常

所以下面的定义中,前子类1-3重写和子类5-7都是有效的,但子类4重写是错误的。

父类:method() throws IOException {}子类1:method() throws {}子类2:method() throws IOException {}子类3:method() throws FileNotFoundException {}子类4:method() throws Exception {}子类5:method() throws RuntimeException {}子类6:method() throws IOException,RuntimeException {}子类7:method() throws IOException,ArithmeticException {}

自定义异常

异常是类,当产生异常时会构建此类的异常对象。

自定义异常时需要考虑异常类的构造方法是否接参数,参数需要如何处理实现怎样的逻辑。

自定义的异常一般都从Exception继承。

例如下面定义了一个银行卡存、取钱时的程序,其中自定义了一个Exception类错误。

// User Define Exceptionclass OverDrawException extends Exception {    private double amount;    public OverDrawException(double amount) {        this.amount = amount;    }    public OverDrawException(String message) {        super(message);    }    public double getAmount() {        return amount;    }}// Card classclass Card {    //cardNum:卡号,balance:余额    private String cardNum;    private double balance;    Card(String n,double b) {        this.cardNum = n;        this.balance = b;    }    //方法:存钱    public void cunQian(double n) {        balance += n;    }    //方法:取钱    public void quQian(double n) throws OverDrawException {        if (n <= this.balance) {            balance -= n;        } else {            double need = n - balance;            throw new OverDrawException(need);        }    }    //方法:返回余额    public double getBalance() {        return this.balance;    }}public class SuanZhang {    public static void main(String [] args) {        try {            Card card = new Card("62202",300);            System.out.println("卡里余额:" + card.getBalance());            //存钱            card.cunQian(200);            System.out.println("余额:" + card.getBalance());            //取钱            card.quQian(600);            System.out.println("余额:" + card.getBalance());        } catch (OverDrawException e) {            System.out.println(e);            System.out.println("抱歉!您的余额不足,缺少:" + e.getAmount());        }    }}

注:若您觉得这篇文章还不错请点击右下角推荐,您的支持能激发作者更大的写作热情,非常感谢!

转载地址:http://nwwgl.baihongyu.com/

你可能感兴趣的文章
tweak
查看>>
SSM学习系列(三) Hello Spring MVC
查看>>
chrome扩展推荐:帮你留住每一次ctrl+c --- Clipboard History 2
查看>>
恶意软件盯上了加密货币,两家以色列公司受到攻击
查看>>
专访《Haskell函数式编程入门》作者张淞:浅谈Haskell的优点与启发
查看>>
VS2017 15.4提供预览版,面向Windows 10秋季更新(FCU)
查看>>
Spring Web Services 3.0.4.RELEASE和2.4.3.RELEASE发布
查看>>
如何自动搞定全站图片的alt属性?
查看>>
配置一次,到处运行:将配置与运行时解耦
查看>>
突发热点事件下微博高可用注册中心vintage的设计\u0026实践
查看>>
用Elm语言降低失败的风险
查看>>
抓住热门话题一对一直播,如何在风浪四起的直播市场劈风斩浪? ...
查看>>
手把手教你用owncloud搭建属于自己的云盘
查看>>
epoll+socket实现 socket并发 linux服务器
查看>>
Kubernetes + CRI + Kata + Firecracker
查看>>
菜鸟成都未来园区启动,无人车首次进入园区调拨运输环节 ...
查看>>
算法不扎实的程序员,每个都很慌
查看>>
规划一个智能工厂应避免的十个坑
查看>>
Linux 虚拟网络设备详解之 Bridge 网桥
查看>>
LaTeX的简单使用方法
查看>>