Java中的异常处理
# Java中的异常处理
在 Java 中,异常(Exception)是程序执行过程中发生的意外情况或错误条件。Java 提供了丰富的异常处理机制来帮助开发者捕获和处理这些异常,以确保程序的健壮性和可靠性。根据不同的特性,Java 中的异常可以分为以下几类:
# 分类
# Checked Exception(受检异常)
受检异常定义:这类异常是由编译器检查的异常。如果方法中可能会抛出这种类型的异常,那么该方法必须声明它会抛出这个异常,或者在其内部使用 try-catch
块来处理这个异常。
此外,受检异常指的都是不受程序直接控制的错误。它们通常都是由于与外部资源/网络交互而发生的,例如数据库问题、网络连接错误、文件丢失等问题,比如:IOException、SQLException、ClassNotFoundException、ParseException
这种异常有以下几个特点:
- 编译时检查。
- 必须显式处理。
- 包括所有继承自
Exception
类但不继承自RuntimeException
的类。
那有了所谓的受检异常有————非受检异常吗?答案是有的。
# Unchecked Exception(非受检异常)
非受检定义:这类异常不是由编译器检查的异常。它们通常是由于编程错误引起的,如空指针引用、数组越界等。这类异常不需要强制处理,但通常建议进行适当的处理,以提高程序的健壮性。
这种异常有以下几个特点:
- 运行时检查。
- 不需要显式处理。
- 包括所有继承自
RuntimeException
的类。
# Error(错误)
最后还有一种,它其实不是异常,而是错误。
它的定义:Error
是用于表示 JVM 自身的问题或其他严重问题的对象。这些问题通常是不可恢复的,例如内存不足、JVM 内部错误等。因此,一般情况下我们不会尝试去捕获或处理 Error
类型的异常。
它有以下几个特点:
- 表示严重的系统级问题。
- 大多数情况下不应被应用程序捕获。
发生错误时,通常不应该捕获 Error
,而是应该让程序终止,并考虑从外部解决问题,如增加内存、优化代码等。
# 关系
Java异常机制的核心是Throwable
类,所有的异常和错误都是这个类的子类。Throwable
类有两个重要的子类:Exception
(异常)和Error
(错误)。
Exception(异常):是程序可以处理的异常,通常是由程序中的错误引起的,比如数组越界、类型转换错误等。异常又可以分为受查异常(checked exception)和非受查异常(unchecked exception)。
# 如何处理
对于程序中的异常如何处理呢?通常有2种方式,
- 捕获-然后处理
- 抛出
# 一个问题
对于异常可以一直抛出,不处理吗?就传递到了最终调用者,main方法中,也只是抛出,不对其进行处理?
- 技术上是可以的,但程序最终会报错
从技术角度讲,Java 编译器允许你将受检异常一路向上抛,直到最顶层方法(例如 main
方法),并且这些方法也可以声明抛出这些异常。当程序运行时遇到未捕获的受检异常,JVM 会打印堆栈跟踪信息并终止程序。
所以对于抛出的异常我们应该进行处理或者延迟处理,否则它将结束你的程序。这样一来就可以避免以下错误:
- 用户体验:直接让异常导致程序终止,并打印堆栈跟踪信息,可能会给用户带来困惑,尤其是当用户不是开发人员时。良好的错误处理应该提供更友好的错误信息或采取适当的恢复措施。
- 健壮性:不处理异常意味着你的程序可能无法优雅地应对错误情况,从而影响其健壮性和可靠性。例如,在服务器应用程序中,你不希望一个未捕获的异常导致整个服务崩溃。
# try、catch。finally 的基础用法
# 第一个总结:
在 return 前会先执行 finally 语句块
,
public class TryDemo {
public static void main(String[] args) {
System.out.println(test());
}
public static int test() {
try {
return 1;
} catch (Exception e) {
return 2;
} finally {
System.out.print("3");
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
所以是先输出finally里的3,再输出return的1,所以结果为 31
# 第二个总结:
try返回前 先执行 finally
public class TryDemo {
public static void main(String[] args) {
System.out.println(test1());
}
public static int test1() {
try {
return 2;
} finally {
return 3;
}
}
}
2
3
4
5
6
7
8
9
10
11
12
try返回前先执行 finally,结果finally里不按套路出牌,直接 return了,自然也就走不到 try里面的 return了。输出结果为:3
# 第三个总结:
在执行finally之前,JVM会将i的结果暂存起来
public class TryDemo {
public static void main(String[] args) {
System.out.println(test1());
}
public static int test1() {
int i = 0;
try {
i = 2;
return i;
} finally {
i = 3;
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
【解析】在执行finally之前,JVM会将i的结果暂存起来,然后finally执行完毕后,会返回之前暂存的结果,而不是返回i,所以即使i已经被修改为3,最终返回的还是之前暂存的结果2
# try-catch会影响性能吗
如果不发生异常,则不会。
虽然在字节码层面生成了对应的字节码,但没发生异常,并不会去执行。所以不发生异常,不会影响性能。如果发生了,则会执行多余的字节码,相应的会降低性能。