Java 异常处理全解析

在 Java 开发中,"异常" 是绕不开的话题。它像一把双刃剑:既可能让程序崩溃,也能帮我们精准定位问题。本文将从异常的体系、分类、处理方式到实战技巧,带你全面掌握 Java 异常处理。

一、异常的概述

异常是指程序运行过程中发生的非正常事件,它会打断程序的正常执行流程。比如文件不存在、网络连接中断、数组越界等,都属于异常场景。

Java 通过异常机制来统一处理这些意外情况,让程序在出错时能有 "补救" 或 "优雅退出" 的机会。

二、异常的体系:Throwable 是一切的开始

Java 的异常体系以java.lang.Throwable为根类,它有两个直接子类:ErrorException

1. Error(错误)

  • 属于系统级问题,开发者几乎无法处理。
  • 示例:OutOfMemoryError(内存溢出)、StackOverflowError(栈溢出)。
  • 程序遇到 Error 时,通常会直接崩溃,无需也无法捕获。
// 演示栈溢出Error
public class StackOverflowDemo {
    public static void main(String[] args) {
        stackOverflowMethod();
    }
    
    public static void stackOverflowMethod() {
        stackOverflowMethod(); // 无限递归,导致StackOverflowError
    }
}

2. Exception(异常)

  • 属于业务或逻辑级问题,开发者可以通过代码处理。
  • 又分为三类:编译期异常运行时异常自定义异常
(1)编译期异常(Checked Exception)
  • 编译器强制要求处理的异常(不处理则编译报错)。
  • 示例:IOException(文件读写异常)、SQLException(数据库操作异常)。
import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class CheckedExceptionDemo {
    public static void main(String[] args) {
        // 编译报错:未处理FileNotFoundException
        // FileInputStream fis = new FileInputStream("test.txt");
        
        // 正确处理方式1:try-catch捕获
        try {
            FileInputStream fis = new FileInputStream("test.txt");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        
        // 正确处理方式2:throws向上抛出
        // public static void main(String[] args) throws FileNotFoundException {
        //     FileInputStream fis = new FileInputStream("test.txt");
        // }
    }
}
(2)运行时异常(RuntimeException,Unchecked Exception)
  • 编译器不强制处理,可在运行时才暴露的异常。
  • 示例:NullPointerException(空指针)、ArrayIndexOutOfBoundsException(数组越界)、ClassCastException(类型转换)。
(3)自定义异常
  • 开发者根据业务需求自定义的异常类,需继承ExceptionRuntimeException
  • 场景:用户登录失败、参数校验不通过等业务异常。
// 自定义编译期异常
class LoginFailedException extends Exception {
    public LoginFailedException(String message) {
        super(message);
    }
}

// 自定义运行时异常
class ParamInvalidException extends RuntimeException {
    public ParamInvalidException(String message) {
        super(message);
    }
}

public class CustomExceptionDemo {
    public static void login(String username, String password) throws LoginFailedException {
        if (!"admin".equals(username) || !"123456".equals(password)) {
            throw new LoginFailedException("用户名或密码错误");
        }
    }
    
    public static void checkParam(String param) {
        if (param == null || param.isEmpty()) {
            throw new ParamInvalidException("参数不能为空");
        }
    }
    
    public static void main(String[] args) {
        try {
            login("user", "123");
        } catch (LoginFailedException e) {
            System.out.println("登录失败:" + e.getMessage());
        }
        
        checkParam(null); // 运行时抛出ParamInvalidException
    }
}

三、异常的处理方式:三种策略 + 关键字组合

Java 提供了多种异常处理方式,核心是抛出捕获

1. 向上抛出(throws + throw)

  • throw:在方法内手动抛出一个异常对象。
  • throws:在方法声明处声明该方法可能抛出的异常,将异常 "甩给" 调用者处理。
public class ThrowDemo {
    // 声明可能抛出的异常
    public static void readFile(String path) throws FileNotFoundException {
        if (!"valid.txt".equals(path)) {
            // 手动抛出异常
            throw new FileNotFoundException("文件不存在");
        }
    }
    
    public static void main(String[] args) {
        try {
            readFile("invalid.txt");
        } catch (FileNotFoundException e) {
            System.out.println("捕获到异常:" + e.getMessage());
        }
    }
}

2. try-catch:捕获并处理异常

  • try 块:包裹可能抛出异常的代码。
  • catch 块:捕获指定类型的异常并处理,可多个 catch 按异常从小到大排列。
public class TryCatchDemo {
    public static void main(String[] args) {
        try {
            int[] arr = {1, 2, 3};
            System.out.println(arr[3]); // 数组越界
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("捕获到数组越界异常");
            e.printStackTrace();
        } catch (Exception e) { // 父类异常需放在后面
            System.out.println("捕获到其他异常");
        }
    }
}

3. try-catch-finally:捕获 + 最终执行

  • finally 块:无论是否发生异常,都会执行的代码(常用来释放资源,如关闭流、连接)。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class TryCatchFinallyDemo {
    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("test.txt");
            System.out.println("读取文件中...");
        } catch (FileNotFoundException e) {
            System.out.println("文件未找到");
        } finally {
            // 最终执行:关闭资源
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("finally块执行完毕");
        }
    }
}

4. try-finally:仅捕获最终执行(不处理异常)

  • 场景:需要确保资源释放,但不关心异常本身(异常会继续向上抛)。
public class TryFinallyDemo {
    public static void main(String[] args) {
        try {
            int result = 10 / 0; // 运行时异常
        } finally {
            System.out.println("无论是否异常,这里都会执行");
        }
    }
}

四、面试高频:final 和 finally 的区别

这是 Java 面试的经典问题,核心区别如下:

对比项finalfinally
功能修饰类、方法、变量,表 "不可变"异常处理的关键字,表 "最终执行"
场景类不可继承、方法不可重写、变量不可修改用于 try-catch 块后,确保代码执行
关联与异常处理无直接关联仅在异常处理中使用
// final示例
final class FinalClass {} // 不可继承
final void finalMethod() {} // 不可重写
final int FINAL_VAR = 10; // 不可修改

// finally示例
try {
    // 可能抛异常的代码
} finally {
    // 最终执行的代码
}

五、异常处理的最佳实践

  1. 明确区分异常类型:运行时异常(业务逻辑)和编译期异常(外部依赖)分开处理。
  2. 避免空 catch 块:捕获异常后至少打印日志或做降级处理,否则异常会被 "吃掉"。
  3. 优先捕获具体异常:多个 catch 块时,子类异常在前,父类异常在后。
  4. 资源释放用 finally:涉及 IO、网络连接等资源,务必在 finally 中关闭。
  5. 自定义异常见名知意:业务异常命名要清晰,如OrderNotFoundException

总结

Java 异常体系是保障程序健壮性的基石:

  • 理解ErrorException的区别,聚焦于可处理的Exception
  • 灵活运用抛出捕获策略,结合try-catch-finally关键字组合。
  • 遵循最佳实践,让异常从 "问题" 变成 "调试助手"。

掌握这些知识,你就能在程序出错时从容应对,写出更健壮的 Java 代码!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值