一文教你如何透彻理解Java异常处理

2024-03-31 04:32

本文主要是介绍一文教你如何透彻理解Java异常处理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、JAVA 异常类型结构分类

JavaThrowable是所有异常类型的基类,并且Throwable异常下一层分为两个分支,分别为ErrorExceptionErrorException都继承自基类Throwable

其关系结构图如下:

img

二、Error 和 Exeption区别

1. Error

Error表示不可恢复的情况,例如Java虚拟机(JVM)内存不足,内存泄漏,堆栈溢出错误,库不兼容,无限递归等,描述了 JAVA程序运行时系统的内部错误,通常比较严重,该异常产生的错误通常是程序员无法控制的,我们不应该尝试处理错误,除了通知用户和尽力使应用程序安全地终止之外,无能为力,应用程序不应该尝试去捕获这种异常。通常为一些虚拟机异常,如 StackOverflowError等。

2. Exception

Exception类型下面又分为两个分支,一个分支派生自 RuntimeException,这种异常通常为程序错误导致的异常;

另一个分支为非派生自 RuntimeException的异常,这种异常通常是程序本身没有问题,由于像 I/O错误等问题导致的异常,每个异常类用逗号隔开。

Exception 又分为两种:

  • 受检异常 : 需要用 try…catch…语句捕获并进行处理,并且可以从异常中恢复,受查异常会在编译时被检测。如果一个方法中的代码会抛出受检异常,则该方法必须包含异常处理,即 try-catch代码块,或在方法签名中用 throws关键字声明该方法可能会抛出的受查异常,否则编译无法通过(例如IOException也称为受检异常)。如果一个方法可能抛出多个受查异常类型,就必须在方法的签名处列出所有的异常类。通过 throws 关键字声明可能抛出的异常;

    • 通过 throws 关键字声明可能抛出的异常

      /*** 通过 throws 关键字声明可能抛出的异常*/
      private static void readFile(String filePath) throws IOException {File file = new File(filePath);String result;BufferedReader reader = new BufferedReader(new FileReader(file));while ((result = reader.readLine()) != null) {System.out.println(result);}reader.close();
      }
      
    • 通过try-catch 处理异常

      /*** try-catch 处理异常** @param filePath*/
      private static void readFile(String filePath) {File file = new File(filePath);String result;BufferedReader reader;try {reader = new BufferedReader(new FileReader(file));while ((result = reader.readLine()) != null) {System.out.println(result);}reader.close();} catch (IOException e) {e.printStackTrace();}
      }
      
  • 非受检异常 : 非受检异常是程序运行时错误,例如空指针异常。非受查异常不会在编译时被检测。JAVAErrorRuntimeException类的子类属于非受检异常,除此之外继承自 Exception的类型为受检异常。

    这些异常不在编译时检查,而是在运行时检查。一些常见的运行时异常是:

    • API使用不当 - IllegalArgumentException
    • 空指针访问(缺少变量的初始化)- NullPointerException
    • 越界数组访问 - ArrayIndexOutOfBoundsException
    • 将数字除以0 - ArithmeticException

    你可以这样想:“如果这是一个运行时异常,那就是你的错”。

    如果在使用变量之前检查变量是否已初始化,则不会发生NullPointerException
    如果根据数组边界测试数组索引,则不会发生ArrayIndexOutOfBoundsException

img

三、异常的处理

对于不同的异常,java采用不同的异常处理方式:

(1)Error(错误):一般表示代码运行时JVM出现问题。比如NoClassDefFoundError等。比如说当jvm耗完可用内存时,将出现OutOfMemoryError。此类错误发生时,JVM将终止线程。

(2)运行异常(RuntimeException)将由系统自动抛出,应用本身可以选择捕获处理或者忽略该异常。

(3)受检异常(IOException)必须进行捕获或者在方法签名处使用 throws关键字声明可能会抛出的异常,抛出该方法之外交给上一层处理。或者说要么使用try-catch进行捕获处理,二选其一。

1. throws直接抛出异常

通常,应该捕获那些知道如何处理的异常,将不知道如何处理的异常继续传递下去。传递异常可以在方法签名处使用 throws 关键字声明可能会抛出的异常。

private static void readFile(String filePath) throws IOException {File file = new File(filePath);String result;BufferedReader reader = new BufferedReader(new FileReader(file));while((result = reader.readLine())!=null) {System.out.println(result);}reader.close();}

2. 封装异常再抛出

有时我们会从 catch 中抛出一个异常,目的是为了改变异常的类型。多用于在多系统集成时,当某个子系统故障,异常类型可能有多种,可以用统一的异常类型向外暴露,不需暴露太多内部异常细节。

private static void readFile(String filePath) throws MyException {    try {// code} catch (IOException e) {MyException ex = new MyException("read file failed.");ex.initCause(e);throw ex;}}

3. try-catch捕获异常

在一个 try-catch语句块中可以捕获多个异常类型,并对不同类型的异常做出不同的处理。

private static void readFile(String filePath) {try {// code} catch (FileNotFoundException e) {// handle FileNotFoundException} catch (IOException e){// handle IOException}}

同一个 catch 也可以捕获多种类型异常,用 | 隔开:

private static void readFile(String filePath) {try {// code} catch (FileNotFoundException | UnknownHostException e) {// handle FileNotFoundException or UnknownHostException} catch (IOException e){// handle IOException}}

4. try-catch-finally

当方法中发生异常,异常处之后的代码不会再执行,如果之前获取了一些本地资源需要释放,则需要在方法正常结束时和 catch 语句中都调用释放本地资源的代码,显得代码比较繁琐,finally 语句可以解决这个问题。

private static void readFile(String filePath) throws MyException {File file = new File(filePath);String result;BufferedReader reader = null;try {reader = new BufferedReader(new FileReader(file));while((result = reader.readLine())!=null) {System.out.println(result);}} catch (IOException e) {System.out.println("readFile method catch block.");MyException ex = new MyException("read file failed.");ex.initCause(e);throw ex;} finally {System.out.println("readFile method finally block.");if (null != reader) {try {reader.close();} catch (IOException e) {e.printStackTrace();}}}}

调用该方法时,读取文件时若发生异常,代码会进入 catch 代码块,之后进入finally代码块;若读取文件时未发生异常,则会跳过catch 代码块直接进入 finally代码块。所以无论代码中是否发生异常,fianlly中的代码都会执行。

catch 代码块中包含 return语句,finally中的代码还会执行吗?将以上代码中的 catch子句修改如下

catch (IOException e) {System.out.println("readFile method catch block.");return;}

调用 readFile方法,观察当catch子句中调用 return 语句时,finally子句是否执行
img

可见,即使 catch中包含了 return语句,finally子句依然会执行。若 finally中也包含 return语句,finally中的return会覆盖前面的 return

四、常见的几种RuntimeException

一般面试中java Exception(runtimeException )是必会被问到的问题。常见的异常列出四五种,是基本要求。常见的几种如下:

NullPointerException - 空指针引用异常
ClassCastException - 类型强制转换异常。
IllegalArgumentException - 传递非法参数异常。
ArithmeticException - 算术运算异常
ArrayStoreException - 向数组中存放与声明类型不兼容对象异常
IndexOutOfBoundsException - 下标越界异常
NegativeArraySizeException - 创建一个大小为负数的数组错误异常
NumberFormatException - 数字格式异常
SecurityException - 安全异常
UnsupportedOperationException - 不支持的操作异常
NegativeArrayException —— 数组负下标异常
ArrayIndexOutOfBoundsException - 数组下标越界异常
EOFException - 文件已结束异常
FileNotFoundException - 文件未找到异常
NumberFormatException - 字符串转换为数字异常
SQLException - 操作数据库异常
IOException - 输入输出异常
NoSuchMethodException - 方法未找到异常

五、如何自定义异常

如果希望写一个检查性异常类,则需要继承 Exception 类。

如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。

继承Exception还是继承RuntimeException是由异常本身的特点决定的,而不是由是否是自定义的异常决定的。在Java的规范里说明了,Java Exception分为两种, unchecked exceptionchecked exception ,显然 unchecked exception 是运行时异常继承自RuntimeException,**checked exception **是受检异常继承自Exception。因此在我们决定业务异常的处理时,自定义异常继承RuntimeException

1. 自定义业务异常BusinessException(非受检异常)

/*** 业务异常* * @author cao_wencao*/
public class BusinessException extends RuntimeException
{private static final long serialVersionUID = 1L;protected final String message;public BusinessException(String message){this.message = message;}public BusinessException(String message, Throwable e){super(message, e);this.message = message;}@Overridepublic String getMessage(){return message;}
}

业务场景:

public static void checkScore(int score) throws BusinessException{if(score < 0 || score > 100) {throw new BusinessException("考试成绩不符合要求");		}		System.out.println("考试成绩符合要求");
}

2. 自定义异常(受检异常)

/*** 业务异常-受检异常* * @author cao_wencao*/
public class MyException extends Exception
{private static final long serialVersionUID = -7041169491254546905L;public MyException() {super();}public MyException(String message) {super(message);}public MyException(String message, Throwable cause) {super(message, cause);}public MyException(Throwable cause) {super(cause);}
}

场景:

/*** //调用自定义的受检异常, 根据解析好的content,转化json对象* @param content* @return*/private static JSONObject getJsonResponse(String content) throws MyException {//TODO:  codeJSONObject jsonObject = new JSONObject();return jsonObject;}

3. 受检异常和非受检异常的应用示例

  • 使用自定义的非受检异常处理
    在这里插入图片描述

  • 使用自定义的受检异常
    在这里插入图片描述

  • 两种异常方法示例调用
    在这里插入图片描述

调用getJsonResponse("json字符串");方法时,IDEA工具提示我们需要显示处理该方法抛出来的受检异常,可以使用try..catch或者throws 往外抛异常。

4. 完整code

package com.example.demo;import com.alibaba.fastjson.JSONObject;
import com.example.demo.exception.BusinessException;
import com.example.demo.exception.MyException;
import lombok.extern.slf4j.Slf4j;/*** @desc:* @author: cao_wencao* @date: 2020-11-25 15:51*/
@Slf4j
public class ExceptionTest {/*** 调用自定义的非受检异常* @param score* @throws BusinessException*/public static void checkScore(int score) throws BusinessException {if (score < 0 || score > 100) {throw new BusinessException("考试成绩不符合要求");}System.out.println("考试成绩符合要求");}public void exceptionTest() throws MyException {//调用非受检异常checkScore(99);//调用受检异常,编译器红色波浪线提示需要显示处理受检异常getJsonResponse("json字符串");}/*** //调用自定义的受检异常, 根据解析好的content,转化json对象* @param content* @return*/private static JSONObject getJsonResponse(String content) throws MyException {//TODO:  codeJSONObject jsonObject = new JSONObject();return jsonObject;}
}

这篇关于一文教你如何透彻理解Java异常处理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/863396

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

一篇文章彻底搞懂macOS如何决定java环境

《一篇文章彻底搞懂macOS如何决定java环境》MacOS作为一个功能强大的操作系统,为开发者提供了丰富的开发工具和框架,下面:本文主要介绍macOS如何决定java环境的相关资料,文中通过代码... 目录方法一:使用 which命令方法二:使用 Java_home工具(Apple 官方推荐)那问题来了,

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置

Java 虚拟线程的创建与使用深度解析

《Java虚拟线程的创建与使用深度解析》虚拟线程是Java19中以预览特性形式引入,Java21起正式发布的轻量级线程,本文给大家介绍Java虚拟线程的创建与使用,感兴趣的朋友一起看看吧... 目录一、虚拟线程简介1.1 什么是虚拟线程?1.2 为什么需要虚拟线程?二、虚拟线程与平台线程对比代码对比示例:三

Java中的.close()举例详解

《Java中的.close()举例详解》.close()方法只适用于通过window.open()打开的弹出窗口,对于浏览器的主窗口,如果没有得到用户允许是不能关闭的,:本文主要介绍Java中的.... 目录当你遇到以下三种情况时,一定要记得使用 .close():用法作用举例如何判断代码中的 input

深入理解Mysql OnlineDDL的算法

《深入理解MysqlOnlineDDL的算法》本文主要介绍了讲解MysqlOnlineDDL的算法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小... 目录一、Online DDL 是什么?二、Online DDL 的三种主要算法2.1COPY(复制法)