Java中throw、throws和Throwable的联系与区别

2023-11-02

throw,意为“投掷、抛、扔”。Throw、Throws和Throwable三者都用于异常处理。

1. Throwable

Throwable在Java中是异常处理这个分支的顶级父类,其它所有异常处理的实现都依赖于Throwable

打开Java官方文档(Java8版本),找到Throwable,它的直接子类为ErrorException

image-20211212145310021

Error和Exception两者的特点在于Error异常程序无法处理,只能交由人工介入修改代码,比如栈溢出、堆溢出等等;而Exception异常可以提前发觉,并作出有效处理。

1.1 扩展-Error

在Error中,常见的有栈溢出和堆溢出等等。

image-20211212151054845

举个例子,StackOverflowError

public class ErrorTest {
    public static void main(String[] args) {
        main(args);
    }
}

无限递归,执行这个程序就会报栈溢出异常。

image-20211212151352974

再比如堆异常,OutOfMemoryError

public class ErrorTest {
    public static void main(String[] args) {
        Integer[] testArray = new Integer[1024*1024*1024];
    }
}

image-20211212151542228

1.2 扩展-Exception

在Exception中就有非常多我们所熟知的异常情况了,比如NullPointerException(空指针异常)、ArrayIndexOutOfBoundsException(数组下标越界)、NumberFormatException(数字格式化异常)等等。

public class ExceptionTest {
    public static void main(String[] args) {
        int[] testArray = null;
        System.out.println(testArray[0]);  //空指针异常
    }
}
public class ExceptionTest {
    public static void main(String[] args) {
        int[] testArray = new int[3];
        System.out.println(testArray[3]);   //数组下标越界
    }
}
public class ExceptionTest {
    public static void main(String[] args) {
        String num = "abc";
        System.out.println(Integer.parseInt(num));    //数字格式化异常
    }
}

2. throws

throws应用在方法声明处,指明此方法在执行时可能会出现的异常类型。一旦该方法执行时出现异常,就会在异常代码处生成一个异常类的对象,此对象满足Throws后的异常类型时,就会被抛出。(这里有两个过程,代码有异常时,1.生成一个异常对象;2. throws捕获到这个异常,将异常对象抛出

throws和try-catch-finally一起称为异常处理的两种方式。

try-catch-finally是在出现异常时主动处理掉异常,使得程序可以继续执行下去;而throws捕获到异常之后向上抛出异常对象,不去真正地处理这个异常。

所谓向上抛出异常对象,是将异常对象交给调用者去处理,比如方法A调用方法B,B通过throws抛出异常,而A可以选择使用try-catch-finally处理掉异常,也可以通过throws继续向上抛出异常对象,直到异常被真正处理掉。如果一直没有方法去处理异常,异常对象最终会被抛给JVM,从而导致程序停止运行。

@Test
public void throwsTest(){   //调用者解决抛出的异常
    try{
        formatChange("abc");
    }
    catch (NumberFormatException e){
        System.out.println("转换格式错误!");
    }
    catch (Exception e){
        System.out.println("出现错误");
    }
}

private int formatChange(String str) throws NumberFormatException{    //出现异常向上抛出
    return Integer.parseInt(str);
}
2.1 扩展-如何选择try-catch-finally还是throws?

当一个方法中存在异常需要处理,在大多数情况下,既可以选择try-catch-finally直接处理掉这个异常,也可以选择throws向上抛出异常,交给调用者去处理(异常抛到最后,总要有一方真正地去处理这个异常,怎么处理?还是用try-catch-finally呗),在选择上比较自由,但是,出现以下两种情况时,需要遵循一定的规则(如有补充,敬请指出)。

  • 如果父类中被重写的方法没有使用throws抛出异常,则子类重写的方法也不能使用throws抛出异常,也就意味着这种情况必须使用try-catch-finally去处理。
  • 在方法A中,先后调用了另外的几种方法,这几种方法是递进关系执行的且其中很多方法都存在异常需要处理,这种情况建议被调用的几个方法使用throws向上抛出异常,在方法A中,使用try-catch-finally统一处理掉这些异常。

针对第一条,这是一个规定,子类中重写的方法使用throws抛出的异常必须不大于父类中被重写的方法抛出异常的范围。举个例子,父类中的方法B抛出NullPointerException异常,则子类中重写B方法就不能抛出如Exception这种比NullPointerException范围更大的异常;如果父类中被重写的方法没有抛出任何异常,则子类更不能抛出异常。

为什么?展示一段代码。

//假设父类中的方法B抛出NullPointerException异常,子类中的方法B可以抛出Exception

private void test(ParentClassTest parent){
    try{
        parent.B();
    }
    catch(NullPointerException e){
        System.out.println("出现了空指针异常");
    }
}

在本示例中,假设父类中的方法B抛出NullPointerException异常,子类中重写的方法B可以抛出Exception。那么传进给test方法的参数如果是父类的实例化对象,那么调用test方法没有任何问题。如果传进的参数是子类的实例化对象,再去调用子类重写的方法B,那么就有可能抛出Exception异常,try-catch结构就压不住这个异常了,这显然是一个不合理的操作。

针对第二条,假设方法A中调用了方法C、D、E,这三个方法都有可能产生异常,且存在递进关系,也就是D、E执行需要C执行完成、E执行依赖C、D执行完成。那么就推荐在C、D、E中向上抛出异常,在方法A中集中处理。为什么?如果C、D、E都是向上抛出异常,而A使用try-catch-finally去处理这个异常,如果某个方法真的出现异常,则不再继续执行。而如果C、D、E都使用try-catch-finally直接解决掉异常,那么即使产生了异常,方法A也不会接收到异常的产生,那么还会接着往下执行,但是C出现了异常,再执行D、E没有任何意义。

3. throw

如果在程序编写时有手动抛出异常的需求,则可以使用throw

throw使用在方法体内。与try-catch-finally和throws都不同,异常处理的两个阶段:1.遇到异常,生成异常对象;2.捕获到异常,进行抛出或处理。try-catch-finally和throws都处在第二个阶段,都是捕获到异常后的相关处理,一般使用系统根据异常类型自动生成的异常对象进行处理。而throw应用在第一阶段,手动地产生一个异常对象。

举一个例子,判断一个数值是否为非负数,如果为负数,则抛出异常。

class ThrowTest{

    private int Number;

    public void judge(int num){
        if(num>=0){
            this.Number = num;
        }
        else{
            throw new RuntimeException("传入参数为负数");
        }
    }
}
@Test
public void test2(){
    ThrowTest throwTest = new ThrowTest();
    throwTest.judge(-100);
}

成功抛出异常。

image-20211213001848290

使用try-catch捕获一下异常。

@Test
public void test2(){
    ThrowTest throwTest = new ThrowTest();
    try{
        throwTest.judge(-100);
    }
    catch (RuntimeException e){
        System.out.println(e.getMessage());
    }
}

image-20211213002119033

如果把throw抛出的异常改为Exception,则直接报错,也就是不能编译。Exception包含两种异常:编译时异常和运行时异常,前者在编译前就要检查是否有可能产生编译时异常;后者是在编译后运行时才会判断的异常。而throw new Exception包含了编译时异常,需要显式处理掉这个异常,怎么处理?try-catch-finally或者throws

image-20211213002417836

class ThrowTest{

    private int Number;

    public void judge(int num) throws Exception{
        if(num>=0){
            this.Number = num;
        }
        else{
            throw new Exception("传入参数为负数");
        }
    }
}

调用方也要随着进行更改。

@Test
public void test2(){
    ThrowTest throwTest = new ThrowTest();
    try{
        throwTest.judge(-100);
    }
    catch (RuntimeException e){
        System.out.println(e.getMessage());
    }
    catch (Exception e){
        System.out.println(e.getMessage());
    }
}

image-20211213002930580

3.1 扩展-自定义异常类

throw还可以抛出自定义异常类。

自定义异常类的声明需要继承于现有的异常体系。

class MyException extends RuntimeException{
    static final long serialVersionUID = -703489719076939L;   //可以认为是一种标识

    public MyException(){}

    public MyException(String message){
        super(message);
    }
}

此时我们可以抛出自定义的异常

class ThrowTest{

    private int Number;

    public void judge(int num) throws MyException{
        if(num>=0){
            this.Number = num;
        }
        else{
            throw new MyException("不能输入负数");
        }
    }
}

调用者修改

@Test
public void test2(){
    ThrowTest throwTest = new ThrowTest();
    try{
        throwTest.judge(-100);
    }
    catch (MyException e){
        System.out.println(e.getMessage());
    }
}

image-20211213004438453

4. 总结

三者共同点在于都属于是异常处理的范畴内。

不同点:

  • Throwable是异常处理这个分支的顶层父类,其它异常类的实现都需要继承于Throwable

  • throw应用在方法体内,是生成异常对象的一种方式

  • throws应用在方法声明处,声明出可能要抛出的各种异常类,是处理异常的方式

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java中throw、throws和Throwable的联系与区别 的相关文章

随机推荐

  • 如何设计一个数据库

    前言 我们知道 软件工程是为了解决软件危机的 它是采用工程的概念 原理 技术和方法来开发与维护软件 把经过时间考验而证明正确的管理技术和当前能够得到的最好的技术方法结合起来 在软件开发的过程中 数据库设计是非常重要的 它需要根据需求分析设抽
  • Android合并音频文件

    java view plain copy 需求 将两个amr格式音频文件合并为1个 注意 amr格式的头文件为6个字节的长度 param partsPaths 各部分路径 param unitedFilePath 合并后路径 public
  • cuda nms

    int YoloLayerPlugin nms fun int batch size void inputs void const outputs size t count int detections per im float nms t
  • word如何弄成两竖列_怎么在word中把字变成竖行

    1 如何将word文档中的字变成竖行 一 全部文字改成竖排显示的方法 1 启动Word 新建一篇文档 在文档中输入内容 2 单击 文件 选择 页面设置 3 打开 页面设置 对话框 点击 文档网格 在文字排列中选择 垂直 项 然后点击 确定
  • 关于uniapp的多列选择器

    uniapp的picker组件 它的值如果是个对象数组的话怎么办 三级联动为例
  • 根据字典绘图

    import matplotlib pyplot as plt dictionary 71 35 55 30 61 48 84 7 56 39 51 14 47 9 74 30 77 20 75 25 72 24 79 20 73 41 7
  • 前端兼容问题

    开发过程中遇到的兼容问题 1 ios浏览器new Date 报错 ios浏览器的new Date 方法无法识别 2021 04 26 或2021 04 26这种格式的日期 需转换为2021 04 26 本来以为到此就为止了 结果测试发现了一
  • Linux解决“No space left on device“问题

    维护一个后台运行的程序时 突然报了 No space left on device 的错 因为程序需要一直向磁盘写入图片文件 因此第一反应怀疑是不是图片太多导致磁盘空间不足 但事实并不是这样 通过命令df h查看磁盘占用率发现用量并没有满
  • Nginx access日志配置

    Nginx access日志配置 access log日志配置 access log用来定义日志级别 日志位置 语法如下 日志级别 debug gt info gt notice gt warn gt error gt crit gt al
  • vscode 中,vue导入组件路径提示

    VsCode中 Vue导入组件路径提示 当用Vue组件开发时 经常会引入文件 但又没提示该如何解决 注意 编写vue项目时需要从根目录打开 否则会导致很多插件不能用 注意 设置完成展示 打开VSCode 第一步 点击扩展 第二步 搜索框输入
  • 接口日志记录

    1 添加配置 保证日志记录信息类能执行
  • Bitcoin的Segwit地址

    Segwit地址又称隔离见证地址 在Bitcoin Blockchain上 经常可以看到类似bc1qmy63mjadtw8nhzl69ukdepwzsyvv4yex5qlmkd这样的以bc开头的地址 这种地址就是隔离见证地址 Segwit地
  • 汽车年检记录

    车子已经两年了 需要年检 早就听说年检挺麻烦的 要花不少时间 4S店也可以代为年检 不过第一次年检 我想自己体验一下 于是决定还是自己去年检 下面是年检的过程 备忘一下 另外我是去 浙江省杭州市市区浙江省杭州市西溪路529号 第一检测站进行
  • java并发编程(荣耀典藏版)

    大家好 我是月夜枫 聊一聊java中的并发编程 面试工作中也许都会用到 参考了很大博主的博客 整理了很久的文章 虽然还没有全部整理完 后续慢慢更新吧 并发编程 一 线程的基础概念 一 基础概念 1 1 进程与线程A 什么是进程 进程是指运行
  • 嵌入式LINUX环境搭建 - 写给刚入行的童学

    转自百问网论坛 www 100ask org 1 虚拟机里面的Linux无法上网 注意四个地方即可 第3点特别重要 很多同学的Linux不能上网就是因为这里 一定要选对网卡 千万别选自动 因为它还不够智能 如果你的物理机在用网线进行上网 那
  • C# 结构体(学习心得 16)

    结构体 是 值类型 数据结构 使单一变量可以存储各种数据类型的相关数据 struct 关键字用于创建结构体 结构体用来代表一个记录 超级小白友好 讲解C 基础 每集5分钟轻松学习 拒绝从入门到放弃 一 定义结构体 声明方式 struct B
  • 新知同享

    谷歌致力于通过高效 可靠的方法 构建 AI 驱动的产品 如今已经走过了七年 AI 为先 的旅程 一起来看 2023 Google 开发者大会上 AI 开发如何被广泛应用 简化开发 并将机器学习的强大能力 引入到应用和工作流中 提高开发者工作
  • 离线安装docker,docker安装MySQL,Redis,ES,Kibana,mongoDB,RocketMQ

    目录 安装docker docker compose 离线安装docker docker compose 安装ElasticSearch 安装kibana 下载ik分词器 安装MySQL 设置MySQL主从 启动单机版MySQL可以不做这一
  • 搬砖时遇到的错误

    Unreachable code 我把下面两个写反了位置 下图是正确写法
  • Java中throw、throws和Throwable的联系与区别

    throw 意为 投掷 抛 扔 Throw Throws和Throwable三者都用于异常处理 1 Throwable Throwable在Java中是异常处理这个分支的顶级父类 其它所有异常处理的实现都依赖于Throwable 打开Jav