第十二章 通过异常处理错误

2024-04-10 20:58

本文主要是介绍第十二章 通过异常处理错误,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

这篇关于第十二章 通过异常处理错误的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/892124

相关文章

Java 中的 @SneakyThrows 注解使用方法(简化异常处理的利与弊)

《Java中的@SneakyThrows注解使用方法(简化异常处理的利与弊)》为了简化异常处理,Lombok提供了一个强大的注解@SneakyThrows,本文将详细介绍@SneakyThro... 目录1. @SneakyThrows 简介 1.1 什么是 Lombok?2. @SneakyThrows

在 Spring Boot 中实现异常处理最佳实践

《在SpringBoot中实现异常处理最佳实践》本文介绍如何在SpringBoot中实现异常处理,涵盖核心概念、实现方法、与先前查询的集成、性能分析、常见问题和最佳实践,感兴趣的朋友一起看看吧... 目录一、Spring Boot 异常处理的背景与核心概念1.1 为什么需要异常处理?1.2 Spring B

关于MongoDB图片URL存储异常问题以及解决

《关于MongoDB图片URL存储异常问题以及解决》:本文主要介绍关于MongoDB图片URL存储异常问题以及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录MongoDB图片URL存储异常问题项目场景问题描述原因分析解决方案预防措施js总结MongoDB图

一文详解Java异常处理你都了解哪些知识

《一文详解Java异常处理你都了解哪些知识》:本文主要介绍Java异常处理的相关资料,包括异常的分类、捕获和处理异常的语法、常见的异常类型以及自定义异常的实现,文中通过代码介绍的非常详细,需要的朋... 目录前言一、什么是异常二、异常的分类2.1 受检异常2.2 非受检异常三、异常处理的语法3.1 try-

usb接口驱动异常问题常用解决方案

《usb接口驱动异常问题常用解决方案》当遇到USB接口驱动异常时,可以通过多种方法来解决,其中主要就包括重装USB控制器、禁用USB选择性暂停设置、更新或安装新的主板驱动等... usb接口驱动异常怎么办,USB接口驱动异常是常见问题,通常由驱动损坏、系统更新冲突、硬件故障或电源管理设置导致。以下是常用解决

Java Optional避免空指针异常的实现

《JavaOptional避免空指针异常的实现》空指针异常一直是困扰开发者的常见问题之一,本文主要介绍了JavaOptional避免空指针异常的实现,帮助开发者编写更健壮、可读性更高的代码,减少因... 目录一、Optional 概述二、Optional 的创建三、Optional 的常用方法四、Optio

Java异常架构Exception(异常)详解

《Java异常架构Exception(异常)详解》:本文主要介绍Java异常架构Exception(异常),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. Exception 类的概述Exception的分类2. 受检异常(Checked Exception)

Java报NoClassDefFoundError异常的原因及解决

《Java报NoClassDefFoundError异常的原因及解决》在Java开发过程中,java.lang.NoClassDefFoundError是一个令人头疼的运行时错误,本文将深入探讨这一问... 目录一、问题分析二、报错原因三、解决思路四、常见场景及原因五、深入解决思路六、预http://www

一文带你深入了解Python中的GeneratorExit异常处理

《一文带你深入了解Python中的GeneratorExit异常处理》GeneratorExit是Python内置的异常,当生成器或协程被强制关闭时,Python解释器会向其发送这个异常,下面我们来看... 目录GeneratorExit:协程世界的死亡通知书什么是GeneratorExit实际中的问题案例

Java捕获ThreadPoolExecutor内部线程异常的四种方法

《Java捕获ThreadPoolExecutor内部线程异常的四种方法》这篇文章主要为大家详细介绍了Java捕获ThreadPoolExecutor内部线程异常的四种方法,文中的示例代码讲解详细,感... 目录方案 1方案 2方案 3方案 4结论方案 1使用 execute + try-catch 记录