码迷,mamicode.com
首页 > 编程语言 > 详细

Thinking in Java from Chapter 11

时间:2015-03-19 23:42:28      阅读:223      评论:0      收藏:0      [点我收藏+]

标签:

From Thinking in Java 4th Edition

 

持有对象

// Simple container example (produces compiler warnings.)
// {ThrowsException}
import java.util.*;

class Apple {
	private static long counter;
	private final long id = counter++;
	public long id() { return id;}
}

class Orange {}

public class ApplesAndOrangesWithoutGenerics {
	@SuppressWarnings("unchecked")
	
	public static void main(String[] args) {
		ArrayList apples = new ArrayList();
		for(int i = 0; i < 3; ++i)
			apples.add(new Apple());
		
		// Not prevented from adding an Orange to apples:
		apples.add(new Orange());
		for(int i = 0; i < apples.size(); ++i)
			((Apple)apples.get(i)).id();
			// Orange is detected only at run time
	}
}

 

 

抛出一个异常

if(t == null)
	throw new NullPointerException();

所有标准异常都有两个构造器:一个是默认构造器;另一个是接受字符串作为参数,以便能把相关信息放入异常对象的构造器:

throw new NullPointerException("t = null");

 

异常类型的根类:Throwable对象

 

捕获异常

try {
	// Code that might generate exceptions
} catch(Type1 id1) {
	// Handle exceptions of Type1
} catch(Type2 id2) {
	// Handle exceptions of Type2
} catch(Type3 id3) {
	// Handle exceptions of Type3
}

// etc...

异常处理必须紧跟在try块之后。当异常被抛出时,异常处理机制将负责搜寻参数与异常类型相匹配的第一个处理程序。然后进入catch自居执行,此时认为异常得到了处理。

 

 

建立自己的异常必须从已有的异常类继承:

// Creating your own exceptions.
class SimpleException extends Exception {}

public class InheritingExceptions {
	public void f() throws SimpleException {
		System.out.println("Throw SimpleException from f()");
		throw new SimpleException();
	}
	
	public static void main(String[] args){
		InheritingExceptions sed = new InheritingExceptions();
		try {
			sed.f();
		} catch(SimpleException e) {
			System.out.println("Caught it!");
		}
	}
} /* Output:
Throw SimpleException from f()
Caught it!
*/ 

 

也可以为异常类定义一个接受字符串参数的构造器:

class MyException extends Exception {
	public MyException() {}
	public MyException(String msg) { super(msg);}
}

public class FullConstructors {
	public static void f() throws MyException {
		System.out.println("Throwing MyException from f()");
		throw new MyException();
	}
	
	public static void g() throws MyException {
		System.out.println("Throwing MyException from g()");
		throw new MyException("Originated in g()");
	}
	
	public static void main(String[] args){
		try {
			f();
		} catch(MyException e) {
			e.printStackTrace(System.out);
		}
		
		try {
			g();
		} catch(MyException e) {
			e.printStackTrace(System.out);
		}
	}
} /* Output:
Throwing MyException from f()
MyException
		at FullConstructors.f(FullConstructors.java:11)
		at FullConstructors.main(FullConstructors.java:19)
Throwing MyException from g()
MyException: Originated in g()
		at FullConstructors.g(FullConstructors.java:15)
		at FullConstructors.main(FullConstructors.java:24)
*/

 

 

为了说明某个方法可能会抛出的异常类型,Java运用了异常说明。它属于方法声明的一部分,紧跟在形式参数列表之后,并使用附加的关键字throws。所以,方法定义可能看起来像这样:

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

但是,要是写成这样:

void f() {
	// ... 
}

就表示此方法不会抛出任何异常(除了RuntimeException继承的异常,它们可以在没有异常说明的情况下被抛出。)

 

 

捕获所有异常

通过捕获异常类型的基类Exception,就可以捕获所有异常(事实上还有其他的积累,但Exception是同编程活动相关的基类):

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

最好将它放在处理程序列表的末尾,以防它抢在其他处理程序之前先把异常捕获了。

可以从Exception的基类Throwable继承的方法:

String getMessage()

String getLocalizedMessage()

用来获取详细信息,活用本地语言表示的详细信息。

 

String toString()返回对Throwable的简单描述,要是有详细信息的话,也会把它包含在内。

void printStackTrace()

void PrintStackTrace(PrintStream)

void PrintStackTrace(java.io.PrintWriter)

打印Throwable和Throwable的调用栈轨迹。调用栈显示了“把你带到异常抛出地点”的方法调用序列。

 

也可以使用Throwable从其基类Object继承的方法。对于异常处理,getClass()将返回一个表示此对象类型的信息,然后可以使用getName()方法查询这个Class对象包含包信息的名称,或者使用只产生类名称的getSimpleName()方法。

使用Exception类型的方法:

import static net.mindview.util.Print.*;

public class ExceptionMethods {
	public static void main(String[] args){
		try {
			throw new Exception("My Exception");
		} catch (Exception e) {
			print("Caught Exception");
			print("getMessage():" + e.getMessage());
			print("getLocalizedMessage()" + e.getLocalizedMessage());
			print("toString():" + e);
			print("printStackTrace():");
			e.printStackTrace(System.out);
		}
	}
}

 

 

栈轨迹

printStackTrace()方法所提供的信息可以通过getStackTrace()方法来直接访问,这个方法将:

1. 返回一个由栈轨迹中的元素所构成的数组,其中每个元素都表示栈中的一帧

2. 元素0是栈顶元素,并且是调用序列中的最后一个方法调用

3. 数组中的最后一个元素,即栈底,是调用序列中的第一个方法调用

public class WhoCalled {
	static void f() {
		// Generate an exception to fill in the stack trace
		try {
			throw new Exception();
		} catch(Exception e) {
			for(StackTraceElement ste : e.getStackTrace())
				System.out.println(ste.getMethodName());
		}
	}
	
	static void g() { f();}
	static void h() { g();}
	
	public static void main(String[] args){
		f();
		System.out.println("-----------------------");
		g();
		System.out.println("-----------------------");
		h();
		System.out.println("-----------------------");
	}
} /* Output:
f
main
-----------------------
f
g
main
-----------------------
f
g
h
main
*/

 

 

重新抛出异常会把异常抛给上一级环境中的异常处理程序,同一个try块的后续catch子句将被忽略。

如果只是把异常对象重新抛出,printStackTrace()方法显示的将是原来异常抛出点的调用栈信息。要更新这个信息,可以调用fillIngStackTrace()方法,这将返回一个Throwable对象,它是通过把当前调用栈信息填入原来那个异常对象而建立的:

public class Rethrowing {
	public static void f() throws Exception {
		System.out.println("originating the exception in f()");
		throw new Exception("thrown from f()");
	}
	
	public static void g() throws Exception {
		try {
			f();
		} catch(Exception e) {
			System.out.println("Inside g(), e.printStackTrace()");
			e.printStackTrace(System.out);
			throw e;
		}
	}
	
	public static void h() throws Exception {
		try {
			f();
		} catch(Exception e) {
			System.out.println("Inside h(), e.printStackTrace()");
			e.printStackTrace(System.out);
			throw (Exception)e.fillInStackTrace();
		}
	}
	
	public static void main(String[] args){
		try {
			g();
		} catch(Exception e) {
			System.out.println("main: printStackTrace()");
			e.printStackTrace(System.out);
		}
		
		try {
			h();
		} catch(Exception e) {
			System.out.println("main: printStackTrace()");
			e.printStackTrace(System.out);
		}
	}
} /* Output:
originating the exception in f()
Inside g(), e.printStackTrace()
java.lang.Exception: thrown from f()
	at Rethrowing.f(Rethrowing.java:7)
	at Rethrowing.g(Rethrowing.java:11)
	at Rethrowing.main(Rethrowing.java:29)
main: printStackTrace()
java.lang.Exception: thrown from f()
	at Rethrowing.f(Rethrowing.java:7)
	at Rethrowing.g(Rethrowing.java:11)
	at Rethrowing.main(Rethrowing.java:29)
originating the exception in f()
Inside h(), e.printStackTrace()
jva.lang.Exception: thrown from f()
	at Rethrowing.f(Rethrowing.java:7)
	at Rethrowing.g(Rethrowing.java:20)
	at Rethrowing.main(Rethrowing.java:35)
main: printStackTrace()
java.lang.Exception: thrown from f()
	at Rethrowing.h(Rethrowing.java:24)
	at Rethrwoing.main(Rethrowing.java:35)
*/

 

有可能在捕获异常之后抛出另一个异常,其效果类似于使用fillInStackTrace(),有关原来异常发生点的信息会丢失,剩下的是与新的抛出点有关的信息:

class OneException extends Exception {
	public oneException(String s) { super(s);}
}

class TwoException extends Exception {
	public TwoException(String s) { super(s);}
}

public class RethrowNew {
	public static void f() throws OneException {
		System.out.println("originating the exception in f()");
		throw new OneException("thrown from f()");
	}
	
	public static void main(String[] args){
		try {
			try {
				f();
			} catch(OneExceptione) {
				System.out.println("Caught in inner try, e.printStackTrace()");
				e.printStackTrace(System.out);
				throw new TwoException("from inner try");
			}
		} catch(TwoException) {
			System.out.println("Caught in outer try, e.printStackTrace()");
			e.printStackTrace(System.out);
		}
	}
} /* Output:
originating the exception in f()
Caught in inner try, e.printStackTrace()
OneException: thrown from f()
	at RethrowNew.f(RethrowNew.java:15
	at RethrowNew.main(RethrowNew.java:20)
Caught in outer try, e.printStackTrace()
TwoException: from inner try
	at RethrowNew.main(RethrowNew.java:25)
*/

 

 

异常链

所有的Throwable的子类在构造器中都可以接受一个cause对象作为参数。这个cause就用来表示原始异常。在Throwable子类中,只有三种基本的异常类提供了带cause参数的构造器:Error, Exception, RuntimeException。

在运行时动态地向DynamicFields对象添加字段:

import static net.mindview.util.Print.*;

class DynamicFieldsException extends Exception {}

public class DynamicFields {
	private Object[][] fields;
	
	public DynamicFields(int initialSize) {
		fields = new Object[initialSize][2];
		
		for(int i = 0; i < initialSize; ++i)
			fields[i] = new Object[] {null, null};
	}
	
	public String toString() {
		StringBuilder result = new StringBuilder();
		
		for(Object[] obj : fields) {
			result.append(Obj[0]);
			result.append(": ");
			result.append(Obj[1]);
			result.append("\n");
		}
		
		return result.toString();
	}
	
	private int hasField(String id) {
		for(int i = 0; i < fields.length; ++i)
			if(id.equals(fields[i][0]))
				return i;
		
		return -1;
	}
	
	private int getFieldNumber (String id) throws  NoSuchFieldException {
		int fieldNum = hasField(id);
		if(fieldNum == -1)
			throw new NoSuchFieldException();
		
		return fieldNum;
	}
	
	private int makeField(String id) {
		for(int i = 0; i < fields.length; ++i)
			if(fields[i][0] == null){
				field[i][0] = id;
				return i;
			}
		
		// No empty fields. Add one:
		Object[][] tmp = new Object[fields.length + 1][2];
		for(int i = 0; i < fields.length; ++i)
			tmp[i] = fieldsp[i];
		for(int i = fields.length; i < tmp.length; ++i)
			tmp[i] = new Object[] {null, null};
		fields = tmp;
		
		// Recursive call with expanded fields:
		return makeField(id);
	}
	
	public Object getField(String id) throws NoSuchFieldException {
		return fields[getFieldNumber(id)][1];
	}
	
	public Object setField(String id, Object value) throws DynamicFieldsException {
		if(null == value) {
			// Most exceptions don‘t have a "cause" constructor.
			// In these cases you must use initCause().
			DynamicFieldsException dfe = new DynamicFieldsException();
			dfe.initCause(new NullPointerException());
			throw dfe;
		}
		
		int fieldNumber = hasField(id);
		if(-1 == fieldNumber)
			fieldNumber = makeField(id);
		Object result = null;
		
		try {
			result = getField(id);	// Get old value
		} catch(NoSuchFieldException e) {
			// Use constructor that takes "cause":
			throw new RuntimeException(e);
		}
		
		fields[fieldNumber][1] = value;
		return result;
	}
	
	public static void main(String[] args){
		DynamicFields df = new DynamicFields(3);
		print(df);
		
		try {
			df.setField("d", "A value for d");
			df.setField("number", 47);
			df.setField("number2", 48);
			print(df);
			
			df.setField("d", "A new value for d");
			df.setField("number3", 11);
			print("df: " + df);
			
			print("df.getField(\"d\") : " + df.getField("d"));
			Object field = df.setField("d", null);	// Exception
		} catch(NoSuchFieldException e) {
			e.printStackTrace(System.out);
		} catch(DynamicFieldsException e) {
			e.printStackTrace(System.out);
		}
	}	
} /* Output:
null: null
null: null
null: null

d: A value for d
number: 47
number2: 48

df: d: A new value for d
number: 47
number2: 48
number3: 11

df.getField("d") : A new value for d
DynamicFieldsException
	at DynamicFields.setField(DynamicFields.java:64);
	at DynamicFields.main(DynamicFields.java:94);
Caused by: java.lang.NullPointerException
	at DynamicFields.setFields(DynamicFields.java:66);
*/

 

 

 

Java标准对象

RuntimeException是一个特例,它也许会穿越所有的执行路径直达main()方法,而不会被捕获。编译器不需要异常说明,其输出被报告给了System.err。在程序退出前,将调用异常的printStackTrace()方法。

 

 

使用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
}

实例:

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(2 == count) break; // out of "while"
			}
		}
	}
} /* Output:
ThreeException
In finally clause
No exception
In finally clause
*/

 

当需要把除内存之外的资源恢复到它们的初始状态时,就要用到finally子句。这种需要清理的资源包括

1. 已经打开的文件

2. 已经打开的网络

3. 在屏幕上画的图形

4. 甚至可以是外部世界的某个开关

import static net.mindview.uti.Print.*;

public class Switch {
	private boolean state = false;
	public boolean read() { return state; }
	public void on() { state = true; print(this); }
	public void off() { state = false; print(this); }
	public String toString() { return state ? "on" : "off"; }
}

public class OnOffException1 extends Exception {}
public class OnOffException2 extends Exception {}

// 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();
		}
	}
} /* Output:
on
off
*/

程序的目的是要保证main()方法结束的时候开关必须关闭。但有可能异常被抛出,但没被处理程序捕获,这时候sw.off()就得不到调用。但是有了finally,只有把try块中的清理代码放在一处即可:

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();
		}
	}
} /* Output:
on
off
*/

 

甚至在异常美哦与被当前的异常处理程序捕获的情况下,异常处理机制也会在跳到更高一层的异常处理程序之前,执行finally子句:

import static net.mindview.util.Print.*;

class FourException extends Exception {}

public class AlwaysFinally {
	public static void main(String[] args){
		print("Entering first try block");
		try {
			print("Entering second try block");
			try {
				throw new FourException();
			} finally {
				print("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");
		}
	}
} /* Output:
Entering first try block
Entering second try block
finally in 2nd try block
Caught FourException in 1st try block
finally in 1st try block
*/

 

在return中使用finally

import static net.mindview.util.Print.*;

public class MultipleReturns {
	public static void f(int i) {
		print("Initialization that requires cleanup");
		try {
			print("Point 1");
			if(1 == i) return;
			
			print("Point 2");
			if(2 == i) return;
			
			print("Point 2");
			if(2 == i) return;
			
			print("Point 3");
			if(3 == i) return;
			
			print("End");
			return;
		} finally {
			print("Performing cleanup");
		}
	}
	
	public static void main(String[] args){
		for(int i = 1; i <= 4; ++i)
			f(i);
	}
} /* Output:
Initialization that requires cleanup
Point 1
Performing cleanup
Initialization that requires cleanup
Point 1
Point 2
Performing cleanup
Initialization that requires cleanup
Point 1
Point 2
Point 3
Performing cleanup
Initialization that requires cleanup
Point 1
Point 2
Point 3
End
Performing cleanup
*/

可以看出,无论从哪里返回,finally总是会执行。

 

 

用某些方式使用finally可能会导致异常被忽略

class VeryImportantException extends Exception {
	public String toString() {
		return "A very important exception!";
	}
}

class HoHumException extends Exception {
	public 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(Exception e){
			System.out.println(e);
		}
	}
} /* Output:
A trivial exception
*/

 

一种更加简单的丢失异常的方式是从finally子句中返回:

public class ExceptionSilencer {
	public class void main(String[] args){
		try {
			throw new RuntimeException();
		} finally {
			// Using ‘return‘ inside the finally block
			// will silence any thrown exception
			return;
		}
	}
}

 

 

异常的限制

当覆盖方法的时候,只能抛出在基类方法的异常说明列出的那些异常。

class BaseballException extends Exception {}
class Foul extends BaseballException {}
class Strike extends BaseballException {}

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
}

class StormException extends Exception {}
class RainedOut extends StormException {}
class PopFoul extends Foul {}

interface Storm {
	public void event() throws RainedOut;
	public void rainHard() throws RainedOut;
}

public 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 static void main(String[] args){
		try {
			StormyInning si = new StormyInning();
			si.atBat();
		} 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");
		}
		
		// 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");
		}
	}
}

 

1. Ok to add new exceptions for constructors, but you must deal with the base constructor exceptions:

public StormyInning() throws RainedOut, BaseballException {}

2. Regular methods must CONFORM to base class:

//! void walk() throws PopFoul {}	// Compile error, because the method in base class throws no checked exceptions

3. Interface CANNOT add exceptions to existing methods from the base class:

//! public void event() throws RainedOut {}    // event() method in base class only throws BaseballException

   More over, if we throws both Baseball and RaindeOut Exceptions:

public void event() throws RainedOut, BaseballException {}

then compiler will say can‘t implement event() in storm overriden method doesn‘t throw BaseballException.

4. If the method doesn‘t already exists in the base class, the excpetion is OK:

public void rainHard() throws RainedOut {}

5. You can choose to not throw any exceptions even if the base version does:

public void event() {}

6. Overridden methods can throw inherited exceptions:

public void atBat() throws PopFoul {}    // because PopFoul is inherited from Foul which is the exception throws from atBat() in base class

 

从第5点可以看出,一个出现在基类方法的异常说明中的异常,不一定会出现在派生类方法的异常说明里。这点同继承的规则明显不同,在继承中,基类的方法必须出现在派生类里。

 

 

 

构造器

构造器会把对象设置成安全的初始状态,但还会有别的动作,比如打开一个文件,这样的动作只有在对象使用完毕并且用户调用了特殊的清理方法之后才能得以清理。如果在构造器内部抛出异常,这些清理行为也许就不能正常工作了。

也许会认为finally可以解决问题,但因为finally会每次都执行清理代码。如果构造器在其执行过程中半途而废,也许该对象的某些部分还没有被成功创建,而这些部分在finally子句中却要被清理:

import java.io.*;

public class InputFile {
	private BufferReader in;
	
	public InputFile(String fname) throws Exception {
		try {
			in = new BufferedReader(new FileReader(fname));
			// Other code that might throw exceptions
		} catch(FileNotFoundException e) {
			System.out.println("Could not open " + fname);
			// Wasn‘t open, so don‘t close it
			throw e;
		} catch(Exception e) {
			// All other exceptions must close it
			try {
				in.close();
			} catch(IOException e2) {
				System.out.println("in.close() unsuccessful");
			}
			
			throw e;	// Rethrow
		} finally {
			// Don‘t close it here!!!
		}
	}
	
	public String getLine() {
		String s;
		
		try {
			s = in.readLine();
		} catch(IOException e) {
			throw new RuntimeException("readLine() failed");
		}
		
		return s;
	}
	
	public void dispose() {
		try {
			in.close();
			System.out.println("dispose() successful");
		} catch(IOException e) {
			throw new RuntimeException("in.close() failed");
		}
	}	
}

 

对于在构造阶段可能会抛出异常,并且要求清理,最安全的使用方式是使用嵌套的try子句:

public class Cleanup {
	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.");
		}
	}
} /* Output:
dispose() successful
*/

注意这里的逻辑:对InputFile对象的构造器在其自己的try语句块中有效,如果构造失败,将进入外部的catch语句。在这种方式中,finally子句在构造失败时是不会执行的,而在构造成功时将总是执行。

这种通用的清理惯用法在构造器不抛出任何异常时也应该运用,其基本规则是:在创建需要清理的对象之后,立即进入一个try-finally语句块:

// Each disposable object must be followed by a try-finally

class NeedsCleanup {	// Construction can‘t fail
	private static long counter = 1;
	private final long id = counter++;
	
	public void dispose() {
		System.out.println("NeedsCleanup " + id + " disposed");
	}
}

class ConstructionException extends Exception {}

class NeedsCleanup2 {
	// Construction can fail:
	public NeedsCleanup2() throws ConstructionException {}
}

public class CleanupIdiom {
	public static void main(String[] args){
		// Section 1:
		NeedsCleanup nc1 = new NeedsCleanup();
		
		try{
			// ...
		} finally {
			nc1.dispose();
		}
		
		// Section 2:
		// If construction cannot fail you can group objects
		NeedsCleanup nc2 = new NeedsCleanup();
		NeedsCleanup nc3 = new NeedsCleanup();
		
		try {
			// ...
		} finally {
			nc3.dispose();	// Reverse order of construction
			nc2.dispose();
		}
		
		// Section 3:
		// If construction can fail you must guard each one:
		try {
			NeedsCleanup2 nc4 = new NeedsCleanup2();
			
			try {
				NeedsCleanup2 nc5 = new NeedsCleanup2();
				
				try {
					// ...
				} finally {
					nc5.dispose();
				}
			} catch(ConstructionException e) {	// nc5 constructor
				System.out.println(e);
			} finally {
				nc4.dispose();
			}
		} catch(ConstructionException e) {	// nc4 constructor
			System.out.println(e);
		}
	}
} /* Output:
NeedsCleanup 1 disposed
NeedsCleanup 3 disposed
NeedsCleanup 2 disposed
NeedsCleanup 5 disposed
NeedsCleanup 4 disposed
*/

 

异常匹配

并不要求抛出的异常同处理程序所声明的异常完全匹配。派生类的对象也可以匹配其基类的处理程序:

// Catching exception hierarchies

class Annoyance extends Exception {}
class Sneeze extends Annoyance {}

public class Human {
	public static void main(String[] args){
		// Catch the exact type:
		try {
			throw new Sneeze();
		} catch(Sneeze s) {
			System.out.println("Caught Sneeze");
		} catch(Annoyance a) {
			System.out.println("Caught Annoyance");
		}
		
		// Catch the base type:
		try {
			throw new Sneeze();
		} catch(Annoyance a) {
			System.out.println("Caught Annoyance");
		}
	}
} /* Output:
Caught Sneeze
Caught Annoyance
*/

 

如果把捕获基类的catch子句放在最前面,以此想把派生类的异常全给“屏蔽”掉,就像这样:

try {
	throw new Sneeze();
} catch(Annoyance a) {
	// ...
} catch(Sneeze s) {
	// ...
}

则编译器会报错。

 

把异常传递给控制台

import java.io.*;

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

main()作为一个方法也可以有异常说明,这里异常的类型是Exception,它也是所有“被检查的异常”的基类。这里是通过把它传递给控制台,就不必在main()里写try-catch子句了。

 

把“被检查的异常”转换为“不检查的异常”

可以直接把“被检查的异常”包装进RuntimeException里面:

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

 

这种技巧给了一种选择,可以不写try-catch子句和/或异常说明,直接忽略异常,让它自己沿着调用栈往上“冒泡”。同时,还可以用getCause()捕获并处理特定的异常:

// "Turn off" checked exceptions
import java.io.*;
import static net.mindview.util.Print.*;

class WrapCheckedException {
	void throwRuntimeException(int type) {
		try {
			switch(type) {
				case 0: throw new FileNotFoundException();
				case 1: throw new IOExceptin();
				case 2: throw new RuntimeException("Where am I?");
				default: return;
			}
		} catch(Exception e) { // Adapt to unchecked:
			throw new RuntimeException(e);
		}
	}
}

class SomeOtherException extends Exception {}

public class TurnOffChecking {
	public static void main(String[] args){
		WrapCheckedException wce = new WrapCheckedException();
		
		// You can call throwRuntimeExceptino() without a try
		// block, and let RuntimeExceptions leave the method:
		wce.throwRuntimeException(3);
		
		// Or you can choose to catch exceptions:
		for(int i = 0; i < 4; ++i) 
			try {
				if(i < 3)
					wce.throwRuntimeException();
				else
					throw new SomeOtherException();
			} catch(SomeOtherException e) {
				print("SomeOtherException: " + e);
			} catch(RuntimeException re) {
				try {
					throw re.getCause();
				} catch(FileNotFoundException e) {
					print("FileNotFoundException: " + e);
				} catch(IOException e) {
					print("IOException: " + e);
				} catch(Throwable e) {
					print("Throwable: " + e);
				}
			}
		
	}
} /* Output:
FileNotFoundException: java.io.FileNotFoundException
IOException: java.io.IOException
Throwable: java.lang.RuntimeException: Where am I?
SomeOtherException: SomeOtherException
*/

在TurnOffChecking里,可以不用try块就调用throwRuntimeException(),因为它没有抛出“被检查的异常”。但是,当你准备好去捕获异常的时候,还是可以用try块来捕获任何你想捕获的异常的。RuntimeException要放到最后去捕获。然后把getCause()的结果(也就是被包装的那个原始异常)抛出来。这样就可以把原先的那个异常给提取出来了。 

 

Thinking in Java from Chapter 11

标签:

原文地址:http://www.cnblogs.com/kid551/p/4320394.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!