在我们日常编程中,异常处理是必不可少的,异常处理是否得当关系到程序的健壮性和后续维护成本。

试想一下,如果一个项目从头到尾没有考虑过异常处理,当程序出错从哪里寻找出错的根源?但是如果一个项目异常处理设计地过多,又会严重影响到代码质量以及程序的性能。因此,如何高效简洁地设计异常处理是一门艺术,今天我们来讲解一下。

1、什么是异常

异常Exception,就是非正常情况。比如常见的:数组越界异常、内存溢出异常、空指针异常等。在我们编写程序的过程中错误会时常发生,包括编译期间和运行期间的错误,在编译期间出现的错误有编译器帮助我们一起修正,然而运行期间的错误便不是编译器力所能及了,并且运行期间的错误往往是难以预料的。假若程序在运行期间出现了错误,如果置之不理,程序便会终止或直接导致系统崩溃,显然这是不行的。

那么,我们如何对运行期间出现的错误进行处理和补救呢?Java提供了一套异常处理机制,通过异常机制来处理程序运行期间出现的错误。通过异常机制,我们可以更好地提升程序的健壮性。

我们看下Jdk文档:

总结下,异常类的结构层次图如下图所示:

在Java中异常被当做对象来处理,根类是java.lang.Throwable类,在Java中定义了很多异常类(如OutOfMemoryError、NullPointerException、IndexOutOfBoundsException等),这些异常类分为两大类:Error和Exception。

Error是无法处理的异常,比如OutOfMemoryError,一般发生这种异常,JVM会选择终止程序。因此我们编写程序时不需要关心这类异常。

Exception,也就是我们经常见到的一些异常情况,比如NullPointerException、IndexOutOfBoundsException,这些异常是我们可以处理的异常。Exception类的异常包括checked exception和unchecked exception。

unchecked exception(非检查异常),也称运行时异常:都是RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundsException等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。运行时Java编译器不会去检查该类异常,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。

checked exception(检查异常),也称非运行时异常 (编译异常):是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等。

2、Java中如何处理异常

① 用try、catch捕获异常

try {File f = new File("d:/1.txt");if(!f.exists()) {f.createNewFile();}} catch (IOException e) {e.printStackTrace();}

可能发生异常的代码块用try{}括上,一旦发生异常,异常便会被catch捕获到(异常需要和catch块中的参数匹配上,发生的异常为catch块中的参数子类或本身则匹配上),然后在catch块中进行异常处理。

② 用throws向上层抛出异常

public static void main(String[] args) {try {createFile();} catch (IOException e) {e.printStackTrace();}}public static void createFile() throws IOException {File f = new File("d:/1.txt");if(!f.exists()) {f.createNewFile();}}

createFile方法中并没有捕获异常,而是用throws关键字声明抛出异常,即告知这个方法的调用者此方法可能会抛出IOException。那么在main方法中调用createFile方法的时候,采用try…catch块进行了异常捕获处理。

注:如果抛出的异常是非运行时异常(checked exception),此方法的调用者必须显示地用try…catch块进行捕获或者继续向上层抛出异常。如果抛出的异常是运行时异常,此方法的调用者可以选择地进行异常捕获处理。

③ 手动用throw向上层抛出异常

public static void main(String[] args) {int[] array = {1, 2, 3};try {getData(0, array);} catch (Exception e) {e.printStackTrace();}}public static int getData(int i, int[] array) {if(i<0 || i>=array.length) {throw new ArrayIndexOutOfBoundsException("数组索引越界");}return array[i];}

手动抛出了一个ArrayIndexOutOfBoundsException,上层调用的方法main来进行处理这个异常。

注:如果抛出的异常是非运行时异常(checked exception),此方法的调用者必须显示地用try…catch块进行捕获或者继续向上层抛出异常。如果抛出的异常是运行时异常,此方法的调用者可以选择地进行异常捕获处理。

3、运用try,catch,finally,throws,throw五个关键字

① try,catch,finally

try关键字用来包围可能会出现异常的逻辑代码,它单独无法使用,必须配合catch或者finally使用。Java编译器允许的组合使用形式只有以下三种形式:

try…catch…;try…finally…;try…catch…finally…

当然catch块可以有多个,注意try块只能有一个,finally块是可选的(但是最多只能有一个finally块)。三个块执行的顺序为try—>catch—>finally。

当然如果没有发生异常,则catch块不会执行。但是finally块无论在什么情况下都是会执行的(这点要非常注意,因此多数情况下,都会将释放资源的操作放在finally块中进行)。

在有多个catch块的时候,是按照catch块的先后顺序进行匹配的,一旦异常类型被一个catch块匹配,则不会与后面的catch块进行匹配。

在使用try…catch…finally块的时候,注意千万不要在finally块中使用return,因为finally中的return会覆盖已有的返回值。看下面例子:

public static void main(String[] args) {String s = readFile();System.out.println(s);}public static String readFile()  {try {FileInputStream f = new FileInputStream("d:/1.txt");f.read();return "1";} catch (FileNotFoundException e) {System.out.println("FileNotFoundException...");return "2";} catch (IOException e) {System.out.println("IOException...");return "3";} finally {System.out.println("finally block...");//return "4";}}

输出结果,文件没有找到,抛出FileNotFoundException异常,执行finally块,正常返回2。

FileNotFoundException...finally block...2

接下来把//return “4”;的注释去掉,再次运行,输出结果,我们看到finally的返回值4覆盖了catch块的返回值2。因此如果方法有返回值,切忌不要在finally中使用return,这样会使得程序结构变得混乱。

FileNotFoundException...finally block...4

② throws和thow关键字

1)throws出现在方法的声明中,表示该方法可能会抛出的异常,然后交给上层调用它的方法程序处理,throws后面允许跟着多个异常类型;

2)一般会用于程序出现某种逻辑时程序员主动抛出某种特定类型的异常。throw只会出现在方法体中,当方法在执行过程中遇到异常情况时,将异常信息封装为异常对象,然后throw出去。

4、子类进行方法重写的时候,如何声明抛出异常

看代码:

从上面四个图,可以得出:

如果抛出的是RuntimeException类型的异常,Java编译器不会去检查它,所以在子类和父类中使用它都没有关系。
如果不是RuntimeException类型的异常,子类不能抛出比父类范围更广的异常。

5、几点建议

① 只在必要使用异常的地方才使用异常,不要用异常去控制程序的流程

谨慎地使用异常,异常捕获的代价非常高昂,异常使用过多会严重影响程序的性能。如果在程序中能够用if语句和Boolean变量来进行逻辑判断,那么尽量减少异常的使用,从而避免不必要的异常捕获和处理。  

② 切忌使用空catch块

在捕获了异常之后什么都不做,相当于忽略了这个异常。千万不要使用空的catch块,空的catch块意味着你在程序中隐藏了错误和异常,并且很可能导致程序出现不可控的执行结果。最好用Log日志将该异常进行记录,以便日后方便更新和维护。  

③ 有多个catch块时,需要注意catch块之间的顺序

多个catch块应该按照先子类后父类的顺序排列。如果先父类后子类排列,则后面的子类catch块不会执行到。

④ 异常处理尽量放在高层进行

尽量将异常统一抛给上层调用者,由上层调用者统一之时如何进行处理。如果在每个出现异常的地方都直接进行处理,会导致程序异常处理流程混乱,不利于后期维护和异常错误排查。由上层统一进行处理会使得整个程序的流程清晰易懂。  

⑤ 在finally中释放资源

如果有使用文件读取、网络操作以及数据库操作等,记得在finally中释放资源。这样不仅会使得程序占用更少的资源,也会避免不必要的由于资源未释放而发生的异常情况。

欢迎小伙伴们留言交流~~

java请求超时异常捕获_我异常了,快来捕获我,Java异常简述相关推荐

  1. Java 启动和停止界面_一文详解各种花里胡哨的Java调试技巧,多图预警,记得收藏...

    欢迎关注专栏<Java架构筑基>--专注于Java技术的研究与分享! Java架构筑基​zhuanlan.zhihu.com Java架构筑基--专注于Java技术的研究与分享! 后续文章 ...

  2. java二级考试真题_计算机等级考试真题2(JAVA)

    1. D (A)类属于JAVA语言的引用数据类型. (B)接口属于JAVA语言的引用数据类型. (C)数组属于JAVA语言的引用数据类型. (D)double不属于JAVA语言的引用数据类型. 2. ...

  3. java 异常 过滤器_在过滤器Filter中抛出一个全局异常可以捕获的异常

    1.创建自定义异常 public class TokenException extends RuntimeException { private static final long serialVer ...

  4. java 请求https 握手失败_记一次https访问握手失败(handshake failure)

    那么基本可以排除不是证书的问题了,而且把curl的verbose/debug模式打开也看到,ssl认证是ok的,如下 * Connected to cdn.example.com (52.222.23 ...

  5. 获取异常信息_如何在 ASP.NET Core 中实现全局异常拦截

    异常是一种运行时错误,当异常没有得到适当的处理,很可能会导致你的程序意外终止,这篇就来讨论一下如何在 ASP.Net Core MVC 中实现全局异常处理,我会用一些 样例代码 和 截图 来说明这些概 ...

  6. java 请求超时处理_android 网络请求超时简单处理(基于rxjava)

    implementation 'com.squareup.retrofit2:adapter-rxjava:2.1.0' public class ExceptionHandle { private ...

  7. java请求百度短链接_长链接生成短链接Java源码(调用百度接口)

    public static DefaultHttpClient httpclient; static { httpclient = new DefaultHttpClient(); //httpcli ...

  8. java项目怎样提高性能_从代码的角度谈如何优化JAVA代码以提高性能【初、中级程序员必看】...

    最近复习Java基础感触颇深,同样是几行代码,别人写的运行效率就很大,自己写的就不是那么随意,这就牵扯到代码的性能优化问题,这个问题在初级阶段往往没有引起太多的重视,导致后来的工作中就没有往优化这个角 ...

  9. java 关闭阻塞线程池_如果优雅地关闭ExecutorService提供的java线程池

    每一个线程都会占用系统资源,因此线程池的关闭与清理同样重要,本文介绍我们如何优雅地关闭线程池. 一. ExecutorService中关闭线程池的方法 1. shutdown() 停止接收新任务,原来 ...

最新文章

  1. Python中的注释(转)
  2. python代码实现二叉树的序列化和反序列化
  3. Flink状态后端配置(设置State Backend)
  4. 过滤内容字段_巧用参数组件和过滤组件,教你快速定位目标数据
  5. 分布式id 实现方式
  6. Azure运维系列 3:善用Azure捕获功能事半功倍
  7. Linux系统二进制bin文件操作的10种方法
  8. windows mysql kip grant tables_数据库mysql数据库mysql: [ERROR] unknown option '--skip-grant-tables'...
  9. 【软件开发底层知识修炼】五 gcc-C语言编译器
  10. JAVA编程思想读书笔记(三)--RTTI
  11. 信息学奥赛一本通 1006:A+B问题 | OpenJudge NOI 1.3 01
  12. Jenkins持续集成之小试牛刀
  13. eas之Uuid和BOSUuid 区别
  14. Introduction to Latent Dirichlet Allocation
  15. 如何利用大数据进行价值兑现才是正经事
  16. 【企业】走近华为,微观世界
  17. 主流数据库优缺点以及性能分析
  18. 微信公众号推送(Java)
  19. 17、Python特征工程,用相关系数法选择数据特征
  20. 更干净的PC电源,还能净化空气,艾湃电竞AP-550Ti电源上手

热门文章

  1. 1.每天一个linux命令 ls
  2. javascript紧接上一张for循环的问题,我自己的理解
  3. Cocos2d-x 3.0修改Android平台帧率fps - 解决游戏运行手机发热发烫问题
  4. [原]敏捷开发-项目启动
  5. 【转】PLI是什么以及怎么用
  6. 官网下的 demoMUI,a链接失效和点击事件失效
  7. Android10获取唯一ID最佳做法
  8. 深入剖析Android音频(三)AudioPolicyService
  9. Android系统信息获取 之十二:获取屏幕分辨率及密度
  10. tensorflow之卷积神经网络