异常和断言

异常

  • 异常是一个事件,它发生在程序运行期间,干扰了正常的指令流程
  • 异常情形是指阻止当前方法或者作用域继续执行的问题。在这里一定要明确一点:异常代码某种程度的错误,尽管Java有异常处理机制,但是我们不能以“正常”的眼光来看待异常,异常处理机制的原因就是告诉你:这里可能会或者已经产生了错误,您的程序出现了不正常的情况,可能会导致程序失败!

异常继承层次

  • 所有的异常类都派生自 Throwable 类。

  • 其中Error为错误,是程序无法处理的,如OutOfMemoryError、ThreadDeath等,出现这种情况你唯一能做的就是听之任之,交由JVM来处理,不过JVM在大多数情况下会选择终止线程。

  • 而Exception是程序可以处理的异常。它又分为两种CheckedException(已检查异常)。

  • 已检查异常用于错误可以被提前预知的情况。一个常见的错误原因是输入和输出,文件可能损坏,网络连接可能会失败。

  • 一种是UncheckedException(未检查异常)。其中CheckException发生在编译阶段,必须要使用try…catch(或者throws)否则编译不通过。而UncheckedException发生在运行期,具有不确定性,主要是由于程序的逻辑问题所引起的,例如没有检查空指针异常。

try catch finally

  1. 当程序遇到异常时会终止程序的运行(即后面的代码不在执行),控制权交由异常处理机制处理。

  2. catch捕捉异常后,执行里面的函数。

  3. 不论程序是否发生异常,finally代码块总是会执行。所以finally一般用来关闭资源。

  4. 一旦某个catch捕获到匹配的异常类型,将进入异常处理代码。一经处理结束,就意味着整个try-catch语句结束。其他的catch子句不再有匹配和捕获异常类型的机会。

抛出异常

throws抛出异常

如果一个方法可能会出现异常,但没有能力处理这种异常,可以在方法声明处用throws子句来声明抛出异常。

语法:

methodname throws Exception1,Exception2,..,ExceptionN  
{
    //方法具体内容  
} 

Throws抛出异常的规则:

  1. 如果是不可查异常(unchecked exception),即Error、RuntimeException或它们的子类,那么可以不使用throws关键字来声明要抛出的异常,编译仍能顺利通过,但在运行时会被系统抛出。

  2. 必须声明方法可抛出的任何可查异常(checked exception)。即如果一个方法可能出现受可查异常,要么用try-catch语句捕获,要么用throws子句声明将它抛出,否则会导致编译错误

  3. 仅当抛出了异常,该方法的调用者才必须处理或者重新抛出该异常。当方法的调用者无力处理该异常的时候,应该继续抛出,而不是囫囵吞枣。

  4. 调用方法必须遵循任何可查异常的处理和声明规则。若覆盖一个方法,则不能声明与覆盖方法不同的异常。声明的任何异常必须是被覆盖方法所声明异常的同类或子类。

使用throw抛出异常

throw总是出现在函数体中,用来抛出一个Throwable类型的异常。程序会在throw语句后立即终止,它后面的语句执行不到,然后在包含它的所有try块中(可能在上层调用函数中)从里向外寻找含有与其匹配的catch子句的try块。

要注意的是,throw 抛出的只能够是可抛出类Throwable 或者其子类的实例对象。

语法:

throw new IOException;

如果抛出了检查异常,则还应该在方法头部声明方法可能抛出的异常类型。该方法的调用者也必须检查处理抛出的异常。

自定义异常

使用Java内置的异常类可以描述在编程时出现的大部分异常情况。除此之外,用户还可以自定义异常。用户自定义异常类,只需继承Exception类即可。

在程序中使用自定义异常类,大体可分为以下几个步骤。

  1. 创建自定义异常类。

  2. 在方法中通过throw关键字抛出异常对象。

  3. 如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理;否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作。

  4. 在出现异常方法的调用者中捕获并处理异常。

try-with-resource (java7特性)语句

Try-with-resources是java7中一个新的异常处理机制,它能够很容易地关闭在try-catch语句块中使用的资源。

语法:

try(ResourceType1 res1 = init1; ResourceType2 res2 = init2;...){
    statements
}

每一个资源必须属于一个实现了 AutoCloseable 接口的类。

断言

定义

断言是防御性编程中一种常见的语法。假设你的代码依赖于某属性,且该属性需要满足某些条件,例如,你可能正在计算:

double y = Math.sqrt(x);

你确信 x 不是负值。不过你任然想再次确认。你也可以抛出一个异常:

if(x < 0) throw new IllegalStateException(x + "<0");

不过,及时测试完成后,这个异常触发条件也会一直待在程序中,减慢运行速度。相反,断言机制允许你在测试时加入检测条件,并且在生产代码中自动移除他们。

在 Java 中,断言用于调试目的以验证内部假设。

使用断言

在 java 中,有两种形式的断言语句:

  • assert condition;
  • assert condition : expression;

assert 会计算条件表达式的值,如果为 false,则会抛出一个 AssertionError。在第二种表达式中,表达式会转变为一个字符串,这个字符串会成为错误对象的消息。

例如:

为了断言 x 是一个非负数,你可以直接使用:

assert x >= 0;

或者你可以将 x 的实际值传进 AssertionError 对象,这样后面就可以显示它:

assert x >= 0 : x;

启用和禁用断言

默认情况下,断言是被禁用的。在运行程序时,加上 -enableassertions 或者 -ea 选项可以启用断言。

参考博客

  1. 深入理解java异常处理机制
  2. java提高篇(十六)—–异常(一)
  3. try-with-resource