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


通过异常处理错误

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从多个点返回
  1. void count(int num){  
  2.      try{  
  3.           System.out.println(”count 1”);  
  4.           if(num == 1return;  
  5.            System.out.println(”count 2”);  
  6.           if(num == 2return;  
  7.           System..out.println(”count 3”);  
  8.      }finally{  
  9.           System.out.println(”All count!”);  
  10.      }  
  11. }  
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块,直到满意结果;
  1. int[ ] list =new int[3];  
  2. int index = 5;  
  3. whlie(true){  
  4.      try{  
  5.           list[index] = 1024;  //list[5] = 1024 抛出异常,并被下面catch捕获,每次抛出异常,重新进入try块  
  6.           break;               //当上面try的代码不抛出异常时,跳出while(true)循环;  
  7.      }catch(ArrayIndexOutOfBoundsException ex){  
  8.           ex.printStackTrace(System.err);  
  9.       }finally{  
  10.           System.out.println(”Ready insert array”);  
  11.       }     
  12. }  
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日志和记录工具来见输出记录到日志中:
  1. import java.util.logging.*;  
  2. import java.io.*;  
  3. //构建可写入日志的异常类  
  4. public class LoggingException extends Exception{     
  5.        private static Logger logger = Logger.getLogger(“LoggingException”);  //构建Logger日志对象  
  6.        public LoggingException(){  
  7.              StringWriter trace = new StringWriter();   //回收字符缓冲区中的输出来构造字符串字符流;  
  8.           //printStackTrace(new PrintWriter(trace)); //将该字符串流格式加入到栈轨迹后,写入标准输出流;  
  9.              logger.severe(trace.toString()); //通过logger发送一条指定内容的severe到标准输出;  
  10.        }  
  11. }  
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到标准输出;
       }
}


//测试异常类
  1. public class LoggingExceptions {  
  2.        public static void main(String[] args){  
  3.              try{  
  4.                     throw new LoggingException();  
  5.              }catch(LoggingException ex){  
  6.                     System.err.println(”Caught ”+ex);  
  7.              }  
  8.        }  
  9. }  
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()可以更新抛出点,(栈轨迹会从重新抛出点开始);
  1. public class Rethrowing {  
  2.        public static void srcMethod() throws Exception{  
  3.              throw new Exception(“throw from srcMethod;”);  
  4.        }  
  5.        //直接抛出  
  6.        public static void a() throws Exception{  
  7.              try{ srcMethod();  
  8.              }catch(Exception ex){  
  9.                     ex.printStackTrace();     //用作抛出前后栈轨迹的对比;  
  10.                     throw ex;  
  11.              }  
  12.        }  
  13.        //用fillInStackTrace()对抛出位置进行更新后(更新栈底),再抛出  
  14.        public static void b() throws Exception{  
  15.              try{ srcMethod();  
  16.              }catch(Exception ex){  
  17.                     ex.printStackTrace();     //用作抛出前后栈轨迹的对比;  
  18.                     throw (Exception)ex.fillInStackTrace();  
  19.              }  
  20.        }  
  21.        //测试两种抛出方式的区别  
  22.        public static void main(String[] args){  
  23.              //测试a()  
  24.              try{     a();}  
  25.             catch(Exceptionex){     ex.printStackTrace(); }  
  26.              //测试b()  
  27.              try{    b(;}  
  28.             catch(Exceptionex){   ex.printStackTrace(); }  
  29.        }  
  30. }  
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()方法;
  1. public class ExceptionCause{  
  2.      private static void test1(){     throw new NullPointerException( );     }  
  3.      private static void test2(){  
  4.           try{     test1();     }  
  5.           catch(NullPointerException ex){  
  6.             //①使用指定的cause参数构造Exception;  
  7.                throw new Exception(ex);       
  8.             //②使用initCause()方法指定Throwable的cause参数;  
  9.                /*Exception bussinessEx = new Exception (); 
  10.                bussinessEx.initCause(ex); 
  11.                throw bussinessEx;   */  
  12.              //③使用fillInStackTrace方法更新抛出点,不可能通过编译,因为Throwable的cause不能使它自身;  
  13.                /*throw (Exception)ex.fillInStackTrace().initCause(ex); */  
  14.   
  15.               ex.printStackTrace();  
  16.           }  
  17.      }  
  18.      public static void main(String[] args){  
  19.          test2();  
  20.     }   
  21. }  
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、异常的限制
当覆盖方法时,只能抛出基类方法的异常说明里列出的那些异常,这意味着,当基类的代码应用到其派生对象时,也可以正常工作(保证向上转型);













  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值