关于异常的那些事

之前面试的时候,被问到关于异常的继承关系,自定义异常,断言异常怎么处理的。
对这块知识不太了解,刚好做个博客整理一下。

异常的继承关系

在这里插入图片描述

  • 运行时异常和非运行时异常的区别,分别含义是什么?
    从继承关系图中可以看出,运行时异常包含了一些常见异常,如:NullPointerException,ClassNotFoundException等,一般爆出这些异常的时候,系统会中断运行,并且使用idea等编辑器不会提示需要进行try catch的操作。
    但是非运行时异常又是什么?当时我说的是,在编译期间能够被检查出来的异常。
    百度了一下答案,
    检查异常(checked exception)
    检查异常通常是用户错误,程序员并不可预见的问题。例如,如果一个文件被打开,但该文件无法找到,则会出现异常。这些异常并不能在编译时被发现。
    运行时异常(runtime exception也叫unchecked exception)
    运行时异常时本来可以由程序避免的异常。而不是已检查异常,运行时异常是在编译时被忽略。这里的运行时异常并不是我们所说的运行期间产生的异常,只是Java中用运行时异常这个术语来表示而已。另外,所有Exception异常都是在运行期间产生的。
    错误(error)
    无法处理的异常,比如OutOfMemoryError,一般发生这种异常,JVM会选择终止程序。因此我们编写程序时不需要关心这类异常。

1.运行时异常是不需要捕获的,程序员可以不去处理,当异常出现时,虚拟机会处理。常见的运行时异常有空指针异常。
2.非运行时异常就必须得捕获了,否则编译不过去,java编译器要求程序员必须对这种异常进行catch,Java认为Checked异常都是可以被处理(修复)的异常,所以Java程序必须显式处理Checked异常。
常见的非运行异常有io异常和sql异常。
参考CSDN博客链接:java 运行时异常与非运行时异常理解

自定义异常

自定义异常一般继承Exception或者RuntimeException
然后定义构造方法,在构造方法需要显式用super调用父类的构造方法。

  • 为什么自定义异常的构造方法需要显式用super调用父类的构造方法?
    跟踪了一下调用父类的构造函数,自定义异常继承自RuntimeException ,RuntimeException 继承 Exception,Exception继承Throwable,在Throwable之前,每个异常类的构造函数都使用了super函数。那看看Throwable的构造函数有什么特别的。
public Throwable() {
    fillInStackTrace();
}

    public Throwable(String message) {
        fillInStackTrace();
        detailMessage = message;
    }
    
    public Throwable(String message, Throwable cause) {
        fillInStackTrace();
        detailMessage = message;
        this.cause = cause;
    }
    
    public Throwable(Throwable cause) {
        fillInStackTrace();
        detailMessage = (cause==null ? null : cause.toString());
        this.cause = cause;
    }
    
    protected Throwable(String message, Throwable cause,
                        boolean enableSuppression,
                        boolean writableStackTrace) {
        if (writableStackTrace) {
            fillInStackTrace();
        } else {
            stackTrace = null;
        }
        detailMessage = message;
        this.cause = cause;
        if (!enableSuppression)
            suppressedExceptions = null;
    }

值得关注的是,fillInStackTrace()函数,那跟踪一下源码,看看这个函数做了什么

    /**
     * Fills in the execution stack trace. This method records within this
     * {@code Throwable} object information about the current state of
     * the stack frames for the current thread.
     *
     * <p>If the stack trace of this {@code Throwable} {@linkplain
     * Throwable#Throwable(String, Throwable, boolean, boolean) is not
     * writable}, calling this method has no effect.
     *
     * @return  a reference to this {@code Throwable} instance.
     * @see     java.lang.Throwable#printStackTrace()
     */
public synchronized Throwable fillInStackTrace() {
    if (stackTrace != null ||
        backtrace != null /* Out of protocol state */ ) {
        fillInStackTrace(0);
        stackTrace = UNASSIGNED_STACK;
    }
    return this;
}

private native Throwable fillInStackTrace(int dummy);

翻译一下,这个方法的注释信息。

填充执行堆栈跟踪。此方法在{@code Throwable}对象中记录当前线程堆栈帧的当前状态的信息。
如果这个{@code Throwable}{@linkplain的堆栈跟踪Throwable#Throwable(String,Throwable,boolean,boolean)不是可写},调用此方法无效。
返回值: 对这个{@code Throwable}实例的引用。
参见: java.lang.Throwable打印堆栈跟踪()

重点是java.lang.Throwable#printStackTrace()这个方法,查看除了Throwable的异常类,源码都很简单,只是处理了构造函数,那也就是,当我们在对异常try catch打印堆栈信息的时候,调用的是父类Throwable的printStackTrace方法。

printStackTrace的翻译信息:  注:源码的printStackTrace注释信息有很多,感兴趣的可以自行查看源码
*打印这个可丢弃的东西和它的回溯到
*标准错误流。此方法为此打印堆栈跟踪
*错误输出流上的{@code Throwable}对象
*字段{@code的值系统错误}. 第一行
*输出包含的{@link\#toString()}方法的结果
*这个物体。其余行表示以前记录的数据
*方法{@link#fillInStackTrace()}

大概就是要把之前存入堆栈的信息打印出来,所以按我的理解,之前构造的时候需要让Throwable绑定一个堆栈信息,因为fillInStackTrace最后的时候前面的标识是native,调用的本地方法(JVM将控制调用本地方法的所有细节)。所以所有自定义异常类都需要继承Throwable,并且在构造函数中使用super调用父类的构造方法。否则无法打印异常的堆栈信息。

断言异常怎么处理

一般业务异常,我们会使用断言处理,给用户良好的错误信息提示。提高用户使用感受。
那断言异常又是怎么定义的?而且对于前端而言,我们不能使用直接接口报错的方式抛出异常,一般是自定义异常码值,抛出错误的message。
自定义异常类:

public class AssertException extends RuntimeException {
    private String errorMessage;
    private String logMessage;
    private Integer errCode;

    public AssertException(String errorMessage, String logMessage) {
        super(errorMessage);
        this.errorMessage = errorMessage;
        this.logMessage = logMessage;
    }

    public AssertException(String errorMessage, String logMessage, Integer errCode) {
        super(errorMessage);
        this.errorMessage = errorMessage;
        this.logMessage = logMessage;
        this.errCode = errCode;
    }

    public Integer getErrCode() {
        return this.errCode;
    }

    public void setErrCode(Integer errCode) {
        this.errCode = errCode;
    }

    public String getErrorMessage() {
        return this.errorMessage;
    }

    public String getLogMessage() {
        return this.logMessage;
    }
}

可以看到自定义了三个私有属性
private String errorMessage; // 错误信息(断言提示语)
private String logMessage; // 日志信息
private Integer errCode; // 错误码

  • 怎么使用自定义的断言异常呢?
    一、定义如何使用自定义异常类的类以及对应方法,在需要使用方法中,用throw抛出自定义异常。
            StackTraceElement methodCaller = Thread.currentThread().getStackTrace()[2];
            StringBuffer logMessage = new StringBuffer();
            logMessage.append("\nBusiness Exception:{");
            logMessage.append("\n\tServices Name: " + methodCaller.getClassName());
            logMessage.append("\n\tMethod Name: " + methodCaller.getMethodName());
            logMessage.append("\n\tLine Num: " + methodCaller.getLineNumber());
            logMessage.append("\n\tService Error Code: " + errorCode);
            logMessage.append("\n\tService Error Message: " + message);
            logMessage.append("\n}\n");
            throw new AssertException(message, logMessage.toString(), errorCode);

二、在统一返回结果的处理类中,去接收这个异常
try catch捕获异常,利用instanceof 关键字 判断当前的异常是否为自定义异常的实例,做对应的处理,如设置特定码值,以及把错误信息放到返回信息中。

if (ex instanceof AssertException) {
            AssertException serviceAssertException = (AssertException)ex;
            this.outputInfoLog(sb, ex);
            response.setCode(402);
            response.setMessage(serviceAssertException.getMessage());
        }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值