Exception和Error之间的区别

Exception和Error都继承于Throwable这个类,在java中只有Throwable类型的实例才可以被抛出(Throw)和捕获(catch),它是异常处理机制的基本组成类型。
Exception是程序正常运行总可以被预料并且捕获并处理的,而Error则是不可预料的且不大可能出现的绝大部分Error都会导致程序(如:JVM)处于非正常且不可恢复的状态。既然是非正常情况,所以不便于也不需要捕获,常见的比如 OutOfMemoryError (内存溢出)就是Error的一个子类。

Exception又分为RunTimeException和其他Exception。
Exception则分为可检查异常(checked)和不可检查异常(unchecked),可检查异常通常在编译期就需要显式的进行处理(比如 关键字写错,语法不规范等),不可检查异常通常是需要程序在运行期间才能发现的异常(如 NullPointerException(空指针异常)、ArrayIndexOutOfBoundsException (数组下标越界)等)通常是可以编码避免的逻辑错误,具体根据需要来判断是否需要捕获,并不会在编译期强制要求。

继承图:
在这里插入图片描述

异常处理的两个基本原则:
第一,尽量不要捕获Exception这种通用异常,而是应该捕获特定异常。
第二,不要生吞(swallow)异常。这是异常处理中要特别注意的事情,因为很可能会导致非常难以诊断的诡异情况。生吞异常往往是基于假设这段代码可能不会发生,或者感觉忽略异常是无所谓的,所以可能导致程序在后续代码中不可控。
我们一般会将异常抛出来,或者输出到日志中。

异常中:Throw early(早抛出), catch late(晚处理?) 原则。
Throw early:在发现问题的时候,第一时间抛出,能够更加清晰地反映问题。
catch late:其实是我们经常苦恼的问题,捕获异常后,需要怎么处理呢?最差的处理方式,就是我前面提到的“生吞异常”,本质上其实是掩盖问题。如果实在不知道如何处理,可以选择保留原有异常的 cause 信息,直接再抛出或者构建新的异常抛出去。在更高层面,因为有了清晰的(业务)逻辑,往往会更清楚合适的处理方式是什么。

关于异常中的注意点:
当一个try后跟了很多个catch时,必须先捕获小的异常再捕获大的异常。
try catch finally执行流程,与 return,break,continue等混合使用注意代码执行顺序
打印异常信息是一个比较重的操作,会导致程序变慢
try catch最好是包括需要检验异常的代码,不要包含过长代码,这样会降低JVM的优化效率。
不建议以异常控制业务流程

关于NoClassDefFoundError和ClassNotFoundException的问题:
ClassNotFoundException是在写编码的时候编译器就能告诉你这个地方需要捕获异常,如:你使用Class.forName的时候就必须要你捕获或者throws这个异常。
而NoClassDefFoundError在Javac已经把程序成功的编译成字节码文件了,当JVM进程启动,通过类加载器加载字节码文件,然后由JIT去编译解释字节码指令的时候,在classpath下找不到对应的类进行加载时就会发生NoClassDefFoundError这个错误。

自定义异常需要注意什么:
第一:是否需要定义成 Checked Exception,因为这种类型设计的初衷更是为了从异常情况恢复,作为异常设计者,我们往往有充足信息进行分类。
第二:在保证诊断信息足够的同时,也要考虑避免包含敏感信息,因为那样可能导致潜在的安全问题。如果我们看 Java 的标准类库,你可能注意到类似 java.net.ConnectException,出错信息是类似“ Connection refused (Connection refused)”,而不包含具体的机器名、IP、端口等,一个重要考量就是信息安全。类似的情况在日志中也有,比如,用户数据一般是不可以输出到日志里面的。

怎么写一个自定义异常类?
今天我们要来理解的是什么是自定义异常,为什么要使用自定义异常,使用自定义异常有哪些好处,有哪些不好的地方?
要使用自定义异常就跟你和女朋友相处一样的,首先你得知道你为什么要跟你女朋友在一起,你女朋友有哪些好处,有哪些不好的地方,再来和你女朋友谈婚论嫁过一辈子,我们就先来说说这些问题,最后再来看怎么使用自定义异常,自定义异常的实现和使用非常简单,关键还是理解why的内容。

为什么要使用自定义异常,有什么好处?
1.我们在工作的时候,项目是分模块或者分功能开发的 ,基本不会你一个人开发一整个项目,使用自定义异常类就统一了对外异常展示的方式。
2.有时候我们遇到某些校验或者问题时,需要直接结束掉当前的请求,这时便可以通过抛出自定义异常来结束,如果你项目中使用了SpringMVC比较新的版本的话有控制器增强,可以通过@ControllerAdvice注解写一个控制器增强类来拦截自定义的异常并响应给前端相应的信息(关于springMVC控制器增强的知识有空再和大家分享)。
3.自定义异常可以在我们项目中某些特殊的业务逻辑时抛出异常,比如"中性".equals(sex),性别等于中性时我们要抛出异常,而Java是不会有这种异常的。系统中有些错误是符合Java语法的,但不符合我们项目的业务逻辑。
4.使用自定义异常继承相关的异常来抛出处理后的异常信息可以隐藏底层的异常,这样更安全,异常信息也更加的直观。自定义异常可以抛出我们自己想要抛出的信息,可以通过抛出的信息区分异常发生的位置,根据异常名我们就可以知道哪里有异常,根据异常提示信息进行程序修改。比如空指针异常NullPointException,我们可以抛出信息为“xxx为空”定位异常位置,而不用输出堆栈信息。

说完了为什么要使用自定义异常,有什么好处,我们再来看看自定义异常的毛病:
毋庸置疑,我们不可能期待JVM(Java虚拟机)自动抛出一个自定义异常,也不能够期待JVM会自动处理一个自定义异常。发现异常、抛出异常以及处理异常的工作必须靠编程人员在代码中利用异常处理机制自己完成。这样就相应的增加了一些开发成本和工作量,所以项目没必要的话,也不一定非得要用上自定义异常,要能够自己去权衡。

最后,我们来看看怎么使用自定义异常:
在 Java 中你可以自定义异常。编写自己的异常类时需要记住下面的几点。
所有异常都必须是 Throwable 的子类。
如果希望写一个检查性异常类,则需要继承 Exception 类。
如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。
可以像下面这样定义自己的异常类:
class MyException extends Exception{ }

我们来看一个完整的实例:

package com.czgo.exception;

/**
 * 自定义异常类(继承运行时异常)
 * @author AlanLee
 * @version 2016/11/26
 */
public class MyException extends RuntimeException {

    private static final long serialVersionUID = 1L;

    /**
     * 错误编码
     */
    private String errorCode;

    /**
     * 消息是否为属性文件中的Key
     */
    private boolean propertiesKey = true;

    /**
     * 构造一个基本异常.
     *
     * @param message
     *            信息描述
     */
    public MyException(String message)
    {
        super(message);
    }

    /**
     * 构造一个基本异常.
     *
     * @param errorCode
     *            错误编码
     * @param message
     *            信息描述
     */
    public MyException(String errorCode, String message)
    {
        this(errorCode, message, true);
    }

    /**
     * 构造一个基本异常.
     *
     * @param errorCode
     *            错误编码
     * @param message
     *            信息描述
     */
    public MyException(String errorCode, String message, Throwable cause)
    {
        this(errorCode, message, cause, true);
    }

    /**
     * 构造一个基本异常.
     *
     * @param errorCode
     *            错误编码
     * @param message
     *            信息描述
     * @param propertiesKey
     *            消息是否为属性文件中的Key
     */
    public MyException(String errorCode, String message, boolean propertiesKey)
    {
        super(message);
        this.setErrorCode(errorCode);
        this.setPropertiesKey(propertiesKey);
    }

    /**
     * 构造一个基本异常.
     *
     * @param errorCode
     *            错误编码
     * @param message
     *            信息描述
     */
    public MyException(String errorCode, String message, Throwable cause, boolean propertiesKey)
    {
        super(message, cause);
        this.setErrorCode(errorCode);
        this.setPropertiesKey(propertiesKey);
    }

    /**
     * 构造一个基本异常.
     *
     * @param message
     *            信息描述
     * @param cause
     *            根异常类(可以存入任何异常)
     */
    public MyException(String message, Throwable cause)
    {
        super(message, cause);
    }
    
    public String getErrorCode()
    {
        return errorCode;
    }

    public void setErrorCode(String errorCode)
    {
        this.errorCode = errorCode;
    }

    public boolean isPropertiesKey()
    {
        return propertiesKey;
    }

    public void setPropertiesKey(boolean propertiesKey)
    {
        this.propertiesKey = propertiesKey;
    }
    
}

使用自定义异常抛出异常信息:

package com.czgo.exception;

public class MyExceptionTest {

    public static void main(String[] args) {
        
         String[] sexs = {"男性","女性","中性"};
         for(int i = 0; i < sexs.length; i++){
             if("中性".equals(sexs[i])){
                 throw new MyException("你全家都是中性!");
             }else{
                 System.out.println(sexs[i]);
             }
         } 
    }
}

效果图:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值