捕获异常异常处理
1.异常处理有两种基本模式:
- 终止模式:在这种模型中,将假设错误非常关键,以至于程序无法返回到异常发生的地方继续执行,一旦异常被抛出,就表明错误已无法挽回,也不能回来继续执行。(它是Java和c++所支持的模型)
- 恢复模型:异常处理程序的工作是修正错误,然后重新尝试调用出问题的方法,并认为第二次能成功。
创建自定义异常
1.可以用记录日志来关联异常
代码例子:
class LoggingException extends Exception {
//创建一个Logger对象
private static Logger logger = Logger.getLogger("LoggingException");
public LoggingException() {
//输出字符流
StringWriter trace = new StringWriter();
//将栈跟踪输出到字符流对象
printStackTrace(new PrintWriter(trace));
//使用server日志级别将信息输出到指定对象中
logger.severe(trace.toString());
}
}
public class LoggingExceptions {
public static void main(String[] args) {
try {
throw new LoggingException();
} catch(LoggingException e) {
System.err.println("Caught " + e);
}
try {
throw new LoggingException();
} catch(LoggingException e) {
System.err.println("Caught " + e);
}
}
}
运行结果:
二月 26, 2015 9:28:23 下午 beyondboy.LoggingException
严重: beyondboy.LoggingException
at beyondboy.LoggingExceptions.main(LoggingExceptions.java:20)
Caught beyondboy.LoggingException
二月 26, 2015 9:28:25 下午 beyondboy.LoggingException
严重: beyondboy.LoggingException
at beyondboy.LoggingExceptions.main(LoggingExceptions.java:25)
Caught beyondboy.LoggingException
捕获所有异常
1.如果只是把当前异常对象重新抛出,那么printStackTrace()方法显示的将是原来异常抛出点的调用栈信息,而并非重新抛出点的信息,要想更新这个信息,可以调用fillInStackTrace()方法,这将返回一个Throwable对象,它是通过把当前调用栈信息填入原来那个异常对象而建立的(也可以通过使用异常对象的initCause()方法来保留原来的异常信息)。
代码例子:
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);
}
}
}
运行结果:
originating the exception in f()
Inside g(),e.printStackTrace()
java.lang.Exception: thrown from f()
at beyondboy.Rethrowing.f(Rethrowing.java:6)
at beyondboy.Rethrowing.g(Rethrowing.java:10)
at beyondboy.Rethrowing.main(Rethrowing.java:28)
main: printStackTrace()
java.lang.Exception: thrown from f()
at beyondboy.Rethrowing.f(Rethrowing.java:6)
at beyondboy.Rethrowing.g(Rethrowing.java:10)
at beyondboy.Rethrowing.main(Rethrowing.java:28)
originating the exception in f()
Inside h(),e.printStackTrace()
java.lang.Exception: thrown from f()
at beyondboy.Rethrowing.f(Rethrowing.java:6)
at beyondboy.Rethrowing.h(Rethrowing.java:19)
at beyondboy.Rethrowing.main(Rethrowing.java:34)
main: printStackTrace()
java.lang.Exception: thrown from f()
at beyondboy.Rethrowing.h(Rethrowing.java:23)
at beyondboy.Rethrowing.main(Rethrowing.java:34)
2.如果在捕获异常之后抛出另一种异常,有关原来异常发生点的信息会丢失,剩下的是与新的抛出点有关的信息。
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(OneException e) {
System.out.println(
"Caught in inner try, e.printStackTrace()");
e.printStackTrace(System.out);
throw new TwoException("from inner try");
}
} catch(TwoException e) {
System.out.println(
"Caught in outer try, e.printStackTrace()");
e.printStackTrace(System.out);
}
}
}
运行结果:
originating the exception in f()
Caught in inner try, e.printStackTrace()
beyondboy.OneException: thrown from f()
at beyondboy.RethrowNew.f(RethrowNew.java:14)
at beyondboy.RethrowNew.main(RethrowNew.java:19)
Caught in outer try, e.printStackTrace()
beyondboy.TwoException: from inner try
at beyondboy.RethrowNew.main(RethrowNew.java:24)
使用finally进行清理
1.当Java中的异常不允许我们回到异常抛出的地点时,那么我们可以把try块放在循坏里,就建立了一个”程序继续执行之前必须要达到”的条件。
代码例子:
class ThreeException extends Exception {}
public class FinallyWorks {
static int count = 0;
public static void main(String[] args) {
while(true) {
try {
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");
//跳出while
if(count == 2) break;
}
}
}
}
运行结果:
ThreeException
In finally clause
No exception
In finally clause
2.如果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);
}
}
}
运行结果:
A trivial exception
异常的限制
1.当覆盖方法的时候,只能抛出在基类方法的异常说明列出的那些异常,派生类构造器的异常说明必须包含基类构造器的异常说明,而不能捕获基类构造器抛出的异常,当派生类覆盖有抛出异常的基类方法时,可以不带有异常说明,但当覆盖没有抛出异常的基类方法时,不可以带有异常说明。
代码例子:
class BaseballException extends Exception {}
class Foul extends BaseballException {}
class Strike extends BaseballException {}
abstract class Inning {
public Inning() throws BaseballException {}
public void event() throws BaseballException {
}
public abstract void atBat() throws Strike, Foul;
//只能抛出不检查类型的异常
public void walk() {}
}
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 {
//可以增加抛出新的异常,但必需包含基类的构造器的异常
public StormyInning()
throws RainedOut, BaseballException {}
public StormyInning(String s)
throws Foul, BaseballException {}
//基类的这个方法没有声明抛出异常,所以会编译错误
//! void walk() throws PopFoul {}
//不能改变其基类定义抛出的异常说明
//! public void event() throws RainedOut {}
// 这个可以抛出任何异常
public void rainHard() throws RainedOut {}
//不能抛出任何异常
public void event() {}
//重写可以抛出基类抛出其异常的子类
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");
}
try {
// 向上转型
Inning i = new StormyInning();
i.atBat();
//必须根据来基类的方法抛出的异常类型来捕捉
} 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.在关闭物理资源中,由于finally会每次都执行清理代码,如果构造器在其执行过程中半途而废,也许该对象的某些部分还没有被成功创建,而这些部分在finally子句中却要被清理,这样就会产生矛盾,如果构造器有抛出异常,应该在创建需要清理的对象之后,立即进入一个try-finally语句块。
代码例子:
class NeedsCleanup {
//构造器初始出现任何异常
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 extends NeedsCleanup {
//构造器初始可能会出现异常的
public NeedsCleanup2() throws ConstructionException {}
}
public class CleanupIdiom {
public static void main(String[] args) {
// 第一种创建一个构造器没有异常对象的清理情况
NeedsCleanup nc1 = new NeedsCleanup();
try {
} finally {
nc1.dispose();
}
// 第二种创建多个构造器没有异常对象的清理情况
NeedsCleanup nc2 = new NeedsCleanup();
NeedsCleanup nc3 = new NeedsCleanup();
try {
// ...
} finally {
nc3.dispose();
nc2.dispose();
}
//第三种创建多个构造器可能会抛出异常对象的清理情况
try {
NeedsCleanup2 nc4 = new NeedsCleanup2();
try {
NeedsCleanup2 nc5 = new NeedsCleanup2();
try {
// ...
} finally {
nc5.dispose();
}
} catch(ConstructionException e) {
System.out.println(e);
} finally {
nc4.dispose();
}
} catch(ConstructionException e) {
System.out.println(e);
}
}
}
运行结果:
NeedsCleanup 1 disposed
NeedsCleanup 3 disposed
NeedsCleanup 2 disposed
NeedsCleanup 5 disposed
NeedsCleanup 4 disposed
异常匹配
1.当处理被检查异常时,编译器可能会强制你在可能还没准备好处理错误的时候被迫加上catch句子,也许就导致了“吞食则有害”的问题,这里可以通过异常链的方法将检查异常转换成不检查的异常。
代码例子:
块处理这不检查的异常
wce.throwRuntimeException(3);
//测试各种异常类型
for(int i = 0; i < 4; i++)
try {
if(i < 3)
wce.throwRuntimeException(i);
else
throw new SomeOtherException();
} catch(SomeOtherException e) {
System.out.println("SomeOtherException: " + e);
} catch(RuntimeException re) {
try {
//获取最原始的异常信息
throw re.getCause();
} catch(FileNotFoundException e) {
System.out.println("FileNotFoundException: " + e);
} catch(IOException e) {
System.out.println("IOException: " + e);
} catch(Throwable e) {
System.out.println("Throwable: " + e);
}
}
}
}
运行结果:
FileNotFoundException: java.io.FileNotFoundException
IOException: java.io.IOException
Throwable: java.lang.RuntimeException: Where am I?
SomeOtherException: beyondboy.SomeOtherException