南宫网站建设,济南正宗网站建设报价,ui设计属于视觉传达吗,备案的时候需要网站吗【12.0】开场白1#xff09;java的基本理念#xff1a;结构不佳的代码不能运行#xff1b;2#xff09;改进的错误恢复机制#xff1a;是提供代码健壮性的最强有力的方式#xff1b;3#xff09;java异常#xff1a;3.1#xff09;java采用异常来提供一致的错误报告模…【12.0】开场白1java的基本理念结构不佳的代码不能运行2改进的错误恢复机制是提供代码健壮性的最强有力的方式3java异常3.1java采用异常来提供一致的错误报告模型使得构件能够与客户端代码可靠沟通3.2java异常的目的简化大型可靠程序的生成确保你的应用中没有未处理的错误3.3异常处理是java中唯一正式的错误报告机制通过编译器强制执行【12.1】概念1异常的好处能够降低错误处理代码的复杂度【12.2】基本异常1异常情形是指阻止当前方法或作用域继续执行的问题2异常处理程序是将程序从错误状态中恢复以使程序要么换一种方式运行要么继续运行下去【12.2.1】异常参数1标准异常类有2个构造器一个是默认构造器另一个是接受字符串作为参数的构造器/*异常构造器的荔枝 */
public class ExceptionTest {public static void main(String[] args) {int flag 1;if (flag 1) {throw new NullPointerException(t null);} else throw new NullPointerException();}
}
/*
Exception in thread main java.lang.NullPointerException: t nullat chapter12.ExceptionTest.main(ExceptionTest.java:5)
*/【代码解说】1把异常处理看做是一种返回机制 因为可以用抛出异常的方式从当前作用域中退出2Throwable是异常类型的基类可以抛出任意类型的 Throwable对象【12.3】捕获异常【12.3.1】try块1介绍在try块中尝试各种方法调用【12.3.2】异常处理程序1catch块表示异常处理程序在try块之后2终止与恢复2.1异常处理理论上有两种基本模型终止模型 和 恢复模型2.2程序员通常使用了 终止模型而不怎么使用恢复模型【12.4】创建自定义异常1建立新异常类的最简单方法让编译器为你产生默认构造器这通过 继承 Exception 来实现/* 荔枝-自定义异常 */
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(); // 1} catch (SimpleException e) {System.out.println(Caught it!); // 2}System.out.println(continue.); //3, 恢复模型}
}
/*
Throw SimpleException from f()
Caught it!
continue.
*/2把错误信息发送给 标准错误流 System.err 比发给 标准输出流 System.out 要好因为System.out 也许被重定向而System.err 不会随System.out 一起被重定向/* 荔枝-错误信息被定向到标准输出流 System.out 还是 标准错误输出流System.err */
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) {// public final static PrintStream out null;e.printStackTrace(System.out); // 重定向到 标准输出}try {g();} catch (MyException e) {// public final static PrintStream err null;e.printStackTrace(System.err); // 重定向到 标准错误输出e.printStackTrace(); // (同上)重定向到 标准错误输出默认}}
}
/*
Throwing MyException from f()
chapter12.MyExceptionat chapter12.FullConstructors.f(FullConstructors.java:17)at chapter12.FullConstructors.main(FullConstructors.java:27)
Throwing MyException from g()
chapter12.MyException: Originated in g()at chapter12.FullConstructors.g(FullConstructors.java:22)at chapter12.FullConstructors.main(FullConstructors.java:32)
*/【代码解说】1printStackTrace()方法打印 “从方法调用处到抛出异常处” 的方法调用序列栈2printStackTrace() 默认输出流标准错误输出流【12.4.1】异常与记录日志【荔枝-异常与记录日志 】/* 荔枝-异常与记录日志 */
class LoggingException extends Exception { // 自定义异常类型private static Logger logger Logger.getLogger(LoggingException);public LoggingException() {StringWriter trace new StringWriter();// step2, 6printStackTrace(new PrintWriter(trace)); // 打印异常信息即打印方法调用序列从方法调用处到抛异常处到 StringWriter 输出流// step3, 7logger.severe(trace.toString()); // 严重日志调用与日志级别相关联的方法。日志级别为严重 severe}
}
public class LoggingExceptions {public static void main(String[] args) throws InterruptedException {try {System.out.println(抛出第一个异常); // step1throw new LoggingException();} catch (LoggingException e) {System.err.println(Caught e); // step4}try {Thread.sleep(1000);System.out.println(抛出第二个异常); // step5throw new LoggingException();} catch (LoggingException e) {System.err.println(Caught e); // step8}}
}
/*
抛出第一个异常
十一月 27, 2017 11:49:38 上午 chapter12.LoggingException init // 日志
严重: chapter12.LoggingException // 日志 at chapter12.LoggingExceptions.main(LoggingExceptions.java:18) // 方法调用处到抛出异常处的方法调用栈Caught chapter12.LoggingException // // 异常类的toString 方法
抛出第二个异常
十一月 27, 2017 11:49:39 上午 chapter12.LoggingException init // 日志
严重: chapter12.LoggingException // 日志 at chapter12.LoggingExceptions.main(LoggingExceptions.java:25) // 方法调用处到抛出异常处的方法调用栈Caught chapter12.LoggingException // 异常类的toString 方法
*/1通常情况需要捕获和记录其他人编写的异常这就必须在异常处理程序中生成日志/* 荔枝-在异常处理程序中生成日志 */
public class LoggingExceptions2 {private static Logger logger Logger.getLogger(LoggingExceptions2);static void logException(Exception e) {StringWriter trace new StringWriter();logger.severe(trace.toString());}public static void main(String[] args) {try {throw new NullPointerException();} catch (NullPointerException e) {// 需要捕获和记录其他人编写的异常这就必须在异常处理程序中生成日志logException(e); }}
}
/*
十一月 28, 2016 2:09:22 下午 chapter12.LoggingExceptions2 logException
严重:
*/2进一步自定义异常如加入额外的构造器和成员// 自定义新异常添加了字段x 以及设定x值的 构造器和 读取数据的方法.
class MyException2 extends Exception {private int x;public MyException2() {}public MyException2(String msg) { super(msg); }public MyException2(String msg, int x) { //额外的构造器 和 成员super(msg);this.x x;}public int val() { return x; }// 还覆盖了 Throwable.getMessage() 方法, 以产生更详细的信息.Overridepublic String getMessage() { return Detail Message: x super.getMessage();}
}
public class ExtraFeatures {public static void f() throws MyException2 {print(Throwing MyException2 from f());throw new MyException2();}public static void g() throws MyException2 {print(Throwing MyException2 from g());throw new MyException2(Originated in g()); //额外的构造器}public static void h() throws MyException2 {print(Throwing MyException2 from h());throw new MyException2(Originated in h(), 47); //额外的构造器}public static void main(String[] args) {try {f();} catch (MyException2 e) {e.printStackTrace(System.out);}try {g();} catch (MyException2 e) {e.printStackTrace(System.out);}try {h();} catch (MyException2 e) {e.printStackTrace(System.out);System.out.println(e.val() e.val());}}
}
/*
Throwing MyException2 from f()
chapter12.MyException2: Detail Message: 0 nullat chapter12.ExtraFeatures.f(ExtraFeatures.java:20)at chapter12.ExtraFeatures.main(ExtraFeatures.java:32)
Throwing MyException2 from g()
chapter12.MyException2: Detail Message: 0 Originated in g()at chapter12.ExtraFeatures.g(ExtraFeatures.java:24)at chapter12.ExtraFeatures.main(ExtraFeatures.java:37)
Throwing MyException2 from h()
chapter12.MyException2: Detail Message: 47 Originated in h()at chapter12.ExtraFeatures.h(ExtraFeatures.java:28)at chapter12.ExtraFeatures.main(ExtraFeatures.java:42)
e.val() 47
*/【代码解说】1自定义新异常添加了字段x 以及设定x值的 构造器和 读取数据的方法2覆盖了 Throwable.getMessage()方法以产生更详细的信息对于异常类来说getMessage()方法类似于 toString()方法【12.5】异常说明1异常说明java鼓励程序员把方法可能抛出的异常告知调用该方法的程序员这是优雅的做法以异常说明的形式进行告知2异常说明属于方法声明在形式参数列表之后3异常说明使用了 throws 关键字后面跟潜在潜在异常类型列表方法定义如下void f() throws Exception1, Exception2, Exception3, ... {4如果抛出异常而没有进行处理编译器提醒要么处理这个异常要么在异常说明中表明该方法将产生异常这是在编译的时候作出的提醒所以这种异常称为编译时异常5【编码技巧】作弊声明抛出异常但是实际却没有抛出。好处是为异常先占个位置以后抛出这种异常就不用修改已有代码。在定义抽象基类和接口时这种能力很重要的这样派生类或接口实现类就能够抛出这些预先声明的异常不能再干货6被检查的异常编译时异常这种在编译时被强制检查的异常【12.6】捕获所有异常1通过捕获异常类型的基类 Exception 捕获所有异常通常把 捕获Exception 的 catch 子句放在处理程序列表catch块列表末尾避免它抢在其他处理程序前被捕获了2public class Exception extends ThrowableException常用的方法列表getMessage()获取详细信息
getLocalizedMessage()获取本地语言表示的详细信息
toString()3打印Throwable 的调用栈轨迹抛出异常处到方法调用处printStackTrace(); 输出到标准错误输出流
printStackTrace(PrintStream);
printStackTrace(java.io.PrintWriter);4Throwable fillStackTrace()在Throwable对象内部记录栈帧的当前状态。这在程序重新抛出异常或错误非常有用5Throwable继承自Object的其他方法getClass()返回对象类型的对象
getClass().getName()返回对象信息名称
getClass().getSimpleName()【荔枝-如何使用Exception的方法】// Exception 基类 Throwable 的方法列表.
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()); // getMessage():My Exceptionprint(getLocalizedMessage(): e.getLocalizedMessage()); // getLocalizedMessage():My Exceptionprint(toString(): e); // toString():java.lang.Exception: My Exceptionprint(printStackTrace(): );e.printStackTrace(System.out); // 打印抛出异常栈轨迹元素 打印的信息通过 getStackTrace() 来直接获取}}
}
/*
Caught Exception
getMessage():My Exception
getLocalizedMessage():My Exception
toString():java.lang.Exception: My Exception
printStackTrace():
java.lang.Exception: My Exceptionat chapter12.ExceptionMethods.main(ExceptionMethods.java:8)
*/ 【代码解说】每个方法都比前一个方法打印出更多的信息因为每一个都是前一个的超集【12.6.1】栈轨迹1栈轨迹通过 printStackTrace()方法打印打印的信息通过 getStackTrace() 来直接获取2getStackTrace()该方法返回一个由栈轨迹中的元素所构成的数组 // 荔枝-获取调用栈轨迹数组-getStackTrace()
public class WhoCalled {static void f() {try {throw new Exception();} catch (Exception e) {// getStackTrace() 返回 栈轨迹的元素组成的数组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(); // g() - f()System.out.println(--------------------------------);h(); // h() - g() - f()}
}
/*
f
main
-------------------------------- 调用栈轨迹先进后出 f() - g() - main main() 最先调用, f() 最后调用
f
g
main
--------------------------------
f
g
h
main
*/【12.6.2】重新抛出异常分为重新抛出同一种类型的异常 还是 抛出另外一种类型的异常1重新抛出异常语法catch(Exception e) {throw e;}2重新抛出异常会把异常抛给上一级环境的异常处理程序 同一个try块的后续catch子句被会略 3重新抛出异常-fillInStackTrace()printStackTrace() 打印的是原来异常的调用栈信息而不是新的异常抛出处的调用栈信息fillInStackTrace() 返回一个Throwable对象会把当前调用栈填入原来异常对象 【荔枝-重新抛出异常】// fillInStackTrace() 那一行成了异常的新发生地.
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); // 打印抛出异常栈轨迹元素 打印的信息通过 getStackTrace() 来直接获取throw e;}}public static void h() throws Exception {try {f();} catch (Exception e) {System.out.println(Inside h(),e.printStackTrace());e.printStackTrace(System.out);// fillInStackTrace() 那一行成了异常的新发生地.// 调用 fillInStackTrace()方法后轨迹栈 是 main()-h()而不是main() -h() - f()throw (Exception) e.fillInStackTrace(); }}public static void main(String[] args) {try {g(); // g() - f()} catch (Exception e) {System.out.println(main: printStackTrace());e.printStackTrace(System.out); // 打印抛出异常栈轨迹元素 打印的信息通过 getStackTrace() 来直接获取}System.out.println(\n\n main 方法中的 第二个 异常抛出 fillInStackTrace() 测试);try {h(); // h() - f()} catch (Exception e) {System.out.println(main: printStackTrace()2);e.printStackTrace(System.out);}}
}
/*
originating the exception in f()
Inside g(),e.printStackTrace()
java.lang.Exception: thrown from f()at chapter12.Rethrowing.f(Rethrowing.java:6)at chapter12.Rethrowing.g(Rethrowing.java:10)at chapter12.Rethrowing.main(Rethrowing.java:28)
main: printStackTrace()
java.lang.Exception: thrown from f()at chapter12.Rethrowing.f(Rethrowing.java:6)at chapter12.Rethrowing.g(Rethrowing.java:10)at chapter12.Rethrowing.main(Rethrowing.java:28)main 方法中的 第二个 异常抛出
originating the exception in f()
Inside h(),e.printStackTrace()
java.lang.Exception: thrown from f()at chapter12.Rethrowing.f(Rethrowing.java:6)at chapter12.Rethrowing.h(Rethrowing.java:19)at chapter12.Rethrowing.main(Rethrowing.java:35)
main: printStackTrace()
java.lang.Exception: thrown from f()at chapter12.Rethrowing.h(Rethrowing.java:23)at chapter12.Rethrowing.main(Rethrowing.java:35)
*/【荔枝-重新抛出一种新异常】// 荔枝-重新抛出一个新异常(捕获 OneException异常后 抛出 TwoException 异常.)
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);// 在捕获异常后 抛出另外一种异常// 捕获 OneException异常后 抛出 TwoException 异常.throw new TwoException(from inner try);}} catch (TwoException e) {System.out.println(Caught in outer try, e.printStackTrace());e.printStackTrace(System.out); // 最外层的try-catch捕获的异常的调用栈信息 没有 f()方法调用}}
}
/*
originating the exception in f()
Caught in inner try, e.printStackTrace()
chapter12.OneException: thrown from f()at chapter12.RethrowNew.f(RethrowNew.java:16)at chapter12.RethrowNew.main(RethrowNew.java:21)
Caught in outer try, e.printStackTrace()
chapter12.TwoException: from inner tryat chapter12.RethrowNew.main(RethrowNew.java:25)
*/【代码解说】 最外层的catch子句打印的调用栈信息只包含 main() 方法不包含 f()方法的调用栈信息【12.6.3】异常链1异常链常常需要在捕获第一个异常后抛出第二个异常但想保留第一个异常的信息 这被称为异常链2三种基本的异常构造器提供了 cause 参数 该参数吧原始异常第一个异常传递给第二个异常新异常分别是ErrorException 和 RuntimeException3使用 ininCause() 方法可以把其他异常链连起来【荔枝-使用 ininCause() 方法可以把其他异常链连起来】// 荔枝-通过initCause() 方法把 NullPointerException 插入到 DynamicFieldsException异常链中.
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) {fields[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] fields[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];}// 其他方法都可以忽略不看看 这个方法setField() 即可。public Object setField(String id, Object value)throws DynamicFieldsException {if (value null) {// Most exceptions dont have a cause constructor.// In these cases you must use initCause(),// available in all Throwable subclasses.DynamicFieldsException dfe new DynamicFieldsException();// initCause() 方法把 NullPointerException 插入到 DynamicFieldsException异常链中.dfe.initCause(new NullPointerException());throw dfe; // 如果valuenull抛出异常}int fieldNumber hasField(id);if (fieldNumber -1)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 { df });try {df.setField(d, A value for d);df.setField(number, 47);df.setField(number2, 48);print(df { 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); // 把value设置为null故意让setField()方法抛出异常} catch (NoSuchFieldException e) {e.printStackTrace(System.out);} catch (DynamicFieldsException e) {e.printStackTrace(System.out);}}
}
/*
Object field df.setField(d, null); // 仅仅打印这句代码抛出的异常信息 该代码把value设置为null故意让setField()方法抛出异常
chapter12.DynamicFieldsExceptionat chapter12.DynamicFields.setField(DynamicFields.java:66)at chapter12.DynamicFields.main(DynamicFields.java:100)
Caused by: java.lang.NullPointerException // 通过initCause() 方法把 NullPointerException 插入到 DynamicFieldsException异常链中.at chapter12.DynamicFields.setField(DynamicFields.java:68)... 1 more
*/ 【12.7】Java标准异常1异常基类Throwable有两个子类包括 Error 和 Exception1.1Error表示编译时和系统错误程序员不用关心1.2Exception用于方法和运行时可能抛出的异常类型关心【荔枝-Throwable, Exception, RuntimeException, Error, 源码】public class Throwable implements Serializable {
public class Error extends Throwable {
public class Exception extends Throwable {
public class RuntimeException extends Exception {【12.7.1】特例 RuntimeException-运行时异常1属于运行时异常的类型有很多自动被jvm抛出无需程序员抛出但还是可以在代码中抛出 RuntimeException异常因为运行时异常太多了如果都去捕获的话代码显得混乱2不受检查的异常运行时异常RuntimeException 也被称为 不受检查的异常这种异常属于错误被自动捕获【荔枝-显式抛出运行时异常】// 荔枝-显式抛出运行时异常
public class NeverCaught {static void f() {throw new RuntimeException(From f()); // 3-抛出异常}static void g() { f(); // 2}public static void main(String[] args) {g(); // 1 }
}
/*
Exception in thread main java.lang.RuntimeException: From f()at chapter12.NeverCaught.f(NeverCaught.java:6)at chapter12.NeverCaught.g(NeverCaught.java:9)at chapter12.NeverCaught.main(NeverCaught.java:12)
*/【注意】只能在代码中忽略 RuntimeException 及其子类的异常 其他类型异常的处理都是由编译器强制实施的。RuntimeException代表的是编程错误无法预料的错误应该在代码中进行检查的错误【12.8】使用 finally 进行清理1finally块 无论try块中的异常是否抛出finally 块的内容始终执行【荔枝-finally块】// finally 的经典荔枝
class ThreeException extends Exception {}
public class FinallyWorks {static int count 0;public static void main(String[] args) {// 循环了两次第一次抛出异常第二次没有抛出异常并在finally块中结束while (true) {try {if (count 0)throw new ThreeException();System.out.println(No exception); // 3} catch (ThreeException e) {System.out.println(ThreeException); // 1} finally {System.out.println(In finally clause); // 2, 4if (count 2)break; }}}
}
/*
ThreeException
In finally clause
No exception
In finally clause
*/【编程技巧】当java中的异常不允许程序回到异常抛出处应该如何应对把try块放在 循环里。参考ThreeException.java 荔枝【12.8.1】finally 用来做什么1finally块保证 无论try块发生了什么内存总能得到释放2finally的用处当把除内存之外的资源恢复到初始状态时需要用到 finally块【荔枝- 内部finally 先于外部catch() 子句执行】// 荔枝- 内部finally 先于外部catch() 子句执行。
// 无论try块中是否抛出异常 finally语句块被执行.
public class AlwaysFinally {public static void main(String[] args) {print(Entering first try block, 1); // 1try {print(Entering second try block, 2); // 2try {throw new FourException();// java异常不允许我们回到 异常抛出地点 // 故 内部finally 先于外部catch() 子句执行.} finally { // 内部finallyprint(finally in 2nd try block, 3); // 3}} catch (FourException e) { // 外部catchSystem.out.println(Caught FourException in 1st try block, 4); // 4} finally {System.out.println(finally in 1st try block, 5); // 5}}
}
/*
Entering first try block, 1
Entering second try block, 2
finally in 2nd try block, 3
Caught FourException in 1st try block, 4
finally in 1st try block, 5
*/【12.8.2】在return中使用finally1return 返回前会执行finally子句或finally块中的内容// 荔枝-在return使用finally子句
public class MultipleReturns {public static void f(int i) {print(\n Initialization that requires cleanup, point i);try {print(Point 1);if (i 1)return;print(Point 2);if (i 2)return;print(Point 3);if (i 3)return;print(End);return;// finally 子句总是会执行.} finally {print(Performing cleanup in finally clause.);}}public static void main(String[] args) {for (int i 1; i 4; i)f(i);}
}
/*Initialization that requires cleanup, point 1
Point 1
Performing cleanup in finally clause. Initialization that requires cleanup, point 2
Point 1
Point 2
Performing cleanup in finally clause. Initialization that requires cleanup, point 3
Point 1
Point 2
Point 3
Performing cleanup in finally clause. Initialization that requires cleanup, point 4
Point 1
Point 2
Point 3
End
Performing cleanup in finally clause.
*/【12.8.3】缺憾异常丢失【荔枝】异常丢失的荔枝// 异常丢失的荔枝
class VeryImportantException extends Exception {public String toString() {return A very important exception from VeryImportantException!;}
}
class HoHumException extends Exception {public String toString() {return A trivial exception from HoHumException!;}
}
public class LostMessage {void throwVeryImportantException() throws VeryImportantException {throw new VeryImportantException();}void throwHoHumException() throws HoHumException {throw new HoHumException();}public static void main(String[] args) {try {LostMessage lm new LostMessage();try {lm.throwVeryImportantException(); // 抛出 VeryImportantException 异常// 先执行内部finally子句} finally { lm.throwHoHumException(); // 抛出 HoHumException 异常}// 后执行外部 catch 子句} catch (Exception e) { System.out.println(e);}}
}
/*
A trivial exception from HoHumException! 结果抛出了 HoHumException 异常 VeryImportantException 异常被覆盖了。
*/【代码解说】VeryImportantException 异常被 finally 子句里的 HoHumException 所取代 抛出的VeryImportantException异常丢失了【荔枝】异常丢失的荔枝2// 异常丢失荔枝2
// 从finally 子句中返回如果运行这个程序即使抛出了异常也不会产生任何输出
public class ExceptionSilencer {public static void main(String[] args) {try {throw new RuntimeException();} finally {return; // 从finally 子句中返回}}
}
/*
不会产生任何输出*/ 【编码技巧】使用finally 要防止出现 异常丢失的情况【12.9】异常限制1当子类覆盖父类方法时只能抛出基类方法的异常说明里列出的异常【编码技巧】异常限制对构造器不起作用派生类构造器不能捕获基类构造器抛出的异常// BaseballException异常父类
class BaseballException extends Exception {} // BaseballException-棒球
class Foul extends BaseballException {} // Foul-犯规
class Strike extends BaseballException {} // Strike-击打 // Inning-棒球一局
abstract class Inning { public Inning() throws BaseballException, NullPointerException {} // 构造器抛异常public void event() throws BaseballException {} // event-事件public abstract void atBat() throws Strike, Foul; // atBat-在球棒上public void walk() {} // walk-行走
}
class StormException extends Exception {} // StormException-暴风雨异常
class RainedOut extends StormException {} // RainedOut- 因雨取消
class PopFoul extends Foul {} // PopFoul-流行犯规 interface Storm {public void event() throws RainedOut;public void rainHard() throws RainedOut;
}
public class StormyInning extends Inning implements Storm { // StormyInning-暴风雨中的一局棒球赛 public StormyInning() throws RainedOut, BaseballException {} // 构造器抛出异常派生类构造器抛出的异常不受父类构造器抛出异常的限制public StormyInning(String s) throws Foul, BaseballException {} // 构造器抛出异常public void rainHard() throws RainedOut {} // rainHard-下大雨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);} catch(Exception e) {System.out.println(Generic 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);}}
} // 打印结果 为空【代码解说】 Inning 是父类 StormyInning 是子类而子类 StormyInning 抛出的异常不受父类 Inning 抛出异常的限制如下public Inning() throws BaseballException, NullPointerException {} // 父类构造器抛异常
public class StormyInning extends Inning implements Storm {public StormyInning() throws RainedOut, BaseballException {} // 派生类构造器抛出的异常不受父类构造器抛出异常的限制public StormyInning(String s) throws Foul, BaseballException {} // 派生类构造器抛出的异常不受父类构造器抛出异常的限制// ......
}【12.10】构造器1问题 如果异常发生所有东西都能够被清理吗 当涉及到构造器时 问题就出现了即便finally 也不能完全解决。2如果在构造器内抛出异常清理行为就不能正常工作了。所以 编写构造器时要格外小心3如果构造器如创建文件输入流在其执行过程中半途而废文件路径找不到输入流创建失败也许该对象还没有被成功创建而这些部分在 finally 子句中却要被清理这容易触发空指针异常【荔枝-finally块 关闭了没有打开的文件输入流抛出空指针异常的处理方法】// 荔枝-如何处理finally块中抛出异常的情况
public class InputFile {private BufferedReader in;public InputFile(String fname) throws Exception {try {in new BufferedReader(new FileReader(fname));} catch (FileNotFoundException e) {System.out.println(Could not open fname);throw e;} catch (Exception e) {try {in.close(); // close()方法也可能 抛出异常} catch (IOException e2) {System.out.println(in.close() unsuccessful);}throw e; // Rethrow} finally {// Dont close it here!!!}}public String getLine() {String s;try {s in.readLine();} catch (IOException e) {throw new RuntimeException(readLine() failed);}return s;}// dispose-处理处置安排关闭文件输入流public void dispose() {try {in.close();System.out.println(dispose() successful);} catch (IOException e2) {throw new RuntimeException(in.close() failed);}}
}
// 在构造阶段可能抛出异常 并且要求清理的类使用到了 嵌套try 子句
public class Cleanup {static String path System.getProperty(user.dir) File.separator src File.separator chapter12 File.separator;// 用二层 try-catchpublic static void main1(String[] args) {try { // 外层try-catchInputFile in null;try { // 双层 try语句块(嵌套try子句)in new InputFile(path erroPath Cleanup.java); // 错误路径String s;int i 1;while ((s in.getLine()) ! null);} 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);}System.out.println(continue;); // 即便抛出异常还是继续执行}/* 第一个main() 打印结果Could not open D:\classical_books\thinking-in-java\AThinkingInJava\src\chapter12\erroPathCleanup.javaCaught Exception in mainjava.io.FileNotFoundException: D:\classical_books\thinking-in-java\AThinkingInJava\src\chapter12\erroPathCleanup.java (系统找不到指定的文件。)at java.io.FileInputStream.open0(Native Method)at java.io.FileInputStream.open(FileInputStream.java:195)at java.io.FileInputStream.init(FileInputStream.java:138)at java.io.FileInputStream.init(FileInputStream.java:93)at java.io.FileReader.init(FileReader.java:58)at chapter12.InputFile.init(InputFile.java:11)at chapter12.Cleanup.main(Cleanup.java:14)InputFile construction failedcontinue;*/// 用一层 try-catchpublic static void main(String[] args) { InputFile in null;try { // 双层 try语句块(嵌套try子句)in new InputFile(path erroPath Cleanup.java); // 错误路径String s;int i 1;while ((s in.getLine()) ! null);} catch (Exception e) {System.out.println(Caught Exception in main);e.printStackTrace(System.out);} finally {// 关闭文件输入流(如果文件路径错误或其他错误导致文件输入流没有打开的话in为null会抛出空指针异常)in.dispose(); }System.out.println(continue;); // 如果抛出异常程序执行终止这句无法执行}
}
/* 第二个main() 打印结果
Could not open D:\classical_books\thinking-in-java\AThinkingInJava\src\chapter12\erroPathCleanup.java
Caught Exception in main
java.io.FileNotFoundException: D:\classical_books\thinking-in-java\AThinkingInJava\src\chapter12\erroPathCleanup.java (系统找不到指定的文件。)at java.io.FileInputStream.open0(Native Method)at java.io.FileInputStream.open(FileInputStream.java:195)at java.io.FileInputStream.init(FileInputStream.java:138)at java.io.FileInputStream.init(FileInputStream.java:93)at java.io.FileReader.init(FileReader.java:58)at chapter12.InputFile.init(InputFile.java:11)at chapter12.Cleanup.main(Cleanup.java:48)
Exception in thread main java.lang.NullPointerExceptionat chapter12.Cleanup.main(Cleanup.java:57)
*/【代码解说】解说1如果FileReader 构造器失败抛出 FileNotFoundException 异常。对于这种异常不需要关闭文件因为这个文件还没有打开。 然而任何其他捕获异常的catch 子句必须关闭文件因为捕获异常时文件已经打开了。所以 这里就矛盾了。 解说2所以finally块中的 in.dispose() 可能抛出异常所以还需要再封装一层 try-catch 双层try-catch 解说3getLine()方法将异常转换为 RuntimeException 表示这是一个编程错误 解说4最安全的使用方式使用嵌套的try子句就如 上面的 Cleanup.java 荔枝【编码技巧】在创建需要清理的对象后立即进入一个 try-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 {} // 构造器抛出异常
}// 在创建需要清理的对象后立即进入一个 try-finally 语句块.
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 语句块.try { // ...} finally {nc3.dispose(); // 清理内存 或 关闭输入李 或 其他清理操作nc2.dispose(); // 清理内存 或 关闭输入李 或 其他清理操作}// Section 3: 展示了如何处理那些具有可以失败的构造器且需要清理的对象。处理方法是 3层try-catch块 // If construction can fail you must guard each one:try {NeedsCleanup2 nc4 new NeedsCleanup2();try {NeedsCleanup2 nc5 new NeedsCleanup2();// 在创建需要清理的对象后立即进入一个 try-finally 语句块.try {// ...} finally {nc5.dispose(); // 清理内存 或 关闭输入李 或 其他清理操作}} catch (ConstructionException e) { // nc5 constructorSystem.out.println(e);} finally {nc4.dispose();}} catch (ConstructionException e) { // nc4 constructorSystem.out.println(e);}}
}
/*
NeedsCleanup 1 disposed
NeedsCleanup 3 disposed
NeedsCleanup 2 disposed
NeedsCleanup 5 disposed
NeedsCleanup 4 disposed
*/【代码解说】1Section 3 展示了如何处理那些具有可以失败的构造器且需要清理的对象。2处理方法是 3层try-catch块对于每一个构造都必须包含在 try-finally 块中并且每一个对象构造都必须跟随一个 try-finally 块以确保清理【12.11】异常匹配1异常处理系统找到代码书写顺序最近的异常进行处理且不再继续查找【荔枝-异常匹配】// 异常匹配的荔枝
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 1);}// Catch the base type:try {throw new Sneeze(); // 抛出子类异常} catch (Annoyance a) { 捕获基类异常System.out.println(Caught Annoyance 2); // 这里捕获异常}}
}
/*
Caught Sneeze
Caught Annoyance 2
*/【代码解说】 catch (Annoyance a) 会捕获Annoyance 及其子类的异常2如果把捕获基类异常的catch子句放在最前面则会把后面子类的异常捕获子句 catch 子句 屏蔽掉如try{throw new Sneeze(); // 抛出子类异常
} catch (Annoyance a) { // // 捕获基类异常System.out.println(Caught Annoyance 1); // 这里捕获异常
} catch (Sneeze s) { // 捕获子类异常System.out.println(Caught Sneeze); // 异常捕获子句被屏蔽
} 3编译器报错编译器会发现 catch (Sneeze s) 永远不会执行编译器报错【12.12】其他可选方式1异常处理的目标把错误处理的代码和错误发生的地点相分离