2024年最新版零基础详细Java知识笔记【异常】⑤

笔记根据动力节点Java老杜动力节点Java零基础视频教程(中部)编写。

第五章 异常

5.1 异常概述

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

5.2 异常继承结构

  1. 所有的异常和错误都是可抛出的。都继承了Throwable类。

  2. Error是无法处理的,出现后只有一个结果:JVM终止。

  3. Exception是可以处理的。

  4. Exception的分类:

    1. 所有的RuntimeException的子类:运行时异常/未检查异常(UncheckedException)/非受控异常
    2. Exception的子类(除RuntimeException之外):编译时异常/检查异常(CheckedException)/受控异常
  5. 编译时异常和运行时异常区别:

    1. 编译时异常特点:在编译阶段必须提前处理,如果不处理编译器报错。
    2. 运行时异常特点:在编译阶段可以选择处理,也可以不处理,没有硬性要求。
    3. 编译时异常一般是由外部环境或外在条件引起的,如网络故障、磁盘空间不足、文件找不到等
    4. 运行时异常一般是由程序员的错误引起的,并且不需要强制进行异常处理

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

在这里插入图片描述

5.3 自定义异常

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

  1. 定义两个编译时异常:
    • IllegalNameException : 无效名字异常
    • IllegalAgeException: 无效年龄异常
  2. 完成这样的需求:
    1. 编写一个用户注册的方法,该方法接收两个参数,一个是用户名,一个是年龄。如果用户名长度在[6 - 12]位,并且年龄大于18岁时,输出用户注册成功。
    2. 如果用户名长度不是[6 - 12]位时,让程序出现异常,让IllegalNameException异常发生!
    3. 如果年龄小于18岁时,让程序出现异常,让IllegalAgeException异常发生!
package com.xfanny.javase.exception;
/**
 * 无效名字异常。当名字长度小于6位,或者大于12位,认为程序出现了异常。
 *
 * 自定义异常两步:
 *      第一步:编写了类继承RuntimeException或者Exception
 *      第二步:提供两个构造方法,一个无参数的,一个带有String参数的。并且在构造方法中调用super(String)
 */
public class IllegalNameException extends Exception{

    public IllegalNameException() {
    }

    public IllegalNameException(String message) {
        super(message);
    }
}
package com.xfanny.javase.exception;

/**
 * 无效年龄异常,凡事年龄小于18岁的,都是异常。
 */
public class IllegalAgeException extends Exception{

    public IllegalAgeException() {
    }

    public IllegalAgeException(String message) {
        super(message);
    }
}
package com.xfanny.javase.exception;
public class IllegalRealnameException extends IllegalNameException{

    public IllegalRealnameException() {
    }

    public IllegalRealnameException(String message) {
        super(message);
    }
}
package com.xfanny.javase;

import com.xfanny.javase.exception.IllegalAgeException;
import com.xfanny.javase.exception.IllegalNameException;
import com.xfanny.javase.exception.IllegalRealnameException;

import java.util.Scanner;

public class ExceptionTest {
    //public static void main(String[] args) throws IllegalNameException, IllegalAgeException {
    public static void main(String[] args) throws Exception{
    //public static void main(String[] args) throws IllegalRealnameException, IllegalAgeException {
        Scanner scanner = new Scanner(System.in);
        System.out.println("欢迎使用本系统,先进行用户的注册:");
        System.out.print("请输入用户名:");
        String name = scanner.next();
        System.out.print("请输入年龄:");
        int age = scanner.nextInt();

        // 注册
        UserService userService = new UserService();
        userService.register(name, age); // 这里的代码可能出现异常,如果一旦出现异常,后续代码则不再执行。

        System.out.println("main over!");
    }
}

/**
 * 用户的业务类
 */
class UserService {
    public void register(String name, int age) throws IllegalNameException, IllegalAgeException {
        System.out.println("正在注册,请稍后....");
        UserDao userDao = new UserDao();
        userDao.save(name, age); //这里有可能出现异常,出现了异常之后,后续程序则不再执行了。
        System.out.println("注册成功,欢迎[" + name + "]");
    }
}

/**
 * 操作数据库的一个类
 */
class UserDao {
    /**
     * 用户要注册,肯定最后用户名和年龄这个用户相关的信息是需要保存的。
     * @param name 用户名
     * @param age 年龄
     */
    public void save(String name, int age) throws IllegalNameException, IllegalAgeException{
        System.out.println("用户["+name+"]的信息正在保存....");
        if(name.length() < 6 || name.length() > 12){
            throw new IllegalNameException();
            // 这里不能写任何代码,因为这里的代码永远都不会执行。
            //System.out.println("hello world");
        }
        if(age < 18){
            throw new IllegalAgeException();
        }
        System.out.println("用户["+name+"]的信息保存成功!");
    }
}

5.4 异常的处理

5.4.1 概述

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

5.4.2 声明异常 throws关键字

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

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

5.4.3 捕捉异常 try…catch…关键字

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

  • 如果一个异常发生后,不需要调用者知道,也不需要调用者来处理,选择使用捕捉方式处理。
    try{
    // 尝试执行可能会出现异常的代码
    // try块中的代码如果执行出现异常,出现异常的位置往下的代码是不会执行的,直接进入catch块执行
    }catch(AException e){ // 异常类型1  变量1
    // 如果捕捉到AException类型的异常,在这里处理
    }catch(BException e){ // 异常类型2  变量2
    // 如果捕捉到BException类型的异常,在这里处理
    }catch(XException e){
    // 如果捕捉到XException类型的异常,在这里处理
    }
    // 当try..catch..将所有发生的异常捕捉后,这里的代码是会继续往下执行的。
    
  • catch语句块可以看做是分支,try catch语句中,最多只有一个catch分支执行。
  • catch可以写多个。并且遵循自上而下,从小到大。
  • Java7新特性:catch后面小括号中可以编写多个异常,使用运算符“|”隔开。
  • 记住这点,程序出现异常需要调用者知道的就选择throws(取决于需不需要别人知道这个异常)

5.5 异常的常用方法

  • 获取异常的简单描述信息:
    • exception.getMessage();
    • 获取的message是通过构造方法创建异常对象时传递过去的message。
    • 获取当时创建异常对象时给异常构造方法传递的String message参数的值。
  • 打印异常堆栈信息:
    • exception.printStackTrace();
  • 要会看异常的堆栈信息:
    • 异常信息的打印是符合栈数据结构的。
    • 看异常信息主要看最开始的描述信息。看栈顶信息。

5.6 finally语句块

  • finally语句块中的代码是一定会执行的。
  • finally语句块不能单独使用,至少需要配合try语句块一起使用:
    • try...finally
    • try...catch...finally
  • 通常在finally语句块中完成资源的释放
    • 资源释放的工作比较重要,如果资源没有释放会一直占用内存。
    • 为了保证资源的关闭,也就是说:不管程序是否出现异常,关闭资源的代码一定要保证执行
    • 因此在finally语句块中通常进行资源的释放。
  • final、finally、finalize分别是什么?
    • final是一个关键字,修饰的类无法继承,修饰的方法无法覆盖,修饰的变量不能修改。
    • finally是一个关键字,和try一起使用,finally语句块中的代码一定会执行。
    • finalize是一个标识符,它是Object类中的一个方法名。

5.7 方法覆盖与异常

  • 方法重写之后,不能比父类方法抛出更多的异常,可以更少。

在这里插入图片描述

5.8 IDEA继承ChatGPT插件Bito

在这里插入图片描述
登录或注册
在这里插入图片描述

选择语言
在这里插入图片描述
创建工作空间名
在这里插入图片描述


  • 每章一句:“我们努力的意义,就是为了遇见优秀的人和更好的自己”
  • 恭喜你已阅读完第五章!点个赞证明你已经挑战成功,进入第六章关卡《常用类》吧【点击即可跳转 常用类笔记】!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值