2016-07-17 09:50:10 +0000   |     java thinking in java exception io   |   Viewed times   |    

我必须记住的惯用法

IO读写,用双层try,和finally保证每次资源都被正确关闭

这个惯用法在处理IO的时候,非常常用。所以放在最前面。有的时候,记住一个惯用法,胜过万语千言。

public static void main(String[] args) {
    String path = "/filePath/fileName.txt";
    try {
        BufferedReader br = new BufferedReader(new FileReader(new File(path))); //每次创建一个需要清理的对象,后面马上紧跟一个try{}finally{}块。在finally里把资源清理。
        try {
            // do something with the file
            String path2 = "/filePath/fileName.txt";
            try {
                BufferedReader br2 = new BufferedReader(new FileReader(new File(path2)));   //后面要打开的文件也照样二层套嵌
                try {
                    // do something with the second file
                } finally {
                    try {
                        br2.close();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            } catch (FileNotFoundException e) {
                throw new RuntimeException(e);
            }
        } finally {
            try {
                br.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    } catch (FileNotFoundException e) {
        throw new RuntimeException(e);
    }
}

异常(Exception)

首先,异常(Exception) 是指阻止当前程序继续执行的问题。一个典型的异常出现的场景,就是IO读取文件。为什么呢?因为本来处理文本的函数设计地很好,但读进来的万一是个空文件怎么办?而且在对几万甚至几亿个文件读取过程中,出现几个空文件,太正常了。

每种异常(Exception)都是一个类

Java号称什么都是类,所以异常(Exception)当然也是类。所有异常的共同祖先(基类)是Throwable类。Oracle官方手册这是这样描述Throwable类的:

Throwable是Java所有error和exception的基类。

Throwable最常用的一个子类就是Exception类。有时候我们不知道要要捕获什么类,或者向上转型都是转到这个Exception类。Throwable和Exception的声明形式如下,

public class Throwable extends Object implements Serializable {}
public class Exception extends Throwable {}

下图是Java Exception类的家族信息: exception

上图中最著名的就是这个RuntimeException了。它在java.lang包下。其他一些大家耳熟能详,闻风丧胆的名字比如说:

它们都分别被定义在各自对应的特殊的包里。比较集中的有java.lang, java.util, java.io等几个常用包。

自定义异常类

如果Java自带的异常类不能满足我们的需求,还可以自定义新的异常类。需要!!注意:因为Throwable实现了Seriallizable接口,因此要求有一个serialVersionUID成员字段。否则会抛出warning。

warning: [serial] serializable class LoggingException has no definition of serialVersionUID

因此自定义一个新的异常类型,需要这样写:

class NewExeption extends Exception {private static final long serialVersionUID=0;}

对于自定义异常类型,需要记住一点:

一定要取一个合适的名字!一定要取一个合适的名字!一定要取一个合适的名字!

重要的事情说三遍。因为往往异常类内部并不包含太多其他信息字段,通常名字就代表了一个异常全部的有效信息

Exception的两种常用的构造函数:一个默认无参数,一个以String为参数。

new NullPointerException();
new NullPointerException("t = null");

抛出throw

遇到可能抛出的异常,有两种对待方法,一种就是处理掉,另一种就是搁置。如果在函数内部直接能就地处理掉,那一般也没必要抛出异常了。异常处理的价值就在于能延迟异常被处理时机。比如,下面这个简单的函数,把传入的String从中间一刀两断,只返回前半段字符串。

public static String halfString(String s){
    return s.substring(0,s.length()/2);
}

但如果传入的参数String是null的话,系统就会抛出一个NullPointerException。

Exception in thread "main" java.lang.NullPointerException
	at com.ciaoshen.thinkinjava.chapter12.Test.splitString(Test.java:10)
	at com.ciaoshen.thinkinjava.chapter12.Test.main(Test.java:14)

但这函数只是被调用来负责切割,对其他函数怎么读取文本文件的过程一无所知。如果读取到了空String,最合适的处理场景是在读取文本,并调用切割函数的函数内部。

所以异常处理的一个核心理念,就是在遇到无法就地处理的异常的时候,可以先 ”抛出“ 这个异常,表示待定。等待有足够信息解决这个异常的环境,或者是集中处理一批相似异常,以减少代码冗余。“抛出”这个动作由 “throw” 这个关键字完成。throw实际执行的操作,就是 “return” 返回这个exception类型对象。比如之前的代码,编译器遇到空String的时候,会自动抛出NullPointerException。显式地写出来就像下面这样:

public static String halfString(String s){
	if(s == null){
		throw new NullPointerException();
	}else{
		return s.substring(0,s.length()/2);
	}
}

RuntimeException不用抛出

系统之所以不显式的写出来,是因为NullPointerException属于RuntimeException,属于系统默认检查的范围,不需要写出来也会检查。

函数声明抛出异常(Exception Specification)

当函数抛出了一个异常,但内部又没有处理的时候,就必须在函数后加上异常声明 “exception specification”。 声明异常的时候使用关键字 “throws”,注意不是throw。

public static String halfString(String s) throws NullPointerException{
	if(s == null){
		throw new NullPointerException();
	}else{
		return s.substring(0,s.length()/2);
	}
}

这句话的意思是:我这函数可能会抛出这个异常,调用此函数必须要处理它可能抛出的异常。

可以同时声明抛出好几个异常,用 逗号”,” 连接。

void f() throws TooBig, TooSmall, DivZero {
    //...
}

RuntimeException也不需要声明

但这里还是因为NullPointerException属于RuntimeException,系统默认存在,所以也可以不显式地声明异常。但其他不是RuntimeException的其他异常就必须声明。上面的声明和下面这样的写法是一样的:

public static String halfString(String s){
	return s.substring(0,s.length()/2);
}

异常处理handler:try, catch, finally

异常抛出以后,抛出它的函数可以不管,但总有地方要处理。处理的语法很简单,著名的try{}catch{}finally{}三连发大家都知道。

比如面对刚才可能抛出NullPointerException的halfString()函数,要调用它,必须把它放在try{}区块里,然后在catch{}区块处理它抛出的异常。

public static void callHalfString(String line){
        String result="";
        try{	//隔离执行可能抛出异常的函数或代码
            result=Test.halfString(line);
        }catch(NullPointerException e){		//处理异常
            System.out.println("The parameter string is null! Ignore it!");
        }finally{	//完成后的后续操作
            System.out.println(result);
        }
}

finally是用来干什么的?

上面的finally里面打印了抛出的异常。但实际上打印哪里都可以,一般都放在catch{}区块里,捕获了就打印。那finally是用来干什么的呢?况且finally还有一个重要的特性:无论try{}区块有没有正常完成,无论catch{}有没有捕获到异常,finally里的代码都会被执行。哪怕是在try和catch里有return也是一样(因为异常的throw就等同于一个return操作)。

书上是这么说的:

The finally clause is necessary when you need to set something other than memory back to its original state.

也就是说,finally是用来做收尾清理工作的。尤其是函数和外部资源交互的时候,有的时候必须确保交互的资源被正确地恢复到操作之前的初始状态,无论函数本身的操作有没有正确地完成。

也可以不处理,继续往上抛

当然callHalfString()函数如果还是处理不了异常,比如例子里这样,它其实还只是一个中介函数,还不是真正读取外部文件的地方。这时候,callHalfString也可以不处理,而是继续声明异常,继续甩锅!让上一级调用的函数负责处理。

public static void callHalfString2(String line) throws NullPointerException{
        String result=Test.halfString(line);
        System.out.println(result);
}

这就是所谓的延迟异常处理的时间和场景,甚至可以有意归拢底层异常,在某个模块中集中处理。

异常链 & cause

有的时候,就算是甩锅也可以甩地有节操一点,比如说包装一下,再脱手。 Throwable基类里已经定义了Throwable#initCause(Throwable cause)方法。因为Exception继承自Throwable基类,任何Exception的派生类都自带initCause()方法。它的作用就是把A异常当做B异常的原因,包装到B异常里。

    public static void callHalfString(String line) throws MyException{
        String result="";
        try{
            result=Test.halfString(line);
        }catch(NullPointerException e){
            MyException me=new MyException();
            me.initCause(e);	//对捕获的NullPointerException进行包装
            throw me;	//再次抛出
        }finally{
            System.out.println(result);
        }
    }

这时候,捕捉到MyException(),打印出来,一层一层传递过来的错误都会列出来。

//后捕获到的MyException异常
com.ciaoshen.thinkinjava.chapter12.MyException
	at com.ciaoshen.thinkinjava.chapter12.Test.callHalfString4(Test.java:55)
	at com.ciaoshen.thinkinjava.chapter12.Test.main(Test.java:66)
//之前捕获的NullPointerException异被当成原因包装在MyException里面。打印的时候一起被打印出来。
Caused by: java.lang.NullPointerException
	at com.ciaoshen.thinkinjava.chapter12.Test.halfString(Test.java:12)
	at com.ciaoshen.thinkinjava.chapter12.Test.callHalfString4(Test.java:53)
	... 1 more

只有 Error, ExceptionRunTimeException 三个异常可以把cause作为构造器的参数,其他异常必须用initCause()方法来初始化cause。

        }catch(NullPointerException e){
            throw new RuntimeException(e);	//构造RuntimeException可以直接把cause当作构造函数的参数。
        }

RuntimeException还是完全不用处理

RuntimeException继续之前的神勇表现,完全不需要try{}catch{}finally{}这一套。当它不存在就好了。

public static void callHalfString2(String line){
        String result=Test.halfString(line);
        System.out.println(result);
}

Java是Termination模型,不主动回到出错点

Java和C++的异常处理流程支持的是 “终止模型(Termination)”。捕获异常之后,无论处理结果如何,都不在回到try{}的区块里继续执行出错的代码或函数。 还有一种处理方法叫 “恢复模型(Resumption)”。就是指会回到try{}的区块里。

但技术上可以用while loop强制回到try{}区块,直到代码正常运行为止。

public static void callHalfString(){
	String[] strList=new String[5];
	strList[0]="zero";
	strList[1]=null;	//空记录,导致异常
	strList[2]="two";
	strList[3]="three";
	strList[4]="four";

	//可以用loop强制回到try{}区块里
	for(int i=0;i<5;i++){
        String result="";
        try{	//隔离执行可能抛出异常的函数或代码
            result=Test.halfString(line);
			System.out.println(result);
        }catch(NullPointerException e){		//处理异常:打印空记录位置后跳转到下一条
            System.out.println("Index="+i+" is null!");
        }
	}
}

一个异常里到底记录了什么?

前面已经多次显示了,一个异常在console里打印出来是什么样子。

怎么看一个异常记录了什么?

最简单的直接用System.out.println()打印,就是直接调用Exception基类的toString()函数,

try {
	f();
} catch(MyException e) {
	System.out.println(e);
}

//Output:
com.ciaoshen.thinkinjava.chapter12.MyException

可以看到,toString()打印的实际上是 package name + class name

想要打印全部调用历史记录的,可以调用 Throwable接口定义的 printStackTrace() 方法。printStackTrace()方法可以接受一个PrintStream型的参数,而System.out正好就是那个PrintStream型。 printStackTrace systemOut

如下面代码,把System.out当成printStackTrack()的参数,打印异常,

try {
	f();
} catch(Exception e) {
	e.printStackTrace(System.out);
}

打印出来的内容,包含两部分,除了刚才toString()也会打的包名+类名之外,还会列出线程执行过程中每一次的函数的调用历史清单,叫做 “栈轨迹”

com.ciaoshen.thinkinjava.chapter12.LoggingException	//包名+类名
	at com.ciaoshen.thinkinjava.chapter12.CatchThrow.testLoggingException(CatchThrow.java:37)	//历史调用记录
	at com.ciaoshen.thinkinjava.chapter12.CatchThrow.main(CatchThrow.java:63)	//历史调用记录

配合Log日志使用

Java最常用的util库有一个日志包:java.util.logging。配合Exception使用,可以生成更详细的执行信息清单。比如下面代码自定义了一个AutoLogException异常类,继承自基类Exception。

package com.ciaoshen.thinkinjava.chapter12;
import java.util.*;
import java.util.logging.*;	//日志库
import java.io.*;	//读外部文档

class AutoLogException extends Exception{

    /**
     *  PUBLIC METHOD
     */
    public void printLogging(){
        StringWriter trace=new StringWriter();  //create a PrintStream
        printStackTrace(new PrintWriter(trace));    //add trace information to the PrintStream
        autoLogger.severe(trace.toString());    //convert the PrintStream to the String and add it into the Logger
    }

    /**
     *  PRIVATE FIELDS
     */
    private static final long serialVersionUID=0;	//Throwable接口规格
    private Logger autoLogger=Logger.getLogger("Auto Logger");	//Logger
}

AutoLogException类中,定义了一个用来打印日志信息的成员方法printLogging(),其具体操作步骤如下:

最后在main里捕获AutoLogException后,直接调用printLogging()方法,

    public static void main(String[] args){
        try{
            throw new AutoLogException();
        }catch(AutoLogException ale){
            System.err.println("Caught "+ale);
            ale.printLogging();
        }        
    }

打印出日志信息如下:

//System.err.println("Caught "+ale)这行代码的输出,说明Exception基类的toString()方法输出的是package name + class name Caught com.ciaoshen.thinkinjava.chapter12.AutoLogException

//这部分是在没有往Logger里填充trace信息时候autoLogger.severe()方法负责加的Logger报头。
juil. 21, 2016 3:37:43 PM com.ciaoshen.thinkinjava.chapter12.AutoLogException printLogging
GRAVE:

//下面这部分是实际trace信息。也就是printStackTrace()方法填充到StringWriter对象里的信息。
com.ciaoshen.thinkinjava.chapter12.AutoLogException at com.ciaoshen.thinkinjava.chapter12.AutoLogException.main(AutoLogException.java:48)

可以看到,除了printStackTrace()方法也也会打印的包名,类名,以及函数调用记录以外,autoLogger.severe()方法在中间加了一个Logger报头。其中:

关于异常的继承

既然函数可以声明抛出异常,那事情就变得复杂了。因为涉及到类的方法,就会同时涉及到类的继承。当基类的某一方法声明抛出某一异常,那派生类中的同方法会不会也抛出这个异常?

虽然Java为了异常处理的“健壮性”考虑,把规则设置地有点复杂,但细心梳理还是能归纳出一个逻辑条理的:

其中最重要的是第一条,而且很好理解,一切都是为了向上转型。派生类可以有基类没有的方法,是因为派生类向上转型以后,编译器可以规定只允许调用基类中定义了的方法。比如说企鹅的例子:

class Bird {
	public fly(){};
}

class Penguin extends Bird{
	public fly(){};
	public swim(){};
}

public class Test {
	public static void main(){
		Bird birdPenguin=new Penguin();		//企鹅向上转型成鸟类
		birdPenguin.fly();
		//birdPenguin.swin();		//编译器可以禁止鸟类游泳
	}
}

企鹅继承了鸟基类,鸟都不会游泳,但企鹅会。当企鹅向上转型成鸟之后,编译器通过语法检查,可以禁止作为鸟类的企鹅去游泳。因为编译器语法检查的依据就是基类的方法表。虽然Java是后期绑定,会把方法表上的名字和实际调用的函数绑定起来,但方法表上连这个名字都没有,编译器当然是不允许调用的。

但异常处理就不是这么回事了。编译器是没有办法禁止代码出现异常的。还是企鹅的例子:

class Bird {
	public fly() throws hitWindowException{};
}

class Penguin extends Bird{
	public fly() throws cannotFlyException{};	//声明基类没有声明的异常是不允许的
	public swim(){};
}

public class Test {
	public static void main(){
		Bird birdPenguin=new Penguin();		//企鹅向上转型成鸟类
		try{
			birdPenguin.fly();
		}catch(hitWindowException hwe){		//根据基类Bird的异常声明,编译器知道鸟都有撞墙的毛病,可以强制要求检查鸟有没有撞墙
			//do something ...
		}catch(cannotFlyException cfe){		//但编译器根据基类Bird的异常声明,无法知道企鹅身为鸟类,竟然有不会飞的毛病。编译器认为所有鸟都是会飞的。
											//编译器无法强制程序员加上检查鸟会不会飞的语句
											//可是企鹅就是不会飞,于是程序就出现了一个漏洞,就不健壮了。
			//do something ...
		}
	}
}

把上面代码里的注释直接搬过来。根据基类Bird的异常声明,编译器只知道鸟都有撞墙的毛病,可以强制要求程序员检查鸟有没有撞墙。但如果作为鸟类的企鹅有不会飞的毛病。当企鹅向上转型成鸟类,编译器做语法检查,通过查找鸟类的异常声明,是不可能知道企鹅身为鸟类,竟然不会飞。编译器认为所有鸟都是会飞的。编译器无法强制程序员加上检查鸟会不会飞的语句。所以当企鹅不会飞的异常出现的时候,于是程序就出现了一个漏洞,就不健壮了。

这就是Java异常处理为什么麻烦的原因。其实是编译器从语法的角度不允许。

异常处理常见的坑

基本的语法都说完了,下面讲讲书里提到的一些常见的坑。

尽量不要在构造函数里声明异常

语法上,构造器也是可以声明异常的。比如这样,

class Bird {
	Bird() throws hitWindowException{};
}

但,当出现派生类之后,你就不得不面对这样的尴尬:派生类构造器调用基类构造器super()的时候还需要检查异常。就像这样,

class Penguin extends Bird {
	Penguin(){
		try{
            super();
        }catch(Exception e){
			//do something here...
		}
	}
}

这当然是不可以的,编译器会报错。因为调用基类构造器super(),必须在派生类构造器的第一行

call to super must be first statement in constructor

这时候的workaround办法就是派生类构造器里不处理基类构造器的异常,转而直接抛出甩锅:

class Penguin extends Bird {
	Penguin() throws hitWindowException{
		super();
	}
}

构造函数之后销毁对象的正确姿势

关于异常处理,构造函数还有一个坑,就是对象的销毁。为了确保对象在使用完了之后才被销毁,我们可能想当然地在finally里销毁对象,就像这样,

B b;
public dispose(){}

public f(){
	try{
		B b=new B();
	}catch(Exception e){
		//do something here...
	}finally{
		dispose();	//千万别在这里销毁b
	}
}

A类里有一个B类成员字段。可是B类的构造函数会抛出异常。在成员方法f()里,我们需要构建一个B类对象b。一系列操作之后,在finally里调用销毁函数dispose()想把b对象销毁。

这里的问题是,当B类的构造函数出现异常的时候,b对象还没有构造完成。这时候调用dispose()就可能会出错。因为销毁的函数都是基于正确构造好的对象来写的。

这时候,正确的销毁b对象的姿势是:两层套嵌try结构。具体如下,

try{
	B b=new B();//在第一层try里构造b
	try{
		//use the b here...
	}finally{
		dispose();//用完了在第二层finally{}里销毁
	}
}catch(Exception e){
	//do something here...
}

这个结构在第一层try里构造b。然后使用b的时候再开一个第二层try{},用完了b在第二层finally{}里销毁b。如果构造b的时候抛出异常了,会直接跳转到第一层的catch{}里,不会进入到使用以及销毁对象的第二层try{}finally{}。

这里使用第二层try{}finally{},然后在第二层finally{}里销毁b的好处是,哪怕在第二层try{}里使用b的过程中发生了什么异常,finally{}里的dispose()销毁动作是无论如何都会触发的。

所以,这个结构能够确保只有在构造器的构造完成之后才执行dispose()方法。这都是前人在工作总结出的智慧,全是干货。

异常丢失

由于异常本身的性质,在下面两种情况下,异常会消失。

#####第一种:基异常放在了派生异常的前面 第一种丢失原因很简单,因为编译器在类型判断的时候,会自动的向上转型,所有派生类都会被等同于基类。所以在catch异常的时候,如果先检查基异常,那所有的派生异常也都会被捕获,导致后面的派生异常的检查语句永远无法触发。

try{
	//do something here...
}catch(Exception e){	//所有的异常都将在这里被捕获。基类Exception覆盖了后面的RuntimeException。
	//do something here...
}catch(RuntimeException re){
	//do something here...
}

Java编译器编译的时候会对语法进行检查,出现这种情况,编译器会报错,

error: exception NullPointerException has already been caught

#####第二种:finaly里抛出的异常覆盖try中的异常 第二种异常丢失比较隐蔽。出现在之前讲的两层try{}结构里。

void f() throws VeryImportantException {
	throw new VeryImportantException();
}
	void dispose() throws NotImportantException {
throw new HoHumException();
}

public static void main(String[] args){
	try {
		LostMessage lm = new LostMessage();
		try {
			lm.f();	//抛出一个重要异常
		} finally {
			lm.dispose();	//抛出一个不重要的异常,覆盖第一个重要异常
		}
	} catch(Exception e) {
		System.out.println(e);
	}
}

平时使用双层try{}不会出什么状况。但像上面代码一样,当第二层的try{}finally{}里同时抛出异常的时候,问题就来了。

先说一下双层try{}结构里执行的顺序,

如果内层finally里的lm.dispose()被替换成一个return,就变成了一个 “Exception Scilence” 问题。第二个return会覆盖前面的异常。因为原则上,抛出的异常就是一个return操作。而且这时候外部catch什么信息也捕获不到。

void f() throws VeryImportantException {
	throw new VeryImportantException();
}

public static void main(String[] args){
	try {
		LostMessage lm = new LostMessage();
		try {
			lm.f();	//抛出一个重要异常
		} finally {
			return;	//return会覆盖前一个异常。这就是Exception Scilence问题
		}
	} catch(Exception e) {
		System.out.println(e);
	}
}

关于异常处理的一些最佳实践

优先考虑用Java库自带的exception

我们可以自定义很多特殊的异常类型。但调用代码的程序员,很可能并没有你包含这些异常的库,可能会带来麻烦。使用Java自带的异常,就不会有这样的麻烦。

不知道怎么处理异常怎么办?

有的时候我们不是很情愿去处理一些异常,尤其是调用某些库函数的时候。如果我们真的不知道怎么处理某个异常怎么办呢?

让main甩锅给console

这招可以说是甩锅大法的极致了。甩到最后,如果在main里都不处理的话,好吧,恭喜你,再也不用处理了,异常已经成功被甩入console异次元空间了。

public static void main(String[] args) throws Exception {}
把事情闹大成RuntimeException

存在这一条的理由是:与其 “吞掉” 一个异常,就这样忽略掉它,索性彻底暴露它,然后让别人来处理它可能是更好的选择。明枪易躲,暗箭难防,就是这个道理。

t r y {
	// ... to do something useful
} catch(IDontKnowWhatToDoWithThisCheckedException e) {
	throw new RuntimeException(e);
}

catch any exception

有的时候是不是已经被各式各样,花样繁多的异常搞晕了?那么闭上眼睛就捕获基类 “Exception” 或许是个不错的选择。

catch(Exception e) {
	System.out.println("Caught an exception");
}

因为Exception基本是所有常见异常类的基类。甚至包括程序员自定义的异常。而且另一个好处是,Exception基类继承自Java所有错误和异常的始祖Throwable类。比如,

####除非知道怎么处理,否则不要捕获一个异常 说这条,是源于一条重要的事实:使用异常处理的一个重要原因是我们想把业务逻辑代码和异常处理代码分开。这么做不至于让我们的代码看起来乱糟糟。所以我们总是在逻辑代码区块中抛出异常,然后只在我们想集中处理异常的时候再处理异常。

所以这一条也可以被说成是:在合适的地方处理异常。

再来说说到底为什么要异常处理?

说了这么多,是时候坐下来想想到底为什么要这样处理异常了。 按常规的说法,动机无非有四:

到底异常处理是不是必须的?

上面说了这么多异常处理的好处。但仔细想一想,Java的异常处理,在编程语言里来讲,算是比较复杂的。虽然其他语言也做异常处理,几乎没有像Java这样对异常进行这么严格的语法检查的。

作者从个人经验的角度出发,总结下来,当项目的规模变大之后,checked exception就变得微妙起来。

原话是这样的耐人寻味:

“Examination of small programs leads to the conclusion that requiring exception specifications could both enhance developer productivity and enhance code quality, but experience with large software projects suggests a different result—decreased productivity and little or no increase in code quality.”

“…on the whole I think that exceptions are good, but Java checked exceptions are more trouble than they are worth.”

最后的忠告:不要太纠结于异常处理,把注意力集中到更重要的事情上

这句是我整个章节最听的进去的一句话了。

附录:常见异常表

练习

Exercise 1

package com.ciaoshen.thinkinjava.chapter12;

public class Exercise1 {
    public static void main(String[] args) {
        try {
            String msg = "This is Exercise 1 of chapter 12!";
            throw new Exception(msg);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        } finally {
            System.out.println("In finally block of chapter 12 - exercise 1");
        }
    }
}

Exercise 2

package com.ciaoshen.thinkinjava.chapter12;

public class Exercise2 {
    public void foo() {System.out.println("This is Exercise 2 of chapter 12");}
    public static void main(String[] args) {
        try {
            Exercise2 myExercise = null;
            myExercise.foo();
        } catch(Exception e) {
            System.out.println(e);
        }
    }
}

Exercise 3

package com.ciaoshen.thinkinjava.chapter12;

public class Exercise3 {
    public static void main(String[] args) {
        try {
            int arrayLength = 5;
            int[] arrayToOverflow = new int[arrayLength];
            int loopTimes = 10;
            for (int i=0; i<loopTimes; i++) {
                arrayToOverflow[i] = i;
            }
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println(e);
        }
    }
}

Exercise 4

package com.ciaoshen.thinkinjava.chapter12;

public class Exercise4 {
    public static class Exercise4Exception extends Exception {
        private static final long serialVersionUID = 0l;
        private String message = "NULL";
        public Exercise4Exception(){
            super();
        }
        public Exercise4Exception(String msg) {
            message = msg;
        }
        public void showMessage() {
            System.out.println(message);
        }
    }
    public static void main(String[] args) {
        try {
            String justAMessage = "I am the message of Exercise4Exception!";
            throw new Exercise4.Exercise4Exception(justAMessage);
        } catch (Exercise4.Exercise4Exception e) {
            e.showMessage();
        }
    }
}

Exercise 5

package com.ciaoshen.thinkinjava.chapter12;

public class Exercise5 {
    public static void main(String[] args) {
        int offset= 10;
        while (true) {  //try to read the last element in the array
            try {
                int[] arrayToOverflow = {1,2,3,4,5};
                System.out.println("The last element (" + "index=" + offset + ") in the array is: " + arrayToOverflow[offset]);
                break;
            } catch (ArrayIndexOutOfBoundsException e) {
                System.out.println(offset + " is too big!");
                offset--;
            }
        }
    }
}

Exercise 6

package com.ciaoshen.thinkinjava.chapter12;
import java.util.logging.*;
import java.io.*;

public class Exercise6 {
    public static class Exercise6Exception extends Exception {
        private static final long serialVersionUID = 0;
        private Logger log = Logger.getLogger("Exercise6Exception");
        public Exercise6Exception() {
            super();
            StringWriter traceStr = new StringWriter();
            printStackTrace(new PrintWriter(traceStr));
            this.log.severe(traceStr.toString());
        }
    }
    public static void main(String[] args) {
        try {
            throw new Exercise6.Exercise6Exception();
        } catch (Exercise6.Exercise6Exception e) {
            System.err.println(e);
        }
    }
}

Exercise 7

package com.ciaoshen.thinkinjava.chapter12;
import java.util.logging.*;
import java.io.*;
public class Exercise7 {
    public static void main(String[] args) {
        try {
            int arrayLength = 5;
            int[] arrayToOverflow = new int[arrayLength];
            int loopTimes = 10;
            for (int i=0; i<loopTimes; i++) {
                arrayToOverflow[i] = i;
            }
        } catch (ArrayIndexOutOfBoundsException e) {
            Logger log = Logger.getLogger("ArrayIndexOutOfBoundsException");
            StringWriter trace = new StringWriter();
            e.printStackTrace(new PrintWriter(trace));
            log.severe(trace.toString());
            System.err.println(e);
        }
    }
}

Exercise 8

不添加异常说明,通不过编译。

error: unreported exception Exercise4Exception; must be caught or declared to be thrown

必须在方法上加上异常说明,然后在try-catch块里调用。

package com.ciaoshen.thinkinjava.chapter12;

public class Exercise8 {
    public static void callExercise4() throws Exercise4.Exercise4Exception{
        throw new Exercise4.Exercise4Exception("I call Exercise4Exception in Exercise 8!");
    }
    public static void main(String[] args) {
        try {
            Exercise8.callExercise4();
        } catch (Exercise4.Exercise4Exception e) {
            e.showMessage();
        }
    }
}

Exercise 9

package com.ciaoshen.thinkinjava.chapter12;

public class Exercise9 {
    public static class Exercise9Exception1 extends Exception {
        private static final long serialVersionUID = 0;
        private String message = "I am Exception 1 of Exercise 9!";
        public Exercise9Exception1() {
            super();
        }
        public Exercise9Exception1(String msg) {
            message = msg;
        }
        public String getMessage() {
            return message;
        }
    }
    public static class Exercise9Exception2 extends Exception {
        private static final long serialVersionUID = 0;
        private String message = "I am Exception 2 of Exercise 9!";
        public Exercise9Exception2() {
            super();
        }
        public Exercise9Exception2(String msg) {
            message = msg;
        }
        public String getMessage() {
            return message;
        }
    }
    public static class Exercise9Exception3 extends Exception {
        private static final long serialVersionUID = 0;
        private String message = "I am Exception 3 of Exercise 9!";
        public Exercise9Exception3() {
            super();
        }
        public Exercise9Exception3(String msg) {
            message = msg;
        }
        public String getMessage() {
            return message;
        }
    }
    public static void throwThreeException(int inNum) throws Exception{
        switch (inNum) {
            case 1: throw new Exercise9.Exercise9Exception1();
            case 2: throw new Exercise9.Exercise9Exception2();
            case 3: throw new Exercise9.Exercise9Exception3();
            default: System.out.println("Congratulation! You win!");
        }
    }
    public static void main(String[] args) {
        int i = 1;
        while (true) {
            try {
                throwThreeException(i);
                break;
            } catch (Exception e) {
                System.out.println(e.getMessage());
                i++;
            }
        }
    }
}

Exercise 10

package com.ciaoshen.thinkinjava.chapter12;

public class Exercise10 {
    public static class ExceptionInG extends Exception {
        private static final long serialVersionUID = 0;
        public ExceptionInG() {
            super();
            System.out.println("Hello I am ExceptionInG!");
        }
    }
    public static class ExceptionInF extends Exception {
        private static final long serialVersionUID = 0;
        public ExceptionInF() {
            super();
            System.out.println("Hello I am ExceptionInF!");
        }
    }
    public static void f() throws ExceptionInF {
        try {
            g();
        } catch (ExceptionInG e) {
            ExceptionInF exF= new ExceptionInF();
            exF.initCause(e);
            throw exF;
        }
    }
    public static void g() throws ExceptionInG {
        throw new ExceptionInG();
    }
    public static void main(String[] args) {
        try {
            f();
        } catch (Exception e) {
            e.printStackTrace(System.out);
        }
    }
}

Exercise 11

package com.ciaoshen.thinkinjava.chapter12;

public class Exercise11 {
    public static class ExceptionInG extends Exception {
        private static final long serialVersionUID = 0;
        public ExceptionInG() {
            super();
            System.out.println("Hello I am ExceptionInG!");
        }
    }
    public static void f() throws RuntimeException {
        try {
            g();
        } catch (ExceptionInG e) {
            throw new RuntimeException(e);
        }
    }
    public static void g() throws ExceptionInG {
        throw new ExceptionInG();
    }
    public static void main(String[] args) {
        try {
            f();
        } catch (Exception e) {
            e.printStackTrace(System.out);
            e.getCause().printStackTrace(System.out);
        }
    }
}

Exercise 12

Selector.java
package com.ciaoshen.thinkinjava.chapter12;

public interface Selector {
    boolean end();
    Object current();
    void next();
}
Exercise12.java
package com.ciaoshen.thinkinjava.chapter12;

public class Exercise12 {
    public static class Sequence {
        private Object[] items;
        private int next = 0;
        public Sequence(int size) { items = new Object[size]; }
        public void add(Object x) throws  SequenceOutOfBoundsException{
            if(next < items.length) {
                items[next++] = x;
            } else {
                throw new SequenceOutOfBoundsException("Sequence#add() method add too much elements!");
            }
        }
        private static class SequenceOutOfBoundsException extends Exception {
            private static final long serialVersionUID = 0;
            public SequenceOutOfBoundsException() {
                super();
            }
            public SequenceOutOfBoundsException(String msg) {
                super(msg);
            }
        }
        private static class SequenceEmptyException extends Exception {
            private static final long serialVersionUID = 0;
            public SequenceEmptyException() {
                super();
            }
            public SequenceEmptyException(String msg) {
                super(msg);
            }
        }
        private class SequenceSelector implements Selector {
            private int i = 0;
            public boolean end() { return i == items.length; }
            public Object current() { return items[i]; }
            public void next() {
                if (i < items.length-1) {
                    i++;
                } else {
                    throw new RuntimeException(new SequenceEmptyException(this + " #next() reach the end of sequence!"));
                }
            }
        }
        public Selector selector() {
            return new SequenceSelector();
        }
    }
    public static void main(String[] args) {
        int sequenceLength = 10;
        int loopTimes = sequenceLength * 2;
        boolean loopFinish = false;
        boolean sequenceIsFull = false;
        boolean sequenceIsEmpty = false;
        Exercise12.Sequence sequence = new Exercise12.Sequence(sequenceLength);
        while (!loopFinish) {
            try {
                if (! sequenceIsFull) {
                    for (int i = 0; i < loopTimes; i++) {
                        sequence.add(Integer.toString(i));
                    }
                }
                if (! sequenceIsEmpty) {
                    Selector selector = sequence.selector();
                    for (int i = 0; i< loopTimes; i++) {
                        System.out.println(selector.current() + " ");
                        selector.next();
                    }
                }
                System.out.println("Congratulation! Procedure finished!");
                loopFinish = true;
            } catch (Exercise12.Sequence.SequenceOutOfBoundsException fullException) {
                System.out.println(fullException);
                sequenceIsFull = true;
            } catch (RuntimeException emptyException) {
                emptyException.printStackTrace(System.out);
                sequenceIsEmpty = true;
            }
        }
    }
}

Exercise 13

package com.ciaoshen.thinkinjava.chapter12;

public class Exercise13 {
    public static class Exercise13Exception1 extends Exception {
        private static final long serialVersionUID = 0;
        private String message = "I am Exception 1 of Exercise 13!";
        public Exercise13Exception1() {
            super();
        }
        public Exercise13Exception1(String msg) {
            message = msg;
        }
        public String getMessage() {
            return message;
        }
    }
    public static class Exercise13Exception2 extends Exception {
        private static final long serialVersionUID = 0;
        private String message = "I am Exception 2 of Exercise 13!";
        public Exercise13Exception2() {
            super();
        }
        public Exercise13Exception2(String msg) {
            message = msg;
        }
        public String getMessage() {
            return message;
        }
    }
    public static class Exercise13Exception3 extends Exception {
        private static final long serialVersionUID = 0;
        private String message = "I am Exception 3 of Exercise 13!";
        public Exercise13Exception3() {
            super();
        }
        public Exercise13Exception3(String msg) {
            message = msg;
        }
        public String getMessage() {
            return message;
        }
    }
    public static void main(String[] args) {
        Exception[] exceptionArray = new Exception[3];
        exceptionArray[0] = new Exercise13.Exercise13Exception1();
        exceptionArray[1] = new Exercise13.Exercise13Exception2();
        exceptionArray[2] = new Exercise13.Exercise13Exception3();

        for (int i = 0; i < 5; i++) {
            try {
                throw exceptionArray[i];
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                System.out.println("This will be executed!");
            }
        }
    }
}

Exercise 14

package com.ciaoshen.thinkinjava.chapter12;

public class Exercise14 {
    public static class OnOffSwitch {
        private static Switch sw = new Switch();
        public static void f() throws OnOffException1,OnOffException2 {throw new RuntimeException();}
    }
    public static void main(String[] args) {
            try {
                Exercise14.OnOffSwitch.sw.on();
                // Code that can throw exceptions...
                Exercise14.OnOffSwitch.f();
                Exercise14.OnOffSwitch.sw.off();
            } catch(OnOffException1 e) {
                System.out.println("OnOffException1");
                Exercise14.OnOffSwitch.sw.off();
            } catch(OnOffException2 e) {
                System.out.println("OnOffException2");
                Exercise14.OnOffSwitch.sw.off();
            }
    }
}

Exercise 15

package com.ciaoshen.thinkinjava.chapter12;

public class Exercise15 {
    public static class WithFinally {
        private static Switch sw = new Switch();
    }
    public static void main(String[] args) {
        try {
            Exercise15.WithFinally.sw.on();
            // Code that can throw exceptions...
            Exercise14.OnOffSwitch.f();
        } catch(OnOffException1 e) {
            System.out.println("OnOffException1");
        } catch(OnOffException2 e) {
            System.out.println("OnOffException2");
        } finally {
            Exercise15.WithFinally.sw.off();
        }
    }
}

Exercise 16

package com.ciaoshen.thinkinjava.chapter12;

public class Exercise16 {
    public static class Shape {
        Shape(int i) { System.out.println("Shape constructor"); }
        void dispose() { System.out.println("Shape dispose"); }
    }
    public static class Circle extends Shape {
        Circle(int i) {
            super(i);
            System.out.println("Drawing Circle");
        }
        void dispose() {
            System.out.println("Erasing Circle");
            super.dispose();
        }
    }
    public static class Triangle extends Shape {
        Triangle(int i) {
            super(i);
            System.out.println("Drawing Triangle");
        }
        void dispose() {
            System.out.println("Erasing Triangle");
            super.dispose();
        }
    }
    public static class Line extends Shape {
        private int start, end;
        Line(int start, int end) {
            super(start);
            this.start = start;
            this.end = end;
            System.out.println("Drawing Line: " + start + ", " + end);
        }
        void dispose() {
            System.out.println("Erasing Line: " + start + ", " + end);
            super.dispose();
        }
    }
    public static class CADSystem extends Shape {
        private Circle c;
        private Triangle t;
        private Line[] lines = new Line[3];
        public CADSystem(int i) {
            super(i + 1);
            for(int j = 0; j < lines.length; j++) {
                lines[j] = new Line(j, j*j);
            }
            c = new Circle(1);
            t = new Triangle(1);
            System.out.println("Combined constructor");
        }
        public void dispose() {
            System.out.println("CADSystem.dispose()");
            // The order of cleanup is the reverse
            // of the order of initialization:
            t.dispose();
            c.dispose();
            for(int i = lines.length - 1; i >= 0; i--) {
                lines[i].dispose();
            }
            super.dispose();
        }
    }
    public static void main(String[] args) {
        Exercise16.CADSystem x = new Exercise16.CADSystem(47);
        try {
            // Code and exception handling...
            return;
        } finally {
            x.dispose();
        }
    }
}

Exercise 17

package com.ciaoshen.thinkinjava.chapter12;

public class Exercise17 {
    public static class Characteristic {
        private String s;
        Characteristic(String s) {
            this.s = s;
            System.out.println("Creating Characteristic " + s);
        }
        protected void dispose() {
            System.out.println("disposing Characteristic " + s);
        }
    }
    public static class Description {
        private String s;
        Description(String s) {
            this.s = s;
            System.out.println("Creating Description " + s);
        }
        protected void dispose() {
            System.out.println("disposing Description " + s);
        }
    }
    public static class LivingCreature {
        private Characteristic p = new Characteristic("is alive");
        private Description t = new Description("Basic Living Creature");
        LivingCreature() {
            System.out.println("LivingCreature()");
        }
        protected void dispose() {
            System.out.println("LivingCreature dispose");
            t.dispose();
            p.dispose();
        }
    }
    public static class Animal extends LivingCreature {
        private Characteristic p = new Characteristic("has heart");
        private Description t = new Description("Animal not Vegetable");
        Animal() { System.out.println("Animal()"); }
        protected void dispose() {
            System.out.println("Animal dispose");
            t.dispose();
            p.dispose();
            super.dispose();
        }
    }
    public static class Amphibian extends Animal {
        private Characteristic p = new Characteristic("can live in water");
        private Description t = new Description("Both water and land");
        Amphibian() {
            System.out.println("Amphibian()");
        }
        protected void dispose() {
            System.out.println("Amphibian dispose");
            t.dispose();
            p.dispose();
            super.dispose();
        }
    }
    public static class Frog extends Amphibian {
        private Characteristic p = new Characteristic("Croaks");
        private Description t = new Description("Eats Bugs");
        public Frog() { System.out.println("Frog()"); }
        protected void dispose() {
            System.out.println("Frog dispose");
            t.dispose();
            p.dispose();
            super.dispose();
        }
    }
    public static void main(String[] args) {
        Exercise17.Frog frog = new Exercise17.Frog();
        try {
            System.out.println("Bye!");
        } finally {
            frog.dispose();
        }
    }
}

Exercise 18

package com.ciaoshen.thinkinjava.chapter12;

public class Exercise18 {
    private static class VeryImportantException extends Exception {
        private static final long serialVersionUID = 0;
    }
    private static class HoHumException extends Exception {
        private static final long serialVersionUID = 0;
    }
    private static class ThirdException extends Exception {
        private static final long serialVersionUID = 0;
    }
    public static void f() throws VeryImportantException {
        throw new VeryImportantException();
    }
    public static void dispose() throws HoHumException {
        throw new HoHumException();
    }
    public static void third() throws ThirdException {
        throw new ThirdException();
    }
    public static void main(String[] args) {
        try {
            try {
                Exercise18.f();
            } finally {
                Exercise18.third();
                Exercise18.dispose();
            }
        } catch(Exception e) {
            System.out.println(e);
        }
    }
}

Exercise 19

package com.ciaoshen.thinkinjava.chapter12;

public class Exercise19 {
    private static class VeryImportantException extends Exception {
        private static final long serialVersionUID = 0;
    }
    private static class HoHumException extends Exception {
        private static final long serialVersionUID = 0;
    }
    public static void f() throws VeryImportantException {
        throw new VeryImportantException();
    }
    public static void dispose() throws HoHumException {
        throw new HoHumException();
    }
    public static void main(String[] args) {
        try {
            try {
                Exercise19.f();
            }
        } catch(Exception e) {
            System.out.println(e);
        } finally {
            Exercise19.dispose();
        }
    }
}

Exercise 20

package com.ciaoshen.thinkinjava.chapter12;

public class Exercise20 {
    public static class EmpireArgument extends Exception {
        private static final long serialVersionUID = 0;
    }
    public static class BaseballException extends Exception {
        private static final long serialVersionUID = 0;
    }
    public static class Foul extends BaseballException {
        private static final long serialVersionUID = 0;
    }
    public static class Strike extends BaseballException {
        private static final long serialVersionUID = 0;
    }
    public static abstract class Inning {
        public Inning() throws BaseballException {}
        public void event() throws BaseballException {
            // Doesn’t actually have to throw anything
        }
        public abstract void atBat() throws Strike, Foul;
        public void walk() {} // Throws no checked exceptions
    }
    public static class StormException extends Exception {
        private static final long serialVersionUID = 0;
    }
    public static class RainedOut extends StormException {
        private static final long serialVersionUID = 0;
    }
    public static class PopFoul extends Foul {
        private static final long serialVersionUID = 0;
    }
    public static interface Storm {
        public void event() throws RainedOut;
        public void rainHard() throws RainedOut;
    }
    public static class StormyInning extends Inning implements Storm {
        // OK to add new exceptions for constructors, but you
        // must deal with the base constructor exceptions:
        public StormyInning() throws RainedOut, BaseballException {}
        public StormyInning(String s) throws Foul, BaseballException {}
        // Regular methods must conform to base class:
        //! void walk() throws PopFoul {} //Compile error
        // Interface CANNOT add exceptions to existing
        // methods from the base class:
        //! public void event() throws RainedOut {}
        // If the method doesn’t already exist in the
        // base class, the exception is OK:
        public void rainHard() throws RainedOut {}
        // You can choose to not throw any exceptions,
        // even if the base version does:
        public void event() {}
        // Overridden methods can throw inherited exceptions:
        public void atBat() throws PopFoul {}
        public void throwEmpireArgument() throws EmpireArgument {}
    }

    public static void main(String[] args) {
        try {
            StormyInning si = new StormyInning();
            si.atBat();
            si.throwEmpireArgument();
        } catch(PopFoul e) {
            System.out.println("Pop foul");
        } catch(RainedOut e) {
            System.out.println("Rained out");
        } catch(BaseballException e) {
            System.out.println("Generic baseball exception");
        } catch(EmpireArgument e) {
            System.out.println("Empire Argument exception");
        }
        // Strike not thrown in derived version.
        try {
            // What happens if you upcast?
            Inning i = new StormyInning();
            i.atBat();
            // You must catch the exceptions from the
            // base-class version of the method:
        } catch(Strike e) {
            System.out.println("Strike");
        } catch(Foul e) {
            System.out.println("Foul");
        } catch(RainedOut e) {
            System.out.println("Rained out");
        } catch(BaseballException e) {
            System.out.println("Generic baseball exception");
        }
    }
}

Exercise 21

package com.ciaoshen.thinkinjava.chapter12;

public class Exercise21 {
    public static class BaseException extends Exception {
        private static final long serialVersionUID = 0;
    }
    public static class BaseClass {
        public BaseClass() throws BaseException {
            System.out.println("I am Constructor of BaseClass!");
        }
    }
    public static class DerivedClass extends BaseClass {
        public DerivedClass() {
            try {
                //super();  //ERROR: super() must be the first statement in constructor
            } catch (BaseException e) {
                System.out.println("Exception in BaseClass captured!");
            }
        }
    }
    public static void main(String[] args) {
        DerivedClass derived = new DerivedClass();
    }
}

Exercise 22

package com.ciaoshen.thinkinjava.chapter12;
import java.io.*;

public class Exercise22 {
    public static class FailingConstructor {
        private BufferedReader br;
        public FailingConstructor(String path) throws FileNotFoundException {
            try {
                br = new BufferedReader(new FileReader(new File(path)));
                System.out.println(path + " opened!");
            } catch (FileNotFoundException e) {
                throw new FileNotFoundException(path);
            }
        }
        public void close() throws IOException {
            br.close();
        }
    }
    public static void main(String[] args) {
        String correctPath = "/Users/Wei/java/com/ciaoshen/thinkinjava/chapter12/Exercise22.java";
        String wrongPath = "/Users/HelloKitty/java/Chapter12.java";
        FailingConstructor fcWrong;
        FailingConstructor fcRight;
        try {
            fcRight = new FailingConstructor(correctPath);
            try {
                System.out.println("Right file opened!! Working Working!!!");
            } finally {
                try {
                    fcRight.close();
                    System.out.println(correctPath + " closed!");
                } catch(IOException e) {
                    System.out.println("FATAL ERROR: ");
                    e.printStackTrace();
                }
            }
            fcWrong= new FailingConstructor(wrongPath);
            try {
                System.out.println("Wrong file opened!! Working Working!!!");
            } finally {
                try {
                    fcWrong.close();
                    System.out.println(wrongPath+ " closed!");
                } catch(IOException e) {
                    System.out.println("FATAL ERROR: ");
                    e.printStackTrace();
                }
            }
            System.out.println("All work finished! Go home now!");
        } catch (FileNotFoundException e) {
            System.out.println("File " + e.getMessage() + " not found!");
        }
    }
}

Exercise 23

package com.ciaoshen.thinkinjava.chapter12;
import java.io.*;

public class Exercise23 {
    public static class FailingConstructor {
        private BufferedReader br1;
        private BufferedReader br2;
        public FailingConstructor(String path1, String path2) throws FileNotFoundException {
            br1 = new BufferedReader(new FileReader(new File(path1)));
            try {
                System.out.println(path1 + " opened!");
            } finally {
                try {
                    br1.close();
                } catch (IOException e) {
                    System.out.println(path1 + " cannot be closed!");
                }
            }
            br2 = new BufferedReader(new FileReader(new File(path2)));
            try {
                System.out.println(path2 + " opened!");
            } finally {
                try {
                    br2.close();
                } catch (IOException e) {
                    System.out.println(path1 + " cannot be closed!");
                }
            }
        }
    }
    public static void main(String[] args) {
        String correctPath = "/Users/Wei/java/com/ciaoshen/thinkinjava/chapter12/Exercise22.java";
        String wrongPath = "/Users/HelloKitty/java/Chapter12.java";
        FailingConstructor fc;
        try {
            fc= new FailingConstructor(correctPath,wrongPath);
            System.out.println("Two files opened!! Working Working!!!");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

Exercise 24

package com.ciaoshen.thinkinjava.chapter12;
import java.io.*;

public class Exercise24 {
    public static class FailingConstructor {
        private BufferedReader br1;
        private BufferedReader br2;
        public FailingConstructor(String path1, String path2) throws FileNotFoundException {
            br1 = new BufferedReader(new FileReader(new File(path1)));
            System.out.println(path1 + " opened!");
            br2 = new BufferedReader(new FileReader(new File(path2)));
            System.out.println(path2 + " opened!");
        }
        public void dispose() {
            try {
                br1.close();
                System.out.println("File 1 closed!");
                br2.close();
                System.out.println("File 2 closed!");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        String correctPath = "/Users/Wei/java/com/ciaoshen/thinkinjava/chapter12/Exercise22.java";
        String wrongPath = "/Users/HelloKitty/java/Chapter12.java";
        FailingConstructor fc;
        try {
            //fc= new FailingConstructor(correctPath,wrongPath);
            fc= new FailingConstructor(correctPath,correctPath);
            try {
                System.out.println("Two files opened!! Working Working!!!");
            } finally {
                fc.dispose();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

Exercise 25

派生类抛出的异常,必须是基类声明抛出的异常或者是它的派生异常,也可以不抛出异常。但如果派生类向上转型成了基类,在catch语句块里,必须去捕获基类里抛出的异常类型,而不能用派生异常。就像虽然有多态,派生类被强制转型成基类后,调用基类方法其实执行的是派生类覆盖的版本。但是派生类新声明的方法,还是不能被调用的。因为,毕竟还是要遵守基类的接口。

package com.ciaoshen.thinkinjava.chapter12;

public class Exercise25 {
    public static class BaseException extends Exception {
        private static final long serialVersionUID = 0;
    }
    public static class DerivedException extends BaseException {
        private static final long serialVersionUID = 0;
    }
    public static class DerivedLevelTwoException extends DerivedException {
        private static final long serialVersionUID = 0;
    }

    public static class A {
        public void foo() throws BaseException {
            throw new BaseException();
        }
    }
    public static class B extends A {
        public void foo() throws DerivedException {
            throw new DerivedException();
        }
    }
    public static class C extends B {
        public void foo() throws DerivedLevelTwoException {
            throw new DerivedLevelTwoException();
        }
    }
    public static class D extends C {
        public void foo() {}
    }
    public static void main(String[] args) {
        C myC = new C();
        A myA = (A)myC;
        try {
            myA.foo();
        } catch (BaseException e) {
            e.printStackTrace();
        }
        D myD = new D();
        myD.foo();
    }
}

Exercise 26

package com.ciaoshen.thinkinjava.chapter12;
import java.io.*;

public class Exercise26 {
    // Pass all exceptions to the console:
    public static void main(String[] args) throws Exception {
        // Open the file:
        FileInputStream file = new FileInputStream("/Users/HelloKitty.MainException.java");
        // Use the file ...
        // Close the file:
        file.close();
    }
}

Exercise 27

package com.ciaoshen.thinkinjava.chapter12;

public class Exercise27 {
    public static void main(String[] args) {
        try {
            int arrayLength = 5;
            int[] arrayToOverflow = new int[arrayLength];
            int loopTimes = 10;
            for (int i=0; i<loopTimes; i++) {
                arrayToOverflow[i] = i;
            }
        } catch (ArrayIndexOutOfBoundsException e) {
            throw new RuntimeException(e);    
        }
    }
}

Exercise 28

package com.ciaoshen.thinkinjava.chapter12;

public class Exercise28 {
    public static class Exercise28Exception extends RuntimeException {
        private static final long serialVersionUID = 0l;
        private String message = "NULL";
        public Exercise28Exception(){
            super();
        }
        public Exercise28Exception(String msg) {
            message = msg;
        }
        public void showMessage() {
            System.out.println(message);
        }
    }
    public static void main(String[] args) {
        String justAMessage = "I am the message of Exercise4Exception!";
        throw new Exercise28Exception(justAMessage);
    }
}

Exercise 29

package com.ciaoshen.thinkinjava.chapter12;

public class Exercise29 {
    public static class BaseballException extends RuntimeException {
        private static final long serialVersionUID = 0;
    }
    public static class Foul extends BaseballException {
        private static final long serialVersionUID = 0;
    }
    public static class Strike extends BaseballException {
        private static final long serialVersionUID = 0;
    }
    public static abstract class Inning {
        public Inning() throws BaseballException {}
        public void event() throws BaseballException {
            // Doesn’t actually have to throw anything
        }
        public abstract void atBat() throws Strike, Foul;
        public void walk() {} // Throws no checked exceptions
    }
    public static class StormException extends RuntimeException {
        private static final long serialVersionUID = 0;
    }
    public static class RainedOut extends StormException {
        private static final long serialVersionUID = 0;
    }
    public static class PopFoul extends Foul {
        private static final long serialVersionUID = 0;
    }
    public static interface Storm {
        public void event() throws RainedOut;
        public void rainHard() throws RainedOut;
    }
    public static class StormyInning extends Inning implements Storm {
        // OK to add new exceptions for constructors, but you
        // must deal with the base constructor exceptions:
        public StormyInning() throws RainedOut, BaseballException {}
        public StormyInning(String s) throws Foul, BaseballException {}
        // Regular methods must conform to base class:
        public void walk() throws PopFoul {} // No Compile error now
        // Interface CANNOT add exceptions to existing
        // methods from the base class:
        // If the method doesn’t already exist in the
        // base class, the exception is OK:
        public void rainHard() throws RainedOut {}
        // You can choose to not throw any exceptions,
        // even if the base version does:
        public void event() {}
        // Overridden methods can throw inherited exceptions:
        public void atBat() throws PopFoul {}
    }

    public static void main(String[] args) {
        StormyInning si = new StormyInning();
        si.atBat();
        // Strike not thrown in derived version.
        // What happens if you upcast?
        Inning i = new StormyInning();
        i.atBat();
        // You must catch the exceptions from the
        // base-class version of the method:
    }
}

Exercise 30

package com.ciaoshen.thinkinjava.chapter12;

public class Exercise30 {
    public static class Annoyance extends RuntimeException {
        private static final long serialVersionUID = 0;
    }
    public static class Sneeze extends Annoyance {
        private static final long serialVersionUID = 0;
    }
    public static void wrapException(int exceptionNum) {
        try {
            switch (exceptionNum) {
                case 1: throw new Annoyance();
                case 2: throw new Sneeze();
                default: return;
            }
        } catch(Sneeze e) {
            throw new RuntimeException(e);
        } catch(Annoyance e) {
            throw new RuntimeException(e);
        }
    }
    public static void main(String[] args) {
        //wrapException(2);
        // Catch the exact type:
        for (int i : new int[] {1,2}) {
            try {
                wrapException(i);
            } catch(RuntimeException e) {
                try {
                    throw e.getCause();
                } catch(Sneeze s) {
                    System.out.println("Caught Sneeze");
                } catch(Annoyance a) {
                    System.out.println("Caught Annoyance");
                } catch(Throwable t) {
                    System.out.println("Caught other Throwable!");
                }
            }
        }
    }
}