详解Java中的异常体系结构(throw,throws,try-catch,finally,自定义异常)


目录

一.异常的概念

二.异常的体系结构

三.异常的处理

异常处理思路

LBYL:Look Before You Leap

EAFP: It's Easier to Ask Forgiveness than Permission

异常抛出throw

异常的捕获

提醒声明throws

 try-catch捕获处理

finally的作用

四.自定义异常类


一.异常的概念

有一句话说的很好 ”程序员不是在写BUG就是在改BUG” ,在日常开发中,程序员绞尽脑汁的去写出完美的代码,但是在程序运行过程中难免回遇见一些奇奇怪怪的问题。而这些问题与BUG总是很难去控制,用人类的思维去看明明是很完美的一个逻辑处理,但是交给编译器就产生的结果总会与我们的预期大相径庭,在Java中,我们将程序执行过程中发生的不正常的行为称为异常,比如什么算数异常啊,数组越界异常啊,空指针异常啊这都属于异常的范围,我们统称为异常

        System.out.println(10 / 0);
        // 执行结果Exception in thread "main" java.lang.ArithmeticException
        
        int[] arr1 = {1, 2, 3};
        System.out.println(arr1[100]);
        // 执行结果Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException
        
        int[] arr2 = null;
        System.out.println(arr2.length);
        // 执行结果Exception in thread "main" java.lang.NullPointerException

并且我们可以看见在Java中对于不同的异常,都有对应的类来描述


二.异常的体系结构

实际上异常的种类是很多的,为了应对不同的异常或者错误,Java提供了一个非常庞大的异常体系机构供程序员来更好的维护代码的安全性,如下图所示

异常可能发生在编译阶段,可能发生在运行阶段,因此我们可以按照异常发生的时间段将其进行分类:

  • 编译时异常,也叫做受查异常
  • 运行时异常,也叫做非受查异常 

但是诸如将单词拼写错误导致的问题我们程序出现问题的情况不属于异常


三.异常的处理

代码中存在异常并不是什么奇怪的事情,但是在出现异常后,我们需要及时通知程序员去修改,对于异常的处理,我们分为俩种思路

异常处理思路

LBYL:Look Before You Leap

也就是说我们在操作之前就对异常做出充分的检查,也就是事先防御型,比如我们在设计一款游戏的时候,我们就需要对其中可能发生的每一个错误做出处理机制和避免机制

        boolean ret = false;
        ret = loginGame();
        if (!ret) {
            //处理登陆游戏错误;
            return;
        }
        ret = startMatch();
        if (!ret) {
            //处理匹配错误;
            return;
        }
        ret = conGame();
        if (!ret) {
            //处理游戏确认错误;
            return;
        }
        ret = choiceChar();
        if (!ret) {
            //处理选择英雄错误;
            return;
        }
        ret = loading();
        if (!ret) {
            //处理载入游戏错误;
            return;
        }

但这样的处理会有一个缺陷:正常流程和错误处理流程代码混在一起, 代码整体显的比较混乱

EAFP: It's Easier to Ask Forgiveness than Permission

这样的思想主要解决的问题不是如何提前避免异常,而是在异常出现以后如何进行合理的应对

        try {
            loginGame();
            startMatch();
            conGame();
            choiceChar();
            loading();

        } catch (loginGame异常) {
            //处理登陆游戏错误;
        } catch (startMatch异常) {
            //处理匹配错误;
        } catch (conGame异常) {
            //处理游戏确认错误;
        } catch (choiceChar异常) {
            //处理选择英雄错误;
        } catch (loading异常) {
            //处理载入游戏错误;
        }

在Java中对于异常处理的核心机制就是EAFP,Java中常用的有5个异常处理的关键字

  • throw
  • try
  • catch
  • final
  • throws

异常抛出throw

在Java中,可以借助throw关键字,抛出一个指定的异常对象,将错误信息告知给调用者。具体语法如下:

throw new XXXException ("异常产生的原因");

示例: 

    public static int getElement(int[] array, int index){
        if(null == array){
            throw new NullPointerException("传递的数组为null");
        }
        if(index < 0 || index >= array.length){
            throw new ArrayIndexOutOfBoundsException("传递的数组下标越界");
        }
        return array[index];
    }
    public static void main(String[] args) {
        int[] array = {1, 2, 3};
        getElement(array, 3);
    }

 注意:

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

异常的捕获

异常的捕获就是指我们对异常的处理,通常我们有俩种方式去处理:

  • 异常声明throws
  • try-catch捕获处理

提醒声明throws

处在方法声明时参数列表之后,当方法中抛出编译时异常,用户不想处理该异常,此时就可以借助throws将异常抛给方法的调用者来处理。也就是说当前方法不处理异常提醒方法的调用者处理异常

语法格式:

修饰符 返回值类型 方法名 (参数列表) throws 异常类型1,异常类型2...{

}

public class Config {
    File file;
    /*
    FileNotFoundException : 编译时异常,表明文件不存在
    此处不处理,也没有能力处理,应该将错误信息报告给调用者,让调用者检查文件名字是否给错误了
    */
    public void OpenConfig(String filename) throws FileNotFoundException {
        if (filename.equals("config.ini")) {
            throw new FileNotFoundException("配置文件名字不对");
        }
        // 打开文件
    }
}

注意事项:

  • throws 必须跟在方法参数列表之后
  • 抛出的问题必须是Exception 或者 Exception 的子类对象
  • 方法内部如果抛出了多个异常,throws 之后必须跟多个异常类型,之间用逗号隔开,如果抛出多个异常类型具有父子关系,直接声明父类即可
  • 调用声明抛出的异常方法时,调用者必须对异常做出处理,或者继续使用throws 抛出

示例: 

class Config {
    File file;
    // FileNotFoundException 继承自 IOException
    public void OpenConfig(String filename) throws IOException {
        if(filename.endsWith(".ini")){
            throw new IOException("文件不是.ini文件");
        }
        if(filename.equals("config.ini")){
            throw new FileNotFoundException("配置文件名字不对");
        }
        // 打开文件
    }
    public void readConfig(){
    }
    public void openConfig(String s) {
    }
    public static void main(String[] args) throws IOException {
        Config config = new Config();
        config.openConfig("config.ini");
    }
}

 try-catch捕获处理

刚才我们提到的throws 并没有对异常做出处理,他只是将异常报给调用者,让调用者去处理,而如果要对异常真正的处理就需要使用try-catch

try-catch的一般使用语法如下,其中catch:可以有一个也可以有多个,根据具体需求分配,finally可以有也可以没有,但是如果有的话finally中的代码就一定会执行,并且try中的代码也可以不出现异常:

try{
    // 将可能出现异常的代码放在这里
    }catch(要捕获的异常类型 e){
        // 如果try中的代码抛出异常了,此处catch捕获时异常类型与try中抛出的异常类型一致时
        //或者是try中抛出异常的基类时,就会被捕获到
        //对异常就可以正常处理,处理完成后,跳出try-catch结构,继续执行后序代码
    }catch(异常类型 e){
        // 对异常进行处理
    }finally{
        // 此处代码一定会被执行到
    }
}

示例:

class Config {
    File file;
    public void openConfig(String filename) throws FileNotFoundException{
        if(!filename.equals("config.ini")){
            throw new FileNotFoundException("配置文件名字不对");
        }
        // 打开文件
    }
    public void readConfig(){
    }
    public static void main(String[] args) {
        Config config = new Config();
        try {
            config.openConfig("config.txt");
            System.out.println("文件打开成功");
        } catch (IOException e) {
        // 异常的处理方式
        //System.out.println(e.getMessage()); // 只打印异常信息
        //System.out.println(e); // 打印异常类型:异常信息
            e.printStackTrace(); // 打印信息最全面
        }
        // 一旦异常被捕获处理了,此处的代码会执行
        System.out.println("异常如果被处理了,这里的代码也可以执行");
    }
}

注意:

  •  try 块内抛出异常位置之后的代码将不会被执行
  •  如果抛出异常类型与catch时异常类型不匹配,即异常不会被成功捕获,也就不会被处理,继续往外抛,直到JVM收到后中断程序----异常是按照类型来捕获的
  •  try 中可能会抛出多个不同的异常对象,则必须用多个catch来捕获----即多种异常,多次捕获
  • 如果异常之间具有父子关系,一定是子类异常在前catch,父类异常在后catch,否则语法错误

finally的作用

在写程序时,有些特定的代码,不论程序是否发生异常,都需要执行,比如程序中打开的资源:网络连接、数据库连接、IO流等,在程序正常或者异常退出时,必须要对资源进进行回收。另外,因为异常会引发程序的跳转,可能导致有些语句执行不到,finally就是用来解决这个问题的。

关于异常的处理方式,异常的种类有很多,我们要根据不同的业务场景来决定

  • 对于比较严重的问题(例如和算钱相关的场景), 应该让程序直接崩溃, 防止造成更严重的后果
  • 对于不太严重的问题(大多数场景), 可以记录错误日志, 并通过监控报警程序及时通知程序猿
  • 对于可能会恢复的问题(和网络相关的场景), 可以尝试进行重试
  • 在我们当前的代码中采取的是经过简化的第二种方式. 我们记录的错误日志是出现异常的方法调用信息, 能很快速的让我们找到出现异常的位置. 以后在实际工作中我们会采取更完备的方式来记录异常信息

四.自定义异常类

Java中虽然已经内置了丰富的异常类, 但是并不能完全表示实际开发中所遇到的一些异常,此时就需要维护符合我们实际情况的异常结构,自定义异常通常会继承自 ExceptionRunTimeException

  • 继承自 Exception 的异常默认是受查异常
  • 继承自 RunTimeException 的异常默认是非受查异常

具体方式:

  1. 自定义异常类,然后继承自Exception 或者 RunTimeException
  2. 实现一个带有String类型参数的构造方法,参数含义:出现异常的原因

例如我们实现一个用户登陆功能: 

class UserNameException extends Exception {
    public UserNameException(String message) {
        super(message);
    }
}
class PasswordException extends Exception {
    public PasswordException(String message) {
        super(message);
    }
}
class LogIn {
    private String userName = "admin";
    private String password = "123456";
    public static void loginInfo(String userName, String password)
            throws UserNameException,PasswordException{
        if (!userName.equals(userName)) {
            throw new UserNameException("用户名错误!");
        }
        if (!password.equals(password)) {
            throw new PasswordException("用户名错误!");
        }
        System.out.println("登陆成功");
    }
    public static void main(String[] args) {
        try {
            loginInfo("admin", "123456");
        } catch (UserNameException e) {
            e.printStackTrace();
        } catch (PasswordException e) {
            e.printStackTrace();
        }
    }
}



 本次的分享就到此为止了,希望我的分享能给您带来帮助,也欢迎大家三连支持,你们的点赞就是博主更新最大的动力!如有不同意见,欢迎评论区积极讨论交流,让我们一起学习进步!有相关问题也可以私信博主,评论区和私信都会认真查看的,我们下次再见

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深入理解亚信安慧AntDB-T数据库子计划的执行流程

概要&#xff1a; SQL语句在执行时会转换为执行计划&#xff0c;若其中包含了子查询或子链接并且不能被优化&#xff0c;则执行计划会生成子计划&#xff08;查看AntDB的执行计划时看到标记为SubPlan[1] 的部分即为子计划&#xff09;。在整个AntDB数据库中&#xff0c;子计划…

现代C++ 实现单例模式

传统写法有什么问题 如果你了解过单例模式&#xff0c;双重检查锁定模式&#xff08;Double-Checked Locking Pattern&#xff0c;后文简称DCLP&#xff09;的写法你一定不会陌生&#xff0c;甚至你或许认为它是最正确的代码。 class Singleton { public://获取单例Singleton…

MySQL 8.x temp空间不足问题

目录 一、系统环境 二、问题报错 三、问题回顾 四、解决问题 一、系统环境 系统Ubuntu20.04 数据库版本MySQL 8.0.21 二、问题报错 在MySQL上执行一个大的SQL查询报错Error writing file /tmp/MYfd142 (OS errno 28 - No space left on device) Exception in thread …

用C语言实现链队列的基本操作

不多解释&#xff0c;直接上代码&#xff0c;代码已经写了注释&#xff01; //队列链式结构的基本操作&#xff1a; #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> typedef int QueueElememtType; typedef struct QNode//链队的定义 {…

什么是蜘蛛池,蜘蛛池是什么蚂蚁SEO

蜘蛛池是一种通过大量模拟真实用户行为来提升网站搜索引擎排名的技术。这种技术利用大量的网络爬虫程序&#xff0c;模拟搜索引擎蜘蛛的爬行行为&#xff0c;通过大量的模拟爬行和页面抓取&#xff0c;提高网站的权重和排名。 如何联系蚂蚁seo&#xff1f; baidu搜索&#xf…

如何查看自己的文章是否被数据库收入?【查收查引】

致谢&#xff1a;特别感谢图书馆的蔡老师&#xff0c;告诉我怎么操作&#xff01; 另外&#xff0c;查收查引报告中的文章可以分开开&#xff0c;放在一起开不是必须的。&#xff08;放在一起开大概是院士工作量需要的。不是很了解。&#xff09; 如何查看自己的文章是否被数据…

杰发科技AC7840——CAN通信简介(1)

简介 7840支持4路CAN-FD Demo调试 官网下载demo&#xff0c;烧录&#xff0c;打开串口发现打印如下。原因是没有连接CAN盒子&#xff0c;总线错误。 CAN收发器端波形 CAN_L有信号&#xff0c;CAN_H没有 波形放大 GPIO端波形 有持续波形输出 波形放大查看&#xff0c;有50U…

.NET 反射优化的经验分享

比如针对 GetCustomAttributes 通过反射获取属性的优化,以下例子 // dotnet run -c Release -f net7.0 --filter "*" --runtimes net7.0 net8.0public class Tests{public object[] GetCustomAttributes() => typeof(C).GetCustomAttributes(typeof(MyAttribute…

如何快速将图片转为excel?

一、打开金鸣表格文字识别软件。 二、点击添加文件按钮&#xff0c;在打开的窗口中选择目标图片&#xff0c;然后点击“打开”&#xff0c;将图片添加进待识别的列表中。 三、点击提交识别或识别全部。 四、识别完成后点击“打开文件”即可打开识别好的结果文件&#xff08;EXC…

Enabling Application Engine Tracing 启用应用程序引擎跟踪

Enabling Application Engine Tracing 启用应用程序引擎跟踪 By default, all Application Engine traces are turned off. To see a trace or a combination of traces, set trace options before you run a program. 默认情况下&#xff0c;所有应用程序引擎跟踪都处于关闭…

Python学习笔记(四):函数的定义、函数的返回值、None类型、函数说明文档、函数的嵌套调用、局部变量、全局变量、global关键字

目录 一、函数介绍 1. 函数是&#xff1a; 2. 使用函数的好处是&#xff1a; 二、函数的定义&#xff1a; 三、函数的参数 1.传入参数的功能是&#xff1a; 2.函数的传入参数 - 传参定义 3.注意事项&#xff1a; 4.练习&#xff1a;测量体温 四、函数的返回值 1.函数…

宇视科技视频监控 main-cgi 文件信息泄露漏洞

宇视科技视频监控 main-cgi 文件信息泄露漏洞 一、产品简介二、漏概述三、复现环境四、漏洞检测手工抓包自动化检测 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#…

【毕业设计】STM32单片机水质PH值电导率TDS超声波水位检测

1、合集资料汇总&#xff08;部分&#xff09; 2、功能 本系统由STM32单片机核心板、超声波测距模块、PH值传感器模块、电导率传感器、LCD1602液晶及电源组成。 1、超声波传感器采集探测距离&#xff0c;PH传感器采集PH值&#xff08;PH传感器需要根据手册校准&#xff09;&a…

阿里云国际版无法远程连接Windows服务器的排查方法

如果您遇到紧急情况&#xff0c;需要尽快登录Windows实例&#xff0c;请参见以下操作步骤&#xff0c;先检查ECS实例的状态&#xff0c;然后通过云助手向Windows实例发送命令或通过VNC登录实例&#xff0c;具体步骤如下&#xff1a; 步骤一&#xff1a;检查ECS实例状态 无论何…

【Delphi】FMX开发 ios 和 android 异同点(踩坑记)

目录 一、前言 二、补充下基础知识 1. APP程序事件&#xff1a;TApplicationEvent 2. APP内置Web服务器或者UDP服务端或者TCP服务端 三、iOS 和 android 平台的不同点 1. TApplicationEvent的不同点&#xff1a;以下不同点&#xff0c;请仔细阅读&#xff01; 2. APP内置…

yarn或者pnpm第一次执行的时候遇到报错yarn : 无法加载文件......因为在此系统上禁止运行脚本

报错&#xff1a; yarn : 无法加载文件 C:\Users\rina2\AppData\Roaming\npm\yarn.ps1&#xff0c;因为在此系统上禁止运行脚本。有关详细信息&#xff0c;请参阅 https:/http://go.microsoft.com/fwlink/?LinkID135170 中的 about_Execution_Policies。 解决方案&#xff1a…

华清远见嵌入式学习——QT——作业4

作业要求&#xff1a; 代码运行效果图&#xff1a; 代码&#xff1a; 头文件&#xff1a; #ifndef ALARMCLOCK_H #define ALARMCLOCK_H#include <QWidget> #include <QTimerEvent> #include <QTimer> #include <QTime> #include <QTextToSpeech&g…

华为OD试题二(文件目录大小、相对开音节、找最小数)

1. 文件目录大小 题目描述&#xff1a; 一个文件目录的数据格式为&#xff1a;目录id&#xff0c;本目录中文件大小&#xff0c;(子目录id 列表)。其中目录id全局唯一&#xff0c;取值范围[1,200]&#xff0c;本目录中文件大小范 围[1,1000]&#xff0c;子目录id列表个数[0,10…

Java的NIO工作机制

文章目录 1. 问题引入2. NIO的工作方式3. Buffer的工作方式4. NIO数据访问方式 1. 问题引入 在网络通信中&#xff0c;当连接已经建立成功&#xff0c;服务端和客户端都会拥有一个Socket实例&#xff0c;每个Socket实例都有一个InputStream和OutputStream&#xff0c;并通过这…

云基础软件深化合作,云轴科技ZStack与麒麟软件战略签约

12月8日&#xff0c;云轴科技ZStack与麒麟软件战略合作签约仪式在北京举行&#xff0c;双方对过往紧密合作表达了充分肯定&#xff0c;并就进一步联合技术创新、打造重点行业标杆和持续赋能客户达成高度共识。云轴科技创始人&CEO张鑫和麒麟软件高级副总经理谢文征共同见证双…