从浅入深理解JAVA异常

从浅入深理解JAVA异常

  • 一、什么是异常以及异常的分类
  • 二、异常的分类
    • 1、常见的系统错误
    • 2、常见的编译时异常
    • 3、常见的运行时异常
  • 三、创建异常
    • 1、创建JAVA中已经存在的异常 -- throw关键字
    • 1.1 语法
    • 1.2 使用
    • 2、自定义异常
      • (1)区分你要创建哪种异常
      • (2)创建一个异常类,并继承对应的父类
  • 四、异常的处理
    • 1、非受查异常
      • 1.1 处理
      • 1.2 抛出
    • 2、受查异常

一、什么是异常以及异常的分类

异常其实就是错误的意思,那么在编程中,我们会遇到哪些错误呢?

最常见的错误当然是语法错误,在我们写代码的时候,可能会因为粗心导致出现一个语法错误,比如关键字写错了,变量没有初始化等等。那么此时,编辑器就会在这一行代码的下面画出红色的波浪线,来提示我们,这里出现了一个语法错误。如果我们不修改错误语法的话,我们的代码是无法进行编译的,因为编译器不认识你写的代码。这种编译时出现的错误,我们叫做“编译时异常”,也叫做“受查异常”。就是说,我会在你写代码的时候,检查出这种错误,并且让你修改。

那么还有一种错误,这种错误在语法上是通过的,但是当跑起来以后,在某些情况下却出现了错误。比如说我们定义了一个函数,这个函数是实现两个数字相除。然后我们调用这个方法,在这个方法的形参中,我们传入两个数字。单看这两个步骤并没有什么问题,但是,如果我们在传入的时候,传入一个0作为除数。此时在逻辑上就发生了错误,但是语法上并没有错误。因为0也是数字,当然可以作为参数。

但这种错误,只有我们执行代码的时候,真正将传入的参数带入函数运算的情况下,我们才会发现这里出现了一个除数为0的错误。这种错误就叫做“运行时异常”,同时,因为你的语法正确,所以你可以通过编译,也就是说编辑器并没有查出你的错误,所以这种异常也叫做“非受查异常”。

这是常见的两种异常,而出现这两种异常的时候,我们可以在代码上做一些处理,使得程序依旧能完成其原有的任务。比如,出现编译时异常的时候,我们就可以修改一下代码,然后使其通过编译,并且使程序完成其原有的任务。出现运行时异常的时候,按照之前的逻辑,我们可以加一个if语句,特判掉你出现错误的特殊情况,以除数为0距离,我们可以特判,如果除数为0,那就返回一个非常大的数字,而不做除法运算。这样就能保证,我们做完这个操作后,该程序依旧能完成“计算两数”相除这个任务。

像上述这种,我们能够通过代码上的处理,使得程序依旧能完成原定任务的错误,统称为异常

但是还有另外一种,就是你原定的任务本身就不可能实现出来,比如你想实现一个无限递归的功能,那么很明显,必定导致内存中的堆栈溢出。又或者你想开一段非常非常大的空间,此时也必定会导致内存不够的问题。这种问题,你并不能通过对代码的修改而使其顺利完成原定任务。这种问题称为系统错误,系统错误在Java中也是一个非受查异常

当然,有人会说,可以呀,我不让他无限递归不就好了。但是此时你已经改变了你原定的任务或者需求,你原本就是想实现一个无限递归的需求,可是你现在更改了这个需求。而刚刚提到的两数相除的需求,我们仅仅是加了一个特判,但这个程序依旧能满足我们计算除法结果的这一需求。

二、异常的分类

下图为JAVA中异常类的继承情况:
在这里插入图片描述
Throwable是一个超类,所有的异常或者错误都要继承这个超类,只有继承了这个超类,Java虚拟机(JVM)才会将这个类当作异常或错误类。从而进行后续的抛出、捕获等操作。

Error就是刚刚提到的系统错误,Exception就是刚刚提到的两种异常的父类,在它的子类中可以划分为两类,一类是RuntimeException,另一类就是除了RuntimeException之外的子类。前者就是运行时异常,后者就是编译时异常。

那么常见的异常都有哪些呢?

1、常见的系统错误

在Java中,Error是指在应用程序中不太可能处理或恢复的严重问题。这些问题通常是由于系统错误、资源耗尽或虚拟机出现问题等原因而引起的。以下是Java中常见的Error类型:

  1. OutOfMemoryError:当虚拟机无法分配足够的内存空间时,会抛出此错误。例如,当程序尝试创建大量对象或递归调用太深时,可能导致内存溢出。

  2. StackOverflowError:当方法调用的堆栈溢出时,会抛出此错误。这通常是由于无限递归调用或方法嵌套过深导致的。

  3. NoClassDefFoundError:当虚拟机无法找到某个类的定义时,会抛出此错误。可能是由于类文件未正确加载或类文件路径配置错误等原因导致的。

  4. AssertionError:当断言语句失败时,会抛出此错误。断言通常用于在代码中验证预期结果,如果断言失败,则说明代码逻辑有误。

  5. LinkageError:当类库或模块之间的链接失败时,会抛出此错误。可能是由于类库版本不兼容或类文件依赖关系错误导致的。

  6. VirtualMachineError:当Java虚拟机出现内部错误时,会抛出此错误。这些错误通常是由于虚拟机运行时出现问题导致的,例如栈溢出、垃圾回收失败等。

2、常见的编译时异常

Java中常见的编译时异常包括:

  1. FileNotFoundException:文件未找到异常,当尝试打开一个不存在的文件时抛出。
  2. IOException:输入输出异常,当发生输入输出操作错误时抛出。
  3. ClassNotFoundException:类未找到异常,当尝试加载不存在的类时抛出。
  4. SQLException:SQL异常,当操作数据库发生错误时抛出。
  5. InterruptedException:线程中断异常,当线程被中断时抛出。
  6. NoSuchMethodException:方法未找到异常,当尝试调用不存在的方法时抛出。
  7. NoSuchFieldException:字段未找到异常,当尝试访问不存在的字段时抛出。
  8. IllegalAccessException:非法访问异常,当通过反射访问私有方法或字段时抛出。
  9. IllegalArgumentException:非法参数异常,当传入的参数不符合方法要求时抛出。
  10. ArrayIndexOutOfBoundsException:数组索引越界异常,当访问数组超出索引范围时抛出。

3、常见的运行时异常

在Java中,常见的运行时异常包括:

  1. NullPointerException(空指针异常):当一个对象引用为null,但在代码中尝试调用该对象的方法或访问其属性时,就会抛出该异常。
  2. ArrayIndexOutOfBoundsException(数组越界异常):当尝试访问数组中不存在的索引位置时,就会抛出该异常。
  3. ClassCastException(类转换异常):当尝试将一个对象强制转换为不兼容的类型时,就会抛出该异常。
  4. IllegalArgumentException(非法参数异常):当方法接收到一个非法或不合适的参数时,就会抛出该异常。
  5. ArithmeticException(算术异常):当在运算中出现了算术错误,比如除以0,就会抛出该异常。
  6. IndexOutOfBoundsException(索引越界异常):当使用一个不存在的索引操作容器类(如List或字符串)时,就会抛出该异常。
  7. UnsupportedOperationException(不支持的操作异常):当尝试执行一个不支持的操作时,就会抛出该异常。
  8. NullPointerException(空指针异常):一般是因为调用的对象为空,即null。
  9. NumberFormatException(数字格式异常):当字符串无法转换为数字时,就会抛出该异常。
  10. ConcurrentModificationException(并发修改异常):当在迭代容器时,发生了并发修改操作,比如在使用Iterator遍历时,又对容器进行了增删操作,就会抛出该异常。

三、创建异常

1、创建JAVA中已经存在的异常 – throw关键字

在讲解之前我们先看下面的代码。

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

如果我们运行上述的代码,我们会发现下面这个结果。
在这里插入图片描述
这个结果就是告诉我们,这段代码中出现了一个异常,这个异常的类型是:ArithmeticException。那么我是怎么知道的呢?这就需要我们来学习一下如何看终端中打印的红色信息。

那么这个终端中红色字到底记录了哪些信息呢?
在这里插入图片描述

那这个异常是哪里来的呢?
其实就是我们计算a/b的时候,java内部创建出来的。

那么我们能不能在计算a/b之前就创建出这个异常呢?
答案是可以的,这里要用到的就是throw关键字。

1.1 语法

throw new 异常类名(参数);

这里的参数要特殊解释一下,不同的异常规定的构造方法的参数也不一样。常见的参数有:无参数、字符串等。如果是无参数,那就是直接创建一个异常出来,如果是字符串做为参数,那么就是将详细的错误信息传进去。

1.2 使用

比如:

public static void main(String[] args) {
    int a = 10, b = 0;
    if(b == 0){
        throw new ArithmeticException("除数不能为0");
    }
    System.out.println(a / b);
}

此时运行代码,再次观察终端信息。
在这里插入图片描述
此时显示的就是我们自己创建的异常,同时还将我们传入的错误信息打印了出来。

2、自定义异常

在日常的开发过程中,我们会发现某些特殊情况下的异常,这些异常是Java库中不存在的,此时就需要我们自己来定义了。那么如何定义呢?主要分为两步,首先你要区分你想定义刚刚所说的哪种异常(系统错误、编译时异常、运行时异常)。

(1)区分你要创建哪种异常

常见的为后两者,如果你这个错误非常严重,不处理不行,那么这种情况下,你可以创建编译时异常,为什么呢?

因为刚刚说了,编译时异常会在你写代码的时候,就用红色波浪线标识出来,提示你进行处理。所以这种情况下,就很适合那些非常严重的异常。此时你在自定义异常的时候,你的自定义异常类就要继承一个编译时异常作为父类,常见的比如说继承Exception,有人会说,不对呀,运行时异常(RuntimeException)也继承了这个类啊。是的,但这个是一个特殊情况。

如果你的自定义异常继承了Exception,那么就默认你的自定义异常是一个编译时异常。

如果说,你想定义一个运行时异常,那么你的自定义异常就要继承一个运行时异常作为父类,比如你直接继承RuntimeException。

如果你想定义的是一个系统错误,那么你就继承Error。这里要注意一个地方,如果你的异常已经到了Error的级别,说明你的异常原因是与JVM相关的,此时你即使进行处理了,作用也不大。

(2)创建一个异常类,并继承对应的父类

直接看例子:
比如我们创建一个编译时异常:

public class IndexException extends Exception {
    public IndexException(){
        super();
    };
    public IndexException(String str){
        super(str);
    }
}

我们也可以创建一个运行时异常:

public class IndexException extends RuntimeException {
    public IndexException(){
        super();
    };
    public IndexException(String str){
        super(str);
    }
}

同理也可以创建一个系统错误。

public class IndexException extends Error {
    public IndexException(){
        super();
    };
    public IndexException(String str){
        super(str);
    }
}

四、异常的处理

异常的处理大体上主要分为两部分,第一种就是自己处理,第二种就是交给调用者处理,即将异常抛出去。第一种需要使用,try-catch语句,第二种需要用到throws关键字。

通过刚刚的介绍,我们知道异常的分类可以分为两大类,第一类:非受查异常(Error,RuntimeException),第二类:受查异常(Exception)。

1、非受查异常

1.1 处理

这里要用到的是try-catch语句,语法如下:

try{
	//可能会出错的代码
}catch(异常类 异常类名){
	//处理方式
}finally{
	//一般用来释放资源
}

如果不是必须的,finally部分可以不写。

当然有的时候,你try中可能产生的异常不止一个,此时你需要catch多种可能的异常,那么你可以写多个catch。如下:

try{
	//可能会出错的代码
}catch(异常类1 异常类名1){
	//处理方式
}catch(异常类2 异常类名2){
	//处理方式
}catch(异常类3 异常类名3){
	//处理方式
}finally{
	//一般用来释放资源
}

这里要注意的是,这些异常最终只会被成功捕获一个,因为你的try语句中,只要抛出一个异常后,就会直接跳转到对应的catch语句中进行捕获,所以try中后续代码是不会再执行了,那么在这种情况下,try中后续代码既然不执行了,自然就不会再抛出其他异常。

那么,到底是哪个catch来捕获抛出的这个异常呢?此时会从第一个catch开始往下遍历,如果说这个catch中的异常类型是所抛出异常的类或者是其父类,那么就会又该catch进行捕获。

这里就会出现另外一个注意事项。catch的顺序问题,如果说你的第一个catch中的异常类是Exception。这个类是所有编译时异常和运行时异常的父类,所以说,不管你抛出哪个异常,只要这个异常是Exception的子类,都会被这个catch所捕获,此时你后面的catch就没有用了。在这种情况下,我们就要把这种父类异常尽量往后写,前面的catch优先写细分的子类异常。

比如下面的例子:

public class Exception01 {
    public static void main(String[] args) {
        System.out.println(cal(1, 0));
        return;
    }
    public static int cal(int a, int b) {
        int res = 0;
        try{
            res = a / b;
        }catch (ArithmeticException e){
            res = Integer.MAX_VALUE;
        }
        return res;
    }
}

当出现异常的时候,我让这个返回值的值为正无穷。

此时,我们运行结果:
在这里插入图片描述
它就没有打印错误信息,而是按照我们的方式去处理的这个异常。

当然我们也可以打印异常信息,此时需要调用这个方法e.printStackTrace()

public class Exception01 {
    public static void main(String[] args) {
        System.out.println(cal(1, 0));
        return;
    }
    public static int cal(int a, int b) {
        int res = 0;
        try{
            res = a / b;
        }catch (ArithmeticException e){
            e.printStackTrace();
        }
        return res;
    }
}

在这里插入图片描述
我们会发现,打印完异常以后,并没有终止程序,而是继续执行剩下代码,因为最后打印了res的初始值0。这是因为,如果异常我们不手动处理的话,JVM会帮我们处理,比如之前的代码,我们没有try-catch,所以最终是JVM处理的。而JVM的处理方式非常果断,它会打印出异常信息,然后终止程序。

1.2 抛出

当我们遇到一个异常的时候,按照刚刚的说法,我们要写一个try-catch语句来处理该异常,但是如果我们不想处理呢?比如刚刚的例子,我们写了一个实现计算两数相除的函数,你传入了一个0进来,导致出现了异常,那既然是你导致的,能不能由调用者处理呢?答案是可以的。

此时,对于非受查异常而言,我们可以不管。

public class Exception01 {
    public static void main(String[] args) {
        System.out.println(cal(1, 0));
        return;
    }
    public static int cal(int a, int b) {
        int res = 0;
        res = a / b;
        return res;
    }
}

此时,最终就会交给JVM处理。所以我们运行结果后,并没有打印结果,因为JVM直接终止程序了。
在这里插入图片描述

但这种不太负责任,因为相当于我们没有告诉调用者,可能会出现哪些异常,因此,我们引入了一个新的关键字:throws关键字,这个关键字后面可以罗列出该函数可能出现的异常。不同的异常之间用,隔开。

如下:

public class Exception01 {
    public static void main(String[] args) {
        System.out.println(cal(1, 0));
        return;
    }
    public static int cal(int a, int b) throws ArithmeticException{
        int res = 0;
        res = a / b;
        return res;
    }
}

这样写完后,就相当于,我的cal函数没有处理这个异常,我选择交给调用者来处理,但是我告诉你了,我可能会产生哪些异常,处理或者不处理是你的事情。如果都不处理,那最后只能是由JVM处理了。

2、受查异常

受查异常的处理也是分为两种,要么try-catch,要么抛出去。

但是对于非受查异常而言,你写不写throws都可以,但是受查异常要么用try-catch,要么必须写throws声明要抛出的异常。

如果你什么都不写,编译会出错。
在这里插入图片描述
所以你有两种办法:
第一种:

public static int cal(int a, int b) {
    int res = 0;
    if(b == 0){
        try {
            throw new Exception();
        } catch (Exception e) {
            //处理办法
        }
    }
    res = a / b;
    return res;
}

第二种:

public static int cal(int a, int b)throws Exception {
    int res = 0;
    if(b == 0){
        throw new Exception();
    }
    res = a / b;
    return res;
}

这里有一个特殊情况,只能用第一种处理,就是如果你在重写某个方法,既然是重写,你就不能改函数签名,而你写throws就是在更改函数签名,所以如果该方法原本的函数签名中,没有声明抛出这个异常。你也不能在后面声明抛出。只能try-catch处理。

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

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

相关文章

volatile 关键字

我们先看这段代码 class MyCounter {public int flag 0; }public class ThreadDemo15 {public static void main(String[] args) {MyCounter myCounter new MyCounter();Thread t1 new Thread(() -> {while (myCounter.flag 0) {// 这个循环体咱们就空着}System.out.pr…

平板设备IP地址设置指南

在数字化时代,平板电脑作为便携且功能强大的设备,广泛应用于日常生活和工作中。为了确保平板能够正常接入网络并与其他设备进行通信,正确设置IP地址是至关重要的。虎观小二将为您介绍如何设置平板的IP地址,帮助您轻松完成网络配置…

JavaScript ECMAScript标准的与时俱进:从ES6至ES14的革新之路与关键技术特性剖析

ECMAScript(通常缩写为ES)是一种标准化的脚本语言规范,由ECMA International(前身为European Computer Manufacturers Association,欧洲计算机制造商协会)制定。自1997年发布首个版本以来,ECMAS…

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记13:RTC实时时钟

系列文章目录 嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记01:赛事介绍与硬件平台 嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记02:开发环境安装 嵌入式|蓝桥杯STM32G431(…

PAN1026蓝牙收发芯片

1 概述 PAN1026 系列产品是一款低成本、高集成度的无线 BLE 数据收发芯片,工作在射频 2400MHz ~2483MHz 的通用 ISM 频段。具有较低的系统应用成本,只需要一个 MCU 和少量外部无 源组件即可构建满足无线应用的系统。同时,操作方式非…

七大免费SSL证书获取方法

为了保护用户数据的安全,越来越多的网站开始使用SSL证书。SSL证书是一种数字证书,它能够为网站提供一个安全的加密连接,从而保护用户的敏感信息。然而,购买SSL证书需要一定的费用,这对于一些小型企业或个人来说可能是一…

C++ | Leetcode C++题解之第23题合并K个升序链表

题目: 题解: class Solution {// 21. 合并两个有序链表ListNode *mergeTwoLists(ListNode *list1, ListNode *list2) {auto dummy new ListNode(); // 用哨兵节点简化代码逻辑auto cur dummy; // cur 指向新链表的末尾while (list1 && list2) {if (list1…

test4121

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起学习和分享Linux、C、C、Python、Matlab,机器人运动控制、多机器人协作,智能优化算法,滤波估计、多传感器信息融合,机器学习,人工智能等相关领域的知识和…

强化学习基础概念入门

文章目录 1. 什么是强化学习?2. 强化学习的基本元素3. 相关衍生元素3.1 策略(Policy)3.2 状态转移(State Transition)3.3 回报(Return)3.4 价值函数(Value Function) 4. 算法分类4.1 按环境是否已知划分4.2 按学习方式划分4.3 按学习目标划分 参考资料 1. 什么是强化…

本地开发nginx代理服务器(2024-04-10)

1、nginx 解释 nginx 是一个高性能的HTTP和反向代理服务器,同时也是一个IMAP/POP3/SMTP 代理服务器。 在性能上,Nginx占用很少的系统资源,能支持更多的并发连接,达到更高的访问效率; 在功能上,Nginx是优…

QT:QMainWindow、ui界面、资源文件的添加、信号和槽

1.练习:使用手动连接,将登录框中的取消按钮使用qt4版本的连接到自定义的槽函数中,在自定义的槽函数中调用关闭函数 #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(…

OJ 古籍翻译 【进制转化】【C】

这里有两个考察点,一个是进制转换,还有一个是以字符串输出 知识点: sprintf(参数一,参数二,参数三) 将参数三按参数二的格式转换后存入参数一中 sprintf与printf用法基本一样,只是sprintf是将值打印到指…

SketchBook2014 下载地址及安装教程

SketchBook是一款专业级别的绘图与绘画软件。它提供了丰富的绘画工具和创意功能,适用于艺术家、插画师、设计师和数字艺术爱好者。SketchBook具有直观的用户界面,简单而强大的绘制工具,能够帮助用户在数字平台上创造出精美的艺术作品。 Sket…

哪些工作不会被AI替代:人类能力地形图

这一轮AI,到底对人有多强的替代性?这一轮AI的可靠性,是之前任何一代所谓的人工智能都不能比的。 在国内,AI开始被用来筛选简历,而在国外,亚马逊正在用算法跟踪仓库工人的生产率,顺道还会给生产率…

橘子学JDK之JMH-02(BenchmarkModes)

一、案例二代码 这次我们来搞一下官网文档的第二个案例,我删除了一些没用的注释,然后对代码做了一下注释的翻译,可以看一下意思。 package com.levi;import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.runner.Runner; import …

RocketMQ之Topic和Tag最佳实践

一、Topic是什么?它的作用? Topic即主题,是消息队列中用于对消息进行分类和组织的一种机制,它有以下三种作用: 标识消息分类:RocketMQ的主题用于对消息进行分类和组织。通过为不同类型的消息分配不同的主题…

LeetCode-Java:6.Z字形变换

文章目录 题目解① 找规律 题目 将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。 比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下: P A H N A P L S I I G Y I R之后&a…

2024Mathorcup(妈妈杯)数学建模C题python代码+数据教学

2024Mathorcup数学建模挑战赛(妈妈杯)C题保姆级分析完整思路代码数据教学 C题题目:物流网络分拣中心货量预测及人员排班 因为一些不可抗力,下面仅展示部分代码(很少部分部分)和部分分析过程,其…

[python] Numpy库用法(持续更新)

先导入一下 import numpy as np 一、np.random用法 生成随机整数:np.random.randint(low, high, size) low: 最小值high: 最大值size: 生成的数组大小(可以是多维,下面同理) 生成随机浮点数:np.random.uniform(low, …

下线圈和包扎

电机槽内放好 所有槽纸 槽内再放入引槽纸 作用是放线圈的时候避免划伤线 开始放线圈 绑了白色扎带的朝外面 线圈的一边放进去后,另一边还悬在外面 ,这里俗称 吊边。 为了保护吊边 ,在吊边处放一张大的绝缘纸 (如下图&#xff0…