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 执行情况 :只有一种情况不执行 ------ 在 try或catch中调用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类,包含name和age属性,以及对应的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() 方法会接收控制台输入的所有字符 (包括数字、字母、符号、空格等),无论输入的内容本身是什么类型(比如输入 123、abc、20岁 等),最终都会被转换为 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);
    }
}