当前位置:   article > 正文

JavaSE-09笔记【异常(+2024新)】

JavaSE-09笔记【异常(+2024新)】

1. 异常概述

  1. 什么是异常?有什么用?
  • Java中的异常是指程序运行时出现了错误或异常情况,导致程序无法继续正常执行的现象。例如,数组下标越界、空指针异常、类型转换异常等都属于异常情况。
  • Java提供了异常处理机制,即在程序中对可能出现的异常情况进行捕捉和处理。异常机制可以帮助程序员更好地管理程序的错误和异常情况,避免程序崩溃或出现不可预测的行为。
  • 没有异常机制的话,程序中就可能会出现一些难以调试和预测的异常行为,可能导致程序崩溃,甚至可能造成数据损失或损害用户利益。因此,异常机制是一项非常重要的功能,是编写可靠程序的基础。
  1. 异常在Java中以类和对象的形式存在。
  • 现实生活中也有异常,比如地震,火灾就是异常。也可以提取出类和对象,例如:
    ①地震是类:512大地震、唐山大地震就是对象。
    ②空指针异常是类:发生在第52行的空指针异常、发生在第100行的空指针异常就是对象。也就是说:在第52行和第100行发生空指针异常的时候,底层一定分别new了一个NullPointerException对象。

一个示例:

package exceptiontest;

public class ExceptionTest01 {
    public static void main(String[] args) {
        int a = 10;
        int b = 0;
//        int c = a / b; //运行后抛出异常:Exception in thread "main" java.lang.ArithmeticException: / by zero
//                       //at exceptiontest.ExceptionTest01.main(ExceptionTest01.java:7)
//                       //抛出这个异常的时候底层是new了一个ArithmeticException对象
//        System.out.println(a + "/" + b + " = "+ c);

        String s = null;
        s.toString();//这里会发生空指针异常,程序执行到这里JVM会在底层new一个NullPointerException对象
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

2.异常继承结构

在这里插入图片描述

  1. 所有的异常和错误都是可抛出的。都继承了Throwable类。
  2. Error是无法处理的,出现后只有一个结果:JVM终止。
  3. Exception是可以处理的。
  4. Exception的分类:
    ①所有的RuntimeException的子类:运行时异常/未检查异常(UncheckedException)/非受控异常
    ②Exception的子类(除RuntimeException之外):编译时异常/检查异常(CheckedException)/受控异常
  5. 编译时异常和运行时异常区别:
    ①编译时异常特点:在编译阶段必须提前处理,如果不处理编译器报错。
    ②运行时异常特点:在编译阶段可以选择处理,也可以不处理,没有硬性要求。
    ③编译时异常一般是由外部环境或外在条件引起的,如网络故障、磁盘空间不足、文件找不到等
    ④运行时异常一般是由程序员的错误引起的,并且不需要强制进行异常处理。

2.1 编译时异常和运行时异常区别

注意:编译时异常并不是在编译阶段发生的异常,所有的异常发生都是在运行阶段的,因为每个异常发生都是会new异常对象的,new异常对象只能在运行阶段完成。那为什么叫做编译时异常呢?这是因为这种异常必须在编译阶段提前预处理,如果不处理编译器报错,因此而得名“编译时异常”。

2.2 如何让异常发生(throw关键字)

package exceptiontest;

public class ExceptionTest02 {
    public static void main(String[] args) {
//        // 异常的发生需要经历2个阶段
//        // 第一个阶段:创建异常对象
//        NullPointerException e = new NullPointerException();
//        // 第二个阶段:让异常发生(手动抛出异常)
//        throw e;

        //将以上两个阶段合并成一步
        throw new NullPointerException();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

运行结果:
在这里插入图片描述

3.自定义异常

第一步:编写异常类继承Exception或者RuntimeException
第二步:提供一个无参数构造方法,再提供一个带String msg参数的构造方法,在构造方法中调用父类的构造方法。

一个小示例:

①定义两个编译时异常:

  1. IllegalNameException:无效用户名异常
  2. IllegalAgeException:无效年龄异常

②完成如下需求:

  • 编写一个用户注册的方法,该方法接收两个参数,一个是用户名,一个是年龄。如果用户名长度在[6 - 12]位,并且年龄大于18岁时,输出用户注册成功。
  • 如果用户名长度不是[6 - 12]位时,让程序出现异常,让IllegalNameException异常发生!
  • 如果年龄小于18岁时,让程序出现异常,让IllegalAgeException异常发生!

示例代码:

package exceptiontest.exception;

/**
 * 自定义无效用户名异常:当用户名长度小于6位或者大于12位的,认为程序出现了异常
 */
public class IllegalNameException extends Exception {

    public IllegalNameException() {
    }

    public IllegalNameException(String message) {
        super(message);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
package exceptiontest.exception;

/**
 * 自定义无效年龄异常:当年龄小于18岁,认为程序出现了异常
 */
public class IllegalAgeException extends Exception {

    public IllegalAgeException() {
    }

    public IllegalAgeException(String message) {
        super(message);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
package exceptiontest.exception;

import javax.sound.midi.Soundbank;
import java.util.Scanner;

/**
 * 编写一个用户注册的方法,该方法接收两个参数,一个是用户名,一个是年龄。
 * 如果用户名长度在[6 - 12]位,并且年龄大于18岁时,输出用户注册成功。
 * 如果用户名长度不是[6 - 12]位时,让程序出现异常,让IllegalNameException异常发生!
 * 如果年龄小于18岁时,让程序出现异常,让IllegalAgeException异常发生!
 */
public class ExceptionTest03 {
    public static void main(String[] args) {
        System.out.println("欢迎来到用户管理系统,请先注册:");
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String name = scanner.next();
        System.out.println("请输入年龄:");
        int age = scanner.nextInt();
        UserServise userServise = new UserServise();
        userServise.register(name, age);
    }
}

/**
 *用户的业务类
 */

class UserServise{
    public void register(String name, int age){
        System.out.println("正在注册[" + name + "],请稍后......");
        UserDao userDao = new UserDao();
        userDao(name, age);
        System.out.println("注册成功,欢迎[" + name + "]");
    }
}

/**
 * 操作数据库的一个类
 * 用户需要注册,其用户名和年龄是需要保存的
 */
class UserDao{
    public void save(String name, int age){
        System.out.println("用户[" + name + "]的信息正在保存...");
        if(name.length() < 6 || name.length()>12){
            throw  new IllegalNameException();
        }
        if(age < 18){
            throw new IllegalAgeException();
        }
        System.out.println("用户[" + name + "]的信息保存成功!");

    }
}
  • 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
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

但是以上代码在throw new IllegalNameException();IllegalAgeException会报错,因为定义的异常继承的是Exception,是编译时异常,需要对其进行处理,没有处理编译器自然会报错。

4.异常的处理

  • 异常的处理包括两种方式:
    ①声明异常:类似于推卸责任的处理方式。在方法定义时使用throws关键字声明异常,告知调用者,调用这个方法可能会出现异常。这种处理方式的态度是:如果出现了异常则会抛给调用者来处理。
    ②捕捉异常:真正的处理捕捉异常。在可能出现异常的代码上使用try...catch进行捕捉处理。这种处理方式的态度是:把异常抓住。其它方法如果调用这个方法,对于调用者来说是不知道这个异常发生的。因为这个异常被抓住并处理掉了。
  • 异常在处理的整个过程中应该是:声明和捕捉联合使用。若产生异常的位置不捕捉,向上抛出异常,总需要有一个上层调用者捕捉掉,最差是直接抛到main方法中,交给JVM处理。
  • 什么时候捕捉?什么时候声明?
    如果异常发生后需要调用者来处理的,需要调用者知道的,则采用声明方式。否则采用捕捉。

4.1 第一种处理方式:声明异常 (throws关键字)

  • 如果一个异常发生后希望调用者来处理的,使用声明异常(俗话说:交给上级处理)
    public void m() throws AException, BException... {}
  • 如果AExceptionBException都继承了XException,那么也可以这样写:
    public void m() throws XException{}
  • 调用者在调用m()方法时,编译器会检测到该方法上用throws声明了异常,表示可能会抛出异常,编译器会继续检测该异常是否为编译时异常,如果为编译时异常则必须在编译阶段进行处理,如果不处理编译器就会报错。
  • 如果所有位置都采用throws,包括main方法的处理态度也是throws,如果运行时出现了异常,最终异常是抛给了main方法的调用者(JVM),JVM则会终止程序的执行。因此为了保证程序在出现异常后不被中断,至少main方法不要再使用throws进行声明了。
  • 发生异常后,在发生异常的位置上,往下的代码是不会执行的,除非进行了异常的捕捉。

上述例子若使用throws关键字:

package exceptiontest.exception;

import java.util.Scanner;

/**
 *第一种处理方式:声明异常 (throws关键字)
 */
public class ExceptionTest03 {
    public static void main(String[] args) throws IllegalAgeException, IllegalNameException { //向上throws异常,交给JVM处理,即程序中止,产生报错
        System.out.println("欢迎来到用户管理系统,请先注册:");
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String name = scanner.next();
        System.out.println("请输入年龄:");
        int age = scanner.nextInt();
        UserServise userServise = new UserServise();
        userServise.register(name, age); //这里的代码有可能出现异常,出现了异常之后,后续程序不再执行
        System.out.println("main over!");
    }
}

/**
 *用户的业务类
 */

class UserServise{
    public void register(String name, int age) throws IllegalAgeException, IllegalNameException {//向上throws异常
        System.out.println("正在注册[" + name + "],请稍后......");
        UserDao userDao = new UserDao();
        userDao.save(name, age); //这里的代码有可能出现异常,出现了异常之后,后续程序不再执行
        System.out.println("注册成功,欢迎[" + name + "]");
    }
}
/**
 * 操作数据库的一个类
 * 用户需要注册,其用户名和年龄是需要保存的
 */
class UserDao{
    public void save(String name, int age) throws IllegalNameException, IllegalAgeException { //向上throws异常
        System.out.println("用户[" + name + "]的信息正在保存...");
        if(name.length() < 6 || name.length()>12){
            throw new IllegalNameException(); //若程序执行到这产生了异常,则后续代码不再执行
            //这里不能写任何代码,因为这里的代码永远不会执行
        }
        if(age < 18){
            throw new IllegalAgeException();
        }
        System.out.println("用户[" + name + "]的信息保存成功!");
    }
}
  • 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
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

运行结果:

  1. 没有异常:
    在这里插入图片描述
  2. name无效:
    在这里插入图片描述
  3. nameAge都无效(只抛出IllegalNameException异常,因为程序执行到throw new IllegalNameException();就不再往下执行了):
    在这里插入图片描述
  4. age无效:
    在这里插入图片描述

4.2 第二种处理方式:捕捉异常 (try…catch…关键字)

如果一个异常发生后,不需要调用者知道,也不需要调用者来处理,选择使用捕捉方式处理。

try{
// 尝试执行可能会出现异常的代码
// try块中的代码如果执行出现异常,出现异常的位置往下的代码是不会执行的,直接进入catch块执行
}catch(AException e){
// 如果捕捉到AException类型的异常,在这里处理
}catch(BException e){
// 如果捕捉到BException类型的异常,在这里处理
}catch(XException e){
// 如果捕捉到XException类型的异常,在这里处理
}
// 当try..catch..将所有发生的异常捕捉后,这里的代码是会继续往下执行的。

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

上述例子若使用try...catch...关键字:

package exceptiontest.exception.trycatchtest;

import exceptiontest.exception.IllegalAgeException;
import exceptiontest.exception.IllegalNameException;

import java.util.Scanner;

/**
 * 第二种处理方式:捕捉异常 (try...catch...关键字)
 */
public class ExceptionTest04 {
    public static void main(String[] args) {
        System.out.println("欢迎来到用户管理系统,请先注册:");
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String name = scanner.next();
        System.out.println("请输入年龄:");
        int age = scanner.nextInt();
        UserServise userServise = new UserServise();
        try {
            userServise.register(name, age);
            System.out.println("如果以上行的代码出现异常,这里的代码不会执行!");
        } catch (IllegalAgeException e) {
            System.out.println("对不起,年龄不合法!");
        } catch (IllegalNameException e) {
            System.out.println("对不起,用户名不合法!");
        }
        System.out.println("main over!");

    }
}

class UserServise{
    public void register(String name, int age) throws IllegalAgeException, IllegalNameException { //这里可以try...catch,也可以throws,一般系统是要在前端有所提示,所以这里依旧选择throws
        System.out.println("正在注册[" + name + "],请稍后......");
        UserDao userDao = new UserDao();
        userDao.save(name, age);

        System.out.println("注册成功,欢迎[" + name + "]");

    }
}

class UserDao{
    public void save(String name, int age) throws IllegalNameException, IllegalAgeException { //throw new异常的位置一定都是搭配throws,因为产生的异常是需要传递出去的,不能自己产生自己就try...catch...捕捉处理了,这是没有意义的,所以这里使用throws向上抛
        System.out.println("用户[" + name + "]的信息正在保存...");
        if(name.length() < 6 || name.length()>12){
            throw  new IllegalNameException();
        }
        if(age < 18){
            throw new IllegalAgeException();
        }
        System.out.println("用户[" + name + "]的信息保存成功!");
    }
}
  • 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
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

运行结果:

  1. 没有异常(同上)

  2. name无效:
    在这里插入图片描述

  3. nameAge都无效(只提示用户名不合法,因为程序先判断的name,并throw new IllegalNameException();,就直接被对应的catch分支捕捉处理了):
    在这里插入图片描述

  4. age无效:
    在这里插入图片描述

4.2.1 catch可以写多个,并且遵循自上而下,从小到大(其可以看作是分支,最多只有一个catch会被执行)

如果catch时从大到小:

 try {
	userServise.register(name, age);
    System.out.println("如果以上行的代码出现异常,这里的代码不会执行!");
 }catch (Exception e){
     
 } catch (IllegalAgeException e) {
     System.out.println("对不起,年龄不合法!");
 } catch (IllegalNameException e) {
     System.out.println("对不起,用户名不合法!");
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

直接编译报错:
在这里插入图片描述

4.2.2 Java7新特性:catch后面小括号中可以编写多个异常,使用运算符“|”隔开。

try {
	userServise.register(name, age);
	System.out.println("如果以上行的代码出现异常,这里的代码不会执行!");
} catch (IllegalAgeException | IllegalNameException e) { //注意这里只有最后有一个e,|前面没有
	System.out.println("对不起,年龄或名字不合法!");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

运行结果:
在这里插入图片描述
当多个异常的处理方式一样的时候,推荐同时处理;若处理方式不一样(像上面),还是建议分开处理(细粒度更明确)!
同时注意:该用法所处理的异常必须是没有继承关系的,否则报错

5.异常的使用

  1. 自定义异常的使用:
package exceptiontest;

import exceptiontest.exception.IllegalAgeException;

public class ExceptionTest05 {
    public static void main(String[] args) {
        User user  = new User();
        user.setName("zhangsan");
        try { //try...catch...捕捉异常
            user.setAge(-1);
        } catch (IllegalAgeException e) {
            System.out.println("年龄不合法!");
        }
    }
}

class User{
    private String name;
    private int age;

    public User() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) throws IllegalAgeException { //产生异常的位置使用throw将异常抛出
        if(age < 0 || age > 120){ //对于年龄进行异常处理
            throw new IllegalAgeException();
        }
        this.age = age;
    }
}
  • 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
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

运行结果:
在这里插入图片描述
2. JDK内置的异常的使用:

package exceptiontest;

import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class ExceptionTest06 {
    public static void main(String[] args) {
            m1();
    }

    public static void m1(){
        try {  //try...catch...捕捉
            m2();
        } catch (FileNotFoundException e) {
            System.out.println("文件没有找到!");
        }
    }

    public static void m2() throws FileNotFoundException {  //向上抛出
        FileInputStream in = new FileInputStream("D://Tools/a.txt"); //FileInputStream 类 throws FileNotFoundException,所以需要进行对应的异常处理

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

在这里插入图片描述

6.异常的常用方法

  • 获取异常的简单描述信息:
    exception.getMessage();获取的message是通过构造方法创建异常对象时传递过去的String message
  • 打印异常堆栈信息(帮助程序员调试程序,更推荐):
    exception.printStackTrace();
  • 要会看异常的堆栈信息:
    异常信息的打印是符合栈数据结构的。
    看异常信息主要看最开始的描述信息,即看栈顶信息。

exception.getMessage();示例代码:

package exceptiontest.exception.trycatchtest02;

import exceptiontest.exception.IllegalAgeException;
import exceptiontest.exception.IllegalNameException;

import java.util.Scanner;

/**
 * 异常的常用方法一:exception.getMessage()
 */
public class ExceptionTest07 {
    public static void main(String[] args) {
        System.out.println("欢迎来到用户管理系统,请先注册:");
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String name = scanner.next();
        System.out.println("请输入年龄:");
        int age = scanner.nextInt();
        UserServise userServise = new UserServise();
        try {
            userServise.register(name, age);
        } catch (IllegalAgeException e) {
            System.out.println(e.getMessage()); //e.getMessage()返回 《throw new IllegalAgeException("年龄不合法,应该大于18岁!");》中传入的信息
        }catch (IllegalNameException e){
            System.out.println(e.getMessage()); //同上理
        }
        System.out.println("main over!");
    }
}

class UserServise{
    public void register(String name, int age) throws IllegalAgeException, IllegalNameException { //这里可以try...catch,也可以throws,一般系统是要在前端有所提示,所以这里依旧选择throws
        System.out.println("正在注册[" + name + "],请稍后......");
        UserDao userDao = new UserDao();
        userDao.save(name, age);

        System.out.println("注册成功,欢迎[" + name + "]");
    }
}

class UserDao{
    public void save(String name, int age) throws IllegalNameException, IllegalAgeException { //throw new异常的位置一定都是搭配throws,向上throws异常
        System.out.println("用户[" + name + "]的信息正在保存...");
        if(name.length() < 6 || name.length()>12){
            throw new IllegalNameException("用户名不合法,长度应该在6-12位!");
        }
        if(age < 18){
            throw new IllegalAgeException("年龄不合法,应该大于18岁!");
        }
        System.out.println("用户[" + name + "]的信息保存成功!");
    }
}
  • 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
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

IllegalNameExceptionIllegalAgeException两个异常类同前面代码。

运行结果:
在这里插入图片描述
exception.printStackTrace();示例代码:

package exceptiontest.exception.trycatchtest02;

import exceptiontest.exception.IllegalAgeException;
import exceptiontest.exception.IllegalNameException;

import java.util.Scanner;

/**
 * 异常的常用方法一:exception.printStackTrace()
 */
public class ExceptionTest07 {
    public static void main(String[] args) {
        System.out.println("欢迎来到用户管理系统,请先注册:");
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String name = scanner.next();
        System.out.println("请输入年龄:");
        int age = scanner.nextInt();
        UserServise userServise = new UserServise();
        try {
            userServise.register(name, age);
        } catch (IllegalAgeException e) {
            e.printStackTrace(); //打印异常堆栈信息
        }catch (IllegalNameException e){
            e.printStackTrace(); //打印异常堆栈信息
        }
        //try...catch后依旧会执行
        System.out.println("main over!");
    }
}

class UserServise{
    public void register(String name, int age) throws IllegalAgeException, IllegalNameException { //这里可以try...catch,也可以throws,一般系统是要在前端有所提示,所以这里依旧选择throws
        System.out.println("正在注册[" + name + "],请稍后......");
        UserDao userDao = new UserDao();
        userDao.save(name, age);

        System.out.println("注册成功,欢迎[" + name + "]");
    }
}

class UserDao{
    public void save(String name, int age) throws IllegalNameException, IllegalAgeException { //throw new异常的位置一定都是搭配throws,向上throws异常
        System.out.println("用户[" + name + "]的信息正在保存...");
        if(name.length() < 6 || name.length()>12){
            throw new IllegalNameException("用户名不合法,长度应该在6-12位!");
        }
        if(age < 18){
            throw new IllegalAgeException("年龄不合法,应该大于18岁!");
        }
        System.out.println("用户[" + name + "]的信息保存成功!");
    }
}
  • 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
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

运行结果:
在这里插入图片描述
在这里插入图片描述
注意:出现main over!在不同位置输出是因为高版本的JDK使用了多线程并发,处理异常的是另一个线程。

7.finally语句块

  • finally语句块中的代码是一定会执行的。
  • finally语句块不能单独使用,至少需要配合try语句块一起使用:
    ①try…finally
    ②try…catch…finally
  • 通常在finally语句块中完成资源的释放(比如IO流,对文件读写管道的关闭),资源释放的工作比较重要,如果资源没有释放会一直占用内存。为了保证资源的关闭,也就是说:不管程序是否出现异常,关闭资源的代码一定要保证执行。因此在finally语句块中通常进行资源的释放。

7.1 finally的初步使用

  1. finally中的代码一定会执行:
package exceptiontest.finallytest;

public class ExceptionTest08 {
    public static void main(String[] args) {
        try{
            String s = null;
            s.toString();
        }finally {
            System.out.println("finally 中的 main over"); //即使上述程序发生异常,finally语句块中的代码都一定会执行!

        }
        System.out.println("main over"); //由于没有catch,上述程序发生异常后,直接中止,不会执行到这里
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

运行结果:
在这里插入图片描述
2. 使用IO流的示例代码(finally中释放资源):

package exceptiontest.finallytest;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class ExceptionTest09 {
    public static void main(String[] args) {
        FileInputStream in = null;
        try {
            in = new FileInputStream("D:\\workspace\\IDEAProjects\\Java_se\\src\\exceptiontest\\finallytest\\ExceptionTest08.java");
            //开始读
            byte[] bytes = new byte[1024]; //一次读取1KB
            int readCount = 0;
            while ((readCount =in.read(bytes))!=-1){ //in.read(bytes))获取到每次读取的长度,赋给readCount
                System.out.println(new String(bytes, 0, readCount)); //将每次读取到的内容输出
                //in.close(); //要是关闭流的操作放在这里,一旦上述程序出现异常,则该行代码无法执行到,资源无法释放,会一直占用内存 
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            //关闭流释放资源,为保证其一定执行,将其放到finally语句块中
            if (in != null) { //先判断其是否为空
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  • 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
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

运行结果(输出读到的文件内容):
在这里插入图片描述

7.2 final、finally、finalize分别是什么?

①final是一个关键字,修饰的类无法继承,修饰的方法无法覆盖,修饰的变量不能修改。
②finally是一个关键字,使用在异常处理机制中,必须联合try一起使用,finally语句块中的代码一定会执行。
③finalize是一个标识符,它是Object类中的一个方法,是由垃圾回收期GC负责调用的。

7.3 finally相关面试题

  1. 以下程序执行结果是什么?
public static void main(String[] args) {
  // 程序执行结果是?
    System.out.println(get());
}

public static boolean get(){
    try{
        return true;
    }finally {
        return false;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

结果为false,finally语句块一定会执行,在try的 return true;之前,执行finally代码块中的内容,返回false

注意:由于在上面已经return了,所以下面的位置不能写任何代码,否则编译报错:
在这里插入图片描述

  1. 以下程序执行结果是什么?
public static void main(String[] args) {
    // 程序执行结果是?
    System.out.println(get());
}

public static int get(){
    int i = 0;
    try{
        return i;
    }finally {
        i++;
        System.out.println(i);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

finally语句块中的代码一定会执行,所以i++后先输出1,由于Java中遵循代码自上而下执行的规则,所以try中return i;时输出的是i++之前的状态,以下是反编译之后的效果:

public static int get(){
    int i = 0;
    int j = i;
    i++;
    return j;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

所以最后返回的i依旧为0,所以有结果:
在这里插入图片描述

  1. 以下程序执行结果是什么?
public static void main(String[] args) {
    // 程序执行结果是?
    try{
        System.out.println("try...");
        return;
    }finally {
        System.out.println("finally...");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

finally语句块中的代码一定会执行,在try中的return;之前就执行了finally语句块中的代码,所以结果为:
在这里插入图片描述

  1. 以下程序执行结果是什么?
public static void main(String[] args) {
  // 程序执行结果是?
    System.out.println("返回:" + get());
}

public static int get(){
    try{
        System.out.println("try...");
        return 1;
    }finally {
        System.out.println("finally...");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

同上,finally语句块中的代码一定会执行,在try中的return;之前就执行了finally语句块中的代码,所以结果为:
在这里插入图片描述

  1. 以下程序执行结果是什么?
package exceptiontest.finallytest;

public class ExceptionTest10 {
    public static void main(String[] args) {
        // 程序执行结果是?
        try{
            System.out.println("try...");
            System.exit(0); //退出JVM
        }finally {
            System.out.println("finally...");
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

System.exit(0);是退出JVM,此时finally语句块中代码无法执行了,所以结果为:
在这里插入图片描述

  1. 以下程序执行结果是什么?
public static void main(String[] args) {
    // 程序执行结果是?
    System.out.println("返回:" + get());
}

public static int get(){
    try{
        System.out.println("try...");
        System.exit(0); //退出JVM
    }finally {
        System.out.println("finally...");
    }
    return 1;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

同上System.exit(0);是退出JVM,所以 return 1;虽然没有报错,但是实际上是永远执行不到的,所以结果为:
在这里插入图片描述
7. 补充一个s++++s相关的

public static void main(String[] args) {
    int s = 20;
    s++;
    System.out.println(s); 
    s = s++;
    System.out.println(s);
    s= ++s;
    System.out.println(s);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

输出结果为:
21
21
22
最开始 s++;即s自增1,此时s=21而后s = s++;是先执行s赋值给s,此时s++并没有让s自增1,此时s=21,而最后的s= ++s;是先让让s自增1后,再将新的值赋给s,所以此时s=22

8.方法覆盖与异常

原则:方法覆盖之后,不能比父类方法抛出更多的异常,可以更少。

  • 不能更多(编译报错):
    在这里插入图片描述
  • 可以更少:
    在这里插入图片描述

在这里插入图片描述

注意:
对于运行时异常,可以不用遵循上述原则,父类不抛出异常,子类可以抛出RuntimeException。但实际上开发不会说父类方法抛出的异常和子类覆盖的方法抛出不同异常!一般都会保持一致!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/412458
推荐阅读
相关标签
  

闽ICP备14008679号