【软件构造】第七章第二节 错误与异常处理

第七章第二节 错误与异常处理

本节关注:Java中错误和异常处理的典 型技术——把原理落实到代码上!

Outline:

  • Java中的错误和异常(java.lang.throwable)
  • 异常
    • Runtime异常与其他异常(Exception)
    • Checked异常和unchecked异常
  • checked异常的处理机制
  • 自定义异常

Notes:

## Java中的错误和异常

【Throwable】

  • Java.lang.throwable

    • Throwable 类是 Java 语言中所有错误或异常的超类。
    • 继承的类:extends Object。
    • 实现的接口:implements Serializable。
    • 直接已知子类:Error, Exception(直接已知子类:IOException、RuntimeException)。

【Error】

  • Error类描述很少发生的Java运行时系统内部的系统错误和资源耗尽情况(例如,VirtualMachineError,LinkageError)。
  • 对于内部错误:程序员通常无能为力,一旦发生,想办法让程序优雅的结束
  • Error的类型:
    • 用户输入错误

      • 例如:用户要求连接到语法错误的URL,网络层会投诉。
    • 设备错误
      • 硬件并不总是做你想做的。
      • 输出器被关闭
    • 物理限制
      • 磁盘可以填满
      • 可能耗尽了可用内存
  • 典型错误:

## 异常(Exception)

  • 异常:程序执行中的非正常事件,程序无法再按预想的流程执行。
  • 异常处理:
    • 将错误信息传递给上层调用者,并报告“案发现场”的信息。
    • return之外的第二种退出途径:若找不到异常处理程序,整个系统完全退出

【异常按结构层次的分类】

  • 运行时异常:由程序员处理不当造成,如空指针、数组越界、类型转换
  • 其他异常:程序员无法完全控制的外在问题所导致的,通常为IOE异常,即找不到文件路径等

【异常按处理机制角度的分类】

  • 为什么区分checked 和 unchecked:原因其实很简单,编译器将检查你是否为所有的已检查异常提供了异常处理机制,比如说我们使用Class.forName()来查找给定的字符串的class对象的时候,如果没有为这个方法提供异常处理,编译是无法通过的。
  • Checked exception:
    • 编译器可帮助检查你的程序是否已抛出或处理了可能的异常
    • 异常的向上抛出机制进行处理,如果子类可能产生A异常,那么在父类中也必须throws A异常。可能导致的问题:代码效率低,耦合度过高。
    • checked exception是需要强制catch的异常,你在调用这个方法的时候,你如果不catch这个异常,那么编译器就会报错,比如说我们读写文件的时候会catch IOException,执行数据库操作会有SQLException等。
    • 对checked Exception处理机制    

      • 抛出:声明是throws,抛出时throw    
      • 捕获(try/catch):try出现异常,忽略后面代码直接进入catch;无异常不进入catch;若catch中没有匹配的异常处理,程序退出;若子类重写了父类方法,父类方法没有抛出异常,子类应自己处理全部异常而不再传播;子类从父类继承的方法不能增加或更改异常
      • 处理:不能代替简单的测试,尽量苛刻、不过分细化、将正常处理与异常处理分开、利用好层次结构、早抛出晚捕获、避免不必要的检查
      • 清理现场、释放资源(finally):finally中语句不论有无异常都执行

import java.io.*;
public class className
{
  public void deposit(double amount) throws RemoteException
  {
    // Method implementation
    throw new RemoteException();
  }
  //Remainder of class definition
}
  • unchecked exception:

    • 程序猿对此不做任何事情,不得不重写你的代码(不需要在编译时使用try-catch等机制处理)
    • 这类异常都是RuntimeException的子类,它们不能通过client code来试图解决
    • 这种异常不是必须需要catch的,你是无法预料的,比如说你在调用一个 list.szie()的时候,如果这个list为null,那么就会报NUllPointerException,而这个异常就是 RuntimeException,也就是UnChecked Exception
    • 常见的unchecked exception:JVM抛出,如空指针、数组越界、数据格式、不合法的参数、不合法的状态、找不到类等

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

Exception in thread "main"  java.lang.NullPointerException

 【checked和unchecked总结】

  • 当要决定是采用checked exception还是Unchecked exception的时候,问一个问题: “如果这种异常一旦抛出,client会做 怎样的补救?”

    • 如果客户端可以通过其他的方法恢复异常,那么采用checked exception;
    • 如果客户端对出现的这种异常无能为力,那么采用unchecked exception;
    • 异常出现的时候,要做一些试图恢复它的动作而不要仅仅的打印它的信息。
  • 尽量使用unchecked exception来处理编程错误:因为uncheckedexception不用使客户端代码显式的处理它们,它们自己会在出现的地方挂起程序并打印出异常信息。
  • 如果client端对某种异常无能为力,可以把它转变为一个unchecked exception,程序被挂起并返回客户端异常信息

 – Checked exception应该让客户端从中得到丰富的信息。

– 要想让代码更加易读,倾向于用unchecked exception来处理程序中的错误

## checked异常的处理机制

 【异常中的LSP原则】

  • 如果子类型中override了父类型中的函数,那么子类型中方法抛出的异常不能比父类型抛出的异常类型更广泛
  • 子类型方法可以抛出更具体的异常,也可以不抛出任何异常
  • 如果父类型的方法未抛出异常,那么子类型的方法也不能抛出异常。
  • 其他的参考第五章第二节的LSP

【利用throws进行声明】

  • 使用throws声明异常:此时需要告知你的client需要处理这些异常,如果client没有handler来处理被抛出的checked exception,程序就终止执行。
  • 程序员必须在方法的spec中明确写清本方法会抛出的所有checked exception,以便于调用该方法的client加以处理
  • 在使用throws时,方法要在定义和spec中明确声明所抛出的全部checked exception,没有抛出checked异常,编译出错,Unchecked异常和Error可以不用处理。

【利用throw抛出一个异常】

  • 步骤:

    • 找到一个能表达错误的Exception类/或者构造一个新的Exception类
    • 构造Exception类的实例,将错误信息写入
    • 抛出它
  • 一旦抛出异常,方法不会再将控制权返回给调用它的client,因此也无需考虑返回错误代码

 【try-catch语句】

  • 使用 try 和 catch 关键字可以捕获异常。try/catch 代码块放在异常可能发生的地方。
  • try/catch代码块中的代码称为保护代码,
  • Catch 语句包含要捕获异常类型的声明。当保护代码块中发生一个异常时,try 后面的 catch 块就会被检查。
  • 如果发生的异常包含在 catch 块中,异常会被传递到该 catch 块,这和传递一个参数到方法是一样。

【finally语句】

  • 场景:当异常抛出时,方法中正常执行的代码被终止;但如果异常发生前曾申请过某些资源,那么异常发生后这些资源要被恰当的清理,所以需要用finally语句。
  • finally 关键字用来创建在 try 代码块后面执行的代码块。
  • 无论是否发生异常,finally 代码块中的代码总会被执行。
  • 在 finally 代码块中,可以运行清理类型等收尾善后性质的语句。
  • finally 代码块出现在 catch 代码块最后:
  • 注意下面事项:
    • catch 不能独立于 try 存在。
    • 在 try/catch 后面添加 finally 块并非强制性要求的。
    • try 代码后不能既没 catch 块也没 finally 块。
    • try, catch, finally 块之间不能添加任何代码。
 1 public class ExcepTest{
 2   public static void main(String args[]){
 3     int a[] = new int[2];
 4     try{
 5        System.out.println("Access element three :" + a[3]);
 6     }catch(ArrayIndexOutOfBoundsException e){
 7        System.out.println("Exception thrown  :" + e);
 8     }
 9     finally{
10        a[0] = 6;
11        System.out.println("First element value: " +a[0]);
12        System.out.println("The finally statement is executed");
13     }
14   }
15 }

## 自定义异常

  • 如果JDK提供的exception类无法充分描述你的程序发生的错误,可以创建自己的异常类。

    • 如果希望写一个检查性异常类,则需要继承 Exception 类。
    • 如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。

抛出检查型异常:

抛出unchecked exception:

原文地址:https://www.cnblogs.com/hithongming/p/9195084.html

时间: 2024-10-13 00:15:59

【软件构造】第七章第二节 错误与异常处理的相关文章

软件构造 第七章第二节 错误与异常处理

第七章第二节 错误与异常处理 内部错误:程序员通常无能为力,一旦发生,想办法让程序优雅的结束 异常:你自己程序代码发生的,可以捕获处理 [Error] Error类描述很少发生的Java运行时系统内部的系统错误和资源耗尽情况(例如,VirtualMachineError,LinkageError). 对于内部错误:程序员通常无能为力,一旦发生,想办法让程序优雅的结束 Error的类型: 用户输入错误 例如:用户要求连接到语法错误的URL,网络层会投诉. 设备错误 硬件并不总是做你想做的. 输出器

软件构造 第三章第二节 软件规约

第三章第二节 软件spec 客户端无需阅读调用函数的代码,只需理解spec即可. 精确的规约,有助于区分责任,给"供需双方"确定了责任,在调用的时候双方都要遵守. @param @return @throws 例子: Behavioral equivalence (行为等价性) 根据规约判断是否行为等价 与实现无关! 如果两个函数符合这个规约,故它们等价. Specification Structure 前置条件(precondition):对客户端的约束,在使用方法时必须满足的条件.

软件构造 第七章第四节 调试

第七章第四节 调试 [bug的常见类型] 数学bug:例如 零除法,算术溢出 逻辑bug:例如 无线循环和无限递归 源头bug:例如 使用了为被定义的变量.资源泄漏,其中有限的系统资源如内存或文件句柄通过重复分配耗尽而不释放.缓冲区溢出,其中程序试图将数据存储在分配存储的末尾. 团队工程bug:例如 评论过时或者评论错误.文件与实际产品的区别 ## 调试的基本过程 Debug是测试的后续步骤:测试发现问题,debug消除问题:当防御式编程和测试都无法挡住bug时,我们就必须进行debug了: D

软件构造 第七章第三节 断言和防御性编程

第七章第三节 断言和防御性编程 断言:在开发阶段的代码中嵌入,检验某些"假设"是否成立.若成立,表明程序运行正常,否则表明存在错误. 可用于检查: 内部不变量: 表示不变量: 控制流不变量 方法的前置条件 方法的后置条件 断言主要用于开发阶段,避免引入和帮助发现bug 实际运行阶段, 不再使用断言 软件发布阶段,禁用断言避免影响性能. 断言?Correctness 错误/异常处理?Robustness Defensive Programming 对来自外部的数据源要仔细检查,例如:文件

软件构造 第五章第一节 可复用性的度量、形态和外部观察

第五章第一节  可复用性的度量.形态和外部观察 面向复用编程(programming for reuse):开发出可复用的软件 基于复用编程(programming with reuse):利用已有的可复用软件搭建应用系统 代码复用的类型: 白盒复用:源代码可见,可修改和扩展 含义:复制已有代码到正在开发的系统,进行修改 优点:可订制化程度高 缺点:对其修改增加了软件的复杂度,且需要对其内部充分的了解 黑盒服用:源代码不可见,不能修改 含义:只能通过过API接口来使用,无法修改代码 优点:清晰.

【软件构造】第二章第二节 软件构造的过程、系统和工具

第二章第二节 软件构造的过程.系统和工具 Outline 广义的软件构造过程 编程 静态代码分析 动态代码分析 调试与测试 重构 狭义的软件构造过程 构造系统:经典BUILD场景 构造系统的组件 构造过程和构造描述 Java编译工具 子目标和结构变体 构造工具 Notes ## 广义的软件构造过程 [编程(Coding)] 开发语言:如Java.C.Python 使用IDE(集成开发工具)的优势(组成) 方便编写代码和管理文件(有代码编辑器,代码重构工具.文件和库(Library)管理工具) 能

【软件构造】第七章第一节 健壮性和正确性的区别

第七章第一节  健壮性和正确性的区别 第七章:进入软件构造最关键的质量特性 --健壮性和正确性. 本节在1-2节的基础上,重申了Robustness and Correctness的重要性,澄清了二者之 间的差异,并指明了在软件构造中处理二 者的典型技术(防御式编程.异常处理. 测试.调试等) Outline 健壮性(Robustness)和正确性(correctness) 如何测量健壮性和正确性 Notes ## 健壮性(Robustness)和正确性(correctness) [健壮性] 定

【软件构造】第三章第二节 设计规约

第三章第二节 软件规约 这一节我们转向关注"方法/函数/操作"是如何定义的,即讨论编程中的动词,规约. Outline 一个完整的方法 什么是设计规约,我们为什么需要他 行为等价性 规约的结构:前置条件与后置条件 规约的结构 可变方法的规约 规约的评价 规约的确定性 规约的陈述性 规约的强度 如何设计一个好的规约 是否使用前置条件 Notes ## 一个完整的方法 一个完整的方法包括规约spec和实现体implementation: "方法"是程序的积木,它可以被独

软件构造 第一章第二节 软件开发的质量属性

?软件构造 第一章第二节 软件开发的质量属性 1.软件系统质量指标 External quality factors affect users 外部质量因素影响用户 Internal quality factors affect the software itself and its developers 内部质量因素影响软件本身和它的开发者 External quality results from internal quality 外部质量取决于内部质量 外部属性: 正确性:按照预先定义的"