异常
- “异常”,也许不清楚该如何处理,但知道不应该置之不理,要停下来,看看是不是有别人或在别的地方,能够处理这个问题。
Java异常机制¶
- 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.AutoCloseable
或java.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