JavaSE入门---关于异常那些事儿

news2024/11/20 1:36:31

文章目录

  • 什么是异常?
  • 异常的体系结构
  • 异常的分类
    • 一个异常的完整执行流程
  • 异常的处理
    • 防御式编程
    • 异常的声明
    • 异常的捕获
      • 异常声明throws
      • try-catch捕获并处理
      • finally
    • 异常的处理流程
  • 自定义异常类

什么是异常?

在Java中,将程序执行过程中出现的不正常行为称为异常。比如:

		//算术异常
        System.out.println(10/0);
        //输出:Exception in thread "main" java.lang.ArithmeticException: / by zero

        //数组越界异常
        int[] arr = {1,2,3};
        System.out.println(arr[10]);
        //输出:Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 10

        //空指针异常
        int[] array = null;
        System.out.println(array.length);
        //输出:Exception in thread "main" java.lang.NullPointerException

异常的体系结构

异常在Java中以类的形式出现,每一个异常类都可以创建异常对象。异常种类繁多,为了对不同异常或者错误进行很好的分类管理,Java内部维护了一个异常的体系结构:

在这里插入图片描述

注:

  1. throwable是异常体系的顶层类,其派生出两个重要子类error类和exception类;
  2. error类:指JVM无法解决的严重问题,如:JVM的内部错误、资源耗尽等;
  3. exception类:指异常产生后程序员可以通过代码进行处理,使程序继续执行的问题;

异常的分类

可以将异常分为:

  1. 编译时异常,也称为 受检查异常,要求程序员在编写程序阶段必须预先对这些异常进行处理,如果不处理编译器报错;
    编译时异常是在编译阶段发生的吗?
    不是。是在编写程序的时候发生
  2. 运行时异常,也称为 非受检查异常,在编写程序阶段程序员可以预先处理,也可以不管,都行。;

在这里插入图片描述

注:

  1. 异常分为编译时异常和运行时异常,但所有异常都是在运行阶段发生的,因为,只有运行阶段才可以new对象,异常的发生就是new异常对象。
  2. 编译时异常一般发生的概率比较高,需要在运行之前进行预处理;运行时异常一般发生的概率比较低,在运行之前不必进行预处理。

一个异常的完整执行流程

	 public static void main(String[] args) {
        System.out.println(10/0);

        //这里的hello world 没有执行
        System.out.println("hello world");
    }

流程:

  1. 程序执行到System.out.println(10/0),发生了ArithmeticException算术异常,底层new了一个ArithmeticException异常对象,然后抛出了
  2. 由于是main方法调用了10/0,所以这个ArithmeticException抛给了main方法,但main方法没有处理,又会把这个异常抛给JVM
  3. JVM中止程序的执行,此时,System.out.println(“hello world”)不会执行。

异常的处理

防御式编程

  • LAYL:Look Before You Leap,操作之前就做充分的检查,即事前防御型
boolean ret = false;
        ret = 登录游戏();
        if (!ret){
            处理登录游戏异常;
            return;
        }

        ret = 开始匹配();
        if (!ret){
            处理开始匹配异常;
            return;
        }

        ret = 游戏确认();
        if (!ret){
            处理游戏确认异常;
            return;
        }

        ret = 载入游戏画面();
        if (!ret){
            处理载入游戏画面异常;
            return;
        }
        .....

缺点:正常代码和错误代码流程放在一起,代码整体显的比较混乱。

  • EAFP:It‘s Easier to Ask Forgiveness than Permission 事后获取原谅比事前获取许可更容易,也就是先操作,遇到问题再处理;即事后认错型
	try {
            登陆游戏();
            开始匹配();
            游戏确认();
            载入游戏画面();
            ...
        } catch (登陆游戏异常) {
            处理登陆游戏异常;
        } catch (开始匹配异常) {
            处理开始匹配异常;
        } catch (游戏确认异常) {
            处理游戏确认异常;
        } catch (载入游戏画面异常) {
            处理载入游戏画面异常;
        }
        ......

优势:正常流程和错误流程是分离开的,程序员更关注正常流程,代码更清晰,更容易理解。

所以,我们在处理异常时的核心思想就是 EAFP,我们需要明白“异常一旦出现是必须要被处理了才能继续执行后面的代码的”,处理异常可以交给JVM处理:直接中止程序执行;交给用户自己处理:可以按照自己的意愿来进行合适的处理,再执行后面的代码;

在Java中,异常处理主要有5个关键字:throw、throws、try、catch、finally

异常的声明

在编写程序时,如果程序中出现错误,此时就需要把错误的信息告诉调用者;

在Java中,可以通过throw关键字,来声明一个指定的异常对象,将错误的信息告诉调用者。但是它不会处理异常,必须搭配throws或者try-catch来使用。

用throw声明异常后会自动抛出

由方法的调用者使用throws配合使用:上报给上一级,直到交给JVM处理程序直接中止,不会执行后面的代码;
由方法的调用者使用try-catch配合使用:用户自己处理,处理之后可以继续执行后面的代码;

	public static int getElement(int[] arr,int index){
        if (arr == null){
            //通过throw 告诉调用者出现的异常情况信息
            throw new NullPointerException("传递的数组为空");
        }

        if (index < 0 || index >= arr.length){
            //通过throw 告诉调用者出现的异常情况信息
            throw new ArrayIndexOutOfBoundsException("要访问的数组下标越界");
        }
        
        return arr[index];
    }

    //实现一个获取数组指定位置元素的方法
    public static void main(String[] args) {
        int[] arr = {1,2,3};
        int ret = getElement(arr,2);
        System.out.println(ret);
    }

注:

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

异常的捕获

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

异常声明throws

当方法中抛出编译时异常(使用throws抛出异常后必须有人来进行处理),用户不想处理该异常,此时就可以借助throws将异常抛给方法的调用者处理。即当前方法不处理异常,提醒方法的调用者处理异常,如果方法的调用者也不处理,则会交给JVM处理,程序直接中止运行

  • 方法抛出异常后,方法的调用者不处理,继续向上级抛出异常,直到让JVM处理:程序中止。不执行后面的代码。
	public static int getElement(int[] arr,int index) throws ArrayIndexOutOfBoundsException{
        return arr[index];
    }
    public static void main(String[] args) throws ArrayIndexOutOfBoundsException{
        int[] arr = {1,2,3};
        int ret = getElement(arr,3);

        System.out.println("执行后面的代码");
    }

//会打印出异常日志   但不会打印出   执行后面的代码
  • 方法抛出异常后,方法的调用者使用try-catch处理异常。会执行后面的代码。
	public static int getElement(int[] arr,int index) throws ArrayIndexOutOfBoundsException{
        return arr[index];
    }
    public static void main(String[] args) {
        int[] arr = {1,2,3};
        try {
            int ret = getElement(arr,3);
        }catch (ArrayIndexOutOfBoundsException e){
            e.printStackTrace();
        }

        System.out.println("执行后面的代码");
    }

//会打印出异常日志 和 执行后面的代码

注:

  1. 只要异常没有捕捉,采用上报的方式,此方法的后续代码不会执行。就像上面说的第一种解决方法
  2. try-catch捕捉异常后,后续代码可以执行。就像上面说的第二种解决方法。
  3. try语句块中的某一行出现异常,该行后面的代码不会执行
public static int getElement(int[] arr,int index) throws ArrayIndexOutOfBoundsException{
        return arr[index];
    }
    public static void main(String[] args) {
        int[] arr = {1,2,3};
        try {
            int ret = getElement(arr,3);
            //下面的代码不会执行
            System.out.println("getElement之后的语句");
            System.out.println("getElement之后的语句");
        }catch (ArrayIndexOutOfBoundsException e){
            e.printStackTrace();
        }

        //这里的代码还会执行
        System.out.println("执行后面的代码");
    }

//会打印出异常日志 和 执行后面的代码

try-catch捕获并处理

throws并没有对异常进行真正的处理,而是将异常抛出给方法的调用者,由调用者处理;如果真正要对异常进行处理,就需要try-catch。

	public static int getElement(int[] arr,int index) throws ArrayIndexOutOfBoundsException{
        return arr[index];
    }
    public static void main(String[] args) {
        int[] arr = {1,2,3};
        try {
            int ret = getElement(arr,3);
        }catch (ArrayIndexOutOfBoundsException e){
            e.printStackTrace();
        }

        System.out.println("执行后面的代码");
    }

注:

  1. catch()里的 e ,表示是e引用,保存的地址是那个new出来的异常对象的地址;
  2. try中可能会抛出多个异常,则建议使用多个catch来一对一精确捕获;
  3. catch中捕获的异常类型既可以是具体的异常类型,也可以是该异常类型的父类型。
  4. catch中捕获多个异常时,从上到下,必须遵守异常的范围从小到大。否则先捕获到该类的父类型时,就不会继续向下检测,不会捕获到具体的异常类型了。
ArrayIndexOutOfBoundsException,NullPointerException{
        return arr[index];
    }

    public static void main(String[] args) {
        int[] arr = {1,2,3};
        try {
            int ret = getElement(arr,1);
        }catch (ArrayIndexOutOfBoundsException e){
            e.printStackTrace();
        }catch (NullPointerException e){
            e.printStackTrace();
        }catch (Exception e){
            e.printStackTrace();
        }
    } ```

5. catch捕获多个异常时,异常间可以用 | 分割

```java 	public static int getElement(int[] arr, int index) throws
ArrayIndexOutOfBoundsException,NullPointerException{
        return arr[index];
    }

    public static void main(String[] args) {
        int[] arr = {1,2,3};
        try {
            int ret = getElement(arr,1);
        }catch (ArrayIndexOutOfBoundsException  | NullPointerException | Exception){
            System.out.println("出现异常");
        }
    } ```

finally

在某些特定的代码中,我们必须要求其释放持有的资源,比如文件读取等,但是当遇到异常时,它下面的一些代码就不会被执行到,这样就会引发一些问题。因此,我们使用finall来解决这个问题。

finall子句必须和try一起出现,不能单独编写。
即使try语句块中的代码出现了异常,finall中的代码也是一定会执行的。

	public static void main(String[] args) {
        try {
            System.out.println("try");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            System.out.println("finally");
        }
    }

注:

  1. try和finally联用,没有catch;
        try {
            System.out.println("try");
            return;
        } finally {
            System.out.println("finally");
        }
    } ```

2. 如果try语句块中有return,那么tryfinallyreturn的执行顺序是怎样的?

   先执行try、再执行finally、最后执行returnfinally执行的时机是在return以前。

3. 如何让finally失效呢? System.exit()

```	public static void main(String[] args) {
        try {
            System.out.println("try");
            //退出JVM 退出之后finally中的语句就不执行了
            System.exit(0);
        } finally {
            System.out.println("finally");
        }
    } ```

4. 一般在finally中进行一些资源清理的工作。

异常的处理流程

  1. 程序先执行 try 中的代码
  2. 如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配
  3. 如果找到匹配的异常类型, 就会执行 catch 中的代码
  4. 如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者
  5. 无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行).
  6. 如果上层调用者也没有处理的了异常, 就继续向上传递
  7. 一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止.

自定义异常类

Java中,虽然已经内置了丰富的异常类,但是并不能完全表示在实际开发中遇到的所有异常,此时就需要我们自己定义异常。

自定义异常有两步:

  1. 编写一个类,继承Exception或者RuntimeException。
  2. 提供两个构造方法,一个无参,一个带有String参数。

下面以登录为例,演示自定义异常类的使用。

//自定义用户名错误类
class UsernameFalseException extends Exception{
    public UsernameFalseException(){

    }

    public UsernameFalseException(String msg){
        super(msg);
        System.out.println(msg);
    }
}

//自定义密码错误类
class PasswordFalseException extends Exception{
    public PasswordFalseException(){

    }

    public PasswordFalseException(String msg){
        super(msg);
        System.out.println(msg);
    }
}

public class Test {
    private static String USER_NAME = "admin";
    private static String PASS_WORD = "123456";


    public static void login(String username,String password) throws UsernameFalseException, PasswordFalseException {
        if (!username.equals(USER_NAME)){
            throw new UsernameFalseException("用户名错误");
        }

        if (!password.equals(PASS_WORD)){
            throw new PasswordFalseException("密码错误");
        }
    }
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String username = scanner.nextLine();
        String password = scanner.nextLine();

        try {
            login(username,password);
            System.out.println("登录成功");
        }catch (UsernameFalseException e){
            e.printStackTrace();
        }catch (PasswordFalseException e){
            e.printStackTrace();
        }
    }
}

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

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

相关文章

云原生Docker数据管理

目录 Docker的数据管理 数据卷 数据卷容器 容器互联 容器中管理数据主要有两种方式&#xff1a; 数据卷&#xff08;Data Volumes&#xff09;数据卷容器&#xff08;Data Volume Dontainers&#xff09; Docker的数据管理 数据卷 数据卷是一个供容器使用的特殊目录&a…

软考-流量分析

扫描技术是网络攻防的一种重要手段&#xff0c;在攻和防当中都有其重要意义。nmap是一个开放源码的网络扫描工具&#xff0c;可以查看网络系统中有哪些主机在运行以及哪些服务是开放的。 nmap工具的命令选项:sS 用于实现SYN扫描&#xff0c;该扫描类型是通过观察开放端口和关闭…

友思特方案 | 3D点云实例分割现成算法模块助力实现自动化上下料应用

引言 全球范围内的大型汽车制造商和技术公司&#xff0c;以及一些专注于智能制造领域的创新企业&#xff0c;在3D视觉引导汽车部件自动化上下料项目方面都在进行研发和实践。国内外汽车制造行业&#xff0c;越来越多的企业开始采用3D视觉引导技术进行自动化上下料操作。 本文将…

swagger stub https无法访问

有个app的以前别人写的假服务用http访问可以,但是用https去访问就不行 看官方说schema里面配置一个https就可以了但是对我那个server没有用: 官方参考链接: API Host and Base Path 后来领导给我发了个这个: Node.js Express で HTTPSを利用するパターン #Node.js - Qi…

视频编辑不求人,教你一招制胜批量添加封面

视频添加封面是一个相当简单的任务&#xff0c;您只需要一款专门的软件&#xff0c;就能轻松搞定&#xff01;下面就是详细教程啦&#xff01; 首先&#xff0c;您需要在浏览器中搜索“固乔智剪软件”&#xff0c;进入官网并下载这款软件。固乔智剪软件是一款非常专业的视频剪辑…

【网络知识必知必会】传输层UDP协议

文章目录 前言1. UDP 的特点1.1 无连接1.2 不可靠传输1.3 面向数据报1.4 缓冲区1.5 全双工1.6 传输数据大小受限 2. UDP 协议端格式3. UDP 是如何校验的4. 基于 UDP 的应用层协议总结 前言 在传输层中, 有两个知名协议是需要我们必知必会的, 一个是UDP协议, 一个是TCP协议, 本…

2023IG新功能大整理,更多玩法助力营销推广

作为当今全球最为受欢迎的社交媒体之一&#xff0c;Instagram在2023年迎来了一系列重要的功能更新。学习了解Instagram的最新功能&#xff0c;以及如何高效利用这些新的功能和工具&#xff0c;对于跨境品牌在该平台上实现营销推广至关重要。今天给大家详细介绍 Instagram在2023…

门禁管理:这招我居然才知道,不要太好用!

随着社会的不断发展&#xff0c;门禁监控系统已经成为了各种场所的必备设备。门禁监控系统通过技术的创新和智能化的进步&#xff0c;为不同类型的客户提供了高效的安全保障和管理工具。 客户案例 南京某商业大厦管理公司 南京某商业大厦管理公司面临着大量员工和访客的管理挑…

领先一步,效率翻倍:PieCloudDB Database 预聚集特性让查询速度飞起来!

在大数据时代&#xff0c;如何有效地管理和处理海量数据成为了企业面临的核心挑战。为此&#xff0c;拓数派推出了首款数据计算引擎 PieCloudDB Database&#xff0c;作为一款全新的云原生虚拟数仓&#xff0c;旨在提供更高效、更灵活的数据处理解决方案。 PieCloudDB 的设计理…

京东平台销量销额数据查询:2023年9月京东空调行业品牌销售排行榜!

鲸参谋监测的京东平台9月份空调市场销售数据已出炉&#xff01; 鲸参谋数据显示&#xff0c;今年9月份&#xff0c;京东平台大家电品类——空调的整体销售呈现下滑。从数据来看&#xff0c;9月空调的月销量将近60万&#xff0c;环比下滑约59%&#xff0c;同比下滑约6%&#xff…

Python 类继承解释

一、说明 类继承是Python中数据科学家和机器学习工程师需要了解的一个重要概念。在这里&#xff0c;我们的专家解释了它的工作原理。 在Python中&#xff0c;类包含属性和方法。属性是存储数据的变量。类方法是属于类的函数&#xff0c;通常对类属性执行一些逻辑。在本文中&…

npm版本错误——npm ERR! code ERESOLVE 解决方法

起因 项目中echart版本过低&#xff0c;导致某些图表不能正确显示&#xff0c;所以大手一挥&#xff0c;将echart版本从4升级到了5&#xff0c; 再去运行项目的时候 就发现项目报错了 npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree npm ERR! …

C++基础算法----正整数高精度加减乘除

4、正整数高精度 ​ 出现高精度的情况一般只有C会出现&#xff0c;python会无限制&#xff0c;java有大整数&#xff0c;所以基本上不用考虑&#xff0c;一般会出现四种情况 一个较大数A 一个较大数B一个较大数A - 一个较大数B一个较大数A * 一个数一个较大数A / 一个数 这里…

ubuntu系统由于英伟达显卡驱动问题黑屏或者其他报错开机无法进入系统解决办法!

背景&#xff1a; 硬件&#xff1a;CPU&#xff1a;AMD PRO 3955WX、硬盘&#xff1a;一块固态&#xff08;ubuntu&#xff09;一块固态&#xff08;windows&#xff09;双系统、英伟达丽台RTX4000显卡&#xff1b; 故障&#xff1a; 把显卡拆出来&#xff0c;拿到别的地方测…

分享一下商城小程序有哪些营销活动可以做的

随着移动支付的普及和电商行业的快速发展&#xff0c;商城小程序已成为消费者日常生活中不可或缺的一部分。小程序具有便捷性、即时性和个性化等特点&#xff0c;使得品牌商家能够更好地与消费者互动&#xff0c;提升销售额。本文将探讨商城小程序营销活动的几种形式&#xff0…

CentOS7日志文件及journalctl日志查看

一、日 志 文 件 说 明 tail /var/log/messages //系统启动后的信息和错误日志&#xff0c;是Red Hat Linux中最常用的日志之一 tail -f /var/log/secure //与安全相关的日志信息 tail /var/log/maillog //与邮件相关的日志信息 tail /var/log/cro…

8.canvas阴影设置

在canvas里面设置元素阴影相对来说比较简单&#xff0c;他有4个属性控制。 shadowOffsetX 描述阴影水平偏移距离的属性。 shadowOffsetY 描述阴影垂直偏移距离的属性。 shadowBlur 描述模糊效果程度的属性&#xff1b;它既不对应像素值也不受当前转换矩阵的影响。默认值是…

Python + Appium 自动化操作微信入门看这一篇就够了!

简介 Appium 是一个开源的自动化测试工具&#xff0c;支持 Android、iOS 平台上的原生应用&#xff0c;支持 Java、Python、PHP 等多种语言。 Appium 封装了 Selenium&#xff0c;能够为用户提供所有常见的 JSON 格式的 Selenium 命令以及额外的移动设备相关的控制命令&#…

Anaconda创建新的虚拟环境及Jupyter Notebok中、PyCharm中环境的使用

Anaconda创建新的虚拟环境 在windows开始菜单中【徽标键】&#xff0c;查找Anaconda文件夹并打开【Anaconda Prompt】 查看已有虚拟环境 conda env list1.创建 conda create --name mytest python3.7 # 创建一个名称为mytest&#xff0c;python版本为3.7的虚拟环境输入【…

【已解决】Word文档的“打开密码”在哪里设置?

我们知道&#xff0c;Word文档可以设置“打开密码”&#xff0c;保护文件不被随意打开&#xff0c;只有输入正确的密码才能打开文档。那这个“打开密码”要在哪里设置呢&#xff1f;不清楚的小伙伴往下看就知道啦。 在Word文档里&#xff0c;有两个路径可以设置“打开密码”。…