简述java中的异常及处理机制

我们在写代码的过程中,会不可避免的出现各种错误以及异常,所以了解错误和异常的体系对于开发人员来说还是挺重要的,接下来我带大家看一下java中的error和exception。

在开始前我们先来看张图,包含了exception和error的各自两种分类,从左到右分别为直接继承Exception的checkedException(编译时异常)、继承RuntimeException的uncheckedException(运行时异常)、继承VirtualMachineError的虚拟机错误、直接继承Error的错误。
在这里插入图片描述

error和exception的异同

相同点

error和exception都继承了Throwable类,通过源码可以看出error和exception类中只有构造方法,且结构相同。

不同点

error无法通过代码来被抛出或捕获,当程序在运行中出现error的时候,就意味着程序即将停止运行了。而exception是可以被代码捕获或抛出的,也就是说我们可以通过捕获或抛出来将异常对程序的影响降到最低,让代码按照设定好的逻辑来处理异常,让程序尽可能恢复正常并继续执行。

简述exception

通过上述的内容,我们可以得知,在java中error不能被处理,而exception可以处理,这就是说在写代码的时候,我们可以忽略error将更多的精力放在对exception的处理上。

exception分为两种,一种是(checked)可检查异常也叫编译时异常,另一种是(unchecked)不可检查异也叫运行时异常。

异常的处理方式

面对异常最重要的就两个问题:这个异常该交给谁来处理;这个异常该如何处理。jdk提供了两种方式来处理异常:捕获抛出。捕获就是try-catch说明要自己处理异常,如何处理则需要结合实际情况来判断。抛出又分两种分别是throw和throws,则需要调用者处理。异常交给谁来处理是有约定俗成的规则的,在说明规则前需要简单了解下三层架构(视图层、业务逻辑层、数据访问层),视图层只负责接收用户输入的数据和显示处理后用户需要的数据,数据访问层只负责将业务逻辑层处理过的数据持久化到数据库,那现在就只剩下业务逻辑层来处理异常,但通常业务逻辑层需要控制数据库事务,如果捕获异常的话,就需要自己在catch中手动控制事务,为了使代码更加优雅,我们选择将异常抛出在视图层使用全局异常处理机制来处理异常。关于try-catch、throw和throws处理异常的注意点下面结合伪代码来简要说明。

try-catch

try-catch见名知意就是尝试-捕获很好理解,try代码块中的代码可能会出错,如果代码出错则被catch捕获并执行catch代码块中的逻辑,同时代码并不会停止运行,catch可以存在多个以针对不同异常做出不同的处理。catch代码块后可追加finally代码块,finally中的代码在任何时候都会执行,无论try中代码有没有异常,通常在finally代码块中进行一些资源的关闭,例如数据库连接的关闭、io流的关闭等。

public void methodDemo(){
    try {
        从数据库中查询数据
        将查询出的数据写入文件
    }catch (SQLException e){
        logger.error("查询数据出错",e);
    }catch (IOException e){
        logger.error("写入文件出错",e);
    }finally {
        databaseConnection.close();
        ioStream.close();
    }
}

throw和throws

throw和throw都是将异常抛出,如果代码出现异常都将由调用者处理,但两者还是有区别的

throw
  • 写在方法体内,抛出的是Exception或Error子类的实例

  • 只能抛出单个异常

  • 使用throw抛出异常就说明异常一定存在

throws
  • 写在方法名后,抛出的是Exception或Error的子类
  • 可以抛出多个异常,用逗号隔开
  • 使用throws抛出异常说明异常可能存在

checkedException

一般来说直接继承exception的都是编译时异常,在编写代码的时候,ide会提示你进行try-catch或throws处理,常见的有IOException、SqlException等。如果throw或throws抛出则交给调用者处理,当使用try-catch时,就说明我们预料到可能会发生发生某个异常或某几个异常,同时对这些异常做一些针对性的处理。

public void sqlMethod() {
    try{
        业务逻辑1
        执行sql
        业务逻辑2//可能出错的逻辑
    }catch (Exception e){
        e.printStackTrace();
    }
}

这是很典型的一个场景,将业务代码放在try中,如果在处理业务逻辑时报错,就回滚事务。但如果仔细观察就会发现,上述的写法存在几个问题。下面来一一说明

  • try-catch是消耗资源的,所以要尽可能减少try代码块中的代码,只在try代码块中书写必要的代码,所以该写成
public void sqlMethod() {
    业务逻辑1
    执行sql
    try{
        业务逻辑2//可能出错的逻辑
    }catch (Exception e){
        e.printStackTrace();
    }
}
  • catch中处理异常时,应尽可能的精准。try-catch的意义就在于让我们可以捕获特定的异常并进行针对性的处理,如果直接catch-exception就和处理异常的理念背道而驰了,同时编译时异常都为exception的子类,在程序执行时会去遍历寻找符合的异常,会消耗更多的资源,因此严禁直接catch-exception
public void sqlMethod() {
    业务逻辑1
    执行sql
    try{
        业务逻辑2//可能出错的逻辑
    }catch (SQLException e){
        e.printStackTrace();
    }
}
  • 严禁生吞异常,只有对异常进行针对性的处理,捕获才是有意义的,如果不处理请throw或throws抛出交给调用者处理
public void sqlMethod() {
    业务逻辑1
    执行sql
    try{
        业务逻辑2//可能出错的逻辑
    }catch (SQLException e){
        e.printStackTrace();
        回滚事务
    }
}
  • 严禁使用e.printStackTrace()输出异常,如需打印异常信息请使用logger.error输出到控制台。printStackTrace输出字符串到控制台是基于System.err(标准出错)即时输出,而System.out(标准输出)是由缓存的,所以当二者同时使用时输出顺序可能不是真实的输出顺序,会增加定位问题的难度。此外更重要的是,字符串输出在控制台需要字符串常量池所在的内存块有足够的空间,而printStackTrace输出的是堆栈信息一般都很长,会占用不必要的空间甚至锁死导致系统停止运行。基于以上两点在项目中严禁使用e.printStackTrace()输出异常
public void sqlMethod() {
    业务逻辑1
    执行sql
    try{
        业务逻辑2//可能出错的逻辑
    }catch (SQLException e){
        logger.error("提示信息", e);
    }
}

uncheckedException

运行时异常,一般都是逻辑错误,需要程序员在编码过程中规避,处理方式常见的有:对参数格式的校验、对集合和数组长度的判断、判空处理等。以下代码毫无疑问会抛出IndexOutOfBoundsException

public static void runtimeExceptionMethod() {
    ArrayList<Object> list = new ArrayList<>();
    System.out.println(list.get(0));
}

为了避免这种情况我们需要对list的长度进行判断,当list长度大于0时才去输出list的第一个元素

public static void runtimeExceptionMethod() {
    ArrayList<Object> list = new ArrayList<>();
    if(list.size() > 0){
    	System.out.println(list.get(0));
    }
}

自定义Exception

除了jdk自带的两大种异常外,jdk还允许我们自行继承Exception和Error类来实现自定义异常和自定义错误,上面已经说过了error我们不去考虑,接下来简述下自定义异常。其实对于运行时异常,我们有着进阶的处理方式就是抛出自定义异常。

public class MyException extends RuntimeException {
   
    public MyException(String message) {
        super(message);
    }

    public MyException(int code, String message) {
        this(code, message, (Throwable)null);
    }

    public MyException(int code, String message, Throwable cause) {
        super(code, message, cause);
    }
}

public static void runtimeExceptionMethod() {
    ArrayList<Object> list = new ArrayList<>();
    if(list.size() > 0){
    	System.out.println(list.get(0));
    }else{
        throw new MyException("500","集合的长度不合法");
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值