本文主要介绍了Exception的一些用法以及相应的注意点,并中i但介绍了三种try语句的用法,同时还增加了一些对于Exception的扩展,让大家在使用Exception的时候也能体会到Java语言为什么要设计Exception
Exception 和 Error 都是继承了 Throwable 类
,在 Java 中只有 Throwable 类型的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制的基本组成类型。
所以我们应该重点关注的是Exception,本文也主要围绕Exception展开。
Checked Exception
必须要求用语句进行处理,比如用try-catch
包住或者是用throws
抛出异常。Unchecked Exception
则不要求必须进行处理。Unchecked Exception继承自RuntimeException。需要注意的是Error 和RuntimeException都是Unchecked Exception的父类。但我们一般使用并重点关注的是RuntimeException,Error因为无法控制,也无法在程序中被恢复,也不重点关注。Unchecked Exception就是所谓的运行时异常,类似NullPointerException、ArrayIndexOutOfBoundsException之类,通常是可以编码避免的逻辑错误,具体根据需要来判断是否需要捕获,不要求在写代码的时候强制要求进行捕获或者抛出。A方法调用B方法,B方法调用C方法,在执行C方法的时候遇到了异常,则会将异常往上抛,什么叫做往上抛?就是向调用C方法的上一级B方法抛,B方法接到这个异常,若这个异常能被catch住,也就说是catch里面的实例必须是这个异常的实例或者异常的父类的实例,异常被catch住以后就会停止往上抛异常,若没有catch住就会继续向A方法抛异常。若B方法catch住了异常,然后就会执行B方法未执行完的代码,然后B方法执行完以后就会出栈,然后执行A方法。C方法未执行完毕的代码不会再继续执行。
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
makeCall();
}
private static void makeCall() throws ClassNotFoundException {
// 检查异常需要强制处理,需要明确的throws或者catch
System.out.println("调用开始");
call2Exception();
System.out.println("调用结束");
}
private static void call2Exception() throws ClassNotFoundException {
System.out.println("Caller1.call2Exception开始");
try {
call3Exception();
} catch (ClassNotFoundException ex) {
System.out.println("got exception in Caller1: " + ex.getMessage());
}
System.out.println("Caller1.call2Exception结束");
}
private static void call3Exception() throws ClassNotFoundException {
System.out.println("Caller2.call3Exception开始");
callThrowException();
System.out.println("Caller2.call3Exception结束");
}
private static void callThrowException() throws ClassNotFoundException {
System.out.println("Caller3.callThrowException开始");
Class.forName("com.neverland.Rabbit");
System.out.println("Caller3.callThrowException结束");
}
}
执行结果如下图所示
若不在call2Exception()方法中将call3的抛出的异常catch住,异常就会一直往上抛,直至该线程停止。
需要注意的是,虽然我们能使用异常进行程序跳转,但是在程序设计的时候千万不要这么考虑。首先,异常本身就代表程序出了故障,用故障来实现正常的功能是不应该被考虑的,其次,异常的创建和处理是很耗费资源的,程序设计中如果用Exception来做跳转,这样的程序设计是有问题的。
抽象方法中声明抛出异常是接口方法签名的一部分
。也就是说,在实现该抽象方法的时候,不能超出抽象方法中定义的异常类型。实现方法可以不抛出异常,但若是要抛出异常,实现方法中抛出的异常必须是抽象方法中定义的异常或者是其子类。
public class MyException extends Exception {
public MyException() {
}
//传入参数:异常的信息
public MyException(String message) {
super(message);
}
//传入参数:异常的信息,造成异常的异常
public MyException(String message, Throwable cause) {
super(message, cause);
}
//传入参数:造成异常的异常
public MyException(Throwable cause) {
super(cause);
}
}
MyException
public class MyException extends Exception {
public MyException() {
System.out.println("执行MyException()构造方法");
}
//传入参数:异常的信息
public MyException(String message) {
super(message);
System.out.println("执行MyException(String message)构造方法");
}
//传入参数:异常的信息,造成异常的异常
public MyException(String message, Throwable cause) {
super(message, cause);
System.out.println("执行MyException(String message, Throwable cause)构造方法");
}
//传入参数:造成异常的异常
public MyException(Throwable cause) {
super(cause);
System.out.println("执行MyException(Throwable cause)构造方法");
}
}
public class Main {
public static void main(String[] args) {
try{
testMyException();
} catch (MyException e) {
e.printStackTrace();
}
}
public static void testMyException() throws MyException {
throw new MyException("这是我的自定义异常");
}
}
try{
}
catch(Exception ex){
//进行异常的代码,也可以在里面返回一个特殊的值,表示情况不对,有异常
}
finally{
}
注意:若采用写法一的写法,若第一个catch异常的实例对象已经包含了第二,第三个catch中的内容,当然就会报错。比如第一个catch的若是Exception,那后面的catch是不会被执行的,因为所有的异常都继承自Exception,第一个catch中捕获的异常已经包含了后面所有的异常,后面的catch已经没有了存在的意义。
所以,catch中的实例对象的关系只能是并行关系,即没有谁继承谁的说法,或者是从小到大的继承关系。
try {
throwMultiException(0);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
throwMultiException(0);
} catch (ClassNotFoundException | IOException e) {
e.printStackTrace();
}
和资源有关的异常处理往往都比较繁复,尤其是有多个资源的时候,需要考虑如何在出现Exception的情况下把资源回收掉,关掉。这时候Try-with-resources就能很好的胜任这样的工作,在程序出现异常的时候能自动帮我们关闭资源。
try(将需要关闭的资源放入括号里){
}catch(Exception ex){
}
AutoCloseable
是一个资源回收的接口,实现这个接口就可以让try语句自动关闭资源。
public String read() 方法中使用了随机数,模拟读取文件的时候出现的错误,一旦出现错误,就会抛出异常。若文件在读取过程中出现了异常,我们要关闭掉该文件,不然可能造成之前读取的文件丢失。
import java.io.IOException;
public class MyAutoClosableResource implements AutoCloseable {
private String resName;
private int counter;
public MyAutoClosableResource(String resName) {
this.resName = resName;
}
public String read() throws IOException {
counter++;
if (Math.random() > 0.1) {
return "You got lucky to read from " + resName + " for " + counter + " times...";
} else {
throw new IOException("resource不存在哦");
}
}
@Override
public void close() throws Exception {
System.out.println("资源释放了:" + resName);
}
}
public class TryWithResource {
public static void main(String[] args) {
//TODO try括号里面的内容是创建的资源
//todo res1和res2都是两个文件资源,在while循环里不停地读资源,但是不知道什么时候就会出错,一旦出错了要把这两个文件正常的关闭掉
try (
MyAutoClosableResource res1 = new MyAutoClosableResource("res1");
MyAutoClosableResource res2 = new MyAutoClosableResource("res2")
) {
while (true) {
System.out.println(res1.read());
System.out.println(res2.read());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
可以看到,在Main方法里面我们没有调用close方法,但是在程序执行出现异常时中却执行了close方法释放资源/这就是try-with-resource
这个语法带来的功能。
NullPointerException
:当尝试用为null的引用去调用方法的时候会出现这个问题。IndexOutOfBoundException
:数组索引出界ClassCastException
:当把一个类强行转化为另一个类的时候,如果两个类根本没有关系的时候就会出错ClassNotFoundException
:找不到类IOException
:和IO操作有关的Exception前三个是Unchecked Exception
,后两个是Checked Exception
类型
,错误信息
和出错时的调用栈
Java 的异常处理机制会带来两个相对昂贵的开销:
Checked exceptions: Java’s biggest mistake
原文链接点此
译文链接点此
养成一个勤于记录的习惯,记录,让生活更美好。