6 异常处理
Head First Java 和 AcWing Java课程做的总结6。
当在编写可能有异常的方法时,需要处理异常状况的程序。
执行期的大多数问题来自程序的错误,而这些错误应该在开发阶段解决掉,但是某些错误还是有可能在执行期出现,例如找不到文件、服务器出现故障。
6.1 Error
与Exception
的区别
Error
是程序无法处理的错误,比如OutOfMemoryError
、ThreadDeath
等。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。此类异常是程序的致命异常,是无法捕获处理的。
Exception
是程序本身可以处理的异常,这种异常分两大类运行时异常和非运行时异常。 程序中应当尽可能去处理这些异常。
6.2 异常处理(exception-handing)机制
-
是个简捷、轻量化的执行期间例外状况处理方式,它能够将处理错误状况的程序代码摆在一个容易阅读的位置。
-
需要知道所调用的方式是由风险的,也就是方法可能产生异常。看该方法的声明是否有
throws
语句就知道了。 -
try-catch
块-
把有风险的程序代码包含在
try-catch
块中,编译器会知道所调用的方法有风险,并且也已经准备好处理它。 -
try{ //把有风险的程序放在try中 }catch(Exception ex){ //处理异常的程序 }
-
-
try-catch-finally
块-
finally
块是用来存放不管有没有异常都得执行的程序。 -
如果
try
或catch
块有return
指令,finally
还是会执行。 -
try{ //把有风险的程序放在try中 }catch(Exception ex){ //处理异常的程序 }finally{ //都得做的事 }
-
-
duck
异常- 如果调用服务的程序
b
不想处理异常,可以把它duck
掉来避开。让调用程序b
的程序a
来catch
该异常。没有用try-catch
处理有风险的方法,则调用方b
也成有风险的了。 - 只需要让程序
b
声明成再throws
此异常即可。 - 方法抛出异常时,方法会从栈上立即被取出,而异常会再度丢给栈上的方法,也就是调用方。
duck
只是在踢皮球,早晚得有人来处理这件事,若main()
也duck掉异常,则Java虚拟机直接死机。
- 如果调用服务的程序
-
异常处理规则:
catch
与finally
不能没有try
;try
一定要有catch
或finally
,即不能只有try
;try
与catch
之间不能有程序;- 只带有
finally
的try
必须要声明异常。
6.3 异常对象
异常是一种Exception
类型的对象。
- throw: 在函数内抛出一个异常。
- throws:在函数定义时抛出一些可能的异常。
程序代码会抓住异常,而抛出异常的是——声明有异常的方法,也就是该方法把异常抛出来。
方法可以抓住其他方法所抛出的异常。异常总是会丢给调用方。
举例:
//有风险、会抛出异常的程序代码
public void takeRisk() throws BadException{
if(abandonAllHope){
throw new BadException();
}
}
//调用该方法的程序代码
public void crossFinger(){
try{
anObject.takeRisk();
}catch(BadException ex){
System.out.println("Aaargh");
ex.printStackTrace();
}
}
内置异常方法:
方法 | 说明 |
---|---|
public String getMessage() |
返回关于发生的异常的详细信息。这个消息在Throwable 类的构造函数中初始化了 |
public Throwable getCause() |
返回一个 Throwable 对象代表异常原因。 |
public String toString() |
返回此 Throwable 的简短描述。 |
public void printStackTrace() |
将此 Throwable 及其回溯打印到标准错误流 |
public StackTraceElement [] getStackTrace() |
返回一个包含堆栈层次的数组。下标为0的元素代表栈顶,最后一个元素代表方法调用堆栈的栈底。 |
public Throwable fillInStackTrace() |
用当前的调用栈层次填充Throwable 对象栈层次,添加到栈层次任何先前信息中。 |
6.4 异常分类
异常继承树:
不检查异常:
- 上图灰色,都继承自
java.lang.RuntimeException
RuntimeException
被称为不检查异常,可以自己抛出来并抓住它们,但是没有这个必要,编译器也不管。- 大部分的
RuntimeException
都是因为程序逻辑的问题,而不是以无法预测或防止的方式出现的执行期失败状况。不检查异常一般是程序代码写的不够严谨而导致的问题,可以通过修改代码来规避。 try-catch
是用来处理真正的异常。而不是程序的逻辑错误,该快要做的事恢复的尝试,或者至少列出错误信息。- 任何继承过
RuntimeException
的类都不会受编译器是否声明它会抛RuntimeException
的检查,同样的,也不会关调用方是否认识到可能会在运行期间遇到异常。 - 常见的运行时异常:空指针异常
(NullPointerException)
、除零异常(ArithmeticException)
、数组越界异常(ArrayIndexOutOfBoundsException)
等;
检查异常:
- 上图白色,都继承自·
java.lang.Exception
- 所有不是
RuntimeException
派生的Exception
都是检查型异常。 - 编译器所关心得异常,程序必须要意识到这种异常可能的存在。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。
- 检查性异常必须使用try catch或者throws等关键字进行处理,否则编译器会报错;
- 常见的检查异常:输入输出异常
(IOException)
、文件不存在异常(FileNotFoundException)
、SQL语句异常(SQLException)
等。
总的来说:
-
检查性异常: 不处理编译不能通过;
非检查性异常:不处理编译可以通过,如果有抛出直接抛到控制台。
-
运行时异常: 就是非检查性异常
非运行时异常: 就是检查性异常
6.5 内置异常类
非检查性异常:
异常 | 描述 |
---|---|
ArithmeticException |
当出现异常的运算条件时,抛出此异常。例如,一个整数”除以零”时,抛出此类的一个实例。 |
ArrayIndexOutOfBoundsException |
用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。 |
ArrayStoreException |
试图将错误类型的对象存储到一个对象数组时抛出的异常。 |
ClassCastException |
当试图将对象强制转换为不是实例的子类时,抛出该异常。 |
IllegalArgumentException |
抛出的异常表明向方法传递了一个不合法或不正确的参数。 |
IllegalMonitorStateException |
抛出的异常表明某一线程已经试图等待对象的监视器,或者试图通知其他正在等待对象的监视器而本身没有指定监视器的线程。 |
IllegalStateException |
在非法或不适当的时间调用方法时产生的信号。换句话说,即 Java 环境或 Java 应用程序没有处于请求操作所要求的适当状态下。 |
IllegalThreadStateException |
线程没有处于请求操作所要求的适当状态时抛出的异常。 |
IndexOutOfBoundsException |
指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。 |
NegativeArraySizeException |
如果应用程序试图创建大小为负的数组,则抛出该异常。 |
NullPointerException |
当应用程序试图在需要对象的地方使用 null 时,抛出该异常。 |
NumberFormatException |
当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。 |
SecurityException |
由安全管理器抛出的异常,指示存在安全侵犯。 |
StringIndexOutOfBoundsException |
此异常由 String 方法抛出,指示索引或者为负,或者超出字符串的大小。 |
UnsupportedOperationException |
当不支持请求的操作时,抛出该异常。 |
检查性异常:
异常 | 描述 |
---|---|
ClassNotFoundException |
应用程序试图加载类时,找不到相应的类,抛出该异常。 |
CloneNotSupportedException |
当调用 Object 类中的 clone 方法克隆对象,但该对象的类无法实现 Cloneable 接口时,抛出该异常。 |
IllegalAccessException |
拒绝访问一个类的时候,抛出该异常。 |
InstantiationException |
当试图使用 Class 类中的 newInstance 方法创建一个类的实例,而指定的类对象因为是一个接口或是一个抽象类而无法实例化时,抛出该异常。 |
InterruptedException |
一个线程被另一个线程中断,抛出该异常。 |
NoSuchFieldException |
请求的变量不存在 |
NoSuchMethodException |
请求的方法不存在 |
6.6 多重异常&异常多态
方法可以抛出多个异常,但该方法的声明必须要有含有全部可能的检查异常(若存在共同父类,则声明父类即可)。
处理多重异常:
-
要注意
catch
出现的先后顺序; -
public class Laundry{ public void doLaundry() throws PantsException, LingerieException{ } } public class Foo{ public void go(){ Laundry laundry = new Laundry(); try{ laundry.doLaundry(); }catch(PantsException pex){ }catch(LingerieException lex){ } } }
-
有多个
catch
块时,要从小排到大。即异常继承树中越底层,越在catch
中先出现。大的放在上面会导致无法通过编译。
异常的多态性:
-
异常也是对象,所以也能够以多态的方式来引用;
-
这样不必明确的声明每个可能抛出的异常,可以只声明父类即可。
-
//以异常的父型来声明会抛出的异常 public void doLaundry() throws ClothingException{ //声明父类异常可让抛出任何子类异常 } //以抛出异常父型来catch异常 ry{ laundry.doLaundry(); }catch(ClothingException cex){ }
-
但是能够用父型来这么做,不代表就一个这么做
- 全部异常都可以用
catch(Exception ex)
这样处理,但是这样完全搞不清哪里出错。
- 全部异常都可以用
6.7 try-with-resources
JDK7 之后,Java 新增的 try-with-resource 语法糖来打开资源,并且可以在语句执行完毕后确保每个资源都被自动关闭 。
try 用于声明和实例化资源,catch 用于处理关闭资源时可能引发的所有异常。
import java.io.*;
public class Main {
public static void main(String[] args) {
String line;
try (
BufferedReader br = new BufferedReader(new FileReader("input.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"));
) {
while ((line = br.readLine()) != null) {
System.out.println("Line => " + line);
bw.write("copy: " + line + "\n");
}
bw.flush();
} catch (IOException e) {
System.out.println("IOException in try block =>" + e.getMessage());
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?