异常处理
Java的基本理念是“结构不佳的代码不能运行”。
发现错误的理想时机是在编译阶段,余下的问题必须在运行期间解决。改进的错误恢复机制是提供代码健壮性的最强有力的方式。错误恢复
时每个程序的基本要素。异常处理的目的在于通过使用少量目前数量的代码来简化大型、可靠的程序生成。
基本异常
异常情形指组织当前方法或作用域继续执行的问题。 在当前环境下无法获得必要的信息来解决问题。
普通问题是指当前环境下能够得到足够的信息,总能处理这个错误。
抛出一个异常以后要做哪些事情:
首先,使用new在堆上创建异常对象。然后,当前执行的路径被终止,并且从当前环境中弹出对异常对象的引用。此时,异常处理机制接管程
序,并开始寻找一个恰当的地方来继续执行程序。这个恰当的地方就是一场处理程序,它的任务是将程序从错误状态中恢复,以使程序能要么换一
种方式运行,要么继续运行下去。
事务的基本保障是我们所需的在分布式计算中的异常处理。
捕获异常
try块
把所有可能会产生异常的代码都放在try块中,然后只需要在一个地方捕获所有异常。(将完成任务的代码与错误检查的代码分离。)
异常处理程序
示例代码:
try {
throw new LoggingException();
} catch (LoggingException e) {
System.err.println("Caught1 :" + e);
}
当异常被抛出后,一场处理机制负责搜寻参数与异常类型匹配匹配的第一个程序,然后进入catch子句执行。注意,只有匹配的catch子句才能
得到执行。
异常处理有两种模型:终止模型与恢复模型。
终止模型:一旦异常被抛出,表明错误已无法返回,也不能回来继续执行。
恢复模型:异常处理程序的工作是修正错误,然后重新尝试调试调用的问题的方法,并认为第二次能成功。希望异常处理之后能继续执行程
序。(在遇见错误的时候不抛出异常,而是调用方法来修正。或者把try放进while循环,不断进入try块,直到得到满意的结果。)
恢复模型可能导致耦合:程序需要了解异常抛出的地点,这势必要包含依赖于抛出位置的非通用性代码。增加代码的编写与维护困难。
创建自定义异常
要定义异常类,必须继承已有的异常类。
异常与记录日志
package com.mystudy.exception;
/*
* 异常与记录日志
*/
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.logging.Logger;
/*
* 静态的Logger.getLogger()方法创建了一个String参数相关联的Logger对象(通常与错误相关的包名和类名),
* 这个Logger对象会将其输出发送到System.err。
* 向Logger写入的最简单的方式就是直接调用与日志记录消息的
* 级别的相关联的方法,这里使用的是severe()。
是printStackTrace()不会默认的产生字符串,为了获取字符串,
* 我们需要使用重载的printStackTrace()方法,它接受一个java.io.PrintWriter对象作为参数。
就可以将输出抽象为一个String。
*/
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.err.println("Caught1 :" + e);
}
try {
throw new LoggingException();
} catch (LoggingException e) {
System.err.println("Caught2 :" + e);
}
}
}
异常说明
通过关键字throws告知客户端程序员某个方法可能会抛出的一场,程序员进行相应的处理。它属于方法声明的一部分,紧跟在形参列表之后。
在声明方法时将异常抛出,实际上却不抛出。以后就可以抛出这种一场而不用修改已有的代码。在定义抽象基和接口是很重要。
捕获所有异常
Exception是与编程有关的异常的基类,不会包含太多信息。可以调用它从基类Throwable继承的方法:
String getMessage()
String getLocalizedMessage()
获取详细信息,或用本地语言表示详细信息。
String toString()返回对Throwable的简单描述,有详细信息的话也会包含在内。
void printStackTrace()
void printStackTrace(PrintStream)
void printStackTrace(java.io.StringWriter)
打印Throwable和Throwable的调用栈轨迹。
Throwable fillInStackTrace()用于在Throwable对象的内部记录栈帧的当前状态。
也可以使用Throwable从基类Object继承的方法:getClass()返回一个表示此对象类型的对象。然后使用getName()或getSimpleName()方法。
栈轨迹
printStackTrace()方法所提供的信息可以根据getStackTrace()方法直接访问,这个方法将返回一个由栈轨迹中的元素所构成的数组,其
中每一个元素表示栈的一帧。
重新抛出异常
如果只是把当前异常对象重新抛出,那么printStackTrace()方法显示的僵尸异常抛出点的调用栈信息。要想更新这个信息,可以调用
fillInStackTrace()方法,这将返回一个Throwable对象,是通过把当前调用栈信息填入原来那个异常对象建立。
异常链
在捕获一个异常后抛出另一个一场,并且把原始异常的信息保存下来。现在的Throwable的子类在构造器都可以接受一个cause对象作为参
数,用来表示原始异常,通过把原始异常传递给新的异常,即使在当前位置创建并抛出新的异常也能通过这个异常链追踪到最初发生的位置。
只有Error、Exception、和RuntimeException带有这类参数,其它类使用异常链需要使用initCause()方法。
使用finally进行清理
对于一些无论是否抛出异常都需要执行的代码需要放在异常处理程序后的finally子句中。
什么时候需要使用到finally子句:把除内存之外的资源恢复到他们的初始状态。(已经打开的文件或网络连接,在屏幕上画的图形,等)
异常丢失
当try、catch、finally都有*返回异常或者数据*的时候会造成异常丢失,最终只会返回finally的异常或者数据。在执行try或catch程序块
时,若有返回值,则返回值先写入内存,暂时不会返回,等执行完finally里的内容后再返回,若finally里也有返回值,会对前面的
返回值造成覆盖,即丢失。
异常限制
当覆盖方法时,只能抛出比基类方法声明的更少的异常
异常限制对构造器不起作用,派生类构造器不能捕获基类构造器抛出的异常。
异常匹配
抛出异常时,异常处理系统会按照代码的书写顺序找出最近的处理程序。找到匹配的处理程序之后就认为异常将得到处理,不再继续查找。
异常使用指南
1、在恰当的级别处理问题。(在知道该如何处理的情况下才捕获异常)
2、解决问题并且重新调用产生异常的方法。
3、进行少许修补,然后绕过异常发生的地方继续执行。
4、用别的数据进行计算,以代替方法预算会返回的值。
5、把当前环境下能做的事尽量做完,然后把相同的异常重抛到更高层。
6、把当前环境下能做的事尽量做完,然后把不同的异常抛到更高层。
7、终止程序。
8、进行简化。
9、让类库和程序更安全。(调试与程序的健壮性)