本文主要是介绍第十二章 通过异常处理错误,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
2013年7月10日 星期三 00时04分21秒第十二章 通过异常处理错误
12.1 概念
Java的基本理念是“结构不佳的代码不能运行”
Java使用异常来提供一致的错误报告模型,使得构件能够与客户端代码可靠地沟通问题。
12.2 基本异常
异常情形(Exceptional condition)是指阻止当前方法或作用域继续执行的问题。
当抛出异常后,有几件事会随之发生:
首先:同Java中其他对象一样,将使用new在堆上创建异常对象。
然后:当前的执行路径被终止,并且从当前环境中弹出对异常对象的引用。此时异常处理机制接管程序,并开始寻找一个恰当的地方来继续执行程序。这个恰当的地方就是异常处理 程序。
12.2.1 异常参数
与使用Java中的其他对象一样,我们总是使用new在堆上创建异常对象,这也伴随着存储空间的分配和构造器的使用。所以标准异常类都有两个构造器:一个默认构造器,一个棘手 字符串作为参数,以便把相关信息放入异常对象的构造器:
throw new NullPointerException("t=null");
12.3 捕获异常
要明白异常是如何被捕获的,首先理解监控区域(guarded region)的概念。
12.3.1 try块
捕获异常: try{ // }
12.3.2 异常处理程序
抛出的异常必须在某处得以处理。这个“地点”就是异常处理程序。用关键字catch表示:
try{
}catch(){
}catch(){ }
终止与恢复
异常处理理论上有两种基本模型:
1) Java支持终止模型
2) 恢复模型
12.4 创建自定义异常
程序员可以自己定义异常类来表示程序中可能会遇到的特定的问题。
要自己定义异常类,必须从已有的异常类继承,最好是选择意思相近的异常类继承。
package chapter12;
/*@name PriorityQueueDemo.java
* @describe 12.4 创建自定义异常
* @since 2013-07-10 0:45
* @author 张彪
*/
class SimpleException extends Exception{}
public class InheritingException {
public void f() throws SimpleException{
System.out.println("Throw SimpleException from f();");
throw new SimpleException();
}
public static void main(String[] args) {
InheritingException sed=new InheritingException();
try {
sed.f();
} catch (SimpleException e) {
System.out.println("Caught it !");
}
}
}
/*Throw SimpleException from f();
Caught it !*/
===============================================================================
package chapter12;
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);
}
}
}
/*
Throwing MyException from f();
chapter12.MyException
at chapter12.FullConstructors.f(FullConstructors.java:11)
at chapter12.FullConstructors.main(FullConstructors.java:19)
Throwing MyException from g();
chapter12.MyException: Originated in g()
at chapter12.FullConstructors.g(FullConstructors.java:15)
at chapter12.FullConstructors.main(FullConstructors.java:24)*/
在异常处理中,调用了throwable类(Exception即从此类继承)的printStackTrace()方法。它将打印“从方法调用处直到异常抛出处”的方法调用序列。
12.4.1 异常与记录日志
使用java.util.logging工具将输出记录到日志中。
package chapter12;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.logging.Logger;
/*@name LoggingException.java
* @describe 12.4.1 异常与记录日志
* @since 2013-07-10 0:59
* @author 张彪
*/
class LoggingException extends Exception{
private static Logger logger=Logger.getLogger("LoggingException");
public LoggingException(){
StringWriter trace=new StringWriter();
printStackTrace(new PrintWriter(trace));
logger.severe(trace.toString());
}
}
public class LoggingExceptions {
public static void main(String[] args) {
try {
throw new LoggingException();
} catch (LoggingException e) {
System.out.println("Caught"+e);
}
try {
throw new LoggingException();
} catch (LoggingException e) {
System.out.println("Caught"+e);
}
}
}
/*2013-7-10 1:07:56 chapter12.LoggingException <init>
严重: chapter12.LoggingException
at chapter12.LoggingExceptions.main(LoggingExceptions.java:23)
Caughtchapter12.LoggingException
Caughtchapter12.LoggingException
2013-7-10 1:07:56 chapter12.LoggingException <init>
严重: chapter12.LoggingException
at chapter12.LoggingExceptions.main(LoggingExceptions.java:28)*/
这个Logging对象会将其输出发送到System.err。 为了产生日志记录消息,我们欲获取异常抛出处的栈轨迹。
更常见的情况是我们需要捕获和记录其他人编写的异常,异常我们必须在异常处理程序中生成日志消息。
package chapter12;
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;
}
public String getMessage(){
return "Detail Message:"+x+ " "+super.getMessage();
}
}
public class ExtraFeatures{
public static void f() throws MyException2{
System.out.println("Throwing MyException2 from f()");
throw new MyException2();
}
public static void g() throws MyException2{
System.out.println("Throwing MyException2 from g()");
throw new MyException2("Originated in g()");
}
public static void h() throws MyException2{
System.out.println("Throwing MyException2 from h()");
throw new MyException2("Originated in g()",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);
}
}
}
12.5 异常说明
Java鼓励热门把方法可能抛出的异常告知使用此方法的客户端程序员。这是种优雅的做法。异常说明属于方法说明的一部分。紧跟在形式参数的列表后面。
异常说明使用了附加的关键字throws,后面接一个所有潜在异常类型的列表,所以方法定义可能如下:
void f() throws TooBig, TooSmall, DivZero{ // .....}
12.6 捕获所有异常
可以通过异常基类Exception来捕获异常处理程序中所有的异常。
下面展示Exception的用法:
package chapter12;
public class ExceptionMethods{
public static void main(String[] args) {
try {
throw new Exception("My Exception");
} catch (Exception e) {
System.out.println("Caught Exception");
System.out.println("e.getMessage()="+e.getMessage());
System.out.println("e.getLocalizedMessage()="+e.getLocalizedMessage());
System.out.println("toString()"+e);
System.out.println("printStackTrace()");
e.printStackTrace(System.out);
}
}
}
12.6.1 栈轨迹
printStackTrace()方法所提供的信息可以通过getStackTrace()方法直接访问。这个方法将返回一个由栈轨迹中的元素所构成的数组,其中每一个元素都表示栈中的一帧。
下面是简单演示:
package chapter12.exceptions;
/*@name WhoCalled.java
* @describe 12.6.1 栈轨迹
* @since 2013-07-13 02:45
* @author 张彪
*/
public class WhoCalled {
static void f(){
try {
throw new Exception();
} catch (Exception e) {
for(StackTraceElement t: e.getStackTrace()){
System.out.println(t.getMethodName());
}
}
}
static void g(){f();}
static void h(){g();}
public static void main(String[] args) {
f();
System.out.println("-------------------");
g();
System.out.println("-------------------");
h();
}
}
/*f
main
-------------------
f
g
main
-------------------
f
g
h
main*/
元素0是栈顶元素,并且是调用序列中的最后一个方法调用。数组中的最后一个元素和栈底是调用序列中的第一个方法调用。
12.6.2 重新抛出异常
有时候希望把刚捕获的异常重新抛出。如下:
catch (Exception e)
{
System.out.println("An exception was thrown");
throw e;
}
如下例子:
package chapter12.exceptions;
/*@name WhoCalled.java
* @describe 12.6.2 重新抛出异常
* @since 2013-07-13 03:09
* @author 张彪
*/
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);
}
System.out.println("---------");
try {
h();
} catch (Exception e) {
System.out.println("main:printStackTrace()");
e.printStackTrace(System.out);
}
}
}
调用fillInStackTrace()的那一行就成了异常的新发生地了。即原来的异常发生点信息丢失了,剩下的是与新的抛出点有关的信息。
永远不必为清理一个异常而担心,他们都是用New在堆上创建的对象,所以垃圾回收器会自动把他们清理掉。
12.6.3 异常链
12.7 Java标准异常
Throwable这个Java类被用来表示任何可以作为异常被抛出的类。 分为Error 和Exception。
12.7.1 RunException
运行时异常会自动被Java虚拟机抛出
如果RunException没有被捕获而直达main()方法,那么在程序退出前将调用异常的printStackTrace()方法。
12.8 使用finally进行清理
如果希望无论try块中的异常是否抛出,它们都能得到执行,那么可以在异常处理程序后面加上finally子句。
try{}
catch(){}
finaly{}
可以将try块放在循环中,这样就建立了一个“程序继续执行之前必须要达到”的条件。
12.8.1 finally用来做什么
当要把除内存之外的资源恢复到它们的初始状态时,就要用到finally子句。
12.8.2 在return中使用finally
package chapter12.exceptions;
/*@name MultipleReturn.java
* @describe 12.8.2 在return中使用finally
* @since 2013-07-28 10:27
* @author 张彪
*/
public class MultipleReturn {
public static void f(int i){
System.out.println("Initialization that requires cleanup");
try {
System.out.println("Point 1");
if(i==1) return;
System.out.println("Point 2");
if(i==2) return;
System.out.println("Point 3");
if(i==3) return;
System.out.println("Point 4");
if(i==4) return;
} finally {
System.out.println("Performing cleanup");
}
}
public static void main(String[] args) {
for (int i = 1; i < 4; i++) {
MultipleReturn.f(i);
}
}
}
/*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*/
可以发现finally中的方法会在return之前被执行。
12.8.3 缺憾:异常丢失
用某些特殊方法使用finally子句时,有些异常可以轻易的被忽略掉。
package chapter12.exceptions;
class VeryImportantExcepton extends Exception{
public String toString(){
return "A very impotant exception";
}
}
class HoHumException extends Exception{
public String toString(){
return "A trivial exception";
}
}
public class LostMessage {
void f() throws VeryImportantExcepton{
throw new VeryImportantExcepton();
}
void dispose() throws HoHumException{
throw new HoHumException();
}
public static void main(String[] args) {
try{
LostMessage ls=new LostMessage();
try{
ls.f();
}finally{
ls.dispose();
}
}catch (Exception e) {
System.out.println(e);
}
}
}
/*A trivial exception*/
从打印出的信息可以看出VeryImportantExcepton不见了。
一种更加简单丢失异常的方式是从finally中返回:
public class Exceptionilencer {
public static void main(String[] args) {
try {
throw new RuntimeException();
} finally{
//using 'return' inside the finally block will slience any thrown exception
return;
}
}
}
12.9 异常限制
当覆盖方法的时候,只能抛出在基类方法的异常说明里列出的那些异常。
12.10 构造器
如果构造器内抛出了异常,这些清理行为也许就不能正常工作了。这意味着写构造器时要格外细心。
package chapter12.exceptions;
/*@name CleanupIdioom.java
* @describe 12.10 构造器 ,注意构造器时异常的处理
* @since 2013-08-01 20:15
* @author 张彪
*/
class NeedCleanup{
private static long counter=1;
private final long id=counter++;
public void dispose(){
System.out.println("NeedCleanup "+id+" dispose");
}
}
class ConstructionException extends Exception{}
class NeedCleanup2 extends NeedCleanup{
public NeedCleanup2() throws ConstructionException{}
}
public class CleanupIdioom {
public static void main(String[] args) {
NeedCleanup nc1=new NeedCleanup();
try {
}finally {
nc1.dispose();
}
//select2
NeedCleanup nc2=new NeedCleanup();
NeedCleanup nc3=new NeedCleanup();
try{
}finally{
nc3.dispose();
nc2.dispose();
}
//select3
try {
NeedCleanup2 nc4= new NeedCleanup2();
try {
NeedCleanup2 nc5= new NeedCleanup2();
} finally{
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
12.11 异常匹配
抛出异常时,异常处理系统会按照代码的书写顺序找出“最近”的处理程序。找到后,将不再继续查找。
派生类的对象也可以匹配其基类的处理程序。
package chapter12.exceptions;
class Annoyance extends Exception{}
class Sneeze extends Annoyance{}
public class Human {
public static void main(String[] args) {
try {
throw new Sneeze();
} catch (Sneeze s) {
System.out.println("Caught Sneeze");
}catch (Annoyance a) {
System.out.println("Caught Annoyance1");
}
try {
throw new Sneeze();
} catch (Annoyance e) {
System.out.println("Caught Annoyance2");
}
}
}
/*Caught Sneeze
Caught Annoyance2*/
//如果把捕获基类的catch子句放在最前面,依次想把派生类的异常给“屏蔽”掉,就像这样:
try {
throw new Sneeze();
} catch (Annoyance s) {
System.out.println("Caught Sneeze");
}catch (Sneeze a) {
System.out.println("Caught Annoyance1");
}
}
这样编译器会发现Sneeze的catch子句永远也得不到执行,因此它会向你报告错误。
12.12 其它可选方式
事实上,异常处理的一个重要目标是将错误处理的代码和错误发生的地点相分离。
“被检查的异常”及其并发症,以及采用什么方法解决该问题。
12.12.1 历史
12.12.2 观点
12.12.3 把异常传递给控制台
12.12.4 把“被检查的异常”转换为“不检查的异常”
12.13 异常使用指南
12.14 总结
2013-08-01 21:02 记 @jinrongdajie31.xichengqu.beijing
这篇关于第十二章 通过异常处理错误的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!