JavaSE - 异常处理

JavaSE - 异常处理

本节学习目标:

  • 了解异常的概念与类型;
  • 了解并掌握错误处理与异常捕捉;
  • 了解并掌握如何抛出异常;
  • 了解并掌握自定义异常;
  • 了解并掌握Throwable类定义的常用方法。

1. 异常概述

1.1 什么是异常

异常是程序中的一些错误。可能产生于程序员没有预料到的各种情况。

比如经典的算术运算异常ArithmeticException除数为0

public class Test {
     
    public static void main(String[] args) {
     
        int i = 1 / 0;
        System.out.println("发生异常");
    }
}

尝试编译并运行:

JavaSE - 异常处理_第1张图片

发生异常后,产生异常的代码之后的代码都不会执行

1.2 异常信息

一个异常提示信息包括四个部分:

JavaSE - 异常处理_第2张图片

  1. 产生的异常所在的线程
  2. 产生的异常类
  3. 产生异常的原因
  4. 产生异常的源码位置

1.3 异常的类型

在Java中,异常和错误是两种不同的概念。

对于一般语义的错误,分为语法错误运行时错误逻辑错误三大类:

  • 语法错误:不遵守Java语法编写的代码在编译时发生的错误,常见地如少写一个分号等,这类错误不能通过编译,必须严格按照Java语法编写才能编译成功;
  • 运行时错误:编译通过,但在执行时发生的错误,比如例子中的算术运算异常ArithmeticException)和常见的空指针异常NullPointerException),数组越界异常ArrayIndexOutOfBoundsException)等;
  • 逻辑错误:编译通过,执行正常,但是程序运行结果与预期不符,编写的程序无法解决当前问题,需要程序员重新编写。

对于Java语言定义的错误,分为两大类,一个是错误(Error),一个是异常(Exception):

  • 错误:一般指程序运行时遇到的硬件或者操作系统的错误,如栈溢出错误StackOverflowError),这类错误是致命的,发生这类错误将导致程序不能继续运行,程序不能从这类错误中恢复过来;
  • 异常:程序运行时发生的运行时错误,如空指针异常NullPointerException)等,这类错误是非致命的,Java可以捕获处理异常,但不处理也会导致程序的非正常终止

Java定义了一系列的类来描述错误,每个包中都定义了异常类,所有异常类都是Throwable类的子类,同时Throwable异常也是最高级别异常
Throwable类有两个子类,分别是Error类和Exception类,对应上文提到的错误异常Error类是致命性类,它及其子类用来描述Java运行系统中的内部错误以及资源耗尽的错误等;
Exception类是非致命性类,可以通过捕捉处理使程序继续正常执行。Exception类又根据错误发生的原因分为运行时异常RuntimeException)异常和其他异常,继承树如下:

JavaSE - 异常处理_第3张图片

2. 异常处理

为保证程序正常执行,需要对发生的异常进行相应的处理。在Java中,如果某个方法发生了异常,可以在当前方法中进行捕捉并处理。

2.1 try-catch 语句块

try-catch语句块是Java语言中常用的异常处理结构:

try {
     
    // 语句块
} catch (异常类 标识符) {
     
    // 异常处理语句
}

try关键字包括的语句块内用来编写可能发生异常的代码,关键字catch用来捕获由try语句块中可能发生的异常,
需要在catch关键字后面的括号里定义可能发生的异常catch关键字会捕捉try语句块中产生的此类异常及其子类异常
然后在catch语句块中处理异常。

编写代码进行测试:

public class TestTryCatch {
     
    public static void main(String[] args) {
     
        try {
     
            int i = 1 / 0;
            System.out.println("发生异常1");
        } catch (Exception e) {
     
            e.printStackTrace();
            System.out.println("发生异常2");
        }
        System.out.println("发生异常3");
    }
}

尝试运行并编译:

JavaSE - 异常处理_第4张图片

发现第一句输出代码没有执行,但第二句和第三句输出代码执行了。在try语句块中,一旦发生异常,则try语句块中的剩余代码都不会执行。
如果catch关键字捕捉到了try语句块中产生的异常,则执行catch语句块中的语句。try-catch语句块之后的语句会正常继续执行。

2.2 try-catch-finally 语句块

try-catch-finally语句块是在try-catch语句块的基础上再加上finally语句块,try-catch-finally语句块是Java中标准的异常处理结构:

try {
     
    // 语句块
} catch (异常类 标识符) {
     
    // 异常处理语句
} finally {
     
    // 语句块
}

编写代码进行测试:

public class TestTryCatchFinally {
     
    public static void main(String[] args) {
     
        try {
     
            int[] i = {
     1, 2};
            System.out.println(i[2]);
            System.out.println("发生异常1");
        } catch (Exception e) {
     
            e.printStackTrace();
            System.out.println("发生异常2");
        } finally {
     
            System.out.println("发生异常3");
        }
        System.out.println("发生异常4");
    }
}

尝试编译并运行:

JavaSE - 异常处理_第5张图片

在关键字finally语句块中的语句,无论是否发生异常都会执行。完整的异常处理结构一定要包含finally语句块。

2.3 try-with-resource 语句块

在JDK1.7新加入了try-with-resource语句块,可以自动调用的资源进行释放

比如使用Scanner类进行输入,使用完毕需要调用它的close()方法关闭:

import java.util.Scanner;
public class TestTryWithResource {
     
    public static void main(String[] args) {
     
        Scanner input = new Scanner(System.in);
        try {
     
            int i = input.nextInt();
            i /= 0;
        } catch (Exception e) {
     
            e.printStackTrace();
        } finally {
     
            input.close();
        }
    }
}

上面的try-catch-finally语句块可以使用try-with-resource语句块替换:

import java.util.Scanner;
public class TestTryWithResource {
     
    public static void main(String[] args) {
     
        try (Scanner input = new Scanner(System.in)) {
     
            int i = input.nextInt();
            i /= 0;
        } catch (Exception e) {
     
            e.printStackTrace();
        } finally {
     
            // 语句块
        }
    }
}

需要使用的资源流(如文件流等,需要实现Closeable接口)可以在try关键字后的括号声明并初始化,无论是否发生异常,都会自动调用资源流的close()方法安全关闭并释放相关资源。

3. 抛出异常

Java提供了两个关键字throwthrows来抛出异常。

3.1 throw 关键字

throw关键字用于在实际代码中主动抛出一个异常对象(如果没有异常对象,需要使用new关键字创建一个异常对象):

import java.util.Scanner;

public class TestThrow {
     
    public static void main(String[] args) {
     
        int age = new Scanner(System.in).nextInt();
        if (age <= 0) {
     
            throw new RuntimeException("年龄只能为正整数");
        } else {
     
            System.out.println(age);
        }
    }
}

输入-3,运行结果:

JavaSE - 异常处理_第6张图片

使用throw关键字主动抛出的异常也可以被catch关键字捕获并处理

3.2 throws 关键字

在方法中,异常如果不想在当前方法中处理,可以使用throws关键字抛出,由调用此方法语句所在的方法中处理:

public class TestThrows {
     
    public static void main(String[] args) {
     
        try {
     
            function();
        } catch (Exception e) {
     
            e.printStackTrace();
        }
        System.out.println("程序继续执行");
    }
    public static void function() throws ArithmeticException {
     
        int i = 1 / 0;
    }
}

调用使用throws关键字抛出异常(除了RuntimeException异常及其子类异常)的方法时,必须使用try-catch语句块进行显式处理
也可以继续使用throws关键字向上抛出异常*,否则无法通过编译

main()方法同样可以使用throws关键字抛出异常,但可能没有语句去处理从main()方法中throws出去的异常。
构造方法也可以使用throws关键字抛出异常,使用带有throws关键字的构造方法创建对象时,需要进行异常处理

4. 自定义异常

使用Java定义的异常类可以描述大部分异常情况,我们也可以编写自定义异常类来描述其他异常,只需要继承Exception类或者其子类即可。

编写年龄数字异常类,描述年龄不能为0或者负数:

public class AgeNumberException extends Exception {
     
    public AgeNumberException() {
      
        super("年龄不能为0或负数"); // 可以调用父类的构造方法Exception(String message),传入异常原因描述
    }
}

编写代码使用此异常类进行测试:

import java.util.Scanner;
public class CustomException {
     
    public static void main(String[] args) {
     
        try (Scanner input = new Scanner(System.in)) {
     
            int age = input.nextInt();
            if (age <= 0) {
     
                throw new AgeNumberException();
            }
        }
    }
}

输入-3,运行结果:

JavaSE - 异常处理_第7张图片

5. Throwable 类

Throwable类是所有异常类的父类,它定义了很多获取异常信息的方法,子类可以使用。

常用的构造方法(绝大多数子类异常也提供此方法):

  • Throwable():默认构造方法,不提供异常原因。
  • Throwable(String message):常用构造方法,可以传入一个字符串作为异常原因。

Throwable类提供的常用方法:

方法 返回值 功能
getMessage() String 获取异常发生的原因
getCause() Throwable 获取异常对象
toString() String 获取异常对象的字符串形式(异常类名: 异常原因
printStackTrace() void 向控制台打印异常的跟踪信息

你可能感兴趣的:(我的Java基础学习之路,java,javase)