Think in Java 异常类(第12章) 读书笔记

2023-10-30

概念:

首先看一下Java中的异常体系:

Error:一般是指与虚拟机相关的问题,如 OOM,ThreadDeath。

RuntimeExprion:NullPointerException,ClassCastException ,IllegalArgumentException  ,ArrayStoreException ,IndexOutOfBoundsException  ,NumberFormatException,SecurityException......。

非RuntimeExprion:ClassNotFoundException、CloneNotSupportedException、FileNotFoundException、InterruptedException、IOException、SQLException、TimeoutException、UnknownHostException......。


重抛异常:

}catch(Exception e){
    System.out.println("e was thrown");
    throw e;
}

重抛异常会把异常抛给上一级的异常处理程序,同一个 try 块中的 catch 子句将被忽略。异常的所有信息都得意保存。

重抛当前异常,printStackTrace() 打印出的是原来异常抛出点的调用栈信息,而并非重抛点的异常调用栈信息。如果想要更新为重抛点的信息,可以调用 fillInStackTrace() 把当前异常调用栈信息填入原来的异常调用栈信息,注意 fillInStackTrace() 返回Throwable类型,并且是个耗时的操作,如果不关心异常的堆栈信息,可以通过重写fillInStackTrace()方法来实现。代码如下:

package pers.ltx.deep.ExceptionTest.ExceptionChain;

public class Demo1 {
    public static void main(String[] args) {
        try {
            new Demo1().g();
        }catch (Exception e){
            e.printStackTrace();
        }    
    }
    
    public void f() throws Exception1 {
        throw new Exception1();
    }
    
    public void g() throws Exception {
        try {
            f();
        }catch (Exception1 e1){
            throw e1;
            //throw (Exception1)e1.fillInStackTrace();
        }
    }
}

class Exception1 extends Exception{
   /* 
   @Override
    public synchronized Throwable fillInStackTrace() {
        return this;
    }
    */
}

class Exception2 extends Exception{
}

输出:

加上 fillInStackTrace() 则变为:

  public void g() throws Exception {
        try {
            f();
        }catch (Exception1 e1){
            //throw e1;
            throw (Exception1)e1.fillInStackTrace();
        }
    }

输出:

 


异常链:

异常链:在捕获一个异常后需要重新抛出一个新的异常,同时保留原始的异常信息,这称为异常链。

           注意:Throwable子类中,只有三种基本的异常类提供了带 cause 参数的构造器 Error、Exception、RuntimeException,其余情况只能靠initCause()方法。源码如下:

 

 

如果直接在 g() 中抛出一个新的异常 Exception2,会造成之前异常信息的丢失,只会输出新抛出的异常的调用栈信息:

package pers.ltx.deep.ExceptionTest.ExceptionChain;

public class Demo1 {
    public static void main(String[] args) {
        try {
            new Demo1().g();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    
    public void f() throws Exception1 {
        throw new Exception1();
    }
    
    public void g() throws Exception {
        try {
            f();
        }catch (Exception1 e1){
            throw new Exception2();
            //throw (Exception2)new Exception2().initCause(e1);
        }
    }
}

class Exception1 extends Exception{
}

class Exception2 extends Exception{
}

输出:

如果想打印出整条异常链则可以这样操作:

public void g() throws Exception {
    try {
        f();
    }catch (Exception1 e1){
         throw (Exception2)new Exception2().initCause(e1);//利用 initCause()方法
         //throw (Exception2)new Exception(e1);//或者利用构造器
    }
}

输出:

 

我们不应该把异常直接暴露给用户,对用户可以对异常进行封装然后弹出。对异常封装后再抛出,再通过异常链传递,可以使系统更健壮。


finally:

finally 子句总能被执行。如果 try 块中有 return ,则会先把返回值保存在临时变量中,接着执行 finally ,finally 中代码怎样执行都不会影响到返回值。但是如果 finally 中也有 return ,则会忽略 try catch 块中的return ,并且忽略其中的异常直接返回,也就是说会吞食掉异常

比如下面代码会返回 2 :

public int test(){
    try {
        throw new Exception();
    }catch (Exception e){
        return 1;
    }finally {
        return 2;
    }
}

比如下面代码实际上并不会抛出异常,并且返回 2 :

public int test() throws Exception{
    try {
        throw new Exception();
    }finally {
        return 2;
    }
}

finally 可以用于关闭资源:

InputStream in = null;

try {
    in = new BufferedInputStream(new FileInputStream(""));
} catch (FileNotFoundException e) {
    e.printStackTrace();
}finally {
    if (in!=null){
        try {
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

不过这代码写起来也酸爽了吧,没关系此时可以用 try-with-resource 语法糖。

try(InputStream in = new BufferedInputStream(new FileInputStream(""))) {
    //.....
}catch (IOException e){
    e.printStackTrace();
}

反编译看一下:

可以发现,编译器自动帮我们生成了finally块,并且在里面调用了资源的close方法。

使用 try-with-resource,必须实现 AutoClosable 接口,并且重写 close 方法。比如这样:

class Connection implements AutoCloseable{
    
    public void sendData(){
        System.out.println("Connection");
    }
    @Override
    public void close() throws Exception {
        System.out.println("close");
    }
}

关于 try-with-resource,这里不再详细解释了,有兴趣的可以自己学习一下。


缺憾——异常丢失:

Java 的异常也有瑕疵,对于程序中的异常,绝不应该去忽视,但在 Java 中还是有可能被轻易的忽视掉。比如上文 finally  中的异常吞食,再如下面代码:

public class ExceptionMissed {
    void fun1() throws Exception1 {
        throw new Exception1("fun1");
    }
    
    void fun2() throws Exception2 {
        throw new Exception2("fun2");
    }
    
    public static void main(String[] args) {
        try {
            ExceptionMissed em = new ExceptionMissed();
            try {
                em.fun1();
            }finally {
                em.fun2();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

输出:

可以发现 Exception1 不见了。


异常的限制:

1.派生类构造器必须包含基类构造器中的异常说明,派生类构造器抛出的异常可以是基类构造器所抛出异常的父类。异常限制对构造器不起作用。

2.Constructor call must be the first statement in a constructor。所以,构造器中针对 super() 的异常只能异常声明,而不能捕获。

3.派生类重写的基类方法,抛出的异常数量、种类可以比父类少,但异常的范围必须比基类方法的异常声明范围小

异常说明不属于方法类型的一部分,不能根据异常说明进行重载。此外,一个出现在基类中的异常不一定出现在派生类中,即派生类中非构造方法的异常声明一定不大于基类中非构造方法的异常声明。这点与继承规则完全不同。在继承中,派生类的方法可以比基类的方法多。


构造器中异常:

如果在构造器内发生了异常,清理行为也许不能正常工作,所以在编写构造器时需要格外小心。也许你认为加一个 finally 就可以解决问题,但是如果对象没有被成功的创建,这时是不需要进行清理的,然而用 finally 无论怎样都会执行清理过程。

  InputStream in = null;
    
    public InputFile(){
        try {
            in = new BufferedInputStream(new FileInputStream(""));
        }catch (FileNotFoundException e){
            //发生 FileNotFoundException 异常时,
            // in 并没有完成创建,所以不需要进行 close。
        }catch (Exception e){
            //进入此语句,说明 in 已经完成创建,
            //此时需要 close
            try {
                in.close();
            } catch (IOException ex) {
                System.out.println("in.close() successful");
            }
        }finally {
            //此处不需要进行 close
        }
    }

基本规则:在创建失败时不需要进行清理,在创建成功后立即进入 try 语块保证能正确进行清理。


其他注意点:

Java 无谓地发明了“检查异常”,这还只是一次尝试,目前为止并没有别的语言采用这种方式。

“检查异常”的好处很明显,要求针对该异常必须捕获或者在当前方法上进行异常声明,起到了规范效果。

 把“检查异常”转为“非检查异常”:

try {
    throw new Exception();
}catch (Exception e){
    throw (RuntimeException)new RuntimeException().initCause(e);
}

一些异常常见误区,可以点击这里


异常使用指南:

1. 在恰当的级别处理问题。(在知道该如何处理的情况下才捕获异常)。

2. 解决问题,并重新产生异常。

3. 进行少许修补,然后绕过异常发生的地方继续执行。

4. 用别的数据进行计算,代替方法预计会返回的值。

5. 把当前运行环境下的能做的事情尽量做完,然后把相同的异常重抛到更高层。

6. 把当前运行环境下的能做的事情尽量做完,然后把不同的异常抛到更高层。

7. 终止程序

8. 简化异常。

9. 让类库和程序更安全。


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

Think in Java 异常类(第12章) 读书笔记 的相关文章

随机推荐

  • 关于修改ubuntu20.04分辨率的问题

    默认情况下 setting下的dsiplay是无法修改分辨率的 下面开始设置 第一步 添加分辨率 用于后面的配置文件参数修改 cvt 1920 1080 第二步 查看已经支持的分辨率 xrandr 第三步 修改配置文件 输入 sudo ge
  • 解决Python:'gbk' codec can't decode byte 0xae in position 199: illegal multibyte sequence

    我在读取txt文件中报了一个 gbk codec can t decode byte 0xae in position 199 illegal multibyte sequence 解决方法 加上一个 encoding ISO 8859 1
  • AI指数报告:让我们从18个分立的视角来看AI

    AI指数报告 让我们从18个分立的视角来看AI 最近 斯坦福 人工智能百年 AI100 专家小组 非盈利性项目AI Index 发起了一项AI指数报告 追踪学术界 产业界 开源软件和公共兴趣范畴的18个分立的视角评估人工智能活跃度 盘点计算
  • DVWA stored 初级

    这里name被限制长路了 是10 不影响 在message输入 在name随便输入 弹窗1 过
  • 【数模】拟合算法

    拟合算法的介绍 拟合和插值问题的对比 回顾 数模 插值算法 不同 插值算法 得到的多项式f x 要经过所有样本点 但若样本点太多 则该多项式次数过高 就会造成龙格现象 拟合问题 不用曲线一定经过给定的点 尽管分段可避免龙格现象 但多数情况更
  • python实现验证码识别

    前言 大家在做自动化的过程中 应该遇到过登录 需要输入验证码的场景 一般的话 解决方案就是 需要后台的开发同学提供万能验证码 这样每次都麻烦开发也不是很好 所以 还是自己搞一下把 一 图片识别 验证码识别 选择使用ddddocr这个开源库
  • Python爬虫:抓取多级页面数据

    前面讲解的爬虫案例都是单级页面数据抓取 但有些时候 只抓取一个单级页面是无法完成数据提取的 本节讲解如何使用爬虫抓取多级页面的数据 在爬虫的过程中 多级页面抓取是经常遇见的 下面以抓取二级页面为例 对每级页面的作用进行说明 一级页面提供了获
  • java 反射将字符串转换为对应的类型

    做反射的时候可能会有这种需求 给定一个字符串和一个类型 将字符串转换为指定的类型 public class TypeUtils public static Object stringToNullableTarget String strin
  • 妙用Update Select

    最常用的update语法是 UPDATE
  • 计算机键盘快速指南,如何练习盲打(快速学会键盘盲打技巧)

    如何练习盲打 快速学会键盘盲打技巧 在诸如记者招待会之类的信息发布场面里 大家会注意到 有很多人耳目关注着会场声响动静的同时 不用看自己的笔记本电脑键盘 双手就能在电脑键盘上快速地击键打字 把会议的内容现场实况记录下来 通过十指在键盘上流畅
  • uniPush2.0云函数

    首先 按照下面链接 把预备工作做完 基本可以实现dcloud后台网页推送 uniPush2 0 消息推送 没有白天的CXY的博客 CSDN博客 第二步 走完本流程后 会遇到各种坑 一个一个来踩 第一 客户端推送 由于uniapp文档紊乱 跳
  • Android, 关于ViewModel的一些使用小技巧。

    情景 app使用了navigation组件 用一个Activity装载了多个Fragment 通过Fragment间的跳转来实现页面间的切换 当 界面有A fragment 跳转到 B fragment时 会将部分参数字段传递给Fragme
  • mysql基于SSM的学生社团管理系统 毕业设计源码211531

    SSM学生社团管理系统设计与实现 摘 要 21世纪时信息化的时代 几乎任何一个行业都离不开计算机 将计算机运用于社团管理也是十分常见的 过去使用手工的管理方式对学生社团进行管理 造成了管理繁琐 难以维护等问题 如今使用计算机对社团的各项基本
  • flex中dispatchEvent的用法(自定义事件)

    Evevt和EventDispatcher类在 as3的 事件机制中是很重要的角色 dispatchEvent 是EventDispatcher类的一个事件发送方法 它可以发送出Event类或其子类的实例 在as3中所有的显示 对象都可以发
  • MySql数据库修改表字段date类型为datetime类型

    语法 ALTER TABLE 表 MODIFY COLUMN 字段 字段类型 DEFAULT 默认值 COMMENT 说明 例如 alter table t user modify column createTime datetime de
  • 【正点原子FPGA连载】 第三十二章基于lwip的TCP服务器性能测试实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南

    第三十二章基于lwip的TCP服务器性能测试实验 上一章的lwip Echo Server实验让我们对lwip有一个基本的了解 而Echo Server是基于TCP协议的 TCP协议是为了在不可靠的互联网络上提供可靠的端到端字节流而专门设计
  • 游戏服务器开发需要学习的技术

    一 游戏服务器编程语言的选择 所谓的游戏服务器编程语言其实有很多 基本上任何一种语言都可以作为游戏服务器的编程语言 这需要根据自己游戏的类型和要求加以选择 比如C Java Erlang go等等 目前我用过的只有C 和Java 但是以Ja
  • Android Native APP开发笔记:双击返回键退出应用&单击返回键返回桌面

    文章目录 目的 基础说明 双击返回键退出应用 单击返回键回到桌面 总结 目的 双击返回键退出应用 很早之前就是Android中非常常见的一种功能 而目前好多安卓应用为了应用常驻都改为使用 单击返回键返回桌面 这篇文章将对相关内容做个简单记录
  • SQL热身测试

    创建数据库 CREATE DATABASE test 切换数据库 USE test 在test数据库中创建一个student的表格 CREATE TABLE student Id INT 10 PTIMARY KEY NOT NULL AU
  • Think in Java 异常类(第12章) 读书笔记

    概念 首先看一下Java中的异常体系 Error 一般是指与虚拟机相关的问题 如 OOM ThreadDeath RuntimeExprion NullPointerException ClassCastException IllegalA