Java基础11—异常处理

异常处理


参考资料:《Java从入门到精通》/明日科技编著. 4版. 北京:清华大学出版社,2016

一、异常概述

异常是一个在程序执行过程中发生的事件,它中断了正在执行程序的正常指令流。有很多异常的例子,如空指针、数组溢出等。

Java是一门面向对象的编程语言,因此,异常在Java语言中也是作为类的实例的形式出现的。当某一方法发生错误时,这个方法会创建一个对象,并且把它传递给正在运行的系统,这个对象就是异常对象。

通过异常处理机制,可以将非正常情况下的处理代码与程序的主逻辑分离,即在编写代码主流程的同时,在其他地方处理异常。

二、处理程序异常错误

  • 为了保证程序的正常执行,需要对发生的异常进行相应的处理。
  • 在Java中,如果某个方法抛出异常,既可以在当前方法中进捕捉,然后进行处理,也可以将异常向上抛出,由方法调用者来处理。

下面介绍Java中捕捉异常的方法:

1、错误

  • 异常产生后,如果不做任何处理,程序就会被终止
public class Demo1 {
    public static void main(String[] args) {
        String str1 = "23";
        String str2 = "ab";
        
        int age1 = Integer.parseInt(str1);   //将数字型字符串转换成int型
        System.out.println(str1);

        int age2 = Integer.parseInt(str2);   //将数字型字符串转换成int型
        System.out.println(str2);
    }
}

运行结果:
23
Exception in thread "main" java.lang.NumberFormatException: For input string: "ab"
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.lang.Integer.parseInt(Integer.java:580)
	at java.lang.Integer.parseInt(Integer.java:615)
	at com.demo_5_16.Demo1.main(Demo1.java:10)

从上述代码的执行结果可以看出,报出了NumberFormatException(字符串转数字)的异常,str1变量的值已经输出,str2变量的值没有输出,可知程序由于异常而终止。

2、捕捉异常

Java的异常捕捉结构由trycatchfinally组成;

  • try语句块存放的是可能发生异常的Java语句;
  • catch语句在try语句块之后,用来激发被捕获的异常;
  • finallly语句块是异常处理结构的最后执行部分,无论try语句块的代码如何退出,都将执行finally语句块。

语法如下:

try {
    //代码块
}
catch (ExceptionType1 e) {
    //对 ExceptionType1的处理
}
catch (ExceptionType2 e) {
    //对 ExceptionType2的处理
}
...
...
finally {
    //代码块
}

由此可知,异常处理器大致分为try-catch语句块和finally语句块。

(1)try-catch语句块

public class Demo1 {
    public static void main(String[] args) {
        try {
            String str1 = "23";
            String str2 = "ab";

            int age1 = Integer.parseInt(str1);   //字符型转换成int型
            System.out.println(str1);

            int age2 = Integer.parseInt(str2);   //字符型转换成int型
            System.out.println(str2);
            
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("程序结束");
    }
}

运行结果:
23
程序结束
java.lang.NumberFormatException: For input string: "ab"
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.lang.Integer.parseInt(Integer.java:580)
	at java.lang.Integer.parseInt(Integer.java:615)
	at com.demo_5_16.Demo1.main(Demo1.java:12)

从上述代码的运行结果可知,程序输出了最后的提示信息,没有因为异常而终止。

使用try-catch语句块进行异常处理过程:

  1. 当try语句块中的语句发生异常时,程序就会调转到catch代码块中执行;
  2. 执行完catch代码后,将继续执行catch代码块后面的代码,而不会执行try代码块中发生异常语句后面的代码。

其中,Exception是代码块传递给catch代码块的变量类型e是变量名。通常,异常处理常用的3个函数来获取异常的有关信息。

  • getMessage() :输出错误的性质;
  • toString() :给出异常的类型与性质;
  • printStackTrace() :指出异常的类型、性质、栈层次以及出现在程序中的位置。

(2)finally语句块

完整的异常处理语句一定要包括finally语句块,无论程序中有无异常的发生,并且无论之前的try-catch语句是否顺利执行完毕,都会执行finally语句。

在4种情况下finally语句不会执行:

  1. 在finally语句块中出现了异常;
  2. 在前面的代码中使用了System.exit()退出程序;
  3. 程序所在的线程死亡;
  4. 关闭CPU。

四、自定义异常

使用Java内置的异常类可以描述在编程时出现的大部分异常情况。除此之外,用户只需要继承Exception类即可自定义异常类。

在程序中使用自定义异常类,大可分为以下几个步骤:

  1. 创建自定义异常类;
  2. 在方法中通过throw关键字抛出异常对象;
  3. 如果在当前抛出异常的方法中处理异常,可以使用try-catch语句块捕捉异常并处理,否则在方法的声明处通过throw关键字指明要抛出给方法调用者的异常,继续进行下一步;
  4. 在出现异常方法的调用者中捕获并处理异常。
/*
*通过继承Exception类,自定义一个异常类 
*/
class MyException extends Exception {
    //自定义异常类的构造方法
    public MyException (String ErrorMessage){
        //调用父类的构造方法
        super(ErrorMessage);
    }
}

public class Tran {
    //定义方法,并使用throws关键字指明要抛出的异常的类型,即异常类型为MyException类
    static int avg(int n1, int n2) throws MyException {
        //判断参数是否满足指定的条件
        if (n1 < 0 || n2 < 0){
            //抛出异常对象
            throw new MyException("不可以为负数");
        }
        //判断参数是否满足指定的条件
        if (n1 > 100 || n2 > 100){
            //抛出异常对象
            throw new MyException("数字过大");
        }
        //若参数正常,则返回结果
        return (n1 + n2) / 2;
    }

    public static void main(String[] args) {
        //使用try-catch语句块捕捉异常
        try{
            int result = Tran.avg(112,-34);   //调用avg()静态方法
            System.out.println(result);
            
        }catch (MyException e){   //处理捕获到的异常,相当于 MyException e = new MyException("不可以为负数");
        //当然,这里写成"Exception e"也是没有问题的,因为MyException继承了Exception类
            System.out.println(e);   //输出异常信息
            // e.printStackTrace();   //也可以使用该语句打印异常信息
        }
    }
}

运行结果:
com.demo_5_17.MyException: 不可以为负数

五、在方法中抛出异常

若某个方法中可能会发生异常,但是不想在当前方法中处理异常,则可以使用throws、throw关键字在方法中将异常抛出。

1、使用throw关键字抛出异常

throws关键字通常用在声明方法时,用来指定方法可能抛出的异常,多个异常可以用逗号分隔。

public class Shoot {
    //定义方法,并指定抛出异常的类型
    public void pop() throws NegativeArraySizeException {
        int arr[] = new int[-3];   //故意制造一个异常,即指定数组长度为-3
    }

    public static void main(String[] args) {
        try {
            Shoot shoot = new Shoot();
            shoot.pop();
        }catch (NegativeArraySizeException e){
            System.out.println("异常类型为:" + e);
        }
    }
}

运行结果:
异常类型为:java.lang.NegativeArraySizeException

上述代码中,使用throws关键字将异常抛给方法的调用者。使用throw关键字将异常抛给上一级后,如果不想处理该异常,可以继续往上抛,但最终要有能处理该异常的代码。

2、使用throw关键字抛出异常

  • throw关键字通常用于方法体中,并且抛出一组异常对象
  • 程序在执行到throw语句时立即终止,它后面的语句都不执行;
  • 通过throw抛出异常后,如果想在上一级代码中捕获并处理异常,则需要在抛出异常的方法中使用throws关键字在方法声明时指明要抛出的异常。
  • 如果要捕获throw抛出的异常,则必须使用try-catch语句。
  • throw通常用来抛出用户自定义的异常。
/*
*自定义异常类
*/
class MyException2 extends Exception {
    String message;
    public MyException2(String message){
        this.message = message;
    }
    public String getMessage(){
        return this.message;
    }
}

public class Captor {
    /*
    * 在声明方法时,指定抛出异常类型
    * 可以指定多个可能抛出的异常类型
    */
   public int div(int n1, int n2) throws MyException2, ArithmeticException{
        if (n2 < 0){
            throw new MyException2("除数不能是负数");   //抛出异常对象
        }
        if (n2 == 0){
            throw new ArithmeticException();   //抛出异常对象
        }
        return n1 / n2;
    }

    public static void main(String[] args) {
        try{
            Captor captor = new Captor();
            int result = captor.div(24,-3);
        }catch (MyException2 e){
            //若抛出的异常对象为MyException2类型,则执行该代码块
            System.out.println(e.getMessage());
        }catch (ArithmeticException e){
            //若抛出的异常对象为ArithmeticException类型,则执行该代码块
            System.out.println("抛出算术异常");
        }catch (Exception e) {
            //如果还有其他类型,则使用Exception来捕捉
            System.out.println("抛出其他异常");
        }
    }
}

运行结果:
除数不能是负数

上述代码中使用了多个catch语句进行捕捉异常:

  • 如果调用div(24, -3)方法,将发生MyException异常,程序调转到catch (MyException2 e)代码块中执行。
  • 如果调用div(24, 0)方法,将发生ArithmeticException异常,程序调转到catch (ArithmeticException e)代码块中执行。
  • 如果还有其他类型异常发生,则使用catch (Exception e)捕捉异常。

由于Exception是所有异常类的父类,如果将catch (Exception e)代码块放置在其他两个代码块的前面,后面的代码块将永远得不到执行,也就没有什么意义了,因此catch语句的顺序不可随意调换。

六、运行时异常

  • RuntimeException异常是程序运行过程中产生的异常。
  • Java类库的每个包中都定义了异常类,所有的这些类都是Throwable类的子类。
  • Throwable类派生出了两个子类,分别是Exception类和Error类。
  • Error类及其子类用来描述Java运行系统中的内部错误以及资源耗尽的错误,这类错误比较严重
  • Exception类称为非致命性错误类,可以通过捕捉处理使程序继续执行。
  • Exception类又根据错误发生的原因分为RuntimeException异常非RuntimeException异常

Java中提供了常见的RuntimeException异常,这些异常可以通过try-catch语句捕获,如下表所示:

种类 说明
NullPointerException 空指针异常
ArrayIndexOutOfBoundsException 数组下标越界异常
ArithmeticException 算术异常
ArrayStoreException 数组中包含不兼容的值抛出的异常
IllegalArgumentException 非法参数异常
SecurityException 安全性异常
NegativeArraySizeException 数组长度为负数异常

七、异常的使用原则

  • Java异常强制用户去考虑程序的健壮性和安全性。
  • 异常的处理不应该用来控制程序的正常流程,其主要作用是捕获程序在运行时发生的异常并进行相应的处理。

编写代码处理某个方法可能出现的异常时,可遵守以下几条原则

  1. 在当前方法声明中使用try-catch语句捕捉异常;
  2. 一个方法被覆盖时,覆盖它的方法必须抛出相同的异常或该异常的子类;
  3. 如果父类抛出多个异常,则覆盖方法必须抛出那些异常的一个子集,不能抛出新异常。
posted @ 2020-05-17 16:27  黑色幽默2020  阅读(327)  评论(0编辑  收藏  举报