Java异常处理

异常概述、体系

什么是异常

异常是程序在编译或执行的过程中可能出现的错误或问题,如:数索引越界异常、空指针异常、日期格式化异常等

为什么要学习异常

  • 异常一旦出现,如果没有提前处理,程序就会退出JVM虚拟机而终止
  • 正确处理异常,可以保障程序的安全、健壮性
  • Error:错误,是一种特殊的类(java.lang.Error),与普通的Exception异常不同,Error是程序无法处理的错误,Error类及其子类是指我们程序处理不了的错误,这类异常往往代表JVM在运行过程中出现了问题,Error不应该由应用程序捕获和处理,而是由虚拟机自己处理
  • 一些常见的Error异常,如

    • IOError:IO错误
    • Virtual MachineError:Java虚拟机运行错误(内存异出【OutOfMemoryError - 堆内存异出、StackOverflowError - 栈内存异出】)
    • java.lang.NoClassDefFoundError:当虚拟机无法找到要加载的类时抛出的异常
    • LinkageError:类加载过程出现链接错误时抛出的错误

异常体系
QQ20250331-183057.png

  • Error:系统级别的问题
  • Exception:程序本身可以处理的问题

    • Runtime Exception:运行时异常,编译阶段不会报错(空指针异常、数组越界异常)
    • 除了Runtime Exception之外所有异常:编译时异常,编译器必须处理,否则程序不能通过编译(日期格式化异常)

Java中的所有异常都来自顶级父类Throwable,Throwable下有两个子类:Exception和Error

如何处理异常

在Java中有两个处理异常的方式,一个是try...catch...,一个是throws

  • try...catch(捕获异常)

    • 将可能发生异常的代码放到try代码块中,然后使用catch来捕获对应的异常
    • 如果try代码块正常运行,catch则不生效,如果发生了指定异常,catch则生效
    • 捕获异常时还可以接上finally代码块,无论有没有发生异常,finally代码块都会执行
  • throws(异常抛出)

    • 在方法上使用throws关键字可以声明该方法可能会抛出的异常
    • 当我们调用一个方法时,如果这个方法用throws关键字声明了受检异常,此时我们就必须手动处理它声明的异常,否则就会编译失败

什么时候用try...catch,什么时候用throws呢?

  • 当方法需要继续运行下去,就用try...catch,当方法不需要继续运行,就可以选择throws
  • 建议尽量选择捕获异常

常见的运行时异常

运行时异常继承自RuntimeException的异常或其子类,编译阶段是不会出错的,它是运行时阶段可能出现的错误,运行时异常编译阶段可以处理,也可以不处理,代码编译都能通过

  • 数组越界异常:ArrayIndexOutOfBoundsException
  • 空指针异常:NullPointerException。直接输出没问题,但是调用空指针的变量就会报错
  • 类型转换异常:ClassCastException
  • 迭代器遍历没有此元素异常:NoSuchElementException
  • 算数异常:ArithmeticException
  • 数字转换异常:NumberFormatException
  • 非法线程状态异常:llegalThreadStateException
  • 并发修改异常:java.util.ConcurrentModificationException(多线程下集合操作元素)
  • 参数错误:lllegalArgumentException(方法入参类型错误)
  • 安全错误:SecurityException(如权限不够)
  • 不支持的操作错误:UnsupportedOperationException(如重复创建同一用户)
  • 数据存储异常:ArrayStoreException(操作数组时类型不一致)
  • java.lang.IllegalStateException(Queue full依然add()添加元素)
  • FileSizeLimitExceededException:在SpringBoot中,文件上传时默认单个文件最大大小为1M,当上传一个较大的文件(超出1M)时,运行和后端程序报错
  • NoSuchBeanDefinitionException:表示在应用程序中没有找到指定的Bean的定义!在Spring容器ApplicationContext中调用getBean()获取bean对象时,bean的名称填错,就会报改错,或者就跟着没有该Bean对象!
  • UnsupportedClassVersionError:比如在JDK8上面运行JDK11环境下的程序!
  • java.lang.UnsupportedOperationException异常:使用Collections.unmodifiableCollection(Collection c)方法来创建一个只读集合,这样改变集合的任何操作都会抛出该异常

常见的编译时异常

不是RuntimeException或其子类的异常,编译阶段就报错,必须处理,否则代码不通过

  • IOException
  • ClassNotFoundException
  • ParseException
  • SQLException
  • FileNotFoundException
  • java.lang.InterruptedException
  • CloneNotSupportedException
  • java.util.concurrent.ExecutionException
  • java.util.concurrent.TimeoutException
  • java.util.concurrent.BrokenBarrierException {CyclicBarrier.await()}
  • java.util.concurrent.ExecutionException
  • NoSuchMethodException{反射}
  • ServletException

异常的默认处理流程

  1. 默认会在出现异常代码那里自动创建一个异常对象
  2. 异常会从方法中出现的点这里抛出给调用者,调用者最终抛出给JVM虚拟机
  3. 虚拟机收到异常对象后,现在控制台直接输出异常信息
  4. 直接从当前执行的异常点干掉程序
  5. 后续代码没有机会执行,因为程序已死

Throwable类常用方法

  • String getMessage():返回异常发生时的简要描述
  • String toString():返回异常发生的详细信息
  • void printStackTrace():在控制台上打印Throwable对象封装的异常信息
  • public synchronized Throwable getCause():获取实际的异常原因

自定义异常

Java标准库中提供了非常多表的异常类型,用来表达各种异常情况,然而在真实开发中,这些异常并不能完全满足我们的需求,因为标准库的异常往往表达的是技术层面,而不是业务层面,像账号密码错误这种情况,用标准库的异常就不太合适,所以在开发中我们会自定义异常类型,来表达符合我们业务的异常情况

只要继承异常类,就可以定义我们自己的异常,强烈建议大家继承RuntimeException,在自定义异常时,应当照着父类学习,提供多个构造方法,因为这样就能传递错误信息和堆栈信息

定义自定义异常类

// 自定义异常类,继承自Exception(可检查异常)
public class MyCustomException extends Exception {
    // 构造方法1:无参构造方法
    public MyCustomException() {
        super();
    }
​
    // 构造方法2:带异常信息的构造方法
    public MyCustomException(String message) {
        super(message);
    }
​
    // 构造方法3:带异常信息和原因的构造方法
    public MyCustomException(String message, Throwable cause) {
        super(message, cause);
    }
​
    // 构造方法4:带异常原因的构造方法
    public MyCustomException(Throwable cause) {
        super(cause);
    }
}

抛出自定义异常

在代码中,当遇到需要抛出自定义异常的场景时,使用throw关键字抛出异常

public class CustomExceptionDemo {
    public static void main(String[] args) {
        try {
            // 调用可能抛出自定义异常的方法
            checkCondition(false);
        } catch (MyCustomException e) {
            // 捕获并处理自定义异常
            System.out.println("捕获到自定义异常:" + e.getMessage());
        }
    }
​
    // 定义一个可能抛出自定义异常的方法
    public static void checkCondition(boolean condition) throws MyCustomException {
        if (!condition) {
            // 抛出自定义异常
            throw new MyCustomException("条件不满足,抛出自定义异常!");
        }
        System.out.println("条件满足,程序正常执行。");
    }
}

运行结果

运行上述代码时,如果checkCondition方法中的condition为false,程序会抛出自定义异常,并在catch块中捕获并处理异常,输出如下内容

捕获到自定义异常:条件不满足,抛出自定义异常

如果condition为true,程序会正常执行输出

最后修改:2025 年 03 月 31 日
如果觉得我的文章对你有用,请随意赞赏