文档章节

JAVA 的异常那些事

cloud-coder
 cloud-coder
发布于 2016/06/04 15:38
字数 2815
阅读 151
收藏 0

异常的概念

异常指不期而至的各种状况,如:文件找不到、网络连接失败、非法参数等。异常是一个事件,它发生在程序编译或运行期间,干扰了正常的指令流程。

Java中的Throwable类是所有异常的基类。它的的众多子类描述各种不同的异常。因而,Java异常都是对象,是Throwable子类的实例,描述了出现在一段编码中的 错误条件。当条件生成时,错误将引发异常。

Java异常类层次结构及概念

Throwable

    Throwable 类是 Java 语言中所有错误或异常的超类

异常与错误

    注意:异常和错误的区别:异常能被程序本身可以处理,错误是无法处理。

    1 Exception 类及其子类用来处理程序错误,它指出了合理的应用程序想要捕获的条件,表示程序本身可以处理的异常

    2 Error 是及其子类用来处理系统错误,表示仅靠程序本身无法恢复的严重错误,用于指示合理的应用程序不应该试图捕获的严重问题,Java编译器不去检查这类异常

运行时异常

    RuntimeException 类及其子类表示“JVM 常用操作”引发的错误。例如,若试图使用空值对象引用、除数为零或数组越界,则分别引发运行时异常(NullPointerException、ArithmeticException)和 ArrayIndexOutOfBoundException。

可查异常 和 不可查异常  

    通常,Java的异常(包括Exception和Error)分为可查的异常(checked exceptions)和不可查的异常(unchecked exceptions)
      

    可查异常(编译器要求必须处置的异常):正确的程序在运行中,很容易出现的、情理可容的异常状况。可查异常虽然是异常状况,但在一定程度上它的发生是可以预计的,而且一旦发生这种异常状况,就必须采取某种方式进行处理。

      除了RuntimeException及其子类以外,其他的Exception类及其子类都属于可查异常。这种异常的特点是Java编译器会检查它,也就是说,当程序中可能出现这类异常,要么用try-catch语句捕获它,要么用throws子句声明抛出它,否则编译不会通过。

     不可查异常(编译器不要求强制处置的异常):包括运行时异常(RuntimeException与其子类)和错误(Error)。

     Exception 这种异常分两大类运行时异常和非运行时异常(编译异常)。程序中应当尽可能去处理这些异常。

运行时异常 和非运行时异常

       运行时异常:都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。

      运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。
       非运行时异常 (编译异常):是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。

处理异常机制

    在 Java 应用程序中,异常处理机制为:抛出异常,捕捉异常。异常总是先被抛出,后被捕捉的。

        对于运行时异常、错误或可查异常,Java技术所要求的异常处理方式有所不同。

        只有在try里面是有System.exit(0)来退出JVM的情况下finally块中的代码才不会执行。

        由于运行时异常的不可查性,为了更合理、更容易地实现应用程序,Java规定,运行时异常将由Java运行时系统自动抛出,允许应用程序忽略运行时异常。

       对于方法运行中可能出现的Error,当运行方法不欲捕捉时,Java允许该方法不做任何抛出声明。因为,大多数Error异常属于永远不能被允许发生的状况,也属于合理的应用程序不该捕捉的异常。

       对于所有的可查异常,Java规定:一个方法必须捕捉,或者声明抛出方法之外。也就是说,当一个方法选择不捕捉可查异常时,它必须声明将抛出异常。

 Throws抛出异常的规则:

    1) 如果是不可查异常(unchecked exception),即Error、RuntimeException或它们的子类,那么可以不使用throws关键字来声明要抛出的异常,编译仍能顺利通过,但在运行时会被系统抛出。

    2)必须声明方法可抛出的任何可查异常(checked exception)。即如果一个方法可能出现受可查异常,要么用try-catch语句捕获,要么用throws子句声明将它抛出,否则会导致编译错误

    3)仅当抛出了异常,该方法的调用者才必须处理或者重新抛出该异常。当方法的调用者无力处理该异常的时候,应该继续抛出,而不是囫囵吞枣。

    4)调用方法必须遵循任何可查异常的处理和声明规则。若覆盖一个方法,则不能声明与覆盖方法不同的异常。声明的任何异常必须是被覆盖方法所声明异常的同类或子类。

异常处理最佳实践

1)为可恢复的错误使用检查型异常,为编程错误使用非检查型错误。
2)在finally程序块中关闭或者释放资源
3)在堆栈跟踪中包含引起异常的原因

        Java异常类提供了 getCause()方法来检索导致异常的原因,这些(原因)可以对异常的根层次的原因提供更多的信息。该Java实践对在进行调试或排除故障大有帮助。时刻记住,如果你将一个异常包装成另一种异常时,构造一个新异常要传递源异常。
4)始终提供关于异常的有意义的完整的信息
5)避免过度使用检查型异常
6)将检查型异常转为运行时异常

        这是在像Spring之类的多数框架中用来限制使用检查型异常的技术之一,大部分出自于JDBC的检查型异常,都被包装进DataAccessException中,而(DataAccessException)异常是一种非检查型异常。这是Java最佳实践带来的好处,特定的异常限制到特定的模块,像 SQLException 放到DAO层,将意思明确的运行时异常抛到客户层。
7)记住对性能而言,异常代价高昂
        需要记住的一件事是异常代价高昂,同时让你的代码运行缓慢。假如你有方法从ResultSet(结果集)中进行读取,这时常会抛出SQLException异常而不会移到下一元素,这将会比不抛出异常的正常代码执行的慢的多。因此最大限度的减少不必要的异常捕捉和移动,那里没有什么固定的原因。不要仅仅是抛出和捕捉异常,如果你能使用boolean变量去表示执行结果,可能会得到更整洁,更高性能的解决方案。修正错误的根源,避免不必须要的异常捕捉。
8)避免catch块为空    
9)使用标准异常

        对于维护性和一致性,不管是现在还是以后,都是最好的选择
10)记录任何方法抛出的异常
        Java提供了throw和throws关键字来抛出异常,在javadoc中用@throw记录任何方法可能会抛出的异常。如果你编写API或者公共接口,这就变得非常重要。任何方法抛出的异常都有相应的文档记录,这样你就能下意识的提醒任何使用(该方法)的人。

常见的面试问题

1 既然我们可以用RuntimeException来处理错误,那么你认为为什么Java中还存在检查型异常?

答:存在检查型异常是一个设计上的决定,受到了诸如C++等比Java更早的编程语言设计经验的影响。绝大多数检查型异常位于java.io包内,这是合乎情理的,因为在你请求了不存在的系统资源的时候,一段强壮的程序必须能够优雅的处理这种情况。通过把IOException声明为检查型异常,Java 确保了你能够优雅的对异常进行处理。另一个可能的理由是,可以使用catch或finally来确保数量受限的系统资源(比如文件描述符)在你使用后尽早得到释放

  throw 和 throws这两个关键字在java中有什么不同?

答:throws出现在方法声明中,用来标明该成员函数可能抛出的各种异常

       throw出现在方法体中,表示抛出某种异常

3 什么是“异常链”? 

 答:“异常链”是Java中非常流行的异常处理概念,是指在进行一个异常处理时抛出了另外一个异常,由此产生了一个异常链条。该技术大多用于将“ 受检查异常” ( checked exception)封装成为“非受检查异常”(unchecked exception)或者RuntimeException。顺便说一下,如果因为因为异常你决定抛出一个新的异常,你一定要包含原有的异常,这样,处理程序才可以通过getCause()和initCause()方法来访问异常最终的根源。

JDK7中对异常处理做了什么改变?

 答:JDK7中对错误(Error)和异常(Exception)处理主要新增加了2个特性,一是在一个catch块中可以出来多个异常,就像原来用多个catch块一样。另一个是自动化资源管理(ARM), 也称为try-with-resource块。这2个特性都可以在处理异常时减少代码量,同时提高代码的可读性。

5 Java中final,finalize,finally关键字的区别

答:final和finally是Java的关键字,而finalize则是方法。final关键字在创建不可变的类的时候非常有用,只是声明这个类是final的。而finalize()方法则是垃圾回收器在回收一个对象前调用,但也Java规范里面没有保证这个方法一定会被调用。finally关键字异常处理相关的关键字

© 著作权归作者所有

cloud-coder
粉丝 247
博文 194
码字总数 141537
作品 0
广州
架构师
私信 提问
阿里架构师眼里JVM可以说的那些事

前言 对于C语言开发的程序员来说,在内存管理方面,必须负责每一个对象的生命周期,从有到无。 对于Java程序员你来说,在虚拟机内存管理的帮助下,不需要为每个new对象都匹配free操作,内存泄...

2018/07/09
0
0
JAVA 练习题 2019-7-31AM

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 https://blog.csdn.net/ai64/article/details/98097756 JAVA 练习题 2019-7-31AM 第二题:J...

ai_64
08/02
0
0
J2EE 框架 - Spring

Spring Framework 是一个开源的 Java/Java EE 全功能栈(full-stack)的应用程序框架,以 Apache 许可证形式发布,也有 .NET 平台上的移植版本。该框架基于 Expert One-on-One Java EE Desi...

匿名
2008/09/07
637.1K
91
JDBC无法登陆数据库或者无法连接数据库的错误代码

jdbc驱动主要有 jtds 和微软官方的jdbc驱动. 微软官方的jdbc驱动支持failoverParnter 但是性能低下 jtds 性能很好,但是不支持failoverParnter 目前自行扩展dbcp实现failoverParnter. jdbc中登...

光石头
2012/05/08
587
3
Java中有关Null的9件事

对于Java程序员来说,null是令人头痛的东西。时常会受到空指针异常(NPE)的骚扰。连Java的发明者都承认这是他的一项巨大失误。Java为什么要保留null呢?null出现有一段时间了,并且我认为J...

enosh
2015/01/05
133
0

没有更多内容

加载失败,请刷新页面

加载更多

为什么?老程序员申请加薪至30K遭拒愤然辞职,公司转眼花35K招新

身在职场,经常会暗自打听同事工资,尤其是得知身边新入职同事的工资居然比自己高,还高出一大截时,心里自然很不平衡,一心想要离职。 那么,为什么公司宁愿花高价招聘新员工也不愿意给老员...

Java技术剑
18分钟前
6
0
云桌面到底是什么,企业该用云桌面吗

随着云计算和大数据时代的到来,当前企业里讨论最多的话题之一就是:企业到底要不要用云桌面这个话题的,有的人说企业应该使用云桌面的,因为可以降低使用成本和简化运维管理,同时也有的人说...

GZASD
22分钟前
3
0
技术分享 | gh-ost 原理剖析

作者简介: 杨奇龙,网名“北在南方”,7年DBA老兵,目前任职于杭州有赞科技DBA,主要负责数据库架构设计和运维平台开发工作,擅长数据库性能调优、故障诊断。 一、简介 上一篇文章(gh-ost ...

爱可生
26分钟前
5
0
手机短信删除了怎么恢复?几个方法就能恢复

  手机短信删除了怎么恢复?前几天有个小伙伴收到了一条来着面试的通知,这个面试对他很重要,但是可气的是刚好在清理手机里面的垃圾短信,然后收到了短信之后又被删除了,却又不知道该怎么...

科技第六人
40分钟前
5
0
浅谈Builder建造者模式

一、前言 Builder建造者模式和模板模式非常像,但是也有区别,模板模式中父类对子类中的实现进行操作,在父类之中进行一件事情的处理,但是在Builder模式之中,父类和子类都不用关心怎么处理...

青衣霓裳
45分钟前
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部