为什么要设计JAVA异常
早期没有异常处理机制时。错误的处理模式往往是会返回某个特殊值或设置某个标志,并且假定调用者将对这个返回值或标志进行检查,以判定是否发生了错误。然而随着时间的推移,人们发现,高傲的程序员们更倾向于:“错误也许会发生,但那是别人造成的,不关我的事”。所以不检查的情况就不足为奇了。有这样的一部分函数,往往处理这种检查很无聊,比如:println。如果的确每次调用方法都彻底检查,代码将会变得难以阅读。并且需要定义每种包含错误信息的数据结构作为函数的返回类型。
- 如果没有异常,那么就必须检查特定错误,并在程序中处理它,这样代码的复用率会降低。
- 有了异常机制,就可以把“描述在正常执行过程中做什么事”的代码和“出了问题怎么办”的代码分离。
所以设计异常的好处:能够解放判断返回值的方式,提供一种上抛的处理机制,降低代码的判断复杂度,并能保证捕获这个异常,集中处理,增强代码复用率。
异常类的结构![Throwable.jpeg](/uploads/projects/wangqingchun@ayii8n/d60c5f5a7ea023ff84229b49078e9d2a.jpeg)
不受检查异常Unchecked Exception
1.Error和RuntimeException。
2.不希望程序捕获或者无法处理的错误。
检查性异常CheckedException
1.非运行时异常。
2.用户程序可能捕获的异常情况或者程序可以处理的异常。
异常的概念
Java把异常当作对象来处理,并定义一个基类java.lang.Throwable作为所有异常的超类。
Throwable分成了两个不同的分支
一个分支是Error,它表示不希望被程序捕获或者是程序无法处理的错误。
一个分支是Exception,它表示用户程序可能捕捉的异常情况或者说是程序可以处理的异常。其中异常类Exception又分为运行时异常(RuntimeException)和非运行时异常。
Exception:在Exception分支中有一个重要的子类RuntimeException(运行时异常),该类型的异常自动为你所编写的程序定义ArrayIndexOutOfBoundsException(数组下标越界)、NullPointerException(空指针异常)、ArithmeticException(算术异常)、MissingResourceException(丢失资源)、ClassNotFoundException(找不到类)等异常,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生;而RuntimeException之外的异常我们统称为非运行时异常,类型上属于Exception类及其子类,从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。
Error:Error类对象由 Java 虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关。
Error和Exception的区别:Error通常是灾难性的致命的错误,是程序无法控制和处理的,当出现这些异常时,Java虚拟机(JVM)一般会选择终止线程;Exception通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这些异常。
异常处理机制
Java异常处理涉及到五个关键字,分别是:try、catch、finally、throw、throws。下面将骤一介绍,通过认识这五个关键字,掌握基本异常处理知识。
• try用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常时,异常就被抛出。
• catch用于捕获异常。catch用来捕获try语句块中发生的异常。
• finally 语句块总是会被执行。它主要用于回收在try块里打开的物力资源(如数据库连接、网络连接和磁盘文件)。只有finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。
• throw用于抛出异常。
• throws用在方法签名中,用于声明该方法可能抛出的异常。
多重catch
使用多重的catch语句:很多情况下,由单个的代码段可能引起多个异常。处理这种情况,我们需要定义两个或者更多的catch子句,每个子句捕获一种类型的异常,当异常被引发时,每个catch子句被依次检查,第一个匹配异常类型的子句执行,当一个catch子句执行以后,其他的子句将被旁路。
finally和return的顺序
If the try clause executes a return, the compiled code does the following: Saves the return value (if any) in a local variable. Executes a jsr to the code for the finally clause. Upon return from the finally clause, returns the value saved in the local variable.
如果try语句里有return,那么代码的行为如下: 1.如果有返回值,就把返回值保存到局部变量中 2.执行jsr指令跳到finally语句里执行 3.执行完finally语句后,返回之前保存在局部变量表里的值
1.finally块的语句在try或catch中的return语句执行之后返回之前执行
2.finally里的修改语句可能影响也可能不影响try或catch中 return已经确定的返回值,不可变实例不影响,可变类影响。
3.finally里也有return语句则覆盖try或catch中的return语句直接返回。
package com.emar.dealer.manager;
import java.util.HashMap;
import java.util.Map;
public class LocalTest {
public static void main(String[] args) {
LocalTest localTest = new LocalTest();
Integer result = localTest.mm1();
System.out.println(result);
// System.out.println(getMap().get("KEY").toString());
}
public Integer mm1(){
Integer i = 10;
try{
System.out.println("return : ");
return i += 3;
}catch (Exception e){
System.out.println("catch block");
}finally {
/**
* 不可变类,所以finally不会影响返回结果。
*/
i += 5;
System.out.println("finally : ");
}
return i;
}
public static Map<String, String> getMap() {
Map<String, String> map = new HashMap<String, String>();
map.put("KEY", "INIT");
try {
map.put("KEY", "TRY");
return map;
}
catch (Exception e) {
map.put("KEY", "CATCH");
}
finally {
map.put("KEY", "FINALLY");
/**
* finally的map.put("KEY", "FINALLY")起了作用,而map = null;却没起作用呢?
* 这就是Java到底是传值还是传址的问题了,简单来说就是:Java中只有传值没有传址,这也是为什么map = null这句不起作用。
* 因此return x实际返回的是return指令执行时,x在操作数栈顶的一个快照或者叫副本,而不是x这个值。
*/
map = null;
}
return map;
}
}
try-with-resource
public class TryWithResource {
public static void main(String[] args) {
try (BufferedInputStream bin = new BufferedInputStream(new FileInputStream(new File("test.txt")));
BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(new File("out.txt")))) {
int b;
while ((b = bin.read()) != -1) {
bout.write(b);
}
}
catch (IOException e) {
e.printStackTrace();
}
}
}
资源必须实现AutoClosable接口。该接口的实现类需要重写close方法
public class Connection implements AutoCloseable {
public void sendData() {
System.out.println("正在发送数据");
}
@Override
public void close() throws Exception {
System.out.println("正在关闭连接");
}
}
原理
编译器会帮我们添加finally并且还添加addSuppressed。
1.7开始,Throwable类新增了addSuppressed方法,支持将一个异常附加到另一个异常身上,从而避免异常屏蔽。
package com.codersm.trywithresource;
public class TryWithResource {
public TryWithResource() {
}
public static void main(String[] args) {
try {
Connection conn = new Connection();
Throwable var2 = null;
try {
conn.sendData();
} catch (Throwable var12) {
var2 = var12;
throw var12;
} finally {
if (conn != null) {
if (var2 != null) {
try {
conn.close();
} catch (Throwable var11) {
var2.addSuppressed(var11);
}
} else {
conn.close();
}
}
}
} catch (Exception var14) {
var14.printStackTrace();
}
}
}
解决了异常屏蔽的问题。