面向对象(七)之异常详解


 

异常是什么呢?异常就是程序在运行时出现不正常情况。

异常怎么由来的呢?问题也是现实生活中一个具体的事物,也可以通过java的类的形式进行描述,并封装成对象。其实就是java对不正常情况描述后的对象体现。

对于问题的划分,总共两种:

a) 严重的问题:java通过Error类进行描述,对于Error一般不编写针对性的代码对其处理

b) 非严重的问题,java通过Exception类进行描述,对于Exception可以使用针对性的处理方式进行处理

无论Error或者Exception都具有一些共性内容,比如不正常情况的信息,引发原因等。这些共性的内容就Throwable,它是ErrorException的父类

 

异常的处理

try-catch语句

对于异常我们怎么处理呢?java提供了特有的语句(try-catch)进行处理。格式如下:

try{

 需要被检测的代码

}catch(异常类变量)

处理异常的代码;(处理方式)

finally

 一定会执行语句;

蓝色部分可以省略不写

 

例如我们对以下代码除零抛出算数异常进行处理;

处理前:

class ExceptionTest

{

        publicstatic void main(String[] args)

        {

                  Demod=new Demo();

                  intx=d.div(3,0);

                  System.out.println(x);//抛出ArithmeticException异常

        }

}

class Demo

{

   public int div(int a,int b){

           return a/b;

        }

处理后(使用try-catch):

class ExceptionTest

{

        publicstatic void main(String[] args)

        {

                  Demod=new Demo();

                  try{

                  intx=d.div(3,0);

                  System.out.println(x);//异常后,这段代码不执行

                  }catch(Exceptione){  //Exception e =new ArithmeticException();

             System.out.println("嗨,除零了");

          }

        }

}

class Demo

{

   public int div(int a,int b){

           return a/b;

        }

}

对于这个以上Exception e其实是吧new ArithmeticException();对象传给了形参e,就形成了多态。Exception e =new ArithmeticException();对于catch捕获到的异常对象常见的方法操作是什么呢?

a) 获取异常信息:publicString getMessage():返回此 throwable的详细消息字符串

b) public String toString():返回此 throwable的简短描述

c) public void printStackTrace():将此 throwable及其追踪输出至标准错误流。

对于这三种常见方法我们来看看如下代码:

class ExceptionTest

{

public static void main(String[] args)

{

          Demo d=new Demo();

 

          try{

          int x=d.div(3,0);

          System.out.println(x);

          }catch(Exception e){

            System.out.println(e.getMessage()); // /by zero

            System.out.println(e.toString());//异常名称:异常信息

            e.printStackTrace();//异常名称,异常信息,异常所出现的位置

                                //其实jvm默认的异常处理机制,就是在调用printStackTrace()方法,打印异常的堆栈的跟踪信息

            }

}

}

classDemo

{

   public int div(int a,int b){

   return a/b;

}

}

异常声明throws关键字

throws关键字在功能上通过throws的关键字声明了该功能有可能出现问题。会提示你去如何让解决该功能如何解决?不解决编译错误。如以下代码:

class ThrowsExceptionTest

{

        publicstatic void main(String[] args)

        {

                  

                  Demod=new Demo();

                  intx=d.div(3,0);

                  System.out.println(x);

        }

}

class Demo

{

   public int div(int a,int b)throws Exception{  //编译抛出异常

           return a/b;

        }

 

怎么解决编译错误呢?我们在main方法后面再加上)throws Exception{  编译没错,如果运行出错,最终虚拟机会处理,程序结束。

我们还是使用try-catch来处理这种异常,记住throws什么名称的异常,try-catch语句里就处理什么异常,如以下代码:

class Demo

{

   public int div(int a,int b)throwsArithmeticException{  //抛出什么异常

           return a/b;

        }

}

class ThrowsExceptionTest

{

        publicstatic void main(String[] args)

        {

                  try{

                  Demod=new Demo();

                  intx=d.div(3,0);

                  System.out.println(x);

                  }catch(ArithmeticException e){  //处理什么异常

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

                  }

        }

}

 

多异常处理

我们在处理异常时,我们有如下建议:

a)      声明异常时,建议声明更为具体的异常。这样处理的可以更具体

b)      catch里面应该有针对性的异常处理,不要把一切交给父异常Exception处理。甚至不使用,出错了就停止程序,我们去处理程序。为了代码的严谨性。

c)      对方声明几个异常,就对应有几个catch块,如果多个catch块中的异常出现继承关系,父类异常catch块放在最下面,不要定义多余的catch块。

d)      建议在进行catch处理时,catch中一定要定义具体处理方式。不要简单定义一句e.printSrackTrace(),也不要简单的就书写一条输出语句。在实际开发中我们要对异常进行处理。

我们看看多异常处理的代码:

classDemo

{

   public int div(int a,int b)throws ArithmeticException,ArrayIndexOutOfBoundsException{  

      /*

  数组越界异常

  */

          int[] array=new int[a];

          System.out.println(array[2]);

      /*

  除零异常

  */

   return a/b;

}

}

classThrowsExceptionTest

{

public static void main(String[] args)

{

          try{

          Demo d=new Demo();

          int x=d.div(3,0);

          System.out.println(x);

          }catch(ArithmeticException e){  

            System.out.println(e.toString()); //处理除零异常

            System.out.println("除零了");

          }catch(ArrayIndexOutOfBoundsExceptione){ //处理数组越界异常

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

             System.out.println("数组越界异常");

          }catch(Exception e){

             System.out.println("other Exception"); //实际不建议使用,且不能放在catch块第一行

          }

}

}

自定义异常

什么是自定义异常?因为项目中会出现特有的问题,而这些问题并未被java所描述并封装对象。所以对于这些特有的问题可以按照java的对问题封装的思想,将特有的问题,进行自定义的异常封装。

我们如何去自定义异常呢?

 

我们看一下以下程序的需求,在这段程序中,对与除数是-1,也视为是错误的是无法进行运算的。那么就需要对这个问题进行自定义的描述。

class CustomException

{

        publicstatic void main(String[] args)

        {

                  Divisord=new Divisor();

                  try{

                  intnum=d.divisor(4,-1);

                  System.out.println(num);

                  }catch(NegativeExceptione){

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

                            System.out.println("不能除以负数");

                  }

        }

}

class Divisor

{

 

   public int divisor(int a,int b) throws NegativeException{ //必须要抛出异常,否则编译错误

                  if(b<0){

                    throw new NegativeException(); //抛出自定义异常,使用throw

                  }

          return a/b;

        }

   

}

classNegativeException extends Exception //自定义除以负数异常

{

}

 

以上代码中,当在函数内部出现了throw关键字常对象,那么就必须要给对应的处理动作。要么在函数上声明让调用者处理。

一般情况下,函数内出现异常,函数上需要声明。

 

我们发现打印的结果中没有只有异常的名称,却没有异常的信息,因为自定义的异常并未自定义信息。

如何自定义异常信息呢?我们先看看如下代码?

class CustomException

{

        publicstatic void main(String[] args)

        {

                  Divisord=new Divisor();

                  try{

                  intnum=d.divisor(4,-9);

                  System.out.println(num);

                  }catch(NegativeExceptione){

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

                            System.out.println("不能除以负数");

                            System.out.println("this is negative"+e.getValue()); //调用自定义方法

                  }

        }

}

class Divisor

{

 

   public int divisor(int a,int b)throws NegativeException{//必须要抛出异常,否则编译错误

                  if(b<0){

                    throw new NegativeException("/by nagative",b); //抛出自定义异常,使用throw

                  }

          return a/b;

        }

   

}

class NegativeException extendsException //自定义除以负数异常

{

        intvalue; //自定义一个值

        

        NegativeException(Stringmsg, int value){   //自定义构造方法

         super(msg);//super调用父类中的构造方法

     this.value=value;

        }

 

        publicint getValue(){ //子类自定义一个获取方法

         return value; 

        }

}

通过以上代码我们知道了如何自定义信息?因为父类中已经把异常信息的操作都完成了,所以子类只要在构造时,将异常信息传递给父类,是通过super语句,那么就可以直接通过getMessage方法获取自定义的异常信息。

 

总结:当我们自定义异常,必须是自定义类去继承Exception或者ThrowableError。为什么要继承Exception呢,异常体系中有一个特点,因为异常类和异常对象都需要被抛出,他们都具备可抛性这个可抛性是Throwable这个体系中独有的特点,只有这个体系中的类和对象才可以被throwthrows操作。

 

 

throwsthrow的区别:

a)      throws是使用方法上,throws后面跟的是异常类,可以跟多个,用逗号隔开

b)      throw是使用在方法内,throw后跟的是异常对象。

 

RuntimeException

Exception中有一个特殊的子类异常RuntimeException运行时异常。它和别的异常有种特殊的意义在里面。

a) 如果在方法内容抛出该异常,方法上可以不用声明,编译也一样通过。

b) 如果在方法上声明了该异常,调用者可以不用进行处理。编译一样通过。

如以下例子:

class RuntimeExceptionTest

{

        publicstatic void main(String[] args)

        {

                  Divisor2d=new Divisor2();

                  intnum=d.divisor(3,0);

                  System.out.println(num);

        }

}

class Divisor2

{

                  publicint divisor(int a,int b) {

                           if(b==0){

                    throw new ArithmeticException("除零了");//ArithmeticExceptionRuntiemException的子类,如果是new Exception程序出错

                  }

          return a/b;

        }

}

注意:我们之所以不用在方法上声明,是因为不需要让调用者处理。当该异常发生,我们希望该程序去停止?为什么呢?因为在运行时,出现了无法继续运算的情况,希望停止程序后,让我们程序员对代码进行修正。

 

我们来看看自定义异常去继承RuntimeException是什么情况?

classRuntimeExceptionTest

{

public static void main(String[] args)

{

          Divisor2 d=new Divisor2();

          int num=d.divisor(3,-9);

          System.out.println(num);

}

}

classDivisor2

{

          public int divisor(int a,int b){   //这里不用写throwsCustomsException2

                   if(b<0){

                    throw new CustomsException2("除以负数啦");

                   }

                   if(b==0){

            throw new ArithmeticException("除零了");//ArithmeticExceptionRuntiemException的子类

          }

  returna/b;

}

}

classCustomsException2 extends RuntimeException{ //自定义异常去继承RuntimeException

String msg;

CustomsException2(String msg){

 this.msg=msg;    

}

}

从以上代码可以总结:当我们自定义异常时,如果该异常的一旦发生,无法再继续进行运算,就让该自定义异常继承RuntimeException

 

 

总结:对于异常我们分为两种:

a)   编译时被检测的异常

b)   编译时不被检测的异常(运行时异常,RuntimeException以及其子类)

 

finally代码块

finally代码块是用来定义一个一定执行的代码块,通常用于关闭资源。finally是一定会执行的

如以下例子:

class FinallyTest

{

        publicstatic void main(String[] args)

        {

                  Demod=new Demo();

                  try{

                  intnum=d.method(4,0);

         System.out.println(num);

                  }catch(Exceptione){

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

                  }finally{  //这段代码一定会被执行

                   System.out.println("这段代码一定会被执行");

                  }

                  System.out.println("over");

        }

}

class Demo

{

        publicint method(int a,int b){

     return a/b;

        }

 

}

 

关于异常处理的格式总共有三种:

第一个格式:

try{

}catch(){

}

第二个格式:

try{

}catch(){

}finally{

}

第三个格式:

try{

}finally{

}

记住一点:catch是用于处理异常,如果没有catch就代表异常没有被处理过,如果该异常是检测时异常,那么必须声明。

 

 

异常-覆盖

异常在子父类覆盖中的体现:

1.      子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或者该异常的子类

 如以下代码:

classFather //父类

{

  public void method() throws FatherException{

  

  }

}

classchild extends Father //子类

{

   public void method() throws ChildException{ //如果子类复写父类方法,必须抛父类已抛的异常

          //或者是这个异常的子异常,不能抛其他异常。也可以不抛异常

}

}

 

classFatherException extends Exception  //父异常

{

 

}

classChildException extends FatherException //子异常

{

 

}

2.      如果父类方法抛出多个异常,那么子类在覆盖该方法时,只能抛出父类异常的子集或者不抛异常

3.      如果父类或者接口的方法中没有异常抛出,那么子类在覆盖方法时,也不可以抛出异常。如果子类发生异常,就必须进行try处理,绝对不能抛。

异常的总结

异常是什么?异常是对问题的描述。将问题进行对象的封装

异常体系:

     Throwable

           |--Error

           |--Exception

              |--RuntimeException

 

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

 

 

 

throwthrows的用法:

a)   throw定义在方法内,用于抛出异常对象,当方法内容有throw抛出异常对象,并未被进行try处理,必须要在方法上声明,否则都是编译失败,注意,RuntimeException除外,也就是说,方法内如果抛出的RuntimeException异常,方法上可以不用声明。如果方法声明了异常,调用者需要进行处理,处理方法可以throws可以try

b)   throws定义在方法上,用于抛出异常类,可以抛出多个,它们用逗号分隔开。

 

异常有两种:

a) 编译是被检测异常。该异常在编译时,如果没有处理(没有抛没有try),编译失败该异常被标识,代表这可以被处理

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

 

 

异常处理语句:

第一个格式:

try{

}catch(){

}

第二个格式:

try{

}catch(){

}finally{

}

第三个格式:

try{

}finally{

}

注意:1. Finally中定义的通常是关闭资源代码,因为资源必须释放

 2.finally只有一种情况不会执行。当执行到Syetem.exit0);finally不会执行。

 

自定义异常

定义类继承Exception或者RuntimeException

1.      为了让该自定义类具备可抛性

2.      让该类具备操作异常的共性方法

当药定义自定义异常的信息时,可以使用父类已经定义好的功能。

异常异常信息传递给父类的构造方法:

class MyException extends Exception

  MyExceptionString message){

supermessage);

自定义异常:按照java的面向对象思想,将程序中出现的特有问题进行封装

 

 

异常的好处:

a) 将问题进行封装

b) 将正常流程代码和问题处理代码相分离,方便与阅读

 

异常的处理原则:

a) 处理方式有两种:trythrows

b) 调用到抛出异常的功能是,抛出几个,就处理几个。

一个try对应多个catch

c) 多个catch,父类的catch放到最下面。

d) catch内,需要定义针对性的处理方式,不要简单的定义printStackTrace,输出语句。也不要不写

当捕获到的异常,本功能处理不了时,可以继续在catch中抛出

try

 throw new AException

catch(){

 throw e

如果该异常处理不了,但并属于该功能出现的异常可以将异常转换后,在抛出和该功能相关的异常。

或者异常可以处理,当需要将异常产生的和本功能的相关的问题提供出去,当调用者知道,并处理,也可以将捕获异常处理后,转换新的异常

try

throw new AException();

catchAException){

//AException先处理

throw new BException();

比如说汇款的例子。

 

异常的注意事项:

在子父类覆盖时:

1.      子类抛出的异常必须是父类的异常的子类或者子集

2.      如果父类或者接口没有异常抛出时,子类覆盖出现异常,只能try不能抛。

 

 

 

练习1:毕老师上课:

class ExceptionTest2

{

        publicstatic void main(String[] args)

        {

                  Teachert=new Teacher("毕老师");

                  try{

                  t.teach();

                  }catch(NoPlanExceptione){

                     System.out.println("换老师或者换电脑,放假");

                  }

        }

}

class Teacher  //老师

{

        public static String name;  //老师姓名

        privateComputer c;

 

  Teacher(){  

  

  }

 

        Teacher(Stringname){

        this.name=name;

 

        c=newComputer();

 

        }

 

        publicvoid teach() throws NoPlanException{

 

                  try{

                           c.run();

                  }catch(ComputerSmokingExceptione){

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

                           doTest();

                     throw new NoPlanException("课时无法继续");

                  }catch(ComputerBlueScreenExceptione){

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

                         c.reset();

                  }

 

         System.out.println(name+"讲课");

        }

 

   public void doTest(){

          System.out.println(Teacher.name+"让学生去做练习");

        }

}

/*

变量state代表了电脑状态

0数字:电脑状态正常

1数字:电脑状态蓝屏

2数字:电脑状态冒烟

*/

 

class Computer //电脑

{

        intstate=2; 

        Teachert=new Teacher();  

        publicvoid run() throws ComputerBlueScreenException,ComputerSmokingException{  

                  if(state==1)  //电脑状态为1,蓝屏

                  {

           throw new ComputerBlueScreenException("电脑蓝屏了");

                  }elseif(state==2){  //电脑状态为2,冒烟了

                       throw new ComputerSmokingException("电脑冒烟了");

                  }elseif(state==0){

                                    System.out.println("电脑正常运行");

                  }

        }

 

        publicvoid reset(){

         System.out.println(t.name+"重启电脑");

         }

 

}

 

/*

 

自定义异常:

1.电脑冒烟异常

2.电脑蓝屏异常

3.课时无法继续异常

*/

 

class ComputerSmokingException extendsException  //电脑冒烟异常

{

        ComputerSmokingException(Stringmsg){

           super(msg);

        }

 

}

 

class ComputerBlueScreenException extendsException//电脑蓝屏异常

{

 

                  ComputerBlueScreenException(Stringmsg){

                    super(msg);

                  }

 

}

 

class NoPlanException extendsException //课时无法继续异常

{

   NoPlanException(String msg){

          super(msg);

        }

}

 

练习二:计算圆和长方形的面积

class Test2

{

        publicstatic void main(String[] args)

        {

                  Recr=new Rec(1,4);

                  r.area();

 

                  Circlec=new Circle(0);

                  c.area();

        }

}

 

 

 

interface Shape{

  public void area();

}

/*

长方形面积

*/

class Rec implements Shape

{

        intwid,hei;//长方形的长和高

        Rec(intwid,int hei){

                  if(wid<=0||hei<=0){

                  thrownew IllegalValueException("非法字符");

                  }

          this.wid=wid;

          this.hei=hei;

        }

  public void area(){

     System.out.println("长方形的面积area=="+wid*hei);

  }

 

}

 

class Circle implements Shape

{

        intr;

        privatefinal static double PI=3.14;

        Circle(intr){

        if(r<=0){

         throw new IllegalValueException("非法字符");

        }

        this.r=r;

        }

        publicvoid area(){

           System.out.println("三角形的面积是=="+r*PI);

        }

}

 

 

 

 

class IllegalValueException extendsRuntimeException  //非法字符异常

{

      IllegalValueException(String msg){

               super(msg);

          }

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值