javaSE - 异常(Exception 或 RuntimeException)

news2024/11/13 16:50:18

一、异常的背景

1.1、初识异常

其实在我们开发中,就是代码出现意外状况。影响到程序的运行。
其实,在我们接触代码开始,就一直在接触异常,只是从来没有分类。
这点在java中,更加明显。
现在我们就来基本了解一下异常。

算数异常 ArithmeticException

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

在这里插入图片描述

数组越界异常 ArrayIndexOutOfBoundsException

public static void main(String[] args) {
        int[] array = {1,2,3,4,5};
        for (int i = 0; i < 10; i++) {
            System.out.println(array[i]);
        }
    }

在这里插入图片描述

空指针异常 NullPointerException

public static void main(String[] args) {
        String str = null;
        System.out.println(str.length());
    }

在这里插入图片描述

所谓异常指的就是程序在 运行时 出现错误时通知调用者的一种机制

关键字 “运行时”
有些错误是这样的, 例如将 System.out.println 拼写错了, 写成了 system.out.println. 此时编译过程中就会出错, 这是 “编译期” 出错.
而运行时指的是程序已经编译通过得到 class 文件了, 再由 JVM 执行过程中出现的错误

异常的种类有很多, 不同种类的异常具有不同的含义, 也有不同的处理方式

1.2、防御式编程

错误在代码中是客观存在的. 因此我们要让程序出现问题的时候及时通知程序猿. 我们有两种主要的方式

  • LBYL: Look Before You Leap. 在操作之前就做充分的检查.
  • EAFP: It’s Easier to Ask Forgiveness than Permission. “事后获取原谅比事前获取许可更容易”. 也就是先操作, 遇到
    问题再处理

异常的核心思想就是 EAFP

1.3、异常的好处

下面,我们用伪代码演示一下开始一局王者荣耀的过程

LBYL 风格的代码(不使用异常)

boolean ret = false;
ret = 登陆游戏();
if (!ret) {
	处理登陆游戏错误;
	return;
}
ret = 开始匹配();
if (!ret) {
	处理匹配错误;
	return;
}
ret = 游戏确认();
if (!ret) {
	处理游戏确认错误;
	return;
}
ret = 选择英雄();
if (!ret) {
	处理选择英雄错误;
	return;
}
ret = 载入游戏画面();
if (!ret) {
	处理载入游戏错误;
	return;
}

在这里插入图片描述

EAFP 风格的代码(使用异常)

try {
登陆游戏();
开始匹配();
游戏确认();
选择英雄();
载入游戏画面();
...
} catch (登陆游戏异常) {
	处理登陆游戏异常;
} catch (开始匹配异常) {
	处理开始匹配异常;
} catch (游戏确认异常) {
	处理游戏确认异常;
} catch (选择英雄异常) {
	处理选择英雄异常;
} catch (载入游戏画面异常) {
	处理载入游戏画面异常;
}
......

在这里插入图片描述
对比两种不同风格的代码, 我们可以发现, 使用第一种方式, 正常流程和错误处理流程代码混在一起, 代码整体显的比较
混乱. 而第二种方式正常流程和错误流程是分离开的, 更容易理解代码

二、异常的基本用法

2.1、基本语法

try{
	有可能出现异常的语句 ;
}[catch (异常类型 异常对象) {
} ... ]
[finally {
	异常的出口
}]
  • try 代码块中放的是可能出现异常的代码.
  • catch 代码块中放的是出现异常后的处理行为.
  • finally 代码块中的代码用于处理善后工作, 会在最后执行.
  • 其中 catch 和 finally 都可以根据情况选择加或者不加

2.2、使用try-catch处理异常

代码示例1 不处理异常

public static void main(String[] args) {
        int[] array = {1,2,3,4,5};
        System.out.println(array[10]);
    }

在这里插入图片描述

代码示例2 处理异常

public static void main(String[] args) {
        int[] array = {1,2,3,4,5};
        try{
            System.out.println(array[10]);
            System.out.println("asddfddf");
        }catch(ArrayIndexOutOfBoundsException e){
            e.printStackTrace();
            System.out.println("捕捉到了数组越界异常");
            System.out.println("哈哈");
        }
        System.out.println("代码执行到了这里");
    }

在这里插入图片描述
我们发现,一旦 try 中出现异常,那么 try 代码块中的程序就不会继续执行,而是交给 catch 中的代码来执行,catch 执行完毕会继续往下执行

关于异常的处理方式

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

关于 “调用栈”

  • 方法之间是存在相互调用关系的, 这种调用关系我们可以用 “调用栈” 来描述,在 JVM 中有一块内存空间称为 “虚拟机栈” 专门存储方法之间的调用关系,当代码中出现异常的时候,我们就可以使用 e.printStackTrace(); 的方式查看出现异常代码的调用栈

如果我们不处理异常,那这个异常就会交给JVM来处理,那这个程序就会终止
如果而我们处理了异常,那这个程序机会往下执行

public static void main(String[] args) {
        int[] array = {1,2,3,4,5};
        try{
            System.out.println(array[10]);
            System.out.println("asddfddf");
        }catch(ArrayIndexOutOfBoundsException e){
            e.printStackTrace();
            System.out.println("捕捉到了数组越界异常,然后处理异常");
            for (int i = 0; i < array.length; i++) {
                System.out.print(array[i] + " ");
            }
        }
    }

在这里插入图片描述

代码示例3 catch 只能处理对应种类的异常

public static void main(String[] args) {
        int[] array = {1,2,3,4,5};
        try{
            int a = 10 / 0;
            System.out.println(array[10]);
            System.out.println("hello");
        }catch(ArrayIndexOutOfBoundsException e){
            e.printStackTrace();
            System.out.println("捕捉到了数组越界异常,然后处理异常");
            for (int i = 0; i < array.length; i++) {
                System.out.print(array[i] + " ");
            }
        }
    }

在这里插入图片描述

代码示例4 catch 可以有多个

public static void main(String[] args) {
        int[] array = {1,2,3,4,5};
        try{
            int a = 10 / 0;
            System.out.println(array[10]);
            System.out.println("hello");
        }catch(ArrayIndexOutOfBoundsException e){
            e.printStackTrace();
            System.out.println("捕捉到了数组越界异常,然后处理异常");
            for (int i = 0; i < array.length; i++) {
                System.out.print(array[i] + " ");
            }
        } catch(ArithmeticException e){
            e.printStackTrace();
            System.out.println("捕捉到了算数异常,处理异常");
            int a = 10 / 2;
            System.out.println(a);
        }
        System.out.println("哈哈");
    }

简写上面的代码,捕捉个异常中间可以用“|”连接

public static void main(String[] args) {
        int[] array = {1,2,3,4,5};
        try{
            int a = 10 / 0;
            System.out.println(array[10]);
            System.out.println("hello");
        }catch(ArrayIndexOutOfBoundsException | ArithmeticException e){
            e.printStackTrace();
            System.out.println("捕捉到了数组越界异常或者捕捉到了算数异常,然后处理异常");
            int a = 10;
            System.out.println(a / 2);
            for (int i = 0; i < array.length; i++) {
                System.out.print(array[i] + " ");
            }
        }
        System.out.println("哈哈");
    }

在这里插入图片描述

2.3、异常的体系结构

Java 内置了丰富的异常体系, 用来表示不同情况下的异常.
下图表示 Java 内置的异常类之间的继承关系:
在这里插入图片描述

  • 顶层类 Throwable 派生出两个重要的子类, Error 和 Exception
  • 其中 Error 指的是 Java 运行时内部错误和资源耗尽错误. 应用程序不抛出此类异常. 这种内部错误一旦出现,除了告知用户并使程序终止之外, 再无能无力. 这种情况很少出现.
  • Exception 是我们程序猿所使用的异常类的父类.
  • 其中 Exception 有一个子类称为 RuntimeException , 这里面又派生出很多我们常见的异常类NullPointerException , IndexOutOfBoundsException 等.

Java语言规范将派生于 Error 类或 RuntimeException 类的所有异常称为 非受查异常, 所有的其他异常称为 受查
异常

2.2.1、异常的分类

异常的分类:

  • 1、受查异常(编译时异常),
  • 2、非受查异常(运行时异常)
  • 在这里插入图片描述

2.4 finally的使用

代码示例5 也可以用一个 catch 捕获所有异常(不推荐)

public static void main(String[] args) {
        int[] array = {1,2,3,4,5};
        try{
            int a = 10 / 0;
            System.out.println(array[10]);
            System.out.println("hello");
        }catch(Exception e){
            e.printStackTrace();
            System.out.println("捕捉到了数组越界异常或者捕捉到了算数异常,然后处理异常");
            int a = 10;
            System.out.println(a / 2);
            for (int i = 0; i < array.length; i++) {
                System.out.print(array[i] + " ");
            }
        }
        System.out.println("哈哈");
    }

在这里插入图片描述
由于 Exception 类是所有异常类的父类. 因此可以用这个类型表示捕捉所有异常.

  • 备注:catch 进行类型匹配的时候,不光会匹配相同类型的异常对象,也会捕捉目标异常类型的子类对象。
  • 如刚才的代码,ArithmeticException 和 ArrayIndexOutOfBoundsException 都是 Exception 的子类,因此都能被捕获到

catch在捕获异常的时候最好是捕获具体的异常类型(捕获的时候最好是先捕获子类的异常,然后在捕获父类的异常,不然就会报错)

现在我们知道 ArithmeticException 和 ArrayIndexOutOfBoundsException 都是 Exception 的子类
如果我们先捕获父类的异常,此时就会报错

public static void main(String[] args) {
        int[] array = {1,2,3,4,5};
        try{
            int a = 10 / 0;
            System.out.println(array[10]);
            System.out.println("hello");
        }catch(Exception e){
            e.printStackTrace();
            System.out.println("捕捉到了数组越界异常或者捕捉到了算数异常,然后处理异常");
        }catch(ArrayIndexOutOfBoundsException | ArithmeticException e) {
            e.printStackTrace();
            System.out.println("捕捉到了数组越界异常或者捕捉到了算数异常,然后处理异常");
        }
        System.out.println("哈哈");
    }

在这里插入图片描述
所以我们要先捕获子类的异常

public static void main(String[] args) {
        int[] array = {1,2,3,4,5};
        try{
            int a = 10 / 0;
            System.out.println(array[10]);
            System.out.println("hello");
        }catch(ArrayIndexOutOfBoundsException | ArithmeticException e) {
            e.printStackTrace();
            System.out.println("捕捉到了数组越界异常或者捕捉到了算数异常,然后处理异常");
        } catch(Exception e){
            e.printStackTrace();
            System.out.println("捕捉父类的异常");
        }
        System.out.println("哈哈");
    }

在这里插入图片描述

代码示例6 finally 表示最后的善后工作, 例如释放资源

比如我么要在控制台上面输入数据,Scanner也是一种资源,打开了之后,就要进行关闭

public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        try{
            int a = scanner.nextInt();
            int b = 10 / a;
            System.out.println("b = " + b);
        }catch(ArithmeticException e){
            e.printStackTrace();
            System.out.println("捕捉到了算数异常");
        }
        scanner.close();//关闭资源
    }

在这里插入图片描述

public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        try{
            int a = scanner.nextInt();
            int b = 10 / a;
            System.out.println("b = " + b);
        }catch(ArithmeticException e){
            e.printStackTrace();
            System.out.println("捕捉到了算数异常");
        }finally {
            scanner.close();//关闭资源
            System.out.println("Scanner关闭了");
        }
    }

在这里插入图片描述
所以finally{ } 包含的代码,无论有没有捕捉到异常都会被 执行

刚才的代码Scanner在new对象的时候,是在try 外面的,现在还有一种等价的写法

public static void main(String[] args) {

        try (Scanner scanner = new Scanner(System.in)) {
            int a = scanner.nextInt();
            int b = 10 / a;
            System.out.println("b = " + b);
        } catch (ArithmeticException e) {
            e.printStackTrace();
            System.out.println("捕捉到了算数异常");
        } finally {
            //关闭资源
            System.out.println("Scanner关闭了");
        }
    }

在这里插入图片描述

如果我们没有捕捉到异常,异常交给JVM处理,那finally会被执行吗???

public static void main(String[] args) {
        try (Scanner scanner = new Scanner(System.in)) {
            int a = scanner.nextInt();
            int b = 10 / a;
            System.out.println("b = " + b);
        } catch (NullPointerException e) {
            e.printStackTrace();
            System.out.println("捕捉到了算数异常");
        } finally {
            //关闭资源
            System.out.println("Scanner关闭了");
        }
    }

在这里插入图片描述

2.4.1、尽量避面在finally中使用return

代码示例

public static int func(){
        try{
            return 2;
        }catch(ArithmeticException e){
            e.printStackTrace();
        }finally {
            return 100;
        }
    }

    public static void main(String[] args) {
        System.out.println(func());
    }

在这里插入图片描述

2.5、异常处理流程

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

2.6、抛出异常

除了 Java 内置的类会抛出一些异常之外, 程序猿也可以手动抛出某个异常. 使用 throw 关键字完成这个操作
在这里插入图片描述

四、自定义异常

Java 中虽然已经内置了丰富的异常类,但是我们实际场景中可能还有一些情况需要我们对异常类进行扩展,创建符合我们实际情况的异常.

不抛异常的用户登录代码实例

如果我们不抛异常,那这个用户登录代码实现:

public class Test {
    public static String name = "baidu";
    public static String password = "123456";

    public static void login(String name, String password){
        if(!Test.name.equals(name)){
            System.out.println("用户名错误!");
            return;
        }
        if(!Test.password.equals(password)){
            System.out.println("密码错误!");
            return;
        }
        System.out.println("登录成功!");
    }

    public static void main(String[] args) {
        login("baidu","123456");
    }
}

如果我们发现用户名或者密码错误。此时就可以抛出自定义异常
但是自定义异常都要继承系统异常,然后在重写构造方法
此时我们在处理用户名密码错误的时候可能就需要抛出两种异常. 我们可以基于已有的异常类进行扩展(继承), 创建和我们业务相关的异常类.

继承受查异常(Exception ),那就要搭配try - catch使用或者使用throws声明

在这里插入图片描述

继承非受查异常RuntimeException

在这里插入图片描述

抛异常的用户登录代码实例

class NameException extends RuntimeException{
    public NameException(String message) {
        super(message);
    }
}
class PasswordRuntimeException extends RuntimeException{
    public PasswordRuntimeException(String massage){
        super(massage);
    }
}
public class Test2 {
    public static String name = "baidu";
    public static String password = "123456";

    public static void func(int x)  {

    }

    public static void login(String name, String password){
        if(!Test2.name.equals(name)){
            throw new NameException("用户名错误!");
        }
        if(!Test2.password.equals(password)){
            throw new PasswordRuntimeException("密码错误!");
        }
        System.out.println("登录成功!");
    }

    public static void main(String[] args) {
        login("baidu","123456");
    }
}

在这里插入图片描述

4.1、注意事项

  • 自定义异常通常会继承自 Exception 或者 RuntimeException
  • 继承自 Exception 的异常默认是受查异常
  • 继承自 RuntimeException 的异常默认是非受查异常

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

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

相关文章

web靶场搭建之帝国cms7.5

目录 一、漏洞描述 二、漏洞环境 三、环境搭建 四、漏洞复现 后台getshell(CVE-2018-18086) 漏洞原理&#xff1a; 漏洞复现&#xff1a; 源码审计&#xff1a; 代码注入 (CVE-2018-19462) 漏洞原理&#xff1a; 漏洞复现&#xff1a; 源码审计&#xff1a; 后台X…

Innodb存储引擎-表(逻辑存储结构、行记录格式、数据页结构)

文章目录表索引组织表InnoDB逻辑存储结构表空间(tablespace)段(segment)区(extent)页(page)行(record)行记录格式compactRedundant行溢出数据Compressed 和 Dynamic 行记录格式CHAR的行结构存储数据页结构File Header & Page Header & File TrailerInfimum 和 Supremum…

springboot《1》

map的映射 package com.kob.backend.controller.pk;import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;import java.util.HashMap; import java.util.Map;RestControllerRequestMapping("…

国产软件的「硬替代」与「软着陆」之辨

作者 | 曾响铃 文 | 响铃说 疫情倒逼、政策驱动、市场化博弈、国际形势拉锯等等一系列的因素正在综合影响国产软件的走势。在国内&#xff0c;国产软件替代化进程持续加速&#xff0c;国产软件正迎来逆势增长的“窗口期”。 与此同时&#xff0c;从中央到地方&#xff0c;政…

项目中pom.xml文件变灰且中间有横杠改怎么解决?

目录 问题描述: 问题解决&#xff1a; &#x1f49f; 创作不易&#xff0c;不妨点赞&#x1f49a;评论❤️收藏&#x1f499;一下 问题描述: 项目中的部分pom.xml文件是没问题的&#xff0c;部分pom.xml文件没有被识别&#xff0c;变灰且有横杠。 原因&#xff1a;操作的时…

力扣(LeetCode)163. 缺失的区间(C++)

模拟 将 lowerlowerlower 和 upperupperupper 加入数组&#xff0c;避免边界判断。 一次遍历&#xff0c;相邻元素差 111 &#xff0c;无缺失&#xff1b;相邻元素差 222 &#xff0c;缺失中间的一个数&#xff1b;相邻元素相差大于 222 &#xff0c;缺失中间一段数。根据格式…

Less知识点整理

Less 官方文档 在线Less编译工具 Less主要语法 1、变量xxx 2、嵌套 规则嵌套和冒泡 规则&#xff08;例如 media 或 supports&#xff09;可以与选择器以相同的方式进行嵌套。 规则会被放在前面&#xff0c;同一规则集中的其它元素的相对顺序保持不变。这叫做冒泡&#xff08…

计算机毕设Python+Vue新能源汽车销售管理系统(程序+LW+部署)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Oracle21C:Windows版本的安装、卸载、环境变量配置、避坑指南|ORA-12514|为什么安装目录没有bin目录

前言 1、适读对象 安装Oracle21C过程中&#xff0c;被各种报错反复折磨&#xff0c;终不得其法&#xff0c;难以正常使用Oracle。 2、各种工具下载 1&#xff09;Oracle下载地址&#xff1a;官网下载页面 2&#xff09;navecat下载地址&#xff1a;navecate 2&#xff09;sqlp…

基于JAVAWEB的湛江海鲜批发选购系统

开发工具(eclipse/idea/vscode等)&#xff1a; 数据库(sqlite/mysql/sqlserver等)&#xff1a; 功能模块(请用文字描述&#xff0c;至少200字)&#xff1a;

1W字文:什么是 回表查询、索引覆盖、最左匹配原则?聚集索引、非聚集索引的区别?

文章很长&#xff0c;而且持续更新&#xff0c;建议收藏起来&#xff0c;慢慢读&#xff01;疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 &#xff1a; 免费赠送 :《尼恩Java面试宝典》 持续更新 史上最全 面试必备 2000页 面试必备 大厂必备 涨薪必备 免费赠送 经典…

超市进销存系统的设计与实现

开发工具(eclipse/idea/vscode等)&#xff1a; 数据库(sqlite/mysql/sqlserver等)&#xff1a; 功能模块(请用文字描述&#xff0c;至少200字)&#xff1a; 该小型超市进销存系统主要针对模块管理&#xff0c;用户管理&#xff0c;进货管理&#xff0c;销售管理&#xff0c; 库…

HTTP 协议详解

目录 前言 1、HTTP 介绍 2、URL介绍 1&#xff09;了解 URL 和 URI 2&#xff09;URL 格式 3&#xff09;URL encode 3、HTTP 协议格式 1&#xff09;请求报文格式 2&#xff09;响应报文格式 3&#xff09;协议格式总结 4、HTTP 请求&#xff08;Request&#xff0…

JavaEE【Spring】:SpringBoot 热部署

文章目录一、添加框架二、Settings 开启项目自动编译三、开启运行中热部署1、低版本配置&#xff08;idea 2021.2 之前的版本&#xff09;2、高版本设置&#xff08;idea 2021.2 之后的版本&#xff09;四、使用 Debug 启动&#xff08;非Run&#xff09;一、添加框架 增加 sp…

玩以太坊链上项目的必备技能(事件-Solidity之旅十二)

事件&#xff08;Events&#xff09; Solidity 中的事件&#xff08;Events&#xff09;与任何一种编程语言中事件&#xff08;Events&#xff09;一样。 一个事件&#xff08;Events&#xff09;是一个合约可继承的成员&#xff0c;它在触发&#xff08;emit&#xff09;时存…

机器学习之分类-决策树随机森林

决策树 原理&#xff08;信息论&#xff09; 信息熵 信息&#xff1a;消除随机不定性的东西信息熵公式&#xff0c;单位bit H(X)−∑i1n(P(xi)log⁡bP(xi))H(X)-\sum^{n}_{i1}(P(x_i)\log_{b}P(x_i)) H(X)−i1∑n​(P(xi​)logb​P(xi​)) 信息增益 特征A对训练数据集D的信息…

Arthas常用命令和实践分享

一、序言 通常&#xff0c;本地开发环境无法访问生产环境。在实际工作中&#xff0c;我们会经常遇到在客户现场出现各种奇怪的问题&#xff0c;想要查看具体某个具体的对象&#xff0c;可能要打镜像&#xff0c;还要走银行内部的流程&#xff0c;整体很耗费时间&#xff0c;而…

【Python机器学习】Mean Shift、Kmeans聚类算法在图像分割中实战(附源码和数据集)

需要源码和数据集请点赞关注收藏后评论区留言私信~~~ Mean Shift算法是根据样本点分布密度进行迭代的聚类算法&#xff0c;它可以发现在空间中聚集的样本簇。簇中心是样本点密度最大的地方。 Mean Shift算法寻找一个簇的过程是先随机选择一个点作为初始簇中心&#xff0c;然后…

容器网络访问

网络分类 docker网络解决方案基于openstack平台&#xff0c;后演化为两派&#xff1a;一个是docker原生的CNM&#xff08;Container Network Model&#xff09;&#xff0c;另一个是兼容性更好的CNI&#xff08;Container Network Interface&#xff09; 单主机网络&#xff1…

【博学谷学习记录】超强总结,用心分享|架构师-容器编排 Kubernetes简介

文章目录一、k8s简介二、核心概念2.1 节点2.1.1 Master 节点2.1.2 Node2.2 Pod2.3 Replica Set2.4 Service2.5 Namespace一、k8s简介 k8s是市场上最好的容器编排工具之一。 Kubernetes 是一个开源项目&#xff0c;用于统一管理容器化的应用集群。 Kubernetes 负责在大规模服务…