Java异常那些事

Java异常的那些事

一、 前言

在日常的Java编码工作学习中,我们总免不了和异常打交道,比如说新手常见的简称NPE的空指针异常、类型转换异常;读写文件数据库常见的IO异常、SQL异常;还有不是很常见但是总有一天你把程序搞崩遇到的栈溢出异常、内存溢出异常。今天我们就来详谈下Java异常的二三事。

下面是Jdk1.8中的异常类图:
在这里插入图片描述

二、 万物(异常)始于Throwable,不继承Throwable的异常都是耍流氓

Throwable是所有异常的父类,它有两个子类:Exception和Error,在jdk源码中Throwable是这样定义的:

The Throwable class is the superclass of all errors and exceptions in the Java language. Only objects that are instances of this class (or one of its subclasses) are thrown by the Java Virtual Machine or can be thrown by the Java throw statement. Similarly, only this class or one of its subclasses can be the argument type in a code catch clause.

Throwable是所有错误和异常的超类,只有这个类或者它的子类的实例对象才能由Java虚拟机抛出或者被throw语句捕获,同理,也只有它们能作为catch语句的参数。

那Exception和Error的区别又是什么呢:

  • Exception是Java程序运行中可以发现的异常状况,是一种可以且应该被捕获的异常
  • Error是Java程序正常运行不会出现的异常状况,一旦出现程序将会崩溃进入不正常状态且无法恢复

拿它们的子类来讲可能更通俗易懂,例如Exception的子类:ClassNoFoundException、StringIndexOutofBoundsException、NPE,这些异常要不是能在编译阶段能检查出来的异常、要不是在运行期能通过代码捕获修正的异常;而Error的子类:ThreadDeath、StackOverFlowError、OutofMemoryError这些异常都是编译器和运行期都无法处理的异常,一旦发生程序就无法恢复。

三、Exception的两个阵营

理解了上面的内容那我们来细分Exception。我们知道异常会在两个阶段被检测出来,一个是编译阶段,这个阶段能被检测出来的是可检查异常;一个是运行阶段,这个阶段被称为不可检查异常,由于不可检查异常又全部是RuntimeException的子类所以又被称为运行时异常:

  • 可检查的异常:可检查异常是编译期工作的一部分内容,必须在代码中显式的捕获处理,不然是没有办法通过编译的
  • 不可检查异常:编译期检测不出来的异常,即使你不处理编译也能顺利通过,但是程序却很容易因为各种未被捕获处理的异常而中断。

下面这个表列出了Java编码的工作中常见的可检查和不可检查异常:

不可检查异常

类名介绍
ClassCastException类型转换异常
StringIndexOutofBoundsException字符串索引越界
ArrayIndexOutofBoundsException数组索引越界
NullPointerException空指针异常
NumberFormatException转换数字异常

可检查异常

类名介绍
NoSuchFieldException找不到字段
NoSuchMethodException找不到方法
ClassNoFoundException找不到类
IOExceptionIO异常
IOException-EOFException文件已结束
IOException- FieldNotFoundException文件未找到
SQLExceptionSQL异常

四、异常的常见捕获和抛出方式

说了这么多好像还没说异常的捕获和抛出语句,那我们聊一下最基础的语句:

  • try-catch-finally语句:
 //处理异常不抛出
public static void method1()
    {
        try
        {
            // to do something
        }
        catch (NullPointerException e)
        {
            // deal exception
        }
        catch (RuntimeException e2)
        {
            // deal exception
        }
        finally
        {
            // release resource
        }
    }

//不处理异常向上层抛出
public void method2() throws IOException //不处理须向上层抛出
 {
     //to do something
     throw new IOException();
 }

这里基本的使用就不多谈, 无非就是catch语句 范围从小到大(不这样也会报错),finally语句会在return之前执行,除非你在执行finally之前执行了类似System.exit(0)这种操作退出了虚拟机。至于是return语句中语句先完成 再执行finally块再返回还是先执行finally语句再执行return语句再返回不做讨论,不建议这样写代码给自己添加烦恼(出了问题你会被同事按摩半小时)。

Jdk1.8中对try-catch语句进行了拓展,现在我们可以写这样的语句了:

try(BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream("/"))) 
{
    // to do something
}catch(Exception e)
{
   // deal exception
}

我们可以直接在try后面的括号里声明实例,这样的实例我们不用手动使用finally去关闭,当然,实例的类必须实现了接口AutoCloseable。

五、 自定义异常的疑惑:继承Exception异常还是继承RuntimeException异常?

首先我们需要知道继承Exception异常还是继承RuntimeException异常的区别:

  • 继承Exception:此异常的实例必须在产生的地方要不被捕获要不被处理,不然不能通过编译。
  • 继承RuntimeException:此异常的实例不捕获处理也能通过编译,但是跑起来该出的错一个也少不了,但是你可以无视。

说完前提来谈下背景,不然你可能并不清楚为啥要分这么仔细:

在日常工作(banzhuan)中,我们经常会遇到实现一个方法,这个方法可能会产生异常,产生异常就面临两个问题:捕获处理还是向外层抛出。

我们知道对于某些异常是不在方法内处理而是让调用方法者处理的,这样我们需要在方法声明的后面加上throws:

 public void method() throws IOException //.不处理须向上层抛出
 {
     //to do something
     throw new IOException();
 }

但是这样,我们是舒服了,方法调用者却依然面临和你一样的问题,我是捕获还是继续向上抛出呢?他也很苦恼。调用层数比较简单的时候不会遇到这样的问题,但是通常在项目中我们遇到的都不是简单的一层调用,那如果想让最外层的调用者捕获,中间的调用者就必须不断向外抛出,一个异常、两个异常、一层方法、两层方法… 架构看着每个方法后面都跟一个throws的代码表示,他表示也很忧郁啊。

那我们到底是继承Exception异常还是继承RuntimeException异常呢?

  • 如果是系统内部,鉴于自己要针对的异常重要性和对代码耦合程度的要求,可以酌情考虑通过编码成员一起遵守约定代替throws抛出异常,这样避免了大量的方法throws异常,同时也要注意不抛出不代表没有,你忘记约定必会挨打。
  • 如果是提供接口给他人使用,一定要继承Exception,不然接口使用者没有处理你觉得必须处理的异常,代码出了问题,这锅你可跑不掉。

六、结语

好了,本篇从Throwable聊到Execption和Error的区别,又聊了可检查异常和不可检查异常,讲了1.8try-catch语句的新特性,最后聊了下自定义异常的烦恼。相信能让你对Java异常处理机制有一些认识。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值