JAVA--异常

本章目标

•理解异常的概念:  运行时的错误
运用try块、catch块和finally块处理异常
•Java中异常类的继承体系结构
运用多重catch块处理异常
•运用嵌套try/catch块处理异常
运用关键字throw和throws处理异常
•用户自定义异常



什么是异常

•程序中运行时出现的错误被称为异常
•异常可分为两大类:编译时异常(错误)运行时异常
编译时异常一般是指语法错误,可以通过编译器的提示加以修正,这里我们不予讨论;
运行时异常包括:
                             >运行错误:如数组下标越界,除数为0等;
                             >逻辑错误:如年龄超过200岁等。


产生异常的原因

>系统资源不可用:如内存分配失败,文件打开失败,数据源连接失败等等;
>程序控制不当:如被零除,负数开方,数组下标越界等等。


产生异常后的反应

•当异常发生时,程序一般会作出如下反应:
                                                                    >发生异常的部分产生系统定义的错误信息;
                                                                    >程序意外终止,并将控制权返回操作系统;
                                                                    >程序中所有已分配资源的状态保持不变,这样将会导致资源泄漏。

•那么我们就必须对有可能产生的异常进行处理。



try块和catch块

try块:一般用来监视有可能产生异常的代码部分;
catch块:当异常产生后,catch块捕捉异常,并在其中对异常进行处理

try/catch块的一般形式

                 ……

                 try

                     {

                         ……            //监视有可能发生异常的代码段

                      }

                 catch (异常类型)   //捕获发生的异常

                      {

                          ……             //对异常进行处理

                       }

                   ……



Java中的异常类

异    常

说    明

Exception

异常层次结构的根类

RuntimeException

许多java.lang异常的基类

ArithmeticException

算术异常,如:除数为0

IllegalArgumentException

方法接收到非法参数

ArrayIndexOutOfBoundsException

数组下标越界

NullPointerException

访问空引用

ClassNotFoundException

不能加载所需的类

NumberFormatException

字符串转换数字失败

IOException

I/O异常的根类

FileNotFoundException

找不到文件

EOFException

文件结束




异常类体系结构图





异常类体系结构说明

•Throwable有两个直接子类:
                                         >Error类:Error类的异常通常为内部错误,因此在正常情况下并不期望用户程序捕获它们;
                                         >Exception类:绝大部分用户程序应当捕获的异常类的根类;
•一些常用的异常类都直接或间接派生自Exception类,因此我们可以认为绝大部分的异常都属于Exception。



异常类中的常用方法  Throwable

方法原型

说    明

String getMessage()

在Exception类中定义的方法,被继承到所有的异常类中,用于获得与异常相关的描述信息。

void printStackTrace()

在Exception类中定义的方法,用于在控制台上显示有关异常的信息,不但有异常的原因,还涉及产生异常的代码行。




try/catch块示例

public class ExceptionDemo
{
  public static void main(String[] args)
  {
    int a = 10, b = 0, c;

    try   //监视有可能出现异常的代码段
    {
      c = a / b;
      System.out.println(c);
    }
    catch (ArithmeticException ae)  //如果出现异常,将被捕获
    {
      System.out.println("除数为0。");
    }

    System.out.println("程序结束。");
  }
}



finally块

无论try/catch块中发生了什么,finally块都一定会执行
•当异常发生时,程序可能会意外中断,有些被占用的资源就得不到清理。finally块可以确保执行所有的清理工作;
无论是否发生异常,finally块都将执行
•finally块是可选的,可视具体情况决定是否添加;
finally块必须和try块一起使用,不能单独存在

(注:)存在finally块并在try/catch块中有 return() 相关语句,则
                                                  先执行finally块中的语句
                                                      再执行
try/catch块中的 return() 相关语句          (面试/笔试常见问题)



try/catch/finally块示例

        try {
            c = a / b;
            System.out.println(c);
        }
        catch (ArithmeticException ae) {
            System.out.println("除数为0。");
        }
        //不论是否发生异常,finally块中的语句都会执行
        finally {
            System.out.println("finally块中的语句。");
        }
        
        System.out.println("程序结束。");
    }
}




try/catch/finally执行流程



try/catch/finally应用模型

                 try

                       {

                           ……   //连接到数据库的代码,有可能发生异常

                           ……  //对数据库进行操作的代码,有可能发生异常

                        }

                catch(SQLException sqle)  //捕获数据库异常

                       {

                           ……   //对捕获的异常进行处理

                        }

                 finally

                       {

                           ……   //在finally块中执行关闭数据库的操作

                        }



多重catch块

•有时候,在try块中的代码段将有可能产生多种不同类型的异常,而我们又需要针对不同的异常类型进行不同的处理方式,那么我们就可以使用多重catch块,来分别捕获不同类型的异常。

多重catch块示例

public class ExceptionDemo {
  public static void main(String[] args) {
    int a, b, c;
    try {
      //从命令行参数获得用户输入的数字
      a = Integer.parseInt(args[0]);
      b = Integer.parseInt(args[1]);
      c = a / b;
      System.out.println(c);
    }
    catch (ArrayIndexOutOfBoundsException aioobe) {  //捕捉数组下标越界异常
      System.out.println("您没有指定命令行参数。");
    }
    catch (NumberFormatException nfe) {  //捕捉字符串到数字转换异常
      System.out.println("您输入的不是数字。");
    }
    catch (ArithmeticException ae) {     //捕捉算术(除数为0)异常
      System.out.println("除数为0。");
    }
    catch (Exception e) {                //捕捉其它不可预测的异常
      System.out.println(e.getMessage());
    }
    System.out.println("程序结束。");
  }
}



多重catch块的注意事项

•虽然多重catch块可以同时监视多个不同类型的异常,但是try块中一旦有某个异常产生,程序就会跳转到与之异常类型最匹配的catch块中执行,然后执行finally块(如果有finally块的话)或之后的语句;
•也就是说,多重catch块只会捕捉到最先产生的异常,而不是把所有的异常全部捕捉完;
•即:不论有多少个catch块,最多只会执行其中的一个
•请注意catch块的书写顺序:类层次越低的越往上写,越高的越往下写


多重catch块书写顺序示例

public class ExceptionDemo {
  public static void main(String[] args) {
    int a, b, c;
    try {
      a = Integer.parseInt(args[0]);
      b = Integer.parseInt(args[1]);
      c = a / b;
      System.out.println(c);
    }
    //由于Exception类的层次最高,以下的所有异常类型都是其子类,这样写将会报错
    catch (Exception e) {                //捕捉其它不可预测的异常
      System.out.println(e.getMessage());
    }
    catch (ArrayIndexOutOfBoundsException aioobe) {  //捕捉数组下标越界异常
      System.out.println("您没有指定命令行参数。");
    }
    catch (NumberFormatException nfe) {  //捕捉字符串到数字转换异常
      System.out.println("您输入的不是数字。");
    }
    catch (ArithmeticException ae) {     //捕捉算术(除数为0)异常
      System.out.println("除数为0。");
    }
    System.out.println("程序结束。");
  }
}


案例解析

由于Exception类的层次最高,以下的所有异常类型都是其子类,这样写将会报错,

                                     但若将Exception类作为catch模块的最后一个模块,则报错消失




嵌套try/catch块

•有时候,整个语句块可以产生异常,而其中的某个部分又可能产生另外的异常,而我们需要分别进行处理;
•这样,就可以通过嵌套try/catch块来完成;
•嵌套try/catch块就是在一个try/catch块中包含有另外的try/catch块。


嵌套try/catch块示例

public class ExceptionDemo
{
    public static void main(String[] args)
    {
        /*外层try/catch块*/
        try
        {
            System.out.println("传递的参数是:" + args[0]);
            /*嵌套try/catch块*/
            try
            {
                int num = Integer.parseInt(args[0]);
                System.out.println(num + "的平方是" + (num * num));
            }
            catch (NumberFormatException nfe)
            {
                System.out.println("您输入的不是数字。");
            }
        }
        catch (ArrayIndexOutOfBoundsException aioobe)
        {
            System.out.println("您没有输入命令行参数。");
        }
    }
}




throw关键字

•throw语句用于 手工抛出异常
•执行流程将在throw语句后立即停止,转而寻找与之类型相匹配的catch块;
•throw语句的语法是:

         throw (异常类型的实例);


throw语句示例

public class ThrowDemo
{
  public static void main(String[] args)
  {
    try
    {
      int age = Integer.parseInt(args[0]);
      if (age < 0 || age > 100)
      {
        //创建一个异常实例,并将其手工抛出
        throw (new Exception("您输入的年龄无效。"));
      }
      System.out.println("您的年龄是:" + age + "岁。");
    }
    catch (Exception e)  //捕捉异常
    {
      //打印出异常信息
      System.out.println(e.getMessage());
    }
  }
}



用户自定义异常

•Exception类和其子类都是系统内置的异常,这些异常不一定总能捕获程序中发生的所有异常;
•有时候,我们可能要创建用户自定义的异常类;
•用户自定义异常类应该是Exception类的子类;
•类似于:

                 class MyException extends Exception

                                  {

                                        ……

                                   }



自定义异常示例

class AgeException extends Exception {  //用户自定义年龄异常类
  public AgeException() {    //构造方法
    super("年龄无效。");
  }
}
public class Test {
  public static void main(String[] args) {
    try {
      int age = Integer.parseInt(args[0]);
      if (age < 0 || age > 100) {
        throw (new AgeException());  //抛出自定义异常类实例
      }
      System.out.println("您的年龄是:" + age + "岁。");
    }
    catch (AgeException ae) {  //捕捉自定义异常类型
      System.out.println(ae.getMessage());  //打印异常信息
    }
  }
}



throws关键字

•如果某个函数中的代码有可能引发异常,可以使用try/catch块进行处理,这种处理方式成为“内部处理”;
•如果不方便在函数内部进行处理,也可以将异常往函数外部传递,这就要使用到关键字throws
throws用于将函数内部产生的异常抛给主调函数
•一般语法:

                        返回值类型 函数名(参数列表) throws异常类型

                              {

                                    ……

                               }



throws关键字示例

public class Student     //定义学生类
{
    private String mName;   //姓名
    private int mAge;       //年龄
    ……   //其它方法,代码略
    /*为姓名赋值的方法*/
    public void setName(String name) { mName = name; }

    /*为年龄赋值的方法,该方法有可能抛出异常*/
    public void setAge(int age) throws AgeException
    {
        if (age < 0 || age > 100)
        {
            throw (new AgeException());
        }
        mAge = age;
    }
    ……    //其它方法,代码略
}


调用带有throws的函数1

•当调用带有throws关键字的函数时,则必须放在try/catch块中进行监控,否则编译器将会报错;

示例:

public class ThrowsTest {
    public static void main(String[] args) {
        Student std = new Student();

        try {
            std.setName("zhangsan");
            std.setAge(24);   //该方法必须放在try/catch块中
            std.display();
        }
        catch (AgeException ae) {
            System.out.println(ae.getMessage());
        }
    }
}


调用带有throws的函数2

•同样地,如果不便进行监控处理,也可以继续使用throws往外抛出异常,但不太推荐此种做法。

示例:

public class ThrowsTest
{

  public static void main(String[] args) throws AgeException
  {
    Student std = new Student();
    std.setName("zhangsan");
    std.setAge(180);   //对该函数不进行监控,只是将异常继续往外抛
    std.display();
  }

}



Throws注意点

重写方法需要抛出与父类方法所抛出异常类型一致的异常或不抛出异常.



关于Exception的最佳实践

1. Throw early,catch late  (早抛出晚捕获)
2. Do not squelch Exceptions   (不要压制异常)

                 Example:

                    try{
                         FileReader r=new FileReader(filename);
                        }
                    catch(Exception e){ }
3.Do throw specific Exceptions  (异常抛出要明确)

4.Do not use catch and finally in the same try statement   (不在同一个try语句中使用catch和finally)
                  Example:
                    try{
                         PrintWriter out=new PrintWriter(filename);
                             try{
                                         //操作
                                 }
                             finally{
                                     out.close();   
                                     }
                        }
                    catch(IOException ex){
                                           //操作
                                           }




总结

•异常是运行时产生的错误
•可以使用try/catch/finally块,配合使用来处理异常;
•如有多种类型的异常要进行处理,可以使用多重catch块;
•要手动发生异常,使用throw关键字;
•任何抛到函数外部的异常,都必须使用throws关键字指定其异常类型
•请注意throw和throws的区别;
•自定义异常类一般继承于Exception类;
                         Exception类是绝大部分异常类的父类,在异常类型不明的情况下,可以都认为是Exception



作业

•在JAVA中设计学生类,且在学生类中有整型年龄!需要从命令行参数传值,并且为学生类的年龄属性赋值,要求从异常处理!
•在上一章EmployeeTest作业的基础上,将休假天数修改从命令行输入,并且要注意捕捉相关异常!
>例如:ArrayIndexOutOfBoundsException、NumberFormatException以及自定义异常等
>PS:
>int num=Integer.parseInt(String number);
>double basic=Double.parseDouble(String number);





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值