目录
什么是异常 , 异常的分类 ?
异常的基本概念
什么是Throwable ?
Throwable 类常用方法有哪些?
Exception 和 Error 有什么区别?
运行时异常与一般异常有什么区别?
常见的RuntimeException 有哪些 ?
NoClassDefFoundError 和 ClassNotFoundException 有什么区别?
举一个例子什么时候会出现NoClassDefFoundError
什么是自定义异常,如何使用自定义异常 ?
异常处理
Java异常处理的方式?
Java异常处理相关的几个关键字
throw 和 throws 两个关键字的区别吗
try-catch-finally 一系列问题
try-catch-finally 如何使用?
如何使用 try-with-resources 代替try-catch-finally?
try-with-resources的原理(todo)
finally 中的代码一定会执行吗?
try-catch-finally 中哪个部分可以省略?
try-catch-finally中,如果 catch中return了,finally还会执行吗?
使用异常注意事项
异常使用有哪些需要注意的地方
java try-catch 捕获异常真的会影响性能吗?
异常处理相关代码题
参考
什么是异常 , 异常的分类 ?
当程序发生不正确的行为,就代表程序出现了问题 这就代表发生了异常.
异常有很多类型,我们会将异常进行分类
Throwable是 Java 语言中所有错误或异常的基类。
Throwable 又分为Error和Exception,其中
- Error:Error 属于程序无法处理的错误 ,我们没办法通过 catch 来进行捕获不建议通过catch捕获 。例如 Java 虚拟机运行错误(Virtual MachineError)、虚拟机内存不够错误(OutOfMemoryError)、类定义错误(NoClassDefFoundError)等 。这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。
- Exception是程序问题导致的异常 , 可以通过 catch 来进行捕获,又分为两种:
- CheckedException 受检异常:编译器会强制检查并要求处理的异常。比如 IOexception,classNotFoundException
- RuntimeException 运行时异常:程序运行中出现异常,比如我们熟悉的空指针、数组下标越界等等
异常的基本概念
什么是Throwable ?
public class Throwable implements Serializable {}
Throwable是异常最顶级的异常类,他有两个重要的子类分别是
- error
- exception
这两个子类也分别包含了许多子类
Throwable 类常用方法有哪些?
- String getMessage(): 获取异常信息
- String toString(): 返回详细信息 ,包括获取异常类名和异常信息
- void printStackTrace(): 异常的堆栈跟踪信息 , 在控制台上打印异常类名和异常信息,以及异常出现在程序中的位置
- getStackTrace(): 返回一个堆栈跟踪元素数组,每个元素代表一个方法调用,通过 getStackTrace() 方法,我们可以打印异常发生的位置,并追踪调用了哪些方法,这在调试和定位问题时非常有用。
try{int a = 10 / 0;
}catch (Throwable throwable) {System.out.println(throwable.toString());// 返回异常的详细信息,包括异常名字+异常信息System.out.println(throwable.getMessage());// 返回异常信息System.out.println(throwable.getStackTrace());// 返回堆栈信息throwable.printStackTrace(); // 打印堆栈信息
}
Exception 和 Error 有什么区别?
在 Java 中,所有的异常都有一个共同的祖先 java.lang 包中的 Throwable 类。Throwable 类有两个重要的子类:
- Exception : 程序本身可以处理的异常,可以通过 catch 来进行捕获。程序设计的不完善而出现的问题,程序必须处理的问题, Exception 又可以分为 Checked Exception (受检查异常,必须处理) 和 Unchecked Exception (不受检查异常,可以不处理)。
- Error:Error 属于系统级别的错误,Java运行环境内部错误或者硬件问题,并不指望程序处理这样的问题,除了退出别无选择 ,它是由Java虚拟机抛出的。例如 Java 虚拟机运行错误(Virtual MachineError)、虚拟机内存不够错误(OutOfMemoryError)、类定义错误(NoClassDefFoundError),栈溢出(StackOverFlowError)等 。这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。
运行时异常与一般异常有什么区别?
首先Exception就代表程序出现了问题, 分为 运行时异常和一般异常
一般异常 : 编译器强制需要我们进行显式处理这种异常. 否则无法编译通过,这是一种强制的规范 .比如IOException,ClassNotfoundException
需要我们进行捕获或者向上抛出.
就比如FileNotFoundException(表示文件不存在)这个异常,目的就是告诉这个方法的调用者,这个方法并不一定可以成功,是有可能找不到文件的,所以就要对这种情况做出特殊的处理.
所以当我们希望我们的方法调用者,明确的处理一些特殊情况的时候,就应该使用受查异常.
运行时异常 : 是因为代码错误导致的,程序在运行的时候出现了问题.编译器并不强制让我们处理这种异常, 比如 空指针异常,数组越界异常, 算术异常等; 运行时异常无需显式捕获或声明,编译器不会强制要求我们处理这些异常。
这样的异常,我们一般可以理解为由于代码原因导致的,比如空指针,数组越界等. 所以只要代码写的没有问题,这些异常都是可以避免的,不需要我们显示的处理
常见的RuntimeException 有哪些 ?
- 空指针异常
- 非法参数异常
- 类型转换异常
- 算术异常
- 数组越界异常
- 字符串下标越界异常
此异常由 String 方法抛出,指示索引或者为负,或者超出字符串的大小。
- 数组存储异常
- 数字格式化异常
- 并发修改异常
RuntimeException 及其子类都统称为非受检查/运行时异常,常见的有 :
- NullPointerException(空指针异常):当对一个对象引用调用实例方法、访问实例字段时,对象引用为null时抛出该异常。
- IllegalArgumentException(非法参数异常):当传递给一个方法的参数无效或不合法时抛出该异常。
Date date = new Date("2022-09-10");
- IndexOutOfBoundsException(索引越界异常):当访问数组、集合或字符串的索引超出范围时抛出该异常。
- ArithmeticException(算术异常):当进行数学运算出现错误时抛出该异常,例如除以零。
- ClassCastException(类转换异常):当尝试将一个对象转换成不兼容的类型时抛出该异常。
Object object = new String("Hello");
Integer number = (Integer) object; // 尝试将字符串转换为整数
- NumberFormatException(数字格式化异常 字符串转换为数字格式错误,IllegalArgumentException的子类)
// 我们尝试将一个非数字字符串 "abc123" 解析为整数。由于该字符串不符合整数的格式,
// 因此无法将其解析为整数.在这种情况下,会抛出 NumberFormatException 异常。
int l = Integer.valueOf("sffff");
- ConcurrentModificationException(并发修改异常):当在使用迭代器遍历集合时,集合的结构被修改而导致的异常。
int abc = Integer.parseInt("abc");
System.out.println(abc);
- ArrayStoreException 试图将错误类型的对象存储到一个对象数组时抛出的异常。
public static void main(String[] args) {Object x[] = new String[3];x[0] = new Integer(0);
}
NoClassDefFoundError 和 ClassNotFoundException 有什么区别?
首先都是由于运行时找不到要加载的类导致的,有一些区别
总结一下两者的区别:
- NoClassDefFoundError是运行时错误 ,是发生在运行时找不到类导致的,ClassNotFoundException是编译时异常(checked 异常,需要显式处理),在编译时无法找到类导致的(这个类在类路径中没有被找到)
- NoClassDefFoundError通常是在编译的时候,类存在,运行时不存在 可能是因为该类从运行时的类路径中移除或者没有添加, 缺少jar文件 .jar包出现损坏或者篡改. ClassNotFoundException通常是由代码中显式调用了不存在的类或导入了错误的类名导致的。可以检查代码中的类名是否拼写正确、是否导入了正确的类等。
举一个例子什么时候会出现NoClassDefFoundError
当出现NoClassDefFoundError时,意味着某个在编译时存在的类在运行时无法找到。以下是一个简单的例子来说明NoClassDefFoundError的情况:
假设我们有两个Java类:MainClass和HelperClass。MainClass依赖于HelperClass,而HelperClass在编译时存在,但在运行时无法找到。
MainClass.java:
public class MainClass {public static void main(String[] args) {HelperClass helper = new HelperClass();helper.doSomething();}
}
HelperClass.java:
public class HelperClass {public void doSomething() {System.out.println("Doing something...");}
}
在编译时,这两个类被正确编译为MainClass.class和HelperClass.class。然而,如果在运行时只提供MainClass.class文件,而缺少了HelperClass.class文件,那么当运行MainClass时,就会抛出NoClassDefFoundError。
可以通过以下步骤来模拟并触发NoClassDefFoundError异常:
- 将HelperClass.class从运行时的类路径中移除,只保留MainClass.class。
- 使用命令行或IDE来运行MainClass。
运行时,将会抛出类似以下的NoClassDefFoundError错误:
Exception in thread "main" java.lang.NoClassDefFoundError: HelperClassat MainClass.main(MainClass.java:4)Caused by: java.lang.ClassNotFoundException: HelperClassat java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583)at java.base/jdk.internal.loader.ClassLoaders\$AppClassLoader.loadClass(ClassLoaders.java:178)at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)... 1 more
这表明虚拟机无法找到HelperClass类,并且导致NoClassDefFoundError异常的抛出。
要解决这个问题,可以确保在运行时的类路径中包含所有必需的类文件,并正确配置依赖关系。
什么是自定义异常,如何使用自定义异常 ?
自定义异常就是开发人员自己定义的异常,一般可以通过继承Exception的子类的方式来实现.
编写自定义异常实际上是继承一个API标准异常类,用新定义的异常处理信息覆盖原有信息的过程.
这种用法在Web开发中还比较常见,一般用来自定义业务异常,如余额不足,重复提交等,这种自定义异常有业务含义,更容易让上层理解和处理.
异常处理
Java异常处理的方式?
异常处理方式主要是有2种方式 :
- 自己进行处理,使用try catch进行捕获
- 向上抛出异常,让调用者来处理
如果自己知道是如何处理,那么自己就捕获异常进行处理. 否则就向上抛,让调用者来处理.
向上抛出异常主要有两个关键字 :
加在方法上 : throws 异常类
方法体中 : throw 异常对象
3. 对于SpringBoot项目来说我们还可以通过异常处理器来进行捕获项目中出现的异常,也就是如果项目中有异常就抛异常就可以, 最终都会被异常处理器锁捕获到,然后进行统一的异常处理.
Java异常处理相关的几个关键字
try , catch , finnaly , throw , throws
try : 我们可以把可能出现异常的代码放到try块中
catch : 使用catch捕获可能发生的异常,如果捕获了,可以对异常进行处理
finaly : 不管代码执行发生了什么,我们都要执行的一段代码可以放在finally块中
throws 加在方法上 throws 异常类,异常类......
throw 在方法体内部, throw 异常对象
throw 和 throws 两个关键字的区别吗
- throws 加载方法上,表示这个方法可能抛出的异常,可以声明多个异常类
- throw 适用于在方法体上抛异常, throw后面跟对象,只能是一个异常对象.
try-catch-finally 一系列问题
try-catch-finally 如何使用?
- try 关键字后面会跟一个大括号 {} , 我们把一些可能发生异常的代码放到大括号里
- try 块后面一般会跟 catch 块,用来处理发生异常的情况
当 try 块中的某一行代码发生异常时,之后的代码就不再执行,而是会跳转到异常对应的 catch 块中,catch 用来捕获不同类型的异常并做相应的处理,如果一个 try 块后面跟了多个与之关联的 catch 块,那么应该把特定的异常放在前面,通用型的异常放在后面,不然编译器会提示错误
- 异常不一定会发生,为了保证发不发生异常都能执行一些代码,就会跟一个 finally 块。
- finally 块不是必选项,有 try 块的时候不一定要有 finally 块。但是有finally块时前面必须有 try 块. 如果 finally 块中的代码可能会发生异常,也应该使用 try-catch 进行包裹。
- 即便是 try 块中执行了 return、break、continue 这些跳转语句,finally 块也会被执行。
但finaly也不一定执行
- 遇到了死循环。
- 执行了 System. exit() 这行代码。
如何使用 try-with-resources 代替try-catch-finally?
面对必须要关闭的资源,我们总是应该优先使用 try-with-resources 而不是try-finally。代码更简短,更清晰,产生的异常对我们也更有用。try-with-resources语句让我们更容易编写必须要关闭的资源的代码.
- 使用try-with-resources 就是在try后面加上个括号,资源都放在括号中,这样就会自动关闭资源.
使用try-finally容易导致丢失异常堆栈信息,使得我们无法正确的定位异常信息.
而用try-with-resources 就可以避免这种情况.
try-with-resources的原理(todo)
try-with-resources语句能够确保每一个资源在语句结束时被关闭,但是有一个前提条件,那就是这个资源必须实现Java.Lang.AutoCloseable or java.io.Closeable 接口,才可以被执行.
try语句中越是最后使用的资源,越是最早被关闭的. 并且还用了addSuppressed/方法,确保不会丢失任何异常.
为 Throwable
类新增了 addSuppressed
方法,支持将一个异常附加到另一个异常身上,从而避免异常屏蔽。
finally 中的代码一定会执行吗?
finally
块在Java中通常用于处理善后工作,如释放资源。
通常情况下, 如果在try块被执行并且程序正常的运行,那么finally块的内容一定会被执行.
否则finally块就不一定执行.
比如, 如下情况 :
- 我们直接kill 杀死掉进程
- 在try或者catch中发生了死循环
- 虚拟机发生了崩溃
- 程序遇到无法恢复的错误,如
OutOfMemoryError
- 调用
System.exit()
方法
System.exit()
方法用于终止当前运行的Java虚拟机。一旦Java虚拟机被终止,任何后续的代码,包括finally
块,都不会被执行。
try-catch-finally 中哪个部分可以省略?
当想要使用try-catch-finally的时候,有几种组合
- try-catch
- try-finally
- try-多个catch捕获异常-finally
可以省略catch或者finally。catch和finally不可以同时省略。
try-catch-finally中,如果 catch中return了,finally还会执行吗?
try
或catch
块中有return
语句,finally
块仍然会执行, 如果finally
块中也有return
语句,那么它将覆盖try
或catch
块中的任何return
语句,成为方法的最终返回值。
使用异常注意事项
异常使用有哪些需要注意的地方
- 不要把异常定义为静态变量,这样可能会让堆栈信息错乱.我们应该每次都new一个异常对象抛出
因为可能发生,每一次抛出异常,都会覆盖静态变量中存储的先前异常信息
- 抛出的异常信息要更加详细
- 抛出的异常类要更加具体, 比如是算术异常,我们就抛出算术异常信息,类型转换异常我们就抛出异常转换异常,而不是为了省事,抛出Exception
- 使用日志打印异常之后就不要再抛出异常了(两者不要同时存在一段代码逻辑中)。
java try-catch 捕获异常真的会影响性能吗?
异常处理相关代码题
参考
Java基础常见面试题总结(下) | JavaGuide