zoukankan      html  css  js  c++  java
  • Java 异常类与捕获异常

    禁止码迷,布布扣,豌豆代理,码农教程,爱码网等第三方爬虫网站爬取!

    异常引发

    我们肯定见过堆栈轨迹,也就是程序执行过程中某个特定点上挂起的方法调用列表。当程序因为未捕获的异常而终止时,就会显示堆栈轨迹。异常会因为程序本身或者错误的操作等情况引发,这会导致操作无法完成或者数据丢失等不良后果。为了避免异常带来的不良后果,程序应该在异常发生时保证:

    1. 向用户通知错误;
    2. 让用户返回到一种安全状态,让用户执行其他的命令;
    3. 允许用户保存工作结果,以妥善的方式终止程序。

    异常处理的任务就是将控制权从产生异常的地方,转移到能够处理异常的异常处理器。异常处理通常需要考虑:

    1. 用户输入错误,当需要用户进行输入时,用户可能不会按照程序的要求来输入;
    2. 设备错误,程序调用的硬件出现问题;
    3. 物理限制,例如程序所需的存储空间耗尽;
    4. 代码错误,程序有的方法编写错误,引发异常。

    传统的方式是返回一个特殊的错误码,方法根据错误码进行分析。或者是返回 null 引用,但无论是空引用和错误码,这些都是程序编写中可能会使用的值。因此在很多情况下,方法并不返回任何值,而是抛出一个封装了错误信息的对象。

    异常类

    异常对象都派生自 Throwable 类的一个类实例,所有的异常都是由 Throwable 继承而来,Throwable 类被分解为 Error 和 Exception 2 个类。Error 类层次结构描述了 Java 运行时系统的内部错误和资源耗尽错误,这类错误抛出的情况很少,而且很多时候都无能为力。
    Exception 层次结构又分为 2 个分支,一个分支派生于 RuntimeRxception,这类异常包括了编程错误导致的异常。另一个分支包含其他异常,但是程序本身是没有问题的,例如数组范围越界就是个 RuntimeRxception 异常,而打开一个不存在的文件就属于其他异常。可以说,如果出现 RuntimeRxception 异常,那一定是程序员的错。

    派生于 Error 类和 RuntimeException 类的所有异常为非检查型异常,其他所有异常成为检查型异常。

    声明异常

    当程序遇到异常时,方法需要抛出一个异常,方法需要告诉编译器可能会返回什么样的异常。声明异常之后的方法发生异常时,方法将抛出一个异常类对象,同时系统就会开始搜索处理异常对象的异常处理器
    可以使用 throws 关键字进行异常规范声明,展示这个方法可能抛出的异常。

    public Image loadImage(String str) throws IOExcpetion
    

    如果一个方法可能抛出多种异常,则需要在方法的首部列出所有的异常类,异常类之间用逗号隔开。

    public Image loadImage(String str) throws FileNotFoundException,IOExcpetion
    

    Error 和 RuntimeException 的非检查型异常都不需要被声明,因为这是程序员一开始就需要避免的事情。因此一个方法需要声明所有可能抛出的检查型异常,而不是非检查型。

    抛出异常

    如果一个类的一个方法声明会抛出一个异常,而这个异常是某个特定类的实例,则这个方法抛出的异常可能属于这个类,也可能属于这个类的任意一个子类。一旦异常被抛出,这个方法就不会返回到调用者。声明异常之后应该如何显式地抛出异常?使用 throw 关键字抛出异常对象,异常对象同样需要 new。

    throw new ExceptionName()
    

    异常类中可能有自带一个字符串参数的构造器,通过这个构造器可以给出异常的描述信息。例如 IllegalArgumentException 异常可以通过它的构造器附带说明信息:

    throw new IllegalArgumentException("这是个不合法的参数异常");
    

    创建异常类

    程序可能遇到某种特殊的异常,这种异常用现有的异常类可能很难描述,此时可以自定义一个异常类。自定义的异常类派生于 Excpetion 类或者 Excpetion 类的某个子类,自定义的类应该包括默认构造器和包含详细描述信息的构造器。例如:

    class FileFormatException extends IOException
    {
          public FileFormatException() {}
          public FileFormatException(String message) {
                super(message);
          }
    }
    

    自定义异常类之后,就可以在方法中声明异常并在方法中抛出。

    捕获异常

    光把异常抛出还不够,如果没有在任何地方捕获异常,程序就会终止并在控制台打印异常信息。

    try/catch 语句块

    使用 try 和 catch 关键字可以捕获异常,try/catch 语句块应该被放置在可能产生异常的地方,使用 try/catch 的语法如下:

    try{
       code
    }
    catch(ExceptionName e){
       code
    }
    

    如果 try 语句中的任何代码抛出了 catch 子句指定的一个异常类,则程序会跳过 try 语句块的剩余代码,转去执行 catch 子句中的处理器代码。如果 try 语句块的代码没有抛出任何异常,则 catch 子句的代码会被自动跳过。如果方法中任何代码抛出了 catch 子句中没有生命的一个异常类型,这个方法就会自动退出。例如:

    import java.io.*;
    
    public class ExcepTest{
     
       public static void main(String args[]){
          try{
             int a[] = new int[2];
             System.out.println("Access element three :" + a[3]);
          }
          catch(ArrayIndexOutOfBoundsException e){
             System.out.println("Exception thrown  :" + e);
          }
       }
    }
    

    捕获多个异常

    一个 try 代码块后面跟随多个 catch 代码块,通过对每个异常类型都是用一个单独的 catch 子句就可以实现异常的多重捕获

    try{
       code
    }
    catch(异常类型 1 异常的变量名 1){
      code
    }
    catch(异常类型 2 异常的变量名 2){
      code
    }
    catch(异常类型 3 异常的变量名 3){
      code
    }
    

    同一个 catch 子句可以捕获多个异常,如果 2 种异常捕获后采取的动作是相同的,可以用 “|”进行连接。

    try{
       code
    }
    catch(异常类型1 | 异常类型2 异常的变量名){
      code
    }
    

    异常链

    可以在 catch 子句中再次抛出异常,这种做法可以改变异常的类型。例如开发了一个供其他人使用的子系统,就可以用这个方式指示子系统能识别的异常类型。

    try{
       code
    }
    catch(ExceptionName e1){
       var e2 = new ExceptionName();
       throw e2;
    }
    

    捕获到这种异常时,可以使用 getCause() 方法获取原始异常,这样子系统不仅能获取异常,也不会丢失异常的原始信息。

    finally 子句

    当代码抛出异常时,方法中的剩余代码就会立即停止并退出。这种情况有时候会加重遇到的麻烦,例如方法在退出前调用了一些本地资源,则这些资源就没有办法得到清理。finally 关键字用来创建在 try 代码块后面执行的代码块,无论是否发生异常,finally 代码块中的代码总会被执行。在 finally 代码块中,可以运行清理类型等收尾善后性质的语句。语法为:

    try{
      code
    }
    catch(异常类型 异常的变量名){
      code
    }
    finally{
      code
    }
    

    try 语句可以只有 finally 子句,而忽略 catch 子句。不过 finally 子句也可能有异常,可以使用嵌套的 try 子句来捕获异常,这种方法不仅保险且功能更强。

    try{
          try{
                code
          }
          finally{
                code
          }
    }
    catch(异常类型 异常的变量名)
    {
          code
    }
    

    try-with-Resources 语句

    假设资源属于一个实现了 AutoCloseable 接口的类,Java7 之后可以使用 try-with-resources 语句来代替 finally 子句。try-with-resources 语句可以在 try 块退出是,自动调用 close() 方法关闭资源。

    try (Resources res = ……){
          work with res
    }
    

    捕获还是仅抛出?

    使用 try/catch 语句块可以在 catch 处理异常,但是是否是所有异常都值得补获?还有一种策略是什么都不做,把异常传递给方法调用者,让调用这个方法的程序员自己思考要怎么做。如果想这么搞,就必须声明方法可能抛出的异常。如果调用了一个抛出检查型异常的方法,就必须处理这个异常,或者继续传递这个异常。
    一般的经验是,捕获你知道如何处理的异常,传播你不知道怎么处理的异常。

    异常使用技巧

    1. 异常处理不能替代简单的测试,只在异常情况下使用异常就好。
    2. 不要过分细化异常,将整个任务放在一个 try 语句块就行。
    3. 充分利用异常层次结构,不要只抛出 RuntimeException 异常,应该使用更合适的异常类,不要捕获 Throwable 异常,因为这难以理解和维护。
    4. 不要压制异常,对于异常需要重视并处理,不能够随意忽视。
    5. 检测错误时,应该返回一个能够具体描述异常情况的异常。
    6. 有时候不需要捕获所有抛出的异常,传递异常也是很好的做法。

    参考资料

    菜鸟教程
    《Java 核心技术 卷Ⅰ》,[美]Cay S.Horstmann 著,林琪 苏钰涵 等译,机械工业出版社

  • 相关阅读:
    leetcode------Remove Element
    leetcode------Merge Two Sorted Lists
    [转载]Unity3D 游戏引擎之使用C#语言建立本地数据库(SQLITE)
    [转载]VS2012创建MVC3项目提示错误: 此模板尝试加载组件程序集 “NuGet.VisualStudio.Interop, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a”。
    [转载]Unity3D 访问Access数据库
    [转载]C#导入XLS数据到数据库
    [转载]Unity3D的断点调试功能
    [Unity3D]支持的视频格式
    unity 3d 获取鼠标当前坐标
    [转载]VS2012程序打包部署详解
  • 原文地址:https://www.cnblogs.com/linfangnan/p/13474515.html
Copyright © 2011-2022 走看看