Java基础二十一(异常捕获和处理)

news2024/12/24 2:14:51

1. 异常


1.1 概述

(1) 认识异常

异常是指程序在运行过程中出现非正常情况。

(2) Java 异常体系结构

在这里插入图片描述
所有异常类都是 Throwable 类的子类,它派生出两个子类,Error 类和 Exception 类。
(1)Error 类 : 表示程序无法恢复的严重错误或者恢复起来比较麻烦的错误,例如内存溢出、动态链接失败、虚拟机错误等。应用程序不应该主动抛出这种类型的错误,通常由虚拟机自动抛出。如果出现这种错误,最好的处理方式是让程序安全退出。
(2)Exception 类 : 由Java应用程序抛出和处理的非严重错误,例如文件未找到、网络连接问题、算术错误(如除以零)、数组越界、加载不存在的类、对空对象进行操作、类型转换异常等。Exception类的不同子类对应不同类型的异常。。Exception类又可分为两大类异常:

  • 不受检异常:也称为 unchecked 异常,包括 RuntimeException 及其所有子类。对这类异常并不要求强制进行处理,例如算术异常ArithmeticException等。
  • 受检异常:也称为 checked 异常,指除了不受检异常外,其他继承自 Exception 类的异常。对这类异常要求在代码中进行显式处理。

程序中常见异常

异常类名分类说明
Exception设计时异常异常层次结构的根类
IOException设计时异常IO异常的根类,属于非运行时异常
FileNotFoundException设计时异常文件操作时,找不到文件,属于非运行时异常
RuntimeException运行时异常运行时异常的根类,RuntimeException及其子类不要求处理
ArithmeticException运行时异常算术运算异常,例如除数为零,属于运行时异常
IllegalArgumentException运行时异常方法接收到非法参数,属于运行时异常
ArrayIndexOutOfBoundsException运行时异常数组越界访问异常,属于运行时异常
NullPointerException运行时异常尝试访问null对象的成员时发生的空指针异常,属于运行时异常
ArrayStoreException运行时异常数据存储异常,写数组操作时,对象或数据类型不兼容
ClassCastException运行时异常类型转换异常
IllegalThreadStateException运行时异常试图非法改变线程状态,例如试图启动一已经运行的线程
NumberFormatException运行时异常数据格式异常,试图把字符串非法转换成数值

上述异常类按照设计时异常和运行时异常进行了分类,并提供了相应的说明。设计时异常通常属于编译时异常,需要在代码中进行处理或声明抛出;而运行时异常通常不要求强制处理,可以选择捕获和处理,也可以由调用者处理,如果没有处理,将会导致程序异常终止。

1.2 Java异常处理机制

1.2.1 异常处理

(1)用 trycatchfinally 来捕获和处理异常

  • try 块中包含可能会抛出异常的代码
  • catch 块用于捕获并处理指定类型的异常
  • finally 块中的代码无论是否发生异常都会被执行,通常用于释放资源或清理操作

(2)用 throwthrows 来抛出异常

  • throw 关键字用于手动抛出异常
public void method() throws CustomException {
    if (condition) {
        throw new CustomException("This is a custom exception");
    }
}
  • throws 关键字用于在方法中声明指定可以抛出的异常类型,表示该方法可能会抛出该类的异常,由调用者来处理
public void method() throws IOException, CustomException {
    // 方法体
}

throw 用于具体的异常对象,而 throws 用于异常类型的声明。

1.2.2 捕获异常

(1)try-catch 处理异常

public class ExceptionTryCatch {
    public static void main(String[] args) {
        try {
            int i = 1,j = 0,res;
            System.out.println("begin");
            res = i / j;
            System.out.println("end");
        } catch (Exception e) {
            System.out.println("caught");
            e.printStackTrace();
        }
        System.out.println("over");
    }
}
begin
caught
over
java.lang.ArithmeticException: / by zero
	at kfm.bases.ErrorAndPxception.ExceptionTryCatch.main(ExceptionTryCatch.java:8)

try-catch语句块首先执行try语句块中的语句,这时可能会出现以下3种情况:

  1. 如果try语句块中的所有语句正常执⾏完毕,没有发⽣异常,那么catch语句块中的所有语句都将被忽略。
  2. 如果try语句块在执⾏过程中发⽣异常,并且这个异常与catch语句块中声明的异常类型匹配,那么try语句块中剩下的代码都将被忽略,⽽相应的catch语句块将会被执⾏。匹配是指catch所处理的异常类型与try块所⽣成的异常类型完全⼀致或是它的⽗类。
  3. 如果try语句块在执⾏过程中发⽣异常,⽽抛出的异常在catch语句块中没有被声明,那么程序⽴即终⽌运⾏,程序被强迫退出。
  • catch语句块中可以加⼊⽤⼾⾃定义处理信息,也可以调⽤异常对象的⽅法输出异常信息,常⽤的⽅法如下:
    • void prinStackTrace() :输出异常的堆栈信息。堆栈信息包括程序运行到当前类的执行流程,它将输出从方法调用处到异常抛出处的方法调用的栈序列。
    • String getMessage() :返回异常信息描述字符串,该字符串描述了异常产生的原因,是 printStackTrace() 输出信息的一部分。

(2)try-catch-finally 处理异常
try-catch-finally 语句块组合使用时,无论 try 块中是否发生异常, finally 语句块中的代码总能被执行。

public class TryCatchFinally {
    public static void main(String[] args) {
        try {
            int i = 1,j = 0, res;
            System.out.println("bregin");
            res = i / j;
            System.out.println("end");
        } catch (ArithmeticException e) {
            System.out.println("caught");
            System.out.println(e.getMessage());  // 异常信息描述字符串
            e.printStackTrace();  // 输出异常堆栈信息
            System.out.println("1");
        } finally {
            System.out.println("finally");
        }
        System.out.println("over");
    }
}
bregin
caught
/ by zero
java.lang.ArithmeticException: / by zero
	at kfm.bases.ErrorAndPxception.TryCatchFinally.main(TryCatchFinally.java:8)
1
finally
over

try-catch-finally语句块执行流程大致分为如下两种情况。

  1. 如果 try 语句块中所有语句正常执⾏完毕,程序不会进⼊ catch 语句块执⾏,但是 finally 语句块会被执⾏。
  2. 如果 try 语句块在执⾏过程中发⽣异常,程序会进⼊到 catch 语句块捕获异常, finally 语句块也会被执⾏。

try-catch-finally 结构中 try 语句块是必须存在的,catchfinally 语句块为可选,但两者⾄少出现其中之⼀。

即使在 catch 语句块中存在 return 语句,finally 语句块中的语句也会执行。发生异常时的执行顺序是,先执行 catch 语句块中 return 之前的语句,再执行 finally 语句块中的语句,最后执行 catch 语句块中的 return 语句退出。
finally 语句块中语句不执行的唯一情况是在异常处理代码中执行了 System.exit(1),退出 java 虚拟机。

public class TryCatchFinallyExit {
    public static void main(String[] args) {
        try {
            int i = 1,j = 0,res;
            System.out.println("begin");
            res = i / j;
            System.out.println("end");
        } catch (ArithmeticException e) {
            System.out.println("caught");
            e.printStackTrace();
            System.exit(1);
        } finally {
            System.out.println("finally");
        }
        System.out.println("over");
    }
}
begin
caught
java.lang.ArithmeticException: / by zero
	at kfm.bases.ErrorAndPxception.TryCatchFinallyExit.main(TryCatchFinallyExit.java:8)

System.exit(1) 是一个 Java 中的方法调用,用于终止当前正在执行的 Java 虚拟机(JVM)进程。在调用 System.exit(1) 后,JVM 会立即退出,并返回一个指定的退出状态码 (1 在这里表示非正常退出),0 表示正常退出,非零表示非正常退出。
(3)使用多重 catch 处理异常
当一段代码可能引发多种类型的异常时,可以在一个try语句块后面跟随多个 catch 语句块,分别处理不同类型的异常。一旦系统执行了与异常类型匹配的 catch 语句块,并执行其中的一条 catch 语句后,其后的 catch 语句块将被忽略,程序将继续执行紧随 catch 语句块的代码。

catch 语句块的排列顺序必须是从子类到父类,最后一个一般是 Exception 类。这是因为在异常处理中,catch 语句块会按照从上到下的顺序进行匹配,系统会检测每个 catch 语句块处理的异常类型,并执行第一个与异常类型匹配的 catch 语句块。如果将父类异常放在前面,子类异常的 catch 语句块将永远不会被执行,因为父类异常的 catch 语句块已经处理了异常。

import java.util.InputMismatchException;
import java.util.Scanner;

public class MuchCatch {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.println("计算开始");
        int i, j, res;
        try {
            System.out.println("请输入被除数");
            i = input.nextInt();
            System.out.println("请输入除数");
            j = input.nextInt();
            res = i / j;
            System.out.println("结果为" + res);
        } catch (InputMismatchException e) {
            System.out.println("除数和被除数必须为整数");
        } catch (ArithmeticException e) {
            System.out.println("除数不能为0");
        } catch (Exception e) {
            System.out.println("其他异常" + e.getMessage());
        } finally {
            System.out.println("感谢使用本程序");
        }
        System.out.println("程序结束");
    }
}

1.2.3 抛出异常

(1)使用 throws 声明抛出异常
try-catch-finally 处理的是在一个方法内部发生的异常,在方法内部直接捕获并处理。如果在一个方法体内抛出了异常,并希望调用者能够及时地捕获异常,Java 语言中通过关键字 throws 声明某个方法可能抛出的各种异常,以通知调用者。throws 可以同时声明多个异常,之间用逗号隔开。

import java.util.InputMismatchException;
import java.util.Scanner;

public class ThrowsExcepton {
    public static void main(String[] args) {
        try {
            divide();
        } catch (InputMismatchException e) {
            System.out.println("除数和被除数必须为整数");
        } catch (ArithmeticException e) {
            System.out.println("除数不能为0");
        } catch (Exception e) {
            System.out.println("其他异常" + e.getMessage());
        } finally {
            System.out.println("感谢使用本程序");
        }
        System.out.println("程序结束");
    }

    // 通过 throws 声明抛出设计时异常
    public static void divide() throws Exception {
        Scanner input = new Scanner(System.in);
        System.out.println("计算开始");
        int i, j, res;
        System.out.println("请输入被除数");
        i = input.nextInt();
        System.out.println("请输入除数");
        j = input.nextInt();
        res = i / j;
        System.out.println("结果为" + res);
    }
}

(2)使用 throw 抛出异常
除了系统自动抛出异常外,在编程过程中,有些问题是系统无法自动发现并解决的,如年龄不在正常范围之内,性别输入的不是“男”或“女”等,此时需要程序员而不是系统来自行抛出异常,把问题提交给调用者去解决。在 Java 语言中,可以使用 throw 关键字来自行抛出异常。

throw new Exception("message")

使用 throw 语句抛出异常,让调用者解决异常。

public void divide(int dividend, int divisor) {
    if (divisor == 0) {
        throw new ArithmeticException("Cannot divide by zero");
    } else {
        int result = dividend / divisor;
        System.out.println("Result: " + result);
    }
}

public static void main(String[] args) {
    try {
        int dividend = 10;
        int divisor = 0;
        divide(dividend, divisor);
    } catch (ArithmeticException e) {
        System.err.println("Exception caught: " + e.getMessage());
    }
}

Exception caught: Cannot divide by zero
  • 如果 throw 语句抛出的异常是 Checked 异常,则该 throw 语句要么处于 try 块⾥,显式捕获该异常,要么放在⼀个带 throws 声明抛出的⽅法中,即把该异常交给该⽅法的调⽤者处理;
  • 如果 throw 语句抛出的异常是 Runtime 异常,则该语句⽆须放在 try 块⾥,也⽆须放在带 throws 声明抛出的⽅法中;程序既可以显式使⽤ try…catch来捕获并处理该异常,也可以完全不理会该异常,把该异常交给该⽅法调⽤者处理。

自行抛出Runtime 异常比自行抛出Checked 异常的灵活性更好。同样,抛出 Checked 异常则可以让编译器提醒程序员必须处理该异常。

throw 和 throws 区别

  1. 作⽤不同:throw⽤于程序员⾃⾏产⽣并抛出异常,throws⽤于声明该⽅法内抛出了异常。
  2. 使⽤位置不同:throw位于⽅法体内部,可以作为单独的语句使⽤;throws必须跟在⽅法参数列表的后⾯,不能单独使⽤。
  3. 内容不同:throw抛出⼀个异常对象,只能是⼀个;throws后⾯跟异常类,可以跟多个。

1.2.4 自定义异常

在 Java 中,我们可以通过定义自己的异常类来表示特定的错误情况和处理方式。自定义异常类通常包含以下内容:

  1. 定义异常类,并继承 Exception 或者 RunTimeException
  2. 编写异常类的构造方法,向父类构造方法传入异常描述信息,并继承父类的其他实现方法。
  3. 实例化自定义异常对象,并在程序中使用 throw 抛出。
public class MyException extends Exception {
    private String message;

    public MyException(String message) {
        this.message = message;
    }

    @Override
    public String getMessage() {
        return message;
    }
}
public void myMethod(int arg) throws MyException {
    if (arg < 0 || arg > 100) {
        throw new MyException("Argument out of range");
    } else {
        // 执行正常的代码逻辑
    }
}

使用自定义异常类可以为我们提供更细粒度的错误处理方式,并使代码具有更好的可读性和可维护性。
自定义异常可能是编译时异常,也可以是运行时异常。

  • 如果自定义异常类继承Excpetion,则是编译时异常。
    特点:方法中抛出的是编译时异常,必须在方法上使用throws声明,强制调用者处理。
  • 如果自定义异常类继承RuntimeException,则运行时异常。
    特点:方法中抛出的是运行时异常,不需要在方法上用throws声明。

1.2.5 异常链

在 Java 中,异常链(Exception Chaining)是指将一个异常作为另一个异常的原因而被抛出。通过异常链,我们可以更清晰地了解异常的触发原因,并且可以在捕获异常时获取到完整的异常信息。

在创建异常链时,可以使用以下两种方式之一:

  1. 使用带有 cause 参数的异常构造方法:异常类的构造方法中通常会包含一个带有 cause 参数的重载版本,用于指定异常的原因。

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

    在这种情况下,可以将一个异常对象作为 cause 参数传递给当前异常的构造方法。

  2. 使用 initCause 方法:Throwable 类提供了 initCause 方法,用于将异常对象设置为另一个异常的原因。

    try {
        // ...
    } catch (Exception e) {
        MyException ex = new MyException("Custom exception");
        ex.initCause(e);
        throw ex;
    }
    

    在这种情况下,我们首先创建一个新的异常对象,然后使用 initCause 方法将之前捕获的异常对象设置为其原因。

public class TryDemoFive {
    public static void main(String[] args) {
        try {
            testThree();
        } catch (Exception e) {
//            e.printStackTrace();
            System.out.printf( e.getMessage());
        }
    }

    public static void testOne() throws Exception {
        throw new Exception("第一个异常");
    }

    public static void testTwo() throws Exception {
        try {
            testOne();
        } catch (Exception e) {
            System.out.println(e.getCause());
            throw new Exception("我是新产生的异常1", e);
        }
    }

    public static void testThree() throws Exception {
        try {
            testTwo();
        } catch (Exception e) {
            System.out.println(e.getCause());
            Exception exception = new Exception("新产生的异常2");
            exception.initCause(e);
            throw exception;
        }
    }
}
null
java.lang.Exception: 第一个异常
新产生的异常2

通过使用异常链,可以在异常处理过程中保留和传递更多的信息,使开发人员能够更好地理解异常的来源和根本原因。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/950484.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

blender界面认识01

学习视频 【基础篇】1.2 让手听话_哔哩哔哩_bilibili 目录 控制视角 控制物体 选择对象1 小结 控制视角 长按鼠标中键-----视角旋转 shift鼠标中键-----视角平移 滚动鼠标中键-----视角缩放 也可以通过界面的快捷工具实现 这个视角旋转有一点像catia中罗盘&#xff0c…

基于ADAU1452 DSP ANC和AEC算法的实现

是否需要申请加入数字音频系统研究开发交流答疑群(课题组)?加我微信hezkz17, 本群提供音频技术答疑服务,+群附加赠送,DSP音频项目核心开发资料, 1 使用Sigma中的NLMS算法模块 对应C源代码:

向函数传递参数(传地址)

过往课程 向函数传递参数&#xff08;传值、传引用、传const引用&#xff09; 传地址 向函数传地址&#xff0c;是指将变量的地址传递给函数。 函数通过声明参数为地址变量来接收一个变量的地址。 示例如下&#xff1a; #include <iostream> using namespace std;v…

checkstyle检查Java编程样式:隐藏属性

checkstyle可以使用HiddenField检查是否存在隐藏属性的行为&#xff1a;局部变量或者参数是否隐藏了在同一个类中的属性。 所谓隐藏属性&#xff0c;就是指局部变量、或者参数&#xff08;例如构造器的参数、方法的参数&#xff09;的名字和同一个类中的属性的名字相同。如果相…

kafka架构和原理详解

Apache Kafka 是一个分布式流数据平台,用于高吞吐量、持久性、可扩展的发布和订阅消息。它具有高度的可靠性,被广泛用于构建实时数据流处理、日志收集和数据管道等应用。 基本架构 1. 主题(Topic): 主题是消息的逻辑分类生产者将消息发布到特定的主题中,而消费者可以订阅…

数据结构day07(栈和队列)

今日任务 链式队列&#xff1a; head.h #ifndef __HEAD_H__ #define __HEAD_H__#include <stdio.h> #include <stdlib.h>typedef int datatype; typedef struct link_list{datatype data;struct link_list* next; }link,*linkp; typedef struct circulate_line_t…

nvm版本管理

引文地址&#xff1a;https://blog.csdn.net/HuangsTing/article/details/113857145

【计算机组成原理】一文快速入门,很适合JAVA后端看

作者简介&#xff1a; CSDN内容合伙人、CSDN新星计划导师、JAVA领域优质创作者、阿里云专家博主&#xff0c;计算机科班出身、多年IT从业经验、精通计算机核心理论、Java SE、Java EE、数据库、中间件、分布式技术&#xff0c;参加过国产中间件的核心研发&#xff0c;对后端有…

系统学习Linux-ELK日志收集系统

ELK日志收集系统集群实验 实验环境 角色主机名IP接口httpd192.168.31.50ens33node1192.168.31.51ens33noed2192.168.31.53ens33 环境配置 设置各个主机的ip地址为拓扑中的静态ip&#xff0c;并修改主机名 #httpd [rootlocalhost ~]# hostnamectl set-hostname httpd [root…

【第四阶段】kotlin语言的mutator函数学习

1.mutator特性1&#xff1a;使用list可以直接 - fun main() {val list mutableListOf(123,456,789)//特性1 可是直接使用list -list 111list-123println(list) }执行结果 2.mutator特性2&#xff1a;removeIF() 如果实现是true 会自动遍历整个集合&#xff0c;一个一个的移除…

弯道超车必做好题集锦三(C语言选择题)

前言&#xff1a; 编程想要学的好&#xff0c;刷题少不了&#xff0c;我们不仅要多刷题&#xff0c;还要刷好题&#xff01;为此我开启了一个弯道超车必做好题锦集的系列&#xff0c;每篇大约10题左右。此为第三篇选择题篇&#xff0c;该系列会不定期更新&#xff0c;后续还会…

xxl-job:定时任务执行流程及调度机制

1、Scheduled及Quartz的不足 1.1、Scheduled的不足 Spring的Scheduled对于单机的简单任务使用起来很方便&#xff0c;但只能单节点运行&#xff0c;不利于横向扩展。 1.2、Quartz的不足 Quartz作为开源作业调度中的佼佼者&#xff0c;是作业调度的首选。但是集群环境中Quart…

C语言控制语句——分支语句

条件语句用来根据不同的条件来执行不同的语句&#xff0c;C语言中常用的条件语句包括if语句和switch语句。 if 语句 语法格式&#xff1a; if (条件) {条件成立时&#xff0c;要做的事…… }案例需求&#xff1a; 定义一个整数变量记录年龄判断是否满 18 岁 &#xff08;>…

QOpenGLWidget绘制实时图像

initializeGL()函数&#xff1a; initializeOpenGLFunctions();//创建VBO和VAO对象&#xff0c;并赋予IDglGenVertexArrays(1, &VAO);glGenBuffers(1, &VBO);//绑定VBO和VAO对象glBindVertexArray(VAO);glBindBuffer(GL_ARRAY_BUFFER, VBO);//为当前绑定到target的缓冲…

为什么JVM调优一般都是针对堆内存的,以及堆内存的设置对GC的影响

1、为什么JVM调优一般都是针对堆内存的&#xff1f; 首先JVM的四部分组成&#xff1a;ClassLoader&#xff08;类装载器&#xff09;、Runtime data area 运行数据区、Execution Engine 执行引擎、Native Interface 本地接口。 其中运行数据区&#xff08;Runtime Data Area&am…

AI人员打架识别算法

AI打架识别算法通过yolov8网络模型算法框架&#xff0c;AI打架识别算法识别校园打架斗殴行为&#xff0c;发现立即打架斗殴行为算法会立即抓拍告警推送打架事件信息。目标检测架构分为两种&#xff0c;一种是two-stage&#xff0c;一种是one-stage&#xff0c;区别就在于 two-s…

​无需测试环境!如何利用测试脚手架隔离微服务,实现功能自动化

想在不建立完整测试环境的情况下测试微服务&#xff1f; 想在将变更推送到主线分支之前完成测试&#xff1f; 这是我们在进行项目交付时经常遇到的难题。最近&#xff0c;当我们开始一个新的项目&#xff0c;为客户构建一个新的聚合平台时&#xff0c;我们希望将尽可能多的测…

Java线程 - 详解(2)

一&#xff0c;线程安全问题 有些代码在单个线程的环境下运行&#xff0c;完全正确&#xff0c;但是同样的代码&#xff0c;让多个线程去执行&#xff0c;此时就可能出现BUG&#xff0c;这就是所谓的 "线程安全问题"。举一个例子&#xff1a; public class Demo {s…

Ansible学习笔记12

playbook&#xff1a; playbook&#xff08;剧本&#xff09;&#xff1a;是ansible用于配置、部署和管理被控节点的剧本&#xff0c;用于Ansible操作的编排。 使用的是yaml格式&#xff0c;&#xff08;saltstack、elk、docker、docker-compose、k8s都会使用到yaml格式。&am…

c语言实现二叉树(链式结构)

文章目录 前言一、二叉树的遍历1、二叉树的层序遍历2、二叉树的前序遍历3、二叉树的中序遍历4、二叉树的后序遍历5、代码实现 二、二叉树的一些操作的实现1、求二叉树的结点个数2、求二叉树叶子结点个数3、求二叉树第k层结点个数4、求二叉树深度5、二叉树中查找值为x的结点6、二…