1 public void testExceptionChain() throws MyCustomizedFileException {
2 try {
3 FileInputStream in = new FileInputStream("myfile");
4 } catch (FileNotFoundException e) {
5 //定义了新的,准备再次被抛出的异常对象。
6 Throwable te = new MyCustomizedFileException("access file error.");
7 //将原始异常链接到该异常对象的内部,以供之后需要时通过getCause()方法重新获取。
8 te.initCause(e);
9 throw te;
10 }
11 }
12
13 public static void main(String[] args) {
14 try {
15 testExceptionChain();
16 } catch (MyCustomizedFileException e) {
17 //获取该异常对象的原始异常。
18 Throwable te = e.getCause();
19 System.out.println(te.getClass().getName());
20 }
21 }
22 /* 输出结果如下:
23 FileNotFoundException
24 */
5) finally字句:在Java的异常机制中存在finally这样的关键字,其块中的代码无论异常是否发生都将会被执行,从而可以确保函数内部分配或者打开的资源都能在函数内部进行释放或者关闭,如Socket连接、DB连接,见如下代码:
1 public void testFinally() {
2 InputStream in = null;
3 try {
4 in = new FileInputStream("myfile");
5 } catch (IOException e) {
6 //TODO: do something for this exception.
7 } finally {
8 in.close();
9 }
10 //Do the following code.
11 }
在以上的代码中,无论try块中异常是否发生,finally块中的代码"in.close()" 都将会在函数退出之前或catch处理之后被执行,从而保证了FileInputStream对象能够在函数退出之前被关闭。然而这样的做法仍然可能导致一些影响代码流程的问题,如果try块中的代码没有产生异常,而是在finally中的in.close引发了异常,那么整个try{}catch{}finally{}代码块之后的代码将不会被执行,而是直接退出该函数,同时抛出in.close()引发的异常给该函数的调用者。修正代码如下:
1 public void testFinally() {
2 InputStream in = null;
3 try {
4 in = new FileInputStream("myfile");
5 } catch (IOException e) {
6 //TODO: do something for this exception.
8 try {
9 in.close();
10 } catch (IOException e) {
11 }
12 }
13 //Do the following code.
14 }
在C++中,由于对象是可以在栈上声明并且分配空间的,当栈退出后会自行调用该对象的析构函数,因此该对象的资源释放代码可以放在类的析构函数中。该方式对于一个多出口的函数而言也是非常有效的,特别是对于加锁和解锁操作需要在同一个函数中完成,为了防止在某个退出分支前意外的漏掉解锁操作,可以采用该技巧,见如下代码:
1 template
2 class ScopedLock {
3 public:
4 ScopedLock(T& lock) : _lock(lock) {
5 _lock.lock();
6 }
7
8 ~ScopedLock() {
9 _lock.unlock();
10 }
11 private:
12 LockT _lock;
13 };
14
15 void testFunc() {
16 ScopedLock s1(myLock);
17 if (cond1) {
18 return;
19 } else if (cond2) {
20 //TODO: do something
21 return;
22 } else {
23 //TODO: do something
24 }
25 return;
26 }
对于以上代码,无论函数从哪个分支退出,s1的析构函数都将调用,因此myLock的解锁操作也会被调用。
6) 异常堆栈跟踪:通过Throwable的getStackTrace方法获取在异常即将被抛出的时间点上程序的调用堆栈,这样有利于日志的输出和错误的分析,见如下代码:
1 public void testStackTrace() {
2 try {
3 //TODO: call function, which may be raise some exception.
4 } catch (Throwable e) {
5 StackTraceElement[] frames = e.getStackTrace();
6 for (