黑马程序员__异常及其处理机制

黑马程序员——异常

-------android培训java培训、期待与您交流! ----------

1.      异常:是对问题的描述,将问题进行对象的封装。

2.      异常体系:

所有异常的父类为ThrowableThrowable分为两类:ErrorException

Error:错误,JVM内部产生的错误,程序代码无法控制和处理。

Exception:异常,一般由代码中的逻辑错误,语法错误所导致程序中断。

该体系的特点:异常体系中的所有类以及建立的对象都是具备可抛性的,也就是说可以被throwthrows关键字操作。也只有异常体系具有这个特点。

3.      异常的种类:

编译时被检测异常:在编译时被检测到的异常,该异常在编译时如果没有处理就会编译失败。该异常被表示,代表这可以被处理。

运行时异常:在运行时会发生的异常,该异常在编译时不需要处理,编译器不检查该异常的发生。建议不处理,让程序停止,需要对代码进行修正。

4.      异常处理机制

    (1)使用try...catch捕获异常

     1)如果执行try块里的业务逻辑代码块时出现异常,系统自动生成一个异常对象,该异常对象被提交给Java运行时环境,这个过程被称为抛出(throw)异常, 当Java运行时环境收到异常对象时,会寻找能处理该异常对象的catch块,如果找到合适的catch块,则把该异常对象交给该catch块处理,这个过程被称为捕获(catch)异常;如果Java运行时环境找不到捕获异常的catch块,则运行时环境终止,Java程序也将退出。

    2) catch关键字的形式;catch(Exceptione),这意味着每个catch块都是专门用于处理该异常类及其子类的异常实例。 当Java运行时环境接收到异常对象后,会依次判断该异常对象是否是catch块后异常类或其子类的实例,如果是,Java运行时环境将调用该catch块来处理该异常;否则再次拿该异常对象和下一个catch块里异常类进行比较。当程序进入负责异常处理的catch块时,系统生成的异常对象将会传给catch块后的异常形参,从而允许catch块通过该对象来获得异常的详细信息。

     3) try块后可以有多个catch块,这是为了针对不同的异常类提供不同的异常处理方式。当系统发生不同的意外情况时,系统会生成不同的异常对象,Java运行时就会根据该异常对象所属的异常类来决定使用 哪个catch块来处理该异常。

     

    public class DivTest {

         public static void main(String [] args ) {

                 try {

                         int a =Integer. parseInt(args [0]);

                         int b =Integer. parseInt(args [1]);

                         int c =a/ b;

                        System .out. println("您输入的两个数相除的结果是:" +c);

                 } catch (NumberFormatException e) {

                        System .out. println("数字格式异常:程序只能接收整数参数" );

                 }catch (IndexOutOfBoundsException e ) {

                        System .out. println("数组越界:运行时程序输入的参数个数不够" );

                 }catch (ArithmeticException e ) {

                        System .out. println("算术异常" );

                 }catch (Exception e ) {

                        System .out. println("未知异常" );

                 }

         }

 

}


 

 

   4) try块与if语句不一样,try块后的花括号{...}不可以省略,即使try块里只有一行代码,也不可省略这个花括号。同样的,catch块后的花括号{...} 也不可以省略。

   5)  try块里声明的变量是代码块内的局部变量,它只在try块内有效,在catch块中不能访问该变量。

   6)在进行异常捕获时,一定要先捕获小异常,再捕获大异常。

    7)  Java7提供的多异常捕获:在Java7以前,每个catch块只能捕获一种类型的异常;但从Java7开始,一个catch块可以捕获多种类型的异常。使用时注意:

     <1>捕获多种类型异常时,多种异常类型之间用(|)隔开 

      <2>捕获多种类型异常时,异常之间要是并列的平级关系,不能出现子父类的情况

      <3>捕获多种类型的异常时,异常变量有隐式的final修饰,因此程序不能对异常变量重新赋值。

   

   public class MultiException {

         public static void main(String [] args ) {

                 try {

                         int a =Integer. parseInt(args [0]);

                         int b =Integer. parseInt(args [1]);

                         int c =a/ b;

                        System .out. println("您输入的两个数相除的结果是:" +c);

                 } catch (NumberFormatException | IndexOutOfBoundsException | ArithmeticException e) {

                        System .out. println("程序发生了数字格式异常、数组越界异常、算术异常之一" );

                         //捕获多种异常时,异常变量默认有final修饰,所有下面代码有错

                         //e=newArithmeticException("test");

                 }catch (Exception e ) {

                        System .out. println("未知异常" );

                         //捕获一种类型的异常时,异常变量没有final修饰,所以下面代码完全正确

                        e =new RuntimeException( "test");

                 }

 

         }

}


 

8)访问异常信息:如果程序需要在catch块中访问异常对象的相关信息,则可以通过访问catch后的异常形参来获得。当Java运行时决定调用某个catch块来处理该异常信息时,会将异常对象赋给catch块后的异常参数,程序即可通过该参数来获得异常的相关信息。所有的异常对象都包含了如下几个常用的方法。

         <1>getMessage():返回该异常的详细描述字符串。

          <2>printStackTrace();将该异常的跟踪栈信息输出到标准错误输出。

          <3>printStackTrace(PrintStreams);将该异常的跟踪栈信息输出到指定输出流。

         <4>getStackTrace():返回该异常的跟踪栈信息。

9)使用finally回收资源

     程序在try块中打开了一些物理资源如数据库连接、网络连接和磁盘文件等,这些物理资源都必须显示回收。Java的垃圾回收机制不会回收任何物理资源,垃圾回收机制只能回收堆内存中对象所占用的内存。为了保证一定能回收try块中打开的物理资源,异常处理机制提供了finally块。不管try块中的代码是否出现异常,也不管哪一个catch块被执行,甚至在try块或catch块中执行了return语句,finally块总会被执行。完整的Java异常处理语法结构如下:

try{

     //业务实现代码

...................................

}

catch(SubException e){

     //异常处理块1

....................................

}

catch(SubException e){

//异常处理块2

...............................................

}

..............

finally{

//资源回收

..............................................

}


 

 

     在异常处理语法结构中,只有try块是必须的,也就是说,如果没有try块,则不能有后面的catch块和finally块;catch块和finally块都是可选的,但catch和finally块至少出现其中之一,也可以同时出现;可以有多个catch块,捕获父类异常的catch块必须位于捕获子类异常的后面;但不能只有try块,既没有catch块,也没有finally块;多个catch块必须位于try块之后,finally块必须位于所有的catch块之后。

实例程序代码:

     public class FinallyTest {
         public static void main(String [] args ) {
                FileInputStream fis=null;
                 try {
                        fis =new FileInputStream( "a.txt");
                 } catch (FileNotFoundException e) {
                        System .out. println(e .getMessage()) ;
                         //return语句强制方法返回
                         return ;
                         //使用exit退出虚拟机
                         //System.exit(0);
                 }
                 finally{
                         //关闭磁盘文件,回收资源
                         if (fis!= null) {
                                 try {
                                        fis .close() ;
                                 } catch (IOException e) {
                                        e .printStackTrace() ;
                                 }
                         }
                        System .out. println("执行finally块里的资源回收" );
                 }

         }

}


 

 

    执行结果为:

a.txt (系统找不到指定的文件。)

执行finally块里的资源回收

 

程序分析:上面程序的try块后增加了finally块,用于回收再try块中打开的物理资源。在通常情况下,一旦在方法里执行到return语句的地方,程序将立即结束方法;现在不会了,虽然return语句也强制方法结束,但一定会先执行finally块里的代码。上面的运行结果表明方法返回之前还是执行了finally块的代码。

代码实例:

 

  public class FinallyTest {

         public static void main(String [] args ) {

                FileInputStream fis=null;

                 try {

                        fis =new FileInputStream( "a.txt");

                 } catch (FileNotFoundException e) {

                        System .out. println(e .getMessage()) ;

                         //return语句强制方法返回

                       // return ;

                         使用exit退出虚拟机

                         System.exit(0);

                 }

                 finally{

                         //关闭磁盘文件,回收资源

                         if (fis!= null) {

                                 try {

                                        fis .close() ;

                                 } catch (IOException e) {

                                        e .printStackTrace() ;

                                 }

                         }

                        System .out. println("执行finally块里的资源回收" );

                 }

 

         }

 

}


 

执行结果为:

a.txt (系统找不到指定的文件。)

程序分析:如果将return语句注释掉,也就在异常处理的catch块中使用System.exit(0)语句来退出虚拟机。所以执行上面的代码会有以上的结果,也就是说finally块没有被执行。如果在异常处理代码中使用System.exit(0)语句来退出虚拟机,则finally块将失去执行的机会。

注意:除非在try块、catch块中调用了退出虚拟机的方法,否则不管在try块、catch块中执行怎样的代码,出现怎样的情况,异常处理的finally块总会被执行。在通常情况下,不要在finally块中使用如return或throw等导致方法终止的语句,一旦在finally中使用了return或throw语句,将会导致try块、catch块中的return、throw语句失效。

当Java程序执行try块、catch块时遇到了return或throw语句,这两个语句都会导致该方法立即结束,但是系统执行这两个语句并不会结束方法,而是去寻找该异常处理流程中是否包含finally块,如果没有finally块,程序立即执行return或throw语句,方法终止;如果有finally块,系统立即开始执行finally块——只有当finally块执行完成后,系统才会再次跳回来执行try块、catch块里的return或throw语句;如果finally块里也使用了return或throw等方法导致方法终止的语句,finally块已经终止了方法,系统将不会跳回去执行try块、catch块里的任何代码。

(2)使用throws声明抛出异常

     当前方法不知道如何处理这种类型的异常,该异常应该由上一级调用者来处理;如果main方法也不知道如何处理这种类型的异常,也可以使用throws声明抛出异常,该异常将交给JVM处理。JVM对异常的处理方式是,打印异常的跟踪栈信息,并中止程序运行。

    throws声明抛出只能在方法签名中使用,throws可以声明抛出多个异常类,多个异常类之间使用逗号隔开。语法格式如下:
          throws ExceptionClass1,ExceptionClass2...

     一旦使用throws抛出该异常,程序就无须使用try...catch块来捕获该异常类了。

     如果某段代码中调用了一个带throws声明的方法,该方法声明抛出了Checked异常,则表明该方法希望它的调用者来处理该异常。也就是说,调用该方法时要么放在try块中显式捕获该异常,要么放在另一个带throws声明抛出的方法中。 

(3)使用throw抛出异常

     如果需要在程序中自行抛出异常,则应使用throw语句,throw语句可以单独使用,throw语句抛出的不是异常类,而是一个异常的实例,而且每次只能抛出一个异常实例。throw的语法格式:throw ExceptionInstance;

     不管是系统自动抛出的异常,还是程序员手动抛出的异常,Java运行时环境对异常的处理没有任何差别。

     如果throw语句抛出的异常时Checked异常,则该throw语句要么处于try块里,显式捕获该异常,要么放在一个带throws声明抛出的方法中,即把该异常交给该方法的调用者处理。

     如果throw语句抛出的异常是Runtime异常,则该语句无须放在try块里,也无须放在throws声明抛出的方法中,程序既可以显式使用try...catch来捕获并处理该异常,也可以完全不理会该异常,把该异常交给方法调用者处理。

(4)catch和throw同时使用

     当一个异常出现时,单靠某个方法无法完全处理该异常,必须由几个方法协作才可以完全处理该异常。也就是说,在异常出现的当前方法中,程序只对异常进行部分处理,还有些处理需要在该方法的调用者中才能完成,所以应该再次抛出异常,让该方法的调用者也能捕获到异常。

 

     

public class AutoExceptonTest {

         public static void main(String [] args ) {

                AutoExceptonTest at=new AutoExceptonTest ();

                 try {

                        at .bid("df") ;

                 } catch (AutoException e) {

                         //再次捕获到bid()方法中的异常,并对异常进行处理

                        System .err. println(e .getMessage()) ;

                 }

         }

         private double ininPrice= 30.0 ;

         /**

        因为该方法中显式抛出了AuctionException异常,所有此处需要声明抛出AuctionException异常

        */

        

         public void bid( String bidprice) throws AutoException {

                 double d =0. 0;

                 try {

                        d =Double. parseDouble(bidprice );

                 } catch (Exception e) {

                         //此处完成本方法中对异常执行的修复处理,此处仅仅是在控制台打印异常的跟踪栈信息

                        e .printStackTrace() ;

                         //再次抛出子定义异常

                         throw new AutoException( "竞拍价必须是数值,不能包含其他字符串!" );

                 }

                 if (ininPrice> d) {

                         throw new AutoException( "竞拍价比起拍价低,不允许竞拍" );

                 }

                 ininPrice=d ;

         }

        

        

}


 

5.  自定义异常类

      用户自定义的异常都应该继承Exception基类,如果希望自定义Runtime异常,则应该继承RuntimeException基类。

 

import java.lang.*;

/*

圆形和长方形在求面积时,用异常处理来解决可能会出现的问题

*/

 

 

//图形

abstract class Shape

{

    //求面积的功能

    abstractdouble getArea();

}

//长方形

class Rectangle extends Shape

{

    privatedouble length;

    privatedouble width;

    publicRectangle(double length,double width)

    {

        if(length<=0|| width<=0)

            thrownew IlleglVauleException("出现非法值啦!");

        this.length=length;

        this.width=width;

    }

    //覆写父类的方法

    publicdouble getArea()

    {

        returnlength*width;

    }

}

//圆形

class Circle extends Shape

{

    privatedouble radius;

    publicCircle(double radius)throws IlleglVauleException

    {

        if(radius<=0)

            thrownew IlleglVauleException("出现非法值啦!");

        this.radius=radius;

    }

    //覆写父类的方法

    publicdouble getArea()

    {

        returnMath.PI*radius*radius;

    }

}

//自定义异常

class IlleglVauleException extends RuntimeException

{

    publicIlleglVauleException(String message)

    {

        super(message);

    }

}

class ExceptionDemo

{

    publicstatic void main(String[] args) 

    {

        Rectanglerec=new Rectangle(2.3,1);

        System.out.println("长方形的面积是:"+rec.getArea());

        try{

        Circlecir=new Circle(-1);

        System.out.println("圆形的面积是:"+cir.getArea());

        }

        catch(IlleglVauleExceptione)

        {

            System.out.println("圆形的半径值不合法!");

        }

    }

}

 

6.  异常链:把捕获一个异常然后接着抛出另一个异常,并把原始异常信息保存下来的一种典型的链式处理(23种设计模式之一:职责链模式)。

代码实例:

 

   public calSal() throwsSalException{

          try{

              //实现结算工资的业务逻辑

                   .................

          }

         catch(SQLException sqle){

          //把原始异常记录下来,留给管理员

              .........................

              //下面异常中的sqle就是原始异常

              throw new SalException(sqle);//此处传入了一个Exception对象,所以SalException也应该有相应的构造方法

          }

          catch(Exceptione){

             //把原始异常记录下来,留给管理员

              .........................

                   //下面异常中的e就是原始异常

              throw new SalException(e);//此处传入了一个Exception对象

          }

     }


 

从JDK1.4以后,所有的Throwable的子类在构造器中都可以接收一个cause对象作为参数。这个cause就用来表示原始异常,这样可以把原始异常传递给新的异常,使得即使在当前位置抛出了新的异常,也能通过这个异常链追踪到异常最初发生的位置。

 <p align="left"><span style="font-size:14px;"><strong>public class SalException extends Exception{</strong></span></p><p align="left"><span style="font-size:14px;"><strong>     //无参构造器</strong></span></p><p align="left"><span style="font-size:14px;">     public SalException(){}</span></p><p align="left"><span style="font-size:14px;">     //带一个字符串参数的构造器</span></p><p align="left"><span style="font-size:14px;">     public SalException(Stringmsg){</span></p><p align="left"><span style="font-size:14px;">          super(msg);//调用父类的构造器</span></p><p align="left"><span style="font-size:14px;">     }</span></p><p align="left"><span style="font-size:14px;">     //创建一个可以接收Throwable参数的构造器</span></p><p align="left"><span style="font-size:14px;">     public SalException(Throwablet){</span></p><p align="left"><span style="font-size:14px;">          super(t);</span></p><p align="left"><span style="font-size:14px;">     }</span></p><p align="left"><span style="font-size:14px;"><strong>} </strong></span></p>


 

 

 

创建了这个SalException业务异常类后,就可以用它来封装原始异常,从而实现对异常的链式处理。

注意:

     只有对外部的、不能确定和预知的运行时错误才使用异常。对于完全已知的错误,应该编写处理这种错误的代码,增加程序的健壮性;对于普通的错误,应该编写处理这种错误的代码,增加程序的健壮性。

     异常只应该用于处理非正常的情况,不要使用异常处理来代替正常的流程控制。对于一些完全可预知,而且处理方式清楚的错误,程序应该提供相应的错误处理代码,而不是将其笼统地称为异常。

-------android培训java培训、期待与您交流! ----------

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值