Java异常处理
异常概述、体系
什么是异常
异常是程序在编译或执行的过程中可能出现的错误或问题,如:数索引越界异常、空指针异常、日期格式化异常等
为什么要学习异常
- 异常一旦出现,如果没有提前处理,程序就会退出JVM虚拟机而终止
- 正确处理异常,可以保障程序的安全、健壮性
- Error:错误,是一种特殊的类(java.lang.Error),与普通的Exception异常不同,Error是程序无法处理的错误,Error类及其子类是指我们程序处理不了的错误,这类异常往往代表JVM在运行过程中出现了问题,Error不应该由应用程序捕获和处理,而是由虚拟机自己处理
一些常见的Error异常,如
- IOError:IO错误
- Virtual MachineError:Java虚拟机运行错误(内存异出【OutOfMemoryError - 堆内存异出、StackOverflowError - 栈内存异出】)
- java.lang.NoClassDefFoundError:当虚拟机无法找到要加载的类时抛出的异常
- LinkageError:类加载过程出现链接错误时抛出的错误
异常体系
- 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
异常的默认处理流程
- 默认会在出现异常代码那里自动创建一个异常对象
- 异常会从方法中出现的点这里抛出给调用者,调用者最终抛出给JVM虚拟机
- 虚拟机收到异常对象后,现在控制台直接输出异常信息
- 直接从当前执行的异常点干掉程序
- 后续代码没有机会执行,因为程序已死
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,程序会正常执行输出