Skip to content

异常

  • 异常”,也许不清楚该如何处理,但知道不应该置之不理,要停下来,看看是不是有别人或在别的地方,能够处理这个问题。

Java异常机制

  • image-20231019105850829
  • throwable包含全部的异常可错误
  • Error是严重的问题(系统级错误),普通的程序不应该捕获处理
  • 受检异常:由Exception类及其子类(但不包括RuntimeException的子类)表示的。当一个方法或构造函数可能会抛出这类异常时,必须在方法或构造函数的签名中使用throws子句来声明它。调用该方法或构造函数的代码必须处理或再次声明该异常。
  • 未检异常:也称为运行时异常,它们是RuntimeException及其子类表示的。这类异常不需要强制声明或捕获,因为它们通常代表编程错误,例如空指针或数组越界。

  • 异常链

  • 抛出一个新的异常时可以仍然保留原始异常信息
  • Throwable子类的构造器都支持传入一个原始异常Throwable(String message, Throwable cause)

    • 此外只有Error、Exception、RuntimeException提供了此种构造,其它异常类型使用initCause
    • e.initCause(cause)
  • 异常的约束

  • 重写方法时只能抛出基类版本中说明的异常。也可以选择不抛出
    • 当继承的基类与子类实现的接口冲突时以基类为准
  • 可以为构造器添加新异常,但是必须包含基类构造器的异常子类也不应该捕获基类构造器的异常

  • 构造器在编写构造器时要注意在发生异常时保证资源的正常释放

  • 并不是在finally中释放就一定可以解决,因为此时可能在创建之前就中断了(如读取文件失败就不能关闭文件)

    public static void main(String [] args) {
        try {
            InputFile in = new InputFile("Cleanup.java");
            try {
                String s;
                int i = 1;
                while((s = in.getLine()) != null)
                    ; // Perform line-by-line processing here...
            } catch(Exception e) {
                System.out.println("Caught Exception in main");
                e.printStackTrace(System.out);
            } finally {
                in.dispose();
            }
            //打开文件发生异常,不用关闭
        } catch(Exception e) {
            System.out.println(
                "InputFile construction failed");
        }
    }
    

  • try-with-resources

  • 对于一个类,比如要在生命周期持续对文件读取,则会导致每一步都可能引发异常,这很危险和麻烦
  • 一种解决办法:尽可能一次性实现文件的打开读取和关闭 ,也看了一使用文件创建流,后续的操作通过流来实现
  • 在try后面可以跟一个(),即资源说明头,
    • 用于那些实现了 java.lang.AutoCloseablejava.io.Closeable 接口的资源.比如Stream、文件读取等
    • 在代码块结束时自动释放资源
      try (BufferedReader reader = new BufferedReader(new FileReader("example.txt"))) {
          String line;
          while ((line = reader.readLine()) != null) {
              System.out.println(line);
          }
      } catch (IOException e) {//可选的异常捕获
          e.printStackTrace();
      }
      
  • 即提供了一种类似文件作用域的机制,在范围内使用文件,之后会被自动关闭

抛出异常

  • 使用new在堆上创建异常
  • 所有异常都可以接受一个String参数
  • throw new NullPointerException("t=null");
  • string可以在之后提取出来用于获取关于异常的信息

  • 输出异常跟踪信息

  • e.printStackTrace(System.out);

  • 一个方法后跟throws关键字,它表示这个方法可能会抛出指定的一个或多个异常。当调用这样的方法时,调用者要么需要使用try-catch块来捕获和处理这些异常,要么也需要在其方法签名中声明throws,从而传递异常给其上级调用者

    • 如果声明了会抛出某些异常,则编译器会强制用户对异常进行处理
    • 这种在编译器时被检查并强制实施的异常叫做检查型异常
    • 如果方法中声明throws某种异常,那么如果在执行方法时方法内某处出现了该种异常并且没有被处理,则会自动向上继续抛出(而不需要手动重新抛出)

自定义的异常类型

  • 从异常类Exception创建class SimpleException extends Exception {}
  • 带有完整构造器的版
    class SimpleException extends Exception {
        MyExtion(){}
        MyException(String msg){super(msg);}
    }
    

捕获异常

  • 处理异常

    try {
        // The guarded region: Dangerous activities
    } catch(A a1) {
        // Handler for situation A
    } finally {//无论是否抛出异常总是会被执行
        // Activities that happen every time
    }
    

  • finaly的用途

  • 无论是否发生异常,保证最终状态结果的一致性
  • 也可以用于非异常的情况,无论是使用了break、continue、return,finally的内容也总是会被使用

  • 捕捉任何异常:使用基类Exception

  • 应该放在最后

  • 获取异常的信息

  • getMessage() getLocalizedMessage()
  • printStackTrace()
    • 以数组的形式获取getStackTrace()
  • 获取包信息getName()
  • 获取类名getSimpleName()
  • 可以重写并使用tostring定制输出

  • 多重捕获

    public class SameHandler {
        void x() throws Except1, Except2, Except3, Except4 {}
        void process() {}
        void f() {
            try {
                x();
            } catch(Except1 e) {
                process();
            } catch(Except2 e) {
                process();
            } catch(Except3 e) {
                process();
            } catch(Except4 e) {
                process();
            }
        }
    }
    //使用多重捕获简化
    try {
        x();
    } catch(Except1 | Except2 | Except3 | Except4 e) {
        process();
    }
    

  • 可以用|表示or

  • 重新抛出

  • 重抛异常会把异常抛给上一级环境中的异常处理程序,同一个 try 块的后续 catch 子句将被忽略。
    catch(Exception e) {
        System.out.println("An exception was thrown");
        throw e;
    }
    
  • 要注意的是重新抛出不会对异常栈进行修改,也就是说每一层得到的栈信息是一致的,缺失传递过程
  • 使用throw e.fillInStackTrace();

  • 异常匹配

  • 异常匹配机制按照catch子句的顺序进行。当异常发生时,会查找第一个与之匹配的catch子句。一旦找到匹配的catch子句,将执行该子句中的代码,然后继续执行try-catch块后面的代码,不再检查其它的catch子句。
  • 同样,对于异常的子类也被会匹配

    class SuperException extends Exception { }
    class SubException extends SuperException { }
    class BadCatch {
      public void goodTry() {
        try { 
          throw new SubException();
        } catch (SuperException superRef) { ...
        } catch (SubException subRef) {
          ...// never be reached
        } // an INVALID catch ordering
      }
    }
    

  • RuntimeException

  • 不需要声明及手动抛出
  • 出现时会自动抛出并向上冒泡,如果没有被捕获就一直向上冒泡
  • 如果到达 main 仍然没有被处理则会导致程序终止并打印异常堆栈信息

断言

  • assert (x >= 0) : "x is " + x; 冒号后为断言不满足时输出的附加信息

  • 断言功能默认是关闭的(并不会进行检查)

  • 添加编译参数 -ea 开启断言 java