当前位置:   article > 正文

Day12-异常处理语句

Day12-异常处理语句

Day12 异常处理

学习目标

  • 能够说出异常出现的原因
  • 能够说出异常的继承体系
  • 能够说出异常的分类
  • 能够使用异常处理语句解决代码中的异常
  • 能够使用自定义异常处理程序中的问题

1. 概述

在使用计算机语言进行项目开发的过程中,即使程序员把代码写得尽善尽美,在系统的运行过程中仍然会遇到一些问题,因为很多问题不是靠代码能够避免的,比如:客户输入数据的格式,读取文件是否存在,网络是否始终保持通畅等等。

异常的出现就是为了记录程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止的情况。在Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象。

异常出现的目的是为了保证程序的健壮性。代码出现问题时,曝出异常,程序员解决这些异常以后再继续向下执行。如果不抛出异常,程序就会在错误的基础上继续执行,这样更加危险。

2. 异常体系

异常机制其实是帮助我们找到程序中的问题,异常的根类是java.lang.Throwable,其下有两个子类:java.lang.Errorjava.lang.Exception,平常所说的异常指java.lang.Exception

在这里插入图片描述

  • Error:严重错误Error,无法通过处理的错误,只能事先避免,例如常见的StackOverFlowErrorOutOfMemoryError
  • Exception:表示异常,异常产生后程序员可以通过代码的方式纠正,使程序继续运行,是需要处理的。

Throwable中的常用方法:

  • public void printStackTrace():打印异常的详细信息。

    包含了异常的类型,异常的原因,还包括异常出现的位置,在开发和调试阶段,都得使用printStackTrace。

  • public String getMessage():获取发生异常的原因。

    提示给用户的时候,就提示错误原因。

  • public String toString():获取异常的类型和异常描述信息(不用)。

3. 异常分类

我们平常说的异常就是指Exception,因为这类异常一旦出现,我们就要对代码进行更正,修复程序。

异常(Exception)的分类:根据在编译时期还是运行时期去检查异常?

  • 编译时期异常:checked异常。在编译时期,就会检查,如果没有处理异常,则编译失败。(如日期格式化异常)
  • 运行时期异常:runtime异常。在运行时期,检查异常.在编译时期,运行异常不会编译器检测(不报错)。(如数组索引越界异常)

在这里插入图片描述

4. 异常产生过程

先运行下面的程序,程序会产生一个数组索引越界异常ArrayIndexOfBoundsException。我们通过图解来解析下异常产生的过程。

class ArrayTools {    
    // 对给定的数组通过给定的角标获取元素。    
    public static int getElement(int[] arr, int index) {        
        int element = arr[index];        
        return element;    
     }
}
public class ExceptionDemo {
    public static void main(String[] args) {
        int[] arr = { 34, 12, 67 };
        int num = ArrayTools.getElement(arr, 4)
        System.out.println("num=" + num);
        System.out.println("over");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在这里插入图片描述

5. 异常处理语句

Java异常处理的五个关键字:try、catch、finally、throw、throws

5.1 捕获异常

使用try...catch语句来捕获程序运行过程中出现的异常。

try{
     // 编写可能会出现异常的代码
}catch(异常类型  e){
     // 处理异常的代码
     // 记录日志/打印异常信息/继续抛出异常等
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

注意:try…catch是一个完整的语句,不能分开使用。

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd HH:MM:SS");
try {
    Date date = sdf.parse("2021 01 01 12:10:15");
    // 如果上面 parse 的代码出问题了,下面这行代码将不会被执行!
    System.out.println("解析后的日期是" + date);
} catch (ParseException e) {
    System.out.println("解析错误!");
    e.printStackTrace();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

5.2 finally代码块

因为异常会引发程序跳转,导致有些语句执行不到。但是如果有一些特定的代码无论异常是否发生,都需要执行,就可以讲这些代码写入到finally代码块里。

什么时候的代码必须最终执行?当我们在try语句块中打开了一些物理资源(磁盘文件/网络连接/数据库连接等),我们都得在使用完之后,最终关闭打开的资源。

finally的语法:

try{
  // 程序代码
}catch(异常类型1 异常的变量名1){
  // 程序代码
}catch(异常类型2 异常的变量名2){
  // 程序代码
}finally{
  // 程序代码
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

注意:finally不能单独使用。

比如在我们之后学习的IO流中,当打开了一个关联文件的资源,最后程序不管结果如何,都需要把这个资源关闭掉。

FileInputStream fis = null;
try {
    fis = new FileInputStream("out.txt");
} catch (Exception e) {
    e.printStackTrace();
} finally {
    if (fis != null) {
        try {
            fis.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

finally注意事项:如果方法的finally里有return语句,会将前面的return语句覆盖。

5.3 throw抛出异常

在编写程序时,我们必须要考虑程序出现问题的情况。如果程序在运行的过程中出现了问题,我们可以使用throw关键字将异常抛出。

语法格式:

throw new 异常类名(参数);
  • 1

示例代码:

private static void divide(int a) {
    if (a == 0) {
        //System.out.println("除数不能为0");
        //return;    //可以使用 return 语句结束代码的执行
       	throw new RuntimeException("除数不能为0"); 
     }
     double b = 1.0 / a;
     System.out.println("您传入的数字的倒数是" + b);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

5.4 throws声明异常

throws关键字将问题标识出来,报告给调用者。如果方法内通过throw抛出了编译时异常,那么必须通过throws进行声明,让调用者去处理。

关键字throws运用于方法声明之上,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常(抛出异常)。

public class Test {
    public static void main(String[] args) {
        // divide 方法抛出了异常,不能再这样直接调用
        // divide(0);
        
        // 此时,再调用 divide 方法,要么使用try...catch语句处理异常,要么就再把异常向上抛出
        try {
            divide(3);
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
	
    // 在函数声明时就必须要使用 throws 关键字声明异常类型
    private static void divide(int a) throws Exception {
        if (a == 0) {
            // 这里使用 throw 抛出了一个编译时异常
            throw new Exception("除数不能为0");
        }
        double b = 1.0 / a;
        System.out.println("您传入的数字的倒数是" + b);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

5.5 异常处理注意事项

  • 多个异常时的处理方式。

    try {
    	FileInputStream fis = new FileInputStream("test.txt");
        System.out.println(1 / 0);
        fis.close();
    } catch (FileNotFoundException e) {
    	System.out.println("文件未找到");
        e.printStackTrace();
    } catch (IOException e) {
    	System.out.println("文件关闭失败");
    }catch (Exception e) {  // 如果有父类,父类的异常处理语句要写在最下面
    	 System.out.println("出问题了");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    注意:这种异常处理方式,要求多个catch中的异常不能相同,并且若catch中的多个异常之间有子父类异常的关系,那么子类异常要求在上面的catch处理,父类异常在下面的catch处理。

  • 运行时异常被抛出可以不处理。即不捕获也不声明抛出。

  • 如果finally有return语句,永远返回finally中的结果,避免该情况。

  • 如果父类抛出了多个异常,子类重写父类方法时,抛出和父类相同的异常或者是父类异常的子类或者不抛出异常。

  • 父类方法没有抛出异常,子类重写父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出。

6. 自定义异常

Java里的异常类型都继承自Exception类,常见的Java内置异常类型有:

  • ClassCastException:类型转换异常
  • ArrayIndexOutOfBoundsException:数组下标越界异常
  • NullPointerException:空指针异常。
  • ArithmeticException:算法异常。
  • NumberFormatException:数字格式化异常。
  • FileNotFoundException:文件未找到异常。
  • FileAlreadyExistsException:文件已经存在异常。

6.1 为什么要自定义异常

在Java中,我们会使用throw关键字抛出异常,通常情况下,我们可以创建Java内置的异常类型对象。但是在有些情况下,Java内置的错误类型可能不能够很精确的描述我们的遇到的异常类型,此时就可以考虑自定义异常。

例如:除数为0的时候,Java里默认抛出的异常类型是ArithmeticException,并不能够很好的描述我们遇到的异常类型,开发中可能会要求抛出一个ZeroDivisionException自定义异常。

6.2 如何自定义异常

异常分为两大类,编译时异常和运行时异常。我们在开发中,也可以根据我们的业务逻辑,选择继承的异常类型。

  • 自定义编译时异常:自定义异常类需要继承自Exception类。
  • 自定义运行时异常:自定义异常类需要继承自RuntimeException类。
  • 根据自己的业务需求,还可以继承Exception或者RuntimeException的子类。
public class Test {
    public static void main(String[] args) throws IOException {
        test(0);
    }

    public static void test(int a) {
        if (a == 0) {
            // 在这里抛出一个自定义的异常
            throw new ZeroDivisionError("除数不能为0");
        }
        double x = 1.0 / a;
        System.out.println("计算的结果是" + x);
    }
}

// 根据业务需求分析,ZeroDivisionError 继承自 ArithmeticException 会更加合适
class ZeroDivisionError extends ArithmeticException {
    private String s;

    ZeroDivisionError(String s) {
        super(s);
        this.s = s;
    }
    @Override
    public String toString() {  // 可以选择重写自定义异常的 toString 方法,在打印异常时输出指定的内容
        return "程序运行出错了,错误原因是" + this.s;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/137059
推荐阅读
相关标签
  

闽ICP备14008679号