java异常处理

Java异常的分类
在这里插入图片描述
Java异常的类间关系

  1. Throwable
    Throwable是Java语言中所有错误或异常的超类,它有两个直接子类Error / Exception。
    只有当对象是此类(或其子类之一)的实例时,才能通过 Java虚拟机或者throw语句抛出。类似地,只有此类或其子类之一才可以是catch子句中的参数类型。
    Throwable包含了其线程创建时线程执行堆栈的快照,它提供了printStackTrace()等接口用于获取堆栈跟踪数据等信息。
  2. Error
    Error类,代表了JVM自身内部的错误。Error不能被程序员通过代码处理,也Error很少出现。
    一旦出现,除了通知用户,以及尽量稳妥地终止程序外,几乎什么也无法做(也不应该做什么)。
  3. Exception
    大部分的异常都属于Exception,它们描述的是程序运行过程中和外部环境所引起的错误(比如SQLException)。
    当它们被抛出时,需要程序立即捕捉和处理(do something或向上一层抛出)。
  4. RuntimeException
    RuntimeException是程序设计错误导致的,比如错误的类型转换、数组越界。
    所以,如果代码会产生RuntimeException异常,则需要通过修改代码来避免。
    异常的两种类型
  5. Checked Exceptions 必检异常/可查异常
    除了Error 和 RuntimeException的其它异常。这样的异常一般是由程序的运行环境导致的,表示在运行过程中,出现了不能直接控制的无效外界情况(如用户输入IOException,数据库问题SQLException,网络异常,文件丢失ClassNotFoundException等)。
  6. Unchecked Exceptions 免检异常
    Error 和 RuntimeException 以及他们的子类。javac在编译时,不会提示和发现这样的异常,不要求在程序处理这些异常。否则整个Java代码会充斥着try-catch语句。
    对于这些异常,我们应该修正代码,而不是去通过异常处理器处理 。这样的异常发生的原因多半是代码写的有问题。比如,除0错误ArithmeticException,错误的强制类型转换错误ClassCastException,数组索引越界ArrayIndexOutOfBoundsException,使用了空对象NullPointerException等等。
    类的来源有两个:一是Java运行时环境自动抛出系统生成的异常,而不管你是否愿意捕获和处理,它总要被抛出。二是程序员自己抛出的异常,这个异常可以是程序员自己定义的,也可以是Java语言中定义的,用throw 关键字抛出异常。
    可查异常虽然是异常状况,但在一定程度上它的发生是可以预计的。
    javac强制要求程序员为这样的异常做预备处理工作(使用try…catch…finally或者throws)。

注意事项
• 检查和非检查是对于javac来说的,这样就很好理解和区分了。
• 免检异常的免检指的是编译器不会去检查你是不是处理了抛出的异常,而不是说你不能处理免检异常。1
异常追踪栈
异常是在执行某个函数时引发的,而函数又是层级调用,形成调用栈的,因为,只要一个函数发生了异常,那么他的所有的caller都会被异常影响。当这些被影响的函数以异常信息输出时,就形成的了异常追踪栈。
异常最先发生的地方,叫做异常抛出点。
在这里插入图片描述
从上面的例子可以看出,当devide函数发生除0异常时,devide函数将抛出ArithmeticException异常,因此调用他的CMDCalculate函数也无法正常完成,因此也发送异常,而CMDCalculate的caller——main 因为CMDCalculate抛出异常,也发生了异常,这样一直向调用栈的栈底回溯。这种行为叫做异常的冒泡,异常的冒泡是为了在当前发生异常的函数或者这个函数的caller中找到最近的异常处理程序。由于这个例子中没有使用任何异常处理机制,因此异常最终由main函数抛给JRE,导致程序终止。
异常追踪的具体流程
Java虚拟机用方法调用栈(method invocation stack)来跟踪每个线程中一系列的方法调用过程。该堆栈保存了每个调用方法的本地信息(比如方法的局部变量)。每个线程都有一个独立的方法调用栈。
对于Java应用程序的主线程,堆栈底部是程序的入口方法main()。当一个新方法被调用时,Java虚拟机把描述该方法的栈结构置入栈顶,位于栈顶的方法为正在执行的方法。
当一个方法正常执行完毕,Java虚拟机会从调用栈中弹出该方法的栈结构,然后继续处理前一个方法。如果在执行方法的过程中抛出异常,则Java虚拟机必须找到能捕获该异常的catch代码块。
它首先查看当前方法是否存在这样的catch代码块,如果存在,那么就执行该catch代码块;否则,Java虚拟机会从调用栈中弹出该方法的栈结构,继续到前一个方法中查找合适的catch代码块。
在回溯过程中,如果Java虚拟机在某个方法中找到了处理该异常的代码块,则该方法的栈结构将成为栈顶元素,程序流程将转到该方法的异常处理代码部分继续执行。
当Java虚拟机追溯到调用栈的底部的方法时,如果仍然没有找到处理该异常的代码块,按以下步骤处理。
(1)调用异常对象的printStackTrace()方法,打印来自方法调用栈的异常信息。
(2)如果该线程不是主线程,那么终止这个线程,其他线程继续正常运行。如果该线程是主线程(即方法调用栈的底部为main()方法),那么整个应用程序被终止。
不要随意使用异常
异常处理最根本的优势就是检测错误(由被调用的方法完成)从处理错误(由调用方法完成)中分离出来。这样,可以使程序更易读懂和修改。
但是,应该注意,由于异常处理需要初始化新的异常对象,需要从调用栈中返回,而且还需要沿着方法调用链来传播异常以便找到它的异常处理器,所以,异常处理通常需要更多的时间和资源。
当必须处理不可预料的错误状况时才应该使用它,不要用try-catch处理简单的,可预料的情况。
一定不要把异常处理用做简单的逻辑测试。
异常处理的机制
Java的异常处理模型基于三种操作:声明异常、抛出异常和捕捉异常。

  1. try-catch
    关键词try后的一对大括号将一块可能发生异常的代码包起来,称为监控区域。
    Java方法在运行过程中出现异常,则创建异常对象。立即停止下一条指令的执行,并将异常抛出,由JVM试图寻找匹配的catch子句以捕获异常。
    若有匹配的catch子句,则运行其catch中的代码。
    一定不要使用一个空的catch段!
  2. finally
    finally块不管异常是否发生,只要对应的try执行了,则它一定也执行。
    要注意,finally块没有处理异常的能力,只做异常出现后的扫尾工作。
    在以下4种特殊情况下,finally块不会被完全执行:
    1)在finally语句块中抛出了异常。
    2)在前面的代码中用了System.exit()退出程序。
    3)程序所在的线程死亡。
    4)关闭CPU/关机。
  3. throw
    throw总是出现在函数体中,用来抛出一个Throwable类型的异常。
    程序立即开始处理异常。
  4. throws
    throws声明要抛出的异常,即调用程序需要处理的异常。
    它不同于try-catch-finally,throws仅仅是将函数中可能出现的异常向调用者声明,而自己则不具体处理。
    采取这种异常处理的原因可能是:方法本身不知道如何处理这样的异常,或者说让调用者处理更好。
    在我看来,决定采取try-catch还是throws,要根据解耦的原则来决定。而且,尽量对于throws的每一个异常都使用catch-throw语句。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值