JAVA的异常解析
异常结构图
Throwable
在 Java 中,所有的异常都有一个共同的祖先 Throwable(可抛出)。Throwable 指定代码中可用异常传播机制通过 Java 应用程序传输的任何问题的共性。
Error
- Error指的是Java虚拟机无法解决的严重问题,大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM出现的问题。
- 如
AWTError
,当发生严重的抽象窗口工具包错误时抛出 - 如
VirtualMachineError
,抛出以指示Java虚拟机损坏或已耗尽继续运行所需的资源。- 其子类
OutOfMemoryError
,内存溢出,当Java虚拟机由于内存不足而无法分配对象时抛出,并且垃圾收集器无法提供更多内存。 - 子类
StackOverflowError
,栈溢出,当由于应用程序递归太深而发生堆栈溢出时抛出。
- 其子类
Exception
- Exception是指异常,定义上分为运行时异常
RuntimeException
(不可查异常unchecked Exceptions
)和编译时异常(也称可检查异常checked Exceptions
)- 运行时异常的特点是Java编译器不会检查它,是由程序逻辑错误引起的。当程序中可能出现这类异常,即使没有用try-catch语句捕获它或者没有用throws子句声明抛出它,也会编译通过。
- 如
IndexOutOfBoundsException
,越界异常,抛出以指示某种排序(如数组、字符串或向量)的索引超出范围。 ArithmeticException
,算术异常,发生异常算术条件时抛出。例如,整数“除零”抛出该类的一个实例。NullPointerException
,空指针异常IllegalArgumentException
,非法参数异常
- 如
- RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如
IOException
等以及用户自定义的Exception异常,一般情况下不自定义检查异常。
- 运行时异常的特点是Java编译器不会检查它,是由程序逻辑错误引起的。当程序中可能出现这类异常,即使没有用try-catch语句捕获它或者没有用throws子句声明抛出它,也会编译通过。
捕捉异常时常用方法
public static void main(String[] args) {
int[] a=new int[2];
try {
for (int i = 0; i < 3; i++) {
a[i]=i;
}
}catch (Exception e){
System.err.println(e);
//返回抛出异常的原因。如果 cause 不存在或未知,则返回 null。
System.err.println(e.getCause());
//返回异常的消息信息。
System.err.println(e.getMessage());
//对象的堆栈跟踪输出至错误输出流,作为字段 System.err 的值。
System.err.println(e.getStackTrace());
}
}
处理异常机制
Java规定:对于可查异常(
checked Exceptions
)必须捕捉、或者声明抛出。允许忽略不可查的RuntimeException和Error。简单地说,异常总是先被抛出,后被捕捉的。
抛出异常
throws
-
如果一个方法可能会出现异常,但没有能力处理这种异常,可以在方法声明处用throws子句来声明抛出异常。例如汽车在运行时可能会出现故障,汽车本身没办法处理这个故障,那就让开车的人来处理。
-
throws语句用在方法定义时声明该方法要抛出的异常类型,可以抛出多个异常类型
-
方法将不对这些类型及其子类类型的异常作处理,而抛向调用该方法的方法,由他去处理。
-
使用throws关键字将异常抛给调用者后,如果调用者无法处理该异常,可以继续向上抛出,但最终要有能够处理该异常的调用者。如pop方法没有处理异常NegativeArraySizeException,而是由main函数来处理。
-
import java.lang.Exception; public class TestException { static void pop() throws NegativeArraySizeException { // 定义方法并抛出NegativeArraySizeException异常 int[] arr = new int[-3]; // 创建数组 } public static void main(String[] args) { // 主方法 try { // try语句处理异常信息 pop(); // 调用pop()方法 } catch (NegativeArraySizeException e) { System.out.println("pop()方法抛出的异常");// 输出异常信息 } } }
throw
- throw总是出现在函数体中,用来抛出一个Throwable类型的异常。程序会在throw语句后立即终止,它后面的语句执行不到,
package Test;
import java.lang.Exception;
public class TestException {
static int quotient(int x, int y) throws MyException { // 定义方法抛出异常
if (y < 0) { // 判断参数是否小于0
throw new MyException("除数不能是负数"); // 异常信息
}
return x/y; // 返回值
}
public static void main(String args[]) { // 主方法
int a =3;
int b =0;
try { // try语句包含可能发生异常的语句
int result = quotient(a, b); // 调用方法quotient()
} catch (MyException e) { // 处理自定义异常
System.out.println(e.getMessage()); // 输出异常信息
} catch (ArithmeticException e) { // 处理ArithmeticException异常
System.out.println("除数不能为0"); // 输出提示信息
} catch (Exception e) { // 处理其他异常
System.out.println("程序发生了其他的异常"); // 输出提示信息
}
}
}
class MyException extends Exception { // 创建自定义异常类
String message; // 定义String类型变量
public MyException(String ErrorMessagr) { // 父类方法
message = ErrorMessagr;
}
public String getMessage() { // 覆盖getMessage()方法
return message;
}
}
捕获异常
try、catch和finally
try {
// 可能会发生异常的程序代码
} catch (Type1 id1) {
// 捕获并处理try抛出的异常类型Type1
} catch (Type2 id2) {
// 捕获并处理try抛出的异常类型Type2
} finally {
// 无论是否发生异常,都将执行的语句块
}
- try 块:用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。
- catch 块:用于处理try捕获到的异常。一旦某个catch捕获到匹配的异常类型,将进入异常处理代码。一经处理结束,就意味着整个try-catch语句结束。其他的catch子句不再有匹配和捕获异常类型的机会。
- finally 块:无论是否捕获或处理异常,finally块里的语句都会被执行。**当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。**在以下4种特殊情况下,finally块不会被执行:
- 在finally语句块中发生了异常。
- 在前面的代码中用了System.exit()退出程序。
- 程序所在的线程死亡。
- 关闭CPU。
执行顺序
- 当try没有捕获到异常时:try语句块中的语句逐一被执行,程序将跳过catch语句块,执行finally语句块和其后的语句;
- 当try捕获到异常,catch语句块里没有处理此异常的情况:此异常将会抛给JVM处理,finally语句块里的语句还是会被执行,但finally语句块后的语句不会被执行;
- 当try捕获到异常,catch语句块里有处理此异常的情况:
- ①在try语句块中是按照顺序来执行的,当执行到某一条语句出现异常时,程序将跳到catch语句块
- ②与catch语句块逐一匹配,找到与之对应的处理程序,其他的catch语句块将不会被执行,而try语句块中,出现异常之后的语句也不会被执行
- ③catch语句块执行完后,执行finally语句块里的语句,最后执行finally语句块后的语句;
try、catch和finally中有return/throw
- finally有return/throw,会全面覆盖、替代掉catch里的return/throw。
- catch有return且finally无return,先执行 catch 中非 return 语句,再执行 finally 语句,最后执行 catch 中 return 语句。
- try有return语句,后续还有return语句,分为以下三种情况:
- 如果finally中有return语句,则会将try中的return语句”覆盖“掉,直接执行finally中的return语句,得到返回值,这样便无法得到try之前保留好的返回值。
- 如果finally中没有return语句,也没有改变要返回值,则执行完finally中的语句后,会接着执行try中的return语句,返回之前保留的值。
- 如果finally中没有return语句,但是改变了要返回的值,这里有点类似与引用传递和值传递的区别,分以下两种情况,:
- 如果return的数据是基本数据类型或文本字符串,则在finally中对该基本数据的改变不起作用,try中的return语句依然会返回进入finally块之前保留的值。
- 如果return的数据是引用数据类型,而在finally中对该引用数据类型的属性值的改变起作用,try中的return语句返回的就是在finally中改变后的该属性的值。