如何提高网站知名度,美篇app制作教程,品牌网站建站,游戏网站建设本章概要
Java 标志异常 特例#xff1a;RuntimeException 使用 finally 进行清理 finally 用来做什么#xff1f;在 return 中使用 finally缺憾#xff1a;异常丢失
Java 标准异常
Throwable 这个 Java 类被用来表示任何可以作为异常被抛出的类。Throwable 对象可分为两…本章概要
Java 标志异常 特例RuntimeException 使用 finally 进行清理 finally 用来做什么在 return 中使用 finally缺憾异常丢失
Java 标准异常
Throwable 这个 Java 类被用来表示任何可以作为异常被抛出的类。Throwable 对象可分为两种类型指从 Throwable 继承而得到的类型Error 用来表示编译时和系统错误除特殊情况外一般不用你关心Exception 是可以被抛出的基本类型在 Java 类库、用户方法以及运行时故障中都可能抛出 Exception 型异常。所以 Java 程序员关心的基类型通常是 Exception。要想对异常有全面的了解最好去浏览一下 HTML 格式的 Java 文档可以从 java.sun.com 下载。为了对不同的异常有个感性的认识这么做是值得的。但很快你就会发现这些异常除了名称外其实都差不多。同时Java 中异常的数目在持续增加所以在书中简单罗列它们毫无意义。所使用的第三方类库也可能会有自己的异常。对异常来说关键是理解概念以及如何使用。
基本理念是用异常的名称代表发生的问题。异常的名称应该可以望文知意。异常并非全是在 java.lang 包里定义的有些异常是用来支持其他像 util、net 和 io 这样的程序包这些异常可以通过它们的完整名称或者从它们的父类中看出端倪。比如所有的输入/输出异常都是从 java.io.IOException 继承而来的。
特例RuntimeException
在本章的第一个例子中
if(t null){throw new NullPointerException();
}如果必须对传递给方法的每个引用都检查其是否为 null因为无法确定调用者是否传入了非法引用这听起来着实吓人。幸运的是这不必由你亲自来做它属于 Java 的标准运行时检测的一部分。如果对 null 引用进行调用Java 会自动抛出 NullPointerException 异常所以上述代码是多余的尽管你也许想要执行其他的检查以确保 NullPointerException 不会出现。
属于运行时异常的类型有很多它们被 java 自动抛出所以不必在异常说明中把它们列出来。非常方便的是通过将这些异常设置为 RuntimeException的子类而把它们归类起来这是继承的一个绝佳例子建立具有相同特征和行为的一组类型。
RuntimeException 代表的是编程错误
无法预料的错误。比如从你控制范围之外传递进来的 null 引用。作为程序员应该在代码中进行检查的错误。比如对于 ArrayIndexOutOfBoundsException就得注意一下数组的大小了。在一个地方发生的异常常常会在另一个地方导致错误。
在这些情况下使用异常很有好处它们能给调试带来便利。
如果不捕获这种类型的异常会发生什么事呢因为编译器没有在这个问题上对异常说明进行强制检查RuntimeException 类型的异常也许会穿越所有的执行路径直达 main() 方法而不会被捕获。要明白到底发生了什么可以试试下面的例子
// exceptions/NeverCaught.java
// Ignoring RuntimeExceptions
// {ThrowsException}
public class NeverCaught {static void f() {throw new RuntimeException(From f());}static void g() {f();}public static void main(String[] args) {g();}
}输出结果为 如果 RuntimeException 没有被捕获而直达 main()那么在程序退出前将调用异常的 printStackTrace() 方法。
你会发现RuntimeException或任何从它继承的异常是一个特例。对于这种异常类型编译器不需要异常说明其输出被报告给了 System.err。
请务必记住代码中只有 RuntimeException及其子类类型的异常可以被忽略因为编译器强制要求处理所有受检查类型的异常。
值得注意的是不应把 Java 的异常处理机制当成是单一用途的工具。是的它被设计用来处理一些烦人的运行时错误这些错误往往是由代码控制能力之外的因素导致的然而它对于发现某些编译器无法检测到的编程错误也是非常重要的。
使用 finally 进行清理
有一些代码片段可能会希望无论 try 块中的异常是否抛出它们都能得到执行。这通常适用于内存回收之外的情况因为回收由垃圾回收器完成为了达到这个效果可以在异常处理程序后面加上 finally 子句。完整的异常处理程序看起来像这样
try {
// The guarded region: Dangerous activities
// that might throw A, B, or C
} catch(A a1) {
// Handler for situation A
} catch(B b1) {
// Handler for situation B
} catch(C c1) {
// Handler for situation C
} finally {
// Activities that happen every time
}为了证明 finally 子句总能运行可以试试下面这个程序
// exceptions/FinallyWorks.java
// The finally clause is always executed
class ThreeException extends Exception {
}public class FinallyWorks {static int count 0;public static void main(String[] args) {while (true) {try {// Post-increment is zero first time:if (count 0) {throw new ThreeException();}System.out.println(No exception);} catch (ThreeException e) {System.out.println(ThreeException);} finally {System.out.println(In finally clause);if (count 2) {break; // out of while}}}}
}输出为 从输出中发现无论异常是否被抛出finally 子句总能被执行。这也为解决 Java 不允许我们回到异常抛出点这一问题提供了一个思路。如果将 try 块放在循环里就可以设置一种在程序执行前一定会遇到的异常状况。还可以加入一个 static 类型的计数器或者别的装置使循环在结束以前能尝试一定的次数。这将使程序的健壮性更上一个台阶。
finally 用来做什么
对于没有垃圾回收和析构函数自动调用机制的语言来说finally 非常重要。它能使程序员保证无论 try 块里发生了什么内存总能得到释放。但 Java 有垃圾回收机制所以内存释放不再是问题。而且Java 也没有析构函数可供调用。那么Java 在什么情况下才能用到 finally 呢
当要把除内存之外的资源恢复到它们的初始状态时就要用到 finally 子句。这种需要清理的资源包括已经打开的文件或网络连接在屏幕上画的图形甚至可以是外部世界的某个开关如下面例子所示 Switch.java // exceptions/Switch.java
public class Switch {private boolean state false;public boolean read() {return state;}public void on() {state true;System.out.println(this);}public void off() {state false;System.out.println(this);}Overridepublic String toString() {return state ? on : off;}
}OnOffSwitch.java // exceptions/OnOffSwitch.java
// Why use finally?
public class OnOffSwitch {private static Switch sw new Switch();public static void f()throws OnOffException1, OnOffException2 {}public static void main(String[] args) {try {sw.on();// Code that can throw exceptions...f();sw.off();} catch (OnOffException1 e) {System.out.println(OnOffException1);sw.off();} catch (OnOffException2 e) {System.out.println(OnOffException2);sw.off();}}
}OnOffException2.java // exceptions/OnOffException2.java
public class OnOffException2 extends Exception {
}OnOffException1.java // exceptions/OnOffException1.java
public class OnOffException1 extends Exception {
}输出为 程序的目的是要确保 main() 结束的时候开关必须是关闭的所以在每个 try 块和异常处理程序的末尾都加入了对 sw.off() 方法的调用。但也可能有这种情况异常被抛出但没被处理程序捕获这时 sw.off() 就得不到调用。但是有了 finally只要把 try 块中的清理代码移放在一处即可
// exceptions/WithFinally.java
// Finally Guarantees cleanup
public class WithFinally {static Switch sw new Switch();public static void main(String[] args) {try {sw.on();// Code that can throw exceptions...OnOffSwitch.f();} catch (OnOffException1 e) {System.out.println(OnOffException1);} catch (OnOffException2 e) {System.out.println(OnOffException2);} finally {sw.off();}}
}输出为 这里 sw.off() 被移到一处并且保证在任何情况下都能得到执行。
甚至在异常没有被当前的异常处理程序捕获的情况下异常处理机制也会在跳到更高一层的异常处理程序之前执行 finally 子句
// exceptions/AlwaysFinally.java
// Finally is always executed
class FourException extends Exception {
}public class AlwaysFinally {public static void main(String[] args) {System.out.println(Entering first try block);try {System.out.println(Entering second try block);try {throw new FourException();} finally {System.out.println(finally in 2nd try block);}} catch (FourException e) {System.out.println(Caught FourException in 1st try block);} finally {System.out.println(finally in 1st try block);}}
}输出为 当涉及 break 和 continue 语句的时候finally 子句也会得到执行。请注意如果把 finally 子句和带标签的 break 及 continue 配合使用在 Java 里就没必要使用 goto 语句了。
在 return 中使用 finally
因为 finally 子句总是会执行所以可以从一个方法内的多个点返回仍然能保证重要的清理工作会执行
// exceptions/MultipleReturns.java
public class MultipleReturns {public static void f(int i) {System.out.println(Initialization that requires cleanup);try {System.out.println(Point 1);if (i 1) {return;}System.out.println(Point 2);if (i 2) {return;}System.out.println(Point 3);if (i 3) {return;}System.out.println(End);return;} finally {System.out.println(Performing cleanup);}}public static void main(String[] args) {for (int i 1; i 4; i) {f(i);}}
}输出为 从输出中可以看出从何处返回无关紧要finally 子句永远会执行。
缺憾异常丢失
遗憾的是Java 的异常实现也有瑕疵。异常作为程序出错的标志决不应该被忽略但它还是有可能被轻易地忽略。用某些特殊的方式使用 finally 子句就会发生这种情况
// exceptions/LostMessage.java
// How an exception can be lost
class VeryImportantException extends Exception {Overridepublic String toString() {return A very important exception!;}
}class HoHumException extends Exception {Overridepublic String toString() {return A trivial exception;}
}public class LostMessage {void f() throws VeryImportantException {throw new VeryImportantException();}void dispose() throws HoHumException {throw new HoHumException();}public static void main(String[] args) {try {LostMessage lm new LostMessage();try {lm.f();} finally {lm.dispose();}} catch (VeryImportantException | HoHumException e) {System.out.println(e);}}
}输出为 从输出中可以看到VeryImportantException 不见了它被 finally 子句里的 HoHumException 所取代。这是相当严重的缺陷因为异常可能会以一种比前面例子所示更微妙和难以察觉的方式完全丢失。相比之下C把“前一个异常还没处理就抛出下一个异常”的情形看成是糟糕的编程错误。也许在 Java 的未来版本中会修正这个问题另一方面要把所有抛出异常的方法如上例中的 dispose() 方法全部打包放到 try-catch 子句里面。
一种更加简单的丢失异常的方式是从 finally 子句中返回
// exceptions/ExceptionSilencer.java
public class ExceptionSilencer {public static void main(String[] args) {try {throw new RuntimeException();} finally {// Using return inside the finally block// will silence any thrown exception.return;}}
}如果运行这个程序就会看到即使方法里抛出了异常它也不会产生任何输出。