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

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

相关文章

Python中异常类型ValueError使用方法与场景

《Python中异常类型ValueError使用方法与场景》:本文主要介绍Python中的ValueError异常类型,它在处理不合适的值时抛出,并提供如何有效使用ValueError的建议,文中... 目录前言什么是 ValueError?什么时候会用到 ValueError?场景 1: 转换数据类型场景

Spring中Bean有关NullPointerException异常的原因分析

《Spring中Bean有关NullPointerException异常的原因分析》在Spring中使用@Autowired注解注入的bean不能在静态上下文中访问,否则会导致NullPointerE... 目录Spring中Bean有关NullPointerException异常的原因问题描述解决方案总结

Python中的异步:async 和 await以及操作中的事件循环、回调和异常

《Python中的异步:async和await以及操作中的事件循环、回调和异常》在现代编程中,异步操作在处理I/O密集型任务时,可以显著提高程序的性能和响应速度,Python提供了asyn... 目录引言什么是异步操作?python 中的异步编程基础async 和 await 关键字asyncio 模块理论

详解Python中通用工具类与异常处理

《详解Python中通用工具类与异常处理》在Python开发中,编写可重用的工具类和通用的异常处理机制是提高代码质量和开发效率的关键,本文将介绍如何将特定的异常类改写为更通用的ValidationEx... 目录1. 通用异常类:ValidationException2. 通用工具类:Utils3. 示例文

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

Thymeleaf:生成静态文件及异常处理java.lang.NoClassDefFoundError: ognl/PropertyAccessor

我们需要引入包: <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>sp

深入理解数据库的 4NF:多值依赖与消除数据异常

在数据库设计中, "范式" 是一个常常被提到的重要概念。许多初学者在学习数据库设计时,经常听到第一范式(1NF)、第二范式(2NF)、第三范式(3NF)以及 BCNF(Boyce-Codd范式)。这些范式都旨在通过消除数据冗余和异常来优化数据库结构。然而,当我们谈到 4NF(第四范式)时,事情变得更加复杂。本文将带你深入了解 多值依赖 和 4NF,帮助你在数据库设计中消除更高级别的异常。 什么是

消除安卓SDK更新时的“https://dl-ssl.google.com refused”异常的方法

消除安卓SDK更新时的“https://dl-ssl.google.com refused”异常的方法   消除安卓SDK更新时的“https://dl-ssl.google.com refused”异常的方法 [转载]原地址:http://blog.csdn.net/x605940745/article/details/17911115 消除SDK更新时的“

JVM 常见异常及内存诊断

栈内存溢出 栈内存大小设置:-Xss size 默认除了window以外的所有操作系统默认情况大小为 1MB,window 的默认大小依赖于虚拟机内存。 栈帧过多导致栈内存溢出 下述示例代码,由于递归深度没有限制且没有设置出口,每次方法的调用都会产生一个栈帧导致了创建的栈帧过多,而导致内存溢出(StackOverflowError)。 示例代码: 运行结果: 栈帧过大导致栈内存

org.hibernate.hql.ast.QuerySyntaxException:is not mapped 异常总结

org.hibernate.hql.ast.QuerySyntaxException: User is not mapped [select u from User u where u.userName=:userName and u.password=:password] 上面的异常的抛出主要有几个方面:1、最容易想到的,就是你的from是实体类而不是表名,这个应该大家都知道,注意