认识异常吧

news2024/12/16 4:56:58
Java 中,将程序执行过程中发生的不正常行为称为异常

异常的体系结构

1. Throwable 是异常体系的顶层类,其派生出两个重要的子类 , Error(错误)  Exception(异常)
2. Error 指的是 Java 虚拟机无法解决的严重问题,比如: JVM 的内部错误、资源耗尽等 ,典型代表: StackOverflowError OutOfMemoryError ,一旦发生回力乏术。
3. Exception 异常产生后程序员可以通过代码进行处理,使程序继续执行。

异常的分类

1. 编译时异常
在程序编译期间发生的异常,称为编译时异常,也称为受检查异常(Checked Exception)
2. 运行时异常
在程序执行期间发生的异常,称为运行时异常,也称为非受检查异常(Unchecked Exception)
RunTimeException以及其子类对应的异常,都称为运行时异常。
运行时指的是程序已经编译通过得到 class 文件了 , 再由 JVM 执行过程中出现的错误 .
1. 算术异常
System.out.println(10/0);
2. 数组越界异常
        int[] arr = new int[10];
        System.out.println(arr[20]);
3. 空指针异常
        int[] arr = null;
        System.out.println(arr.length);

异常的处理

防御式编程

1. LBYL : Look Before You Leap. 在操作之前就做充分的检查 . 即: 事前防御型
2. EAFP : It's Easier to Ask Forgiveness than Permission. " 事后获取原谅比事前获取许可更容易 ". 也就是先操 作, 遇到问题再处理 . 即: 事后认错型
优势:正常流程和错误流程是分离开的 , 程序员更关注正常流程,代码更清晰,容易理解代码。
异常处理的核心思想就是 EAFP
Java 中, 异常处理主要的 5 个关键字: throw try catch final throws

1. 异常的抛出

在编写程序时,如果程序中出现错误,此时就需要将错误的信息告知给调用者,比如:参数检测。
Java 中,可以借助 throw 关键字,抛出一个指定的异常对象,将错误信息告知给调用者。具体语法如下:
public class Test {
    public static void main(String[] args) {       
        int[] arr = null;
        if(null==arr){
            System.out.println("可以执行语句");
            throw new NullPointerException("空指针异常");//throw 中断了执行流
            //throw 中断了执行流,导致这一行永远不会被执行。
            //System.out.println("不可达语句");//编译器会报错:unreachable statement
        }
        //如果没有抛出异常或异常被捕获处理,那么执行流会到达这一行并打印。
        //如果异常未处理,则程序在抛出异常后终止,这一行也不会被执行
        System.out.println("不执行语句");
    }
}

注意事项
1. throw 必须写在方法体内部
2. 抛出的对象必须是 Exception 或者 Exception 的子类对象
3. 如果抛出的是 RunTimeException 或者 RunTimeException 的子类,则可以不用处理,直接交给 JVM 来处理
4. 如果抛出的是编译时异常,用户必须处理,否则无法通过编译
5. 异常一旦抛出,其后的代码就不会执行

异常的捕获

异常的捕获,也就是异常的具体处理方式,主要有两种:异常声明throws 以及 try-catch捕获处理

2. 异常声明throws

处在方法声明时参数列表之后,当方法中抛出编译时异常,用户不想处理该异常,此时就可以借助 throws 将异常抛给方法的调用者来处理。即当前方法不处理异常,提醒方法的调用者处理异常
语法格式:
修饰符 返回值类型 方法名(参数列表) throws 异常类型1,异常类型2...{

}

public void methodName() throws ExceptionType1, ExceptionType2 {
    // 方法实现
}
注意事项
1. throws 必须跟在方法的参数列表之后
2. 声明的异常必须是受检异常即编译时异常
3. 方法内部如果抛出了多个异常, throws 之后必须跟多个异常类型,之间用逗号隔开,如果抛出多个异常类型具有父子关系,直接声明父类即可。
4. 调用声明抛出异常的方法时,方法的调用者需要负责处理这些异常(通过 try-catch 或继续声明 throws

3. try-catch捕获并处理

throws 对异常并没有真正处理,而是将异常报告给抛出异常方法的调用者,由调用者处理。如果真正要对异常进行处理,就需要try-catch
关于异常的处理方式
异常的种类有很多 , 我们要根据不同的业务场景来决定 .
对于比较严重的问题 ( 例如和算钱相关的场景 ), 应该让程序直接崩溃 , 防止造成更严重的后果
对于不太严重的问题 ( 大多数场景 ), 可以记录错误日志 , 并通过监控报警程序及时通知程序猿
对于可能会恢复的问题 ( 和网络相关的场景 ), 可以尝试进行重试 .
在我们当前的代码中采取的是经过简化的第二种方式 . 我们记录的错误日志是出现异常的方法调用信息 , 能很
快速的让我们找到出现异常的位置 . 以后在实际工作中我们会采取更完备的方式来记录异常信息
注意事项
1. try 块内抛出异常位置之后的代码将不会被执行
2. 如果抛出异常类型与 catch 时异常类型不匹配,即异常不会被成功捕获,也就不会被处理,继续往外抛,直到JVM收到后中断程序 ---- 异常是按照类型来捕获的
3. try中可能会抛出多个不同的异常对象,则必须用多个 catch 来捕获 ---- 即多种异常,多次捕获
如果异常之间具有父子关系,一定是子类异常在前 catch ,父类异常在后 catch ,否则语法错误
如果多个异常的处理方式是完全相同 ,中间可以用 | 隔开
可以通过一个 catch 捕获所有的异常,即多个异常,一次捕获 ( 不推荐 )
由于 Exception 类是所有异常类的父类 . 因此可以用这个类型表示捕捉所有异常 . 即: catch 进行类型匹配的时候 , 不光会匹配相同类型的异常对象 , 也会捕捉目标异常类型的子类对象 .

4. finally

在写程序时, 有些特定的代码,不论程序是否发生异常,都需要执行,比如程序中打开的资源 :网络连接、数据库
连接、 IO 流等, 在程序正常或者异常退出时,必须要对资源进进行回收 。另外,因为 异常会引发程序的跳转,可能
导致有些语句执行不到 finally 就是用来解决这个问题的。
注意: finally 中的代码一定会执行的,一般在 finally 中进行一些资源清理的扫尾工作
语法格式:
try{
   // 可能会发生异常的代码
}catch(异常类型 e){
   // 对捕获到的异常进行处理
}finally{
   // 此处的语句无论是否发生异常,都会被执行到
}

// 如果没有抛出异常,或者异常被捕获处理了,这里的代码也会执行
        Scanner scan = new Scanner(System.in);
        try{
            int n = scan.nextInt();
            System.out.println("抛出异常之前的代码可以被执行");
            System.out.println(10/n);
            System.out.println("抛出异常之后的代码不会被执行");
        }catch(ArithmeticException a){
            a.printStackTrace();//打印异常的详细信息
            System.out.println("这是算术异常");
        }
        //多个异常的处理方式是完全相同,中间可以用 | 隔开
        catch(ArrayIndexOutOfBoundsException | NullPointerException n){
            n.printStackTrace();
            System.out.println("这是数组越界异常和空指针异常");
        }
        //通过一个catch捕获所有的异常,即多个异常
        catch (Exception e){
            e.printStackTrace();
            System.out.println("这是异常");
        }
        //finally中的代码一定会执行的,不管是否发生异常
        finally {
            System.out.println("finally中的代码一定会被执行");
            scan.close();
        }
        System.out.println("该代码在没有抛出异常或异常被捕获执行,异常未处理不执行");

    public static int meth(){
        try{
            System.out.println(10/0);
            return 0;
        }catch(ArithmeticException a){
            return 1;
        }finally{
            return 2;
        }
    }
    public static void main(String[] args) {
        System.out.println(meth());// 输出:2
    }
finally 执行的时机是在方法返回之前 (try 或者 catch 中如果有 return 会在这个 return 之前执行 finally). 但是如果
finally 中也存在 return 语句 , 那么就会执行 finally 中的 return, 从而不会执行到 try 中原有的 return.

finally 中使用 return 是不推荐的,因为:

  1. 容易混淆逻辑:掩盖了 trycatch 的返回结果。
  2. 难以调试:可能会导致不可预测的行为,特别是在处理资源释放时。

异常的处理流程

关于 " 调用栈 "
方法之间是存在相互调用关系的 , 这种调用关系我们可以用 " 调用栈 " 来描述 . JVM 中有一块内存空间称为
" 虚拟机栈 " 专门存储方法之间的调用关系 . 当代码中出现异常的时候 , 我们就可以使用 e.printStackTrace();
方式查看出现异常代码的调用栈 .
如果本方法中没有合适的处理异常的方式 , 就会沿着调用栈向上传递, 如果向上一直传递都没有合适的方法处理异常 , 最终就会交给 JVM 处理 , 程序就会异常终止 ( 和我们最开始未使用 try catch 时是一样的 )
异常处理流程总结
程序先执行 try 中的代码
如果 try 中的代码出现异常 , 就会结束 try 中的代码 , 看和 catch 中的异常类型是否匹配 .
如果找到匹配的异常类型 , 就会执行 catch 中的代码
如果没有找到匹配的异常类型 , 就会将异常向上传递到上层调用者 .
无论是否找到匹配的异常类型 , finally 中的代码都会被执行到 ( 在该方法结束之前执行 ).
如果上层调用者也没有处理的了异常 , 就继续向上传递 .
一直到 main 方法也没有合适的代码处理异常 , 就会交给 JVM 来进行处理 , 此时程序就会异常终止

自定义异常类

Java 中虽然已经内置了丰富的异常类 , 但是并不能完全表示实际开发中所遇到的一些异常,此时就需要维护符合我们实际情况的异常结构.
注意事项
自定义异常通常会继承自 Exception 或者 RuntimeException
继承自 Exception 的异常默认是受查异常
继承自 RuntimeException 的异常默认是非受查异常
实现一个用户登录功能:
输入用户名和密码,比较是否与我们原有的用户名和密码相同,如果用户名不同,抛出我们自定义的NameException异常,并重新输入用户名,如果密码不同,抛出我们自定义的PasswordException异常,并重新输入密码,直到相同,跳出循环。
//自定义异常
//继承RuntimeException
public class NameException extends RuntimeException{
    //构造方法
    public NameException() {
    }

    public NameException(String message) {
        super(message);
    }
}
//自定义异常
//继承RuntimeException
public class PasswordException extends RuntimeException{
    //构造方法
    public PasswordException() {
    }

    public PasswordException(String message) {
        super(message);
    }
}
import java.util.Scanner;

public class Login{
    private String name = "admin";
    private String password = "123456";
    public void Loginifor(String name,String password){
        if(!this.name.equals(name)){
            throw new NameException("用户名输入错误,请重新输入用户名:");//抛出异常
        }
        if(!this.password.equals(password)){
            throw new PasswordException("密码输入错误,请重新输入密码:");//抛出异常
        }
        System.out.println("登录成功");
    }

    public static void main(String[] args) {
        Login login = new Login();
        Scanner scan = new Scanner(System.in);
        System.out.print("请输入用户名:");
        String name1 = scan.nextLine();
        System.out.print("请输入密码:");
        String password1 = scan.nextLine();
        while(true){
            try{
                login.Loginifor(name1,password1);//可能会抛出异常
                break;//抛出异常后,该代码不被执行  输入正确后,跳出循环
            }catch (NameException n){ //捕获异常
                //处理异常
                n.printStackTrace();//打印异常
                name1=scan.nextLine();//重新输入用户名
            }catch (PasswordException p){
                p.printStackTrace();
                password1=scan.nextLine();
            }
        }
    }
}

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

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

相关文章

Repo管理

文章目录 前言Repo介绍清单仓库清单仓库的组成 初始化Repo同步远程仓库Repo实际应用 前言 我们知道,Git是用来管理某一个仓库,那当一个项目用到了多个仓库时,怎么来同步管理这些仓库呢?这个时候就可以引入Repo管理。 Repo介绍 …

Spring Security6.3 自定义AuthorizationManager问题

项目环境: Springboot3.3.5, 对应的SpringFrameWork6.1,Security为6.3 问题:我想自定义AuthorizationManager接口实现类,在里面判断如果角色为amdin则放行请求; 在AdminAuthorizationManager类的check()方法中pass变量…

十二月第三周python

import tkinter as tk #安装图形窗口工具 import cv2 #处理视频工具 from tkinter import * #安装图形窗口工具 from PIL import Image, ImageTk#处理图形工具 roottk.Tk() #给工具起名字 root.title("子豪")#软件左上角名字 root.geometry("800x800500100&…

玉米叶病预测数据集,使用yolo,coco,voc格式人工标注,10046张原始图片,可识别叶枯病,普通锈病,灰叶斑病,健康的玉米叶

玉米叶病预测数据集,使用yolo,coco,voc格式人工标注,10046张原始图片,可识别叶枯病,普通锈病,灰叶斑病,健康的玉米叶 数据集分割 训练组87% 8790图片 有效集4% 419图片…

NAT网络地址转化技术

1.什么是NAT NAT技术是一种将自己内网的多个私有IP地址转换为一个公网IP进行访问互联网的一项技术,这个技术主要是用来解决IPv4地址不够的问题。 2.NAT技术的具体例子 如果我们用手机使用流量浏览一个网站,那么第一步手机会对这个域名进行DNS解析&#…

使用DuckDB 加载和清洗数据

DuckDB CLI是允许用户直接从命令行与DuckDB交互的工具。前文你看到了如何使用Python与DuckDB交互。但是,有时你只是想直接使用数据库—例如在创建新表、从不同数据源导入数据以及执行与数据库相关的任务时。在这种情况下,直接使用DuckDB CLI要有效得多。…

linux部署ansible自动化运维

ansible自动化运维 1,编写ansible的仓库(比赛已经安装,无需关注) 1、虚拟机右击---设置---添加---CD/DVD驱动器---完成---确定 2、将ansible.iso的光盘连接上(右下角呈绿色状态) 3、查看光盘挂载信息 df -h…

vue3-tp8-Element:对话框实现

效果 参考框架 Dialog 对话框 | Element Plus 具体实现 一、建立view页面 /src/views/TestView.vue 二、将路径写入路由 /src/router/index.js import { createRouter, createWebHistory } from vue-router import HomeView from ../views/HomeView.vueconst router create…

YOLOv11改进,YOLOv11添加DLKA-Attention可变形大核注意力,WACV2024 ,二次创新C3k2结构

摘要 作者引入了一种称为可变形大核注意力 (D-LKA Attention) 的新方法来增强医学图像分割。这种方法使用大型卷积内核有效地捕获体积上下文,避免了过多的计算需求。D-LKA Attention 还受益于可变形卷积,以适应不同的数据模式。 理论介绍 大核卷积(Large Kernel Convolu…

Python数据分析案例67——因子分析回归分析

背景 线性回归,主成分回归都做烂了,我之前的案例有很多这些模型,但是一直没写因子分析的回归案例,这个也是传统统计学流行的方法,在金融经济心理学等人文社科用得非常多。这个案例就演示一下python怎么做因子分析。 数…

FastAPI简介

FastAPI简介 一、FastAPI简介二、FastAPI安装2.1 使用pip安装FastAPI2.2 FastAPI的demo2.3 FastAPI的程序结构 三、装饰器请求方法四、用户请求4.1 路径参数4.1.1 单个路径参数4.1.2 多个路径参数4.1.3 固定路径和路径参数的冲突 4.2 查询参数4.3 默认参数4.4 可选参数 五、请求…

Django结合websocket实现分组的多人聊天

其他地方和上一篇大致相同,上一篇地址点击进入, 改动点1:在setting.py中最后再添加如下配置: # 多人聊天 CHANNEL_LAYERS {"default":{"BACKEND": "channels.layers.InMemoryChannelLayer"} }因此完整的se…

基础学习:(5)不同卷积:transposed convolution,deconvolution,dilated convolution

基础学习:(5)不同卷积 文章目录 基础学习:(5)不同卷积前言1 deconvlution transposed convolution2 对比2.1 Convolution animations2.2 Transposed convolution animations2.3 Dilated convolution 前言 …

Windows安装Jira

下载 Download Jira Data Center | Atlassian https://product-downloads.atlassian.com/software/jira/downloads/atlassian-jira-software-10.3.0-x64.exe 以管理员身份安装,否则弹出以下提醒 创建和配置MySQL数据库:参照 Connecting Jira applicat…

采用qL-MPC技术进行小型固定翼无人机的路径跟随控制

来自论文"Predictive Path-Following Control for Fixed-Wing UAVs Using the qLMPC Framework in the Presence of Wind Disturbances" 控制架构 采用的是 ULTRA-Extra无人机,相关参数如下: 这里用于guidance law的无人机运动学模型为&#…

计算机毕设-基于springboot的青少年心理健康教育网站的设计与实现(附源码+lw+ppt+开题报告)

博主介绍:✌多个项目实战经验、多个大型网购商城开发经验、在某机构指导学员上千名、专注于本行业领域✌ 技术范围:Java实战项目、Python实战项目、微信小程序/安卓实战项目、爬虫大数据实战项目、Nodejs实战项目、PHP实战项目、.NET实战项目、Golang实战…

28.在 Vue 3 中使用 OpenLayers 加载 MVT 格式矢量瓦片数据并显示图形

前言 随着前端开发技术的不断进步,越来越多的强大地图库被广泛应用于 Web 地图应用开发中。OpenLayers 是一个流行的开源 JavaScript 库,能够帮助开发者快速构建交互式地图应用。而 Vue 3 作为现代化的前端框架,已经成为开发者构建高效、响应…

Linux - MySQL迁移至一主一从

Linux - MySQL迁移至一主一从 迁移准备安装MySQL ibd文件迁移原服务器操作目标服务器操作 一主一从增量同步异常解决结尾 首先部分单独安装MySQL,请参考Linux - MySQL安装,迁移数据量比较大约400G左右且网络不通故使用文件迁移,需开启一段时间…

opencv-python的简单练习

题目1.读取一张彩色图像并将其转换为灰度图。 import cv2 # 读取图片文件 img cv2.imread(./1.png)# 将原图灰度化 img_gray cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)# 输出图片 cv2.imshow(img,img) cv2.imshow(img_g,img_gray) # 进行阻塞 cv2.waitKey(0) 题目2:…

go-zero(十三)使用MapReduce并发

go zero 使用MapReduce并发 一、MapReduce 介绍 MapReduce 是一种用于并行计算的编程模型,特别适合在大规模数据处理场景中简化逻辑代码。 官方文档: https://go-zero.dev/docs/components/mr 1. MapReduce 的核心概念 在 MapReduce 中,主…