Exception和Error有什么区别?

《Java核心技术面试精讲–杨晓峰》学习笔记目录

文章目录

      • Throwable、Exception、Error 的设计和分类
        • Exception和Error
      • Java 语言中操作 Throwable 的元素和实践
        • try...catch...finally
          • 基本语法
          • try-with-resources 和 multiple catch(扩展)
        • throw 与 throws
        • 注意:
        • 自定义异常
      • SpringMVC 捕获全局异常 @ControllerAdvice

Throwable、Exception、Error 的设计和分类

Exception和Error有什么区别?_第1张图片

Exception和Error

  • 异常处理机制:异常被抛出时,JVM根据异常中的信息查找处理异常的代 码,通过调用栈进行反向查找,直到找到为止,如果找不到则终止程序。就是你的异常没有被捕捉。异常处理机制将允许代码将错误或者异常事件传递给调用它的代码,因此报错信息中,越前面距离事发地点越近。

  • Exception 和 Error 都继承了Throwable 接口。在 Java 中只有 Throwable 类型的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制的基本组成类型。

  • Exception:编写代码时就可以预料到的意外情况。相较于 Error 容易修复,有些需要捕获,有些不建议捕获。

    • Checked Exception IDEA 提醒你需要进行捕获的异常。

      • 常见子类:
        • ClassNotFoundException - 找不到指定class异常
        • IOException - IO操作异常
    • Unchecked ExceptionRuntimeException):通常是可以编码避免的逻辑错误,具体根据需要来判断是否需要捕获

      • NullPointterException - 空指针引用异常
      • ClassCastException - 类型强制转换异常
      • IllegalArgumentException - 传递非法参数异常
        • NumberFormatException - 数字格式异常
      • IndexOutOfBoundsException - 下标越界异常
        • ArrayIndexOutOfBoundsException 数组下标越界异常
    • ClassNotFoundException 和 NoClassDefFoundException 的差别
      类加载的时候先要把编译好的类文件(.class ,jar包等)加载到 JVM 管理的方法区当中,这个过程叫做加载,如果这个过程中没找类文件就会出现 ClassNotFoundException(比如:Class.forName())。如果加载成功之后,会有一个该类的类对象(class 对象)。想访问类,就通过这个类对象,当在内存当中没有找到这个类对象,就会出现 NotClassDefFoundError。

  • Error:正常情况下不太可能出现的意外情况,程序基本不可以通过后续代码修复,并且不应该被捕获

    • NotClassDefFoundError - 找不到class定义的异常
    • StackOverflowError - 深度递归导致栈被耗尽而抛出的异常
    • OutOfMemoryError - 内存溢出异常

Java 语言中操作 Throwable 的元素和实践

try…catch…finally

基本语法
try{
	//需要检测是否发生异常的代码
}catch(*Exception e){
	//对异常的处理
}finally{
	//清理占用的资源
}
try-with-resources 和 multiple catch(扩展)
  • 回收资源的try,在try()中加入限定那么在try结束后括号中的打开的东西也会关闭。里面的 resources 会在 try 完成时自动调用 resources .close()
try (BufferedReader br = new BufferedReader();
     BufferedWriter writer = new BufferedWriter()) {// Try-with-resources
// do something
catch ( IOException | XEception e) {// Multiple catch
   // Handle it
} 

throw 与 throws

  • throws出现在方法函数头;而throw出现在函数体。
  • throws表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某种异常对象。
  • 两者都是消极处理异常的方式(这里的消极并不是说这种方式不好),只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用处理。

注意:

  • 尽量不要捕获类似 Exception 这样的通用异常,而是应该捕获特定异常

  • 不要生吞(swallow)异常(catch 中不添加代码)

  • 最好使用产品日志,详细地输出到日志系统里。

  • 不要在finally代码块中处理返回值。

  • 按照我们程序员的惯性认知:当遇到return语句的时候,执行函数会立刻返回。但是,在Java语言中,如果存在finally就会有例外。除了return语句,try代码块中的break或continue语句也可能使控制权进入finally代码块。

  • 请勿在try代码块中调用return、break或continue语句。万一无法避免,一定要确保finally的存在不会改变函数的返回值。

  • 函数返回值有两种类型:值类型与对象引用。对于对象引用,要特别小心,如果在finally代码块中对函数返回的对象成员属性进行了修改,即使不在finally块中显式调用return语句,这个修改也会作用于返回值上。

  • 勿将异常用于控制流。

  • 少用异常,异常处理不能代替简单的测试(必要的时候才使用,try…catch 会对 JVM 产生额外的性能呢开销,每实例化一个 Exception 对象都会消耗部分内存)

  • Throw early, catch late(早抛出,晚捕获)

    • 原因:(我在 stack 中找到的比较清晰的解释:
      网址: https://softwareengineering.stackexchange.com/questions/231057/exceptions-why-throw-early-why-catch-late)
      你希望今早的抛出异常,因为他可以使我们更轻松的找到异常产生的原因。以一个因为某些参数而失败的方法为例。如果你在方法开始的时候就检验方法参数失败,你就立即知道错误发生在调用(方法的)代码中。如果等到需要参数时代码停止运行,你就不得不跟踪执行代码,以探查错误是否出现在调用方法的代码中(错误参数)或者是由于方法的其他问题导致的。越早抛出异常,你就越可能的接近bug产生的根本原因,也就越容易找到问题出在了哪里。
      在高层抛出异常的原因是在低层开发代码时,你并不知道如何适当的处理该异常。事实上,根据调用方法的代码的不同将有多种合适的方法处理相同的异常。以打开文件举例。如果你想要打开一个配置文件,然而这个文件并不存在(或者没找到),忽略异常并继续使用默认配置可能是一个合适的办法。如果你打开的是一个对程序执行至关重要的私人文件,但不知何故丢失了,你唯一的选择可能是关闭程序。
      用正确的类型包装异常是一个纯粹的正交关系。

自定义异常

https://baijiahao.baidu.com/s?id=1652432076091822208&wfr=spider&for=pc

格式如下:

public class xxxException extends Exception | RuntimeException{
	//添加一个空参数的构造方法
	public RegisterException(){
		super();
	}
	//添加一个带异常信息的构造方法
	public RegisterException(String message){
		super(message);
	}
}

注意:

  • 自定义异常类一般都是以Exception结尾,说明该类是一个异常类。
  • 自定义异常类,必须继承Exception或者RuntimeException。
    • 继承Exception:自定义的异常类就是一个编译期异常,如果方法内部抛出编译期异常,就必须处理这个异常,要么throws,要么try……catch。
    • 继承RuntimeException:自定义的异常是一个运行期异常,无需处理,交给虚拟机处理(中断处理)
  • 在保证诊断信息足够的同时,也要考虑避免包含敏感信息

SpringMVC 捕获全局异常 @ControllerAdvice

详情可以点击SpringMVC实现全局异常处理器,这篇文章将步骤叙述的的很详细了

	1、为前后端交互设置一个统一的接口 ResponseDto
		接口包含异常信息(Stirng ErrorMsg 一般对应一个异常的枚举类)
	2、自定义异常类 BusinessException,此异常类一般继承 RuntimeException
	3、为当前controller层自定义异常类 ControllerExceptionHandler
		类上注解:@ControllerAdvice 
			注解作用:启动应用后,被 @ExceptionHandler、@InitBinder、@ModelAttribute 注解的方法,
			都会作用在 被 @RequestMapping 注解的方法上。
		在异常类中
			@ExceptionHandler(value = BusinessException.class)//表示下面的方法处理的是BusinessException
		    @ResponseBody
		    public ResponseDto businessExceptionHandler(BusinessException e) {
		        ResponseDto responseDto = new ResponseDto();			 
		        //在下层抛出异常后,设置异常的代码,在枚举类中通过异常代码后去异常信息     
		        LOG.error("业务异常:{}", e.getCode().getDesc());
		        responseDto.setMessage(e.getCode().getDesc());
		        return responseDto;
		    }

参考文献:
https://blog.csdn.net/hbtj_1216/article/details/81102063
https://blog.csdn.net/loongshawn/article/details/77736869
https://time.geekbang.org/column/article/6849
https://blog.csdn.net/chenchaofuck1/article/details/50971994/
https://blog.csdn.net/IT_lukaifang/article/details/82694486
https://www.cnblogs.com/hitycy/p/10882083.html
https://www.dazhuanlan.com/2019/12/04/5de7c3a78f8d1/?cf_chl_jschl_tk=f756484047ffd5824b366b7d622d73b20a43b3ad-1590330613-0-AUBK_oDxEsQTnthTpIL_VAD8QuKqnaTO8pGtg3gGBXb1F7E1mm2umQaQOAD_hWjwZbUlVdXM2Dsqv8XRLBMKiZLhlnc0K-fjB3oub9L1wigyAUOYdpUk38lUEsSFx7A76g0je9Sx8TYK9x_du9hNWsO-V_RnfPsGJSTGks7D9xCqZGLhmodJqNz5L2G8XqBDkfnVa201uEAyhw4sarLwgdZrxqHoVmhZm7dvKYKdyXo0UjYy8DnGUWRDvTzHMOzxIQjb9GuqpqRQjAWxofWH1WWwI9jsgS8xPYY1wnE9dMDFteN6HchNjWs0XH632VImOg

你可能感兴趣的:(Exception和Error有什么区别?)