通过异常处理错误
1、基本异常
(1)普通问题:在当前的环境下能够得到足够的信息,总可以处理该错误;
(2)异常情形(execptional condition):是指阻止当前方法或作用域继续执行的问题,在当前的环境下无法获取必要的信息来解决问题,必须从当前环境跳出,并将问题提交给上一级环境,这是抛出异常发生的事情;
(3)在Java中,
Throwable是所有异常类的基类,Throwable对象可以分为两类:
①Error:表示编译时和系统错误(一般不需要客户端程序员关心);
②Exception:可以被抛出的异常类型;
※RunTimeException是一个特殊的Exception,如果这个类型
的异常没有被捕获而直达main(),那么程序在退出前将自动调用异常的printStackTrace()方法;
※可以把异常看成一种内建的恢复(undo)系统,在程序中可以拥有各种不同的恢复点,当程序某部分运行失败时,异常将“恢复”到程序已知的某个稳定点上;
2、异常参数
一般Java的异常类由两种构造器:
默认构造器、接收解释性字符串参数的构造器;
3、捕获异常
(1)监控区域(guarded region):一段可能产生异常的代码,并且后面要跟随处理这些异常的代码;
(2)Java使用try{ }块放置异常可能抛出异常的代码,并使用catch块捕获try块所有抛出的异常,使用finally块进行清除;
try{ statments;}
catch(Exception ex){ statements; }
finally{ dispose statments;}
(3)finally子句
①对于没有垃圾回收和析构函数自动调用机制的语言而言,finally子句非常重要,它能使程序员保证:
无论try发生什么,内存总是能够得带释放;
②对于Java,finally适用于内存回收之外的情况,即在程序结束,在垃圾回收器回收垃圾之前,要完成的动作;
③在Java中,finally比较大应用为:把除了内存之外的资源恢复到他们的初始状态,比如:比如已经打开的文件或网咯连接,再屏幕上画入队图形,甚至是外部世界的某个开关;
④因为finally总是会被执行,所以在一个方法中,可以使用return从多个点返回:
void count(int num){
try{
System.out.println("count 1");
if(num == 1) return;
System.out.println("count 2");
if(num == 2) return;
System..out.println("count 3");
}finally{
System.out.println("All count!");
}
}
4、终止和恢复
异常处理理论上有两种模型:终止模型和恢复模型;
(1)终止模型:Java和C++都支持终止模型,基于某种错误假设,程序午饭返回到异常发生的地方继续执行;
(2)恢复模型:异常处理程序的工作是修正错误,然后重新调用方法来修正错误,并认为第二次可能成功;
使用Java实现恢复模型,有2中方法:
①在遇见错误时不能抛出异常,而是调用方法来修正该错误;
②将try块放在while循环中,这样就可以不断进入try块,直到满意结果;
int[ ] list =new int[3];
int index = 5;
whlie(true){
try{
list[index] = 1024; //list[5] = 1024 抛出异常,并被下面catch捕获,每次抛出异常,重新进入try块
break; //当上面try的代码不抛出异常时,跳出while(true)循环;
}catch(ArrayIndexOutOfBoundsException ex){
ex.printStackTrace(System.err);
}finally{
System.out.println("Ready insert array");
}
}
5、
自定义异常:
必须从已有的异常类继承,一般选择意思相近的异常类继承,然后直接使用默认构造器或字符串构造器,就基本足够需求了;
6、异常的输出流比较:
(1)
catch(Exception ex){ e.printStackTrace(System.out); }
写入
标准输出流,但是System.out可能会被重定向到文件输出流、IO输出流中;
(2)
catch(Exception ex){ e.printStackTrace(); }
写入
标准错误流,不会被重定向,Java中装门用于输出异常信息;
7、异常和记录日志
可以使用java.util.logging日志和记录工具来见输出记录到日志中:
import java.util.logging.*;
import java.io.*;
//构建可写入日志的异常类
public class LoggingException extends Exception{
private static Logger logger = Logger.getLogger("LoggingException"); //构建Logger日志对象
public LoggingException(){
StringWriter trace = new StringWriter(); //回收字符缓冲区中的输出来构造字符串字符流;
//printStackTrace(new PrintWriter(trace)); //将该字符串流格式加入到栈轨迹后,写入标准输出流;
logger.severe(trace.toString()); //通过logger发送一条指定内容的severe到标准输出;
}
}
//测试异常类
public class LoggingExceptions {
public static void main(String[] args){
try{
throw new LoggingException();
}catch(LoggingException ex){
System.err.println("Caught "+ex);
}
}
}
8、异常说明:throws关键字
(1)如果提供了源代码,客户端程序员可以在源代码中查找throw语句来获知相关信息,但是一般程序不会和源代码一起发布;
(2)为了解决这个问题,
java提供了throws语法,并且强制使用,告知客户端程序员某个方法可能抛出的异常;
void method() throws Exception1,Exception2...(异常列表) { }
(3)
可以声明方法将抛出异常,实际上却不抛出,编译器会强制用户抛出该异常并对其进行捕获处理,这样能使该类的派生类或接口能抛出该种异常,并重用代码;
9、捕获所用异常
(1)
Throwable是Java所有异常和错误的基类(Exception,Error),
Exception是所有编程异常类的基类;
①Exception继承自Throwable的方法:
String getMessage( ):获取详细信息;
String getLocalizedMessage( ):用本地语言表示的详细信息;
String toString( ):返回对Throwable的简单描述(包含详细信息);
②Exception继承自Object方法:
getClass( ):获取表示此类对象类型的对象
getName( ):返回该Class包含信息的名称;
getSimpleName( ): 返回Class类名;
③Exception自身方法:
void printStackTrace( ):打印Throwab和Throwabl的调用轨迹;
void printStackTrace(PrintStream):
void printStackTrace(java.io.PrintWriter):
Throwable fillInStackTrace():同于在Thowable对象的内部记录栈帧的当前状态;
(2)栈轨迹
getStackTrace( )返回一个有栈轨迹中的元素(StackTraceElement)所构成的数组,其中的每一个元素都表示一帧,元素0是栈顶元素(调用序列的最后一个方法抛出的,该Throwable被创建和抛出之处);
(3)重新抛出异常
重抛异常会把
异常重新抛给上一级环境中的异常处理程序,同一个try中的后续catch子句会被忽略,同时异常对象的所有信息都会被保存;
①直接将异常对象重新抛出,使用printStackTrace()显示将是原来异常抛出点的的调用栈信息;
②使用抛出 Throwable fillInStackTrace()可以更新抛出点,(栈轨迹会从重新抛出点开始);
public class Rethrowing {
public static void srcMethod() throws Exception{
throw new Exception("throw from srcMethod;");
}
//直接抛出
public static void a() throws Exception{
try{ srcMethod();
}catch(Exception ex){
ex.printStackTrace(); //用作抛出前后栈轨迹的对比;
throw ex;
}
}
//用fillInStackTrace()对抛出位置进行更新后(更新栈底),再抛出
public static void b() throws Exception{
try{ srcMethod();
}catch(Exception ex){
ex.printStackTrace(); //用作抛出前后栈轨迹的对比;
throw (Exception)ex.fillInStackTrace();
}
}
//测试两种抛出方式的区别
public static void main(String[] args){
//测试a()
try{ a();}
catch(Exceptionex){ ex.printStackTrace(); }
//测试b()
try{ b(;}
catch(Exceptionex){ ex.printStackTrace(); }
}
}
(4)异常链
①异常链:在捕获一个异常对象后再抛出一个异常对象啊,并且希望把原始异常的信息保存下来,使用Throwable类中的cause(因由)参数表示原始异常,于是可以
通过Throwable中的cause参数追踪到异常最初发生的位置;
②cause赋值:
a.通过Throwable带cause参数的构造器创建,在Java中只有Error,Exception,RuntimeException可以使用该方式;
b.使用Throwable的initCause()方法;
public class ExceptionCause{
private static void test1(){ throw new NullPointerException( ); }
private static void test2(){
try{ test1(); }
catch(NullPointerException ex){
//①使用指定的cause参数构造Exception;
throw new Exception(ex);
//②使用initCause()方法指定Throwable的cause参数;
/*Exception bussinessEx = new Exception ();
bussinessEx.initCause(ex);
throw bussinessEx; */
//③使用fillInStackTrace方法更新抛出点,不可能通过编译,因为Throwable的cause不能使它自身;
/*throw (Exception)ex.fillInStackTrace().initCause(ex); */
ex.printStackTrace();
}
}
public static void main(String[] args){
test2();
}
}
10、异常的限制
当覆盖方法时,只能抛出基类方法的异常说明里列出的那些异常,这意味着,当基类的代码应用到其派生对象时,也可以正常工作(保证向上转型);