Java 黑马程序员学习笔记(进阶篇21)

1. 异常的体系结构

Java 中所有异常的根类是 java.lang.Throwable,其下分为两大子类:

(1) Error(错误)
  • 定义:严重的系统级错误(如内存溢出、虚拟机崩溃),程序自身无法处理,只能终止运行。
  • 举例:StackOverflowError(栈溢出)、OutOfMemoryError(内存溢出)。
(2) Exception(异常)

**① 定义:**程序运行时的一般性错误,程序可以通过代码处理,避免程序终止。

② 分类:

  • 编译时异常(受检异常) :除RuntimeException外的Exception子类,编译时必须处理(捕获或抛出),否则编译报错。举例:IOException(文件操作异常)、ClassNotFoundException(类找不到)。
  • 运行时异常(非受检异常)RuntimeException及其子类,编译时无需强制处理,运行时才可能出现。举例:NullPointerException(空指针)、ArrayIndexOutOfBoundsException(数组越界)、ArithmeticException(算术错误,如除以 0)。

2. 异常处理机制(核心)

Java 提供 3 种核心机制处理异常:try-catch-finally(捕获处理)、throws(声明抛出)、throw(手动抛出)。

(1) try-catch-finally:捕获并处理异常

**① 作用:**直接在代码中捕获异常,并定义异常发生后的处理逻辑,避免程序崩溃。

② 语法结构

java 复制代码
try {
    // 可能发生异常的代码(核心业务逻辑)
} catch (异常类型1 异常变量名) {
    // 处理"异常类型1"的逻辑(如打印日志、提示用户)
} catch (异常类型2 异常变量名) {
    // 处理"异常类型2"的逻辑
} finally {
    // 无论是否发生异常,都会执行的代码(通常用于关闭资源:如流、数据库连接)
}
③ 关键注意事项
  • catch 顺序 :子类异常必须放在父类异常前面(如先捕获 NullPointerException,再捕获 RuntimeException),否则子类异常的 catch 块会被忽略(编译器报错)。
  • finally 执行情况 :只有一种情况不执行 ------ 在 trycatch 中调用 System.exit(0)(强制终止 JVM)。
异常对象常用方法
  • e.getMessage():获取异常的详细描述信息。
  • e.printStackTrace():打印异常的堆栈信息**(开发调试时常用)**。
⑤ 示例代码(处理除以零异常)
java 复制代码
public class ExceptionDemo {
    public static void main(String[] args) {
        int a = 10;
        int b = 0;
        try {
            int result = a / b; // 可能发生ArithmeticException
            System.out.println("结果:" + result);
        } catch (ArithmeticException e) {
            // 处理异常
            System.out.println("出错了:" + e.getMessage()); // 输出:出错了:/ by zero
            e.printStackTrace(); // 打印完整堆栈信息
        } finally {
            System.out.println("无论是否出错,我都会执行"); // 一定会执行
        }
    }
}
(2) throws:声明异常(交给调用者处理)
① 作用:

当方法内部无法处理异常时,在方法声明处 "声明" 该异常,将异常的处理责任交给调用该方法的上级代码

② 语法结构
java 复制代码
// 方法返回值 方法名(参数列表) throws 异常类型1, 异常类型2 { ... }
public void readFile() throws IOException {
    // 可能发生IOException的代码(如读取文件)
}
③ 关键注意事项
  • 仅声明编译时异常 :运行时异常(如 NullPointerException)无需声明,声明了也无效。
  • 调用者的责任 :调用带有 throws 声明的方法时,必须处理该异常 ------ 要么用 try-catch 捕获,要么继续用 throws 向上声明。
④ 示例代码(声明 IO 异常)
java 复制代码
// 方法声明时用throws跟异常类型,多个异常用逗号分隔
public void readFile() throws IOException, FileNotFoundException {
    // 可能抛出IO异常的代码
}
  • 注意 :若方法抛出Checked异常,调用者必须用try-catch捕获或继续用throws声明。
(3) throw:手动抛出异常
① 作用

在代码中主动创建并抛出异常(如参数校验不通过时,手动抛IllegalArgumentException)。

使用格式
java 复制代码
public void checkAge(int age) {
    if (age < 0) {
        // 手动创建异常对象并抛出
        throw new IllegalArgumentException("年龄不能为负数");
    }
}
  • 注意throw抛出的是异常对象throws声明的是异常类型,二者常配合使用。

3. 综合练习

题目:女朋友信息录入与校验程序

需求:

编写一个 Java 程序,实现女朋友信息(姓名和年龄)的录入功能。程序需要对输入的姓名和年龄进行合法性校验,并处理可能出现的输入异常,直到录入合法的信息后,输出女朋友的完整信息。

具体要求:

(1) 输入要求:程序通过控制台依次接收两项输入:

  • 女朋友的姓名(字符串类型)
  • 女朋友的年龄(需转换为整数类型)

(2) 校验规则

① 姓名校验:姓名长度必须在 2-10 个字符之间(包含 2 和 10),不符合则视为非法。

② 年龄校验:

  • 年龄必须是整数(若输入非数字字符串,视为格式错误);
  • 年龄数值必须在 18-40 之间(包含 18 和 40),不符合则视为范围错误。

(3) 异常处理

  • 若年龄输入为非整数(如 "abc"、"20.5"),需捕获NumberFormatException,并提示 "年龄的格式有误"。
  • 若姓名长度不符合规则或年龄数值超出范围,需手动抛出RuntimeException,并提示 "姓名的长度或者年龄的范围有误"。
  • 所有异常处理后,程序需重新提示用户输入,直到录入合法信息为止。

(4) 输出要求 :当姓名和年龄均校验通过后,输出女朋友的信息(格式示例:GirlFriend{name='小丽', age=22})。

(5) 提示:
  • 需定义GirlFriend类,包含nameage属性,以及对应的setName(String name)setAge(int age)方法(在 set 方法中实现校验逻辑,不合法时抛异常)。
  • 使用Scanner接收控制台输入,通过try-catch块处理异常,配合循环实现 "不合法则重新输入" 的逻辑。
java 复制代码
package demo3;

import java.util.Scanner;

public class test4 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        GirlFriend gf = new GirlFriend();
        while (true) {
            try {
                System.out.println("请输入你心仪的女朋友的名字");
                String name = sc.nextLine();
                gf.setName(name);

                System.out.println("请输入你心仪的女朋友的年龄");
                String ageStr = sc.nextLine();
                int age = Integer.parseInt(ageStr);
                gf.setAge(age);
                break;
            } catch (NumberFormatException e) {
                System.out.println("年龄的格式有误");
            } catch (RuntimeException e) {
                System.out.println("姓名的长度或者年龄的范围有误");
            }
        }
        System.out.println(gf);
    }
}
关键逻辑 1:String name = sc.nextLine();

nextLine() 方法会接收控制台输入的所有字符 (包括数字、字母、符号、空格等),无论输入的内容本身是什么类型(比如输入 123abc20岁 等),最终都会被转换为 String 类型 存储到 name 变量中。所以可以理解为:"任何类型的输入内容,都会被当作字符串来接收"。

4. 自定义异常

当 Java 自带的异常类型无法满足业务需求时(如 "余额不足" 异常),可自定义异常类。

① 定义步骤
  • 新建类,继承Exception(自定义 Checked 异常)或RuntimeException(自定义 Unchecked 异常)。
  • 提供无参构造和带String message的构造方法(用于传递异常描述信息)。
② 代码示例
java 复制代码
// 自定义"余额不足"异常,继承RuntimeException(Unchecked异常)
public class InsufficientBalanceException extends RuntimeException {
    // 无参构造
    public InsufficientBalanceException() {
        super();
    }

    // 带异常信息的构造
    public InsufficientBalanceException(String message) {
        super(message);
    }
}

// 使用自定义异常
public void withdraw(double money, double balance) {
    if (money > balance) {
        // 手动抛出自定义异常
        throw new InsufficientBalanceException("余额不足,当前余额:" + balance);
    }
}
相关推荐
初见无风7 小时前
2.7 Lua代码中的可变参数
开发语言·lua·lua5.4
Dnui_King7 小时前
Kingbase 接口兼容性测试
java
七月稻草人7 小时前
Rust 应用状态(App State)管理:类型安全与并发控制的艺术
开发语言·安全·rust
Java&Develop7 小时前
IDEA报错:前言中不允许有内容
java
立志成为大牛的小牛7 小时前
数据结构——三十三、Dijkstra算法(王道408)
数据结构·笔记·学习·考研·算法·图论
软件架构师-叶秋7 小时前
spring boot入门篇之开发环境搭建
java·spring boot·后端
何故染尘優7 小时前
docker学习笔记,从入门开始!
笔记·学习·docker
无敌最俊朗@8 小时前
SQLite 约束 (Constraints) 面试核心知识点
java·开发语言·jvm
QX_hao8 小时前
【Go】--接口(interface)
开发语言·后端·golang