菜鸟话Java---Java异常

异常分类

如图是Java异常层次结构的一个简化图:

所有的异常都由Throwable继承而来,之后分为ErrorException两部分。

Error

Error类层次结构描述了Java运行时系统内部错误和资源耗尽错误。应用程序不应该抛出
这种类型的错误。对于这种错误我们无能为力,因此我们不需要关注这种错误。

Exception

根据程序是否有问题可以把Exception分成两类:由程序错误导致的异常属于RuntimeException;由
像I/O错误这类和程序无关的问题导致的异常属于其他异常。可以说,如果出现了RuntimeException异常,
那么就一定是你的问题。

Java语言规范将派生于ErrorRuntimeException的所有异常称为非受查异常,所以其他的异常称为
受查异常。

异常抛出的情况

  • 调用一个抛出受查异常的方法
  • 程序运行过程发现错误,并利用throw语句抛出一个受查异常
  • 程序出现错误
  • Java虚拟机和运行时库出现内部错误报告

抛出异常

关键字throwsthrow用来抛出异常,示例:

1
2
3
4
5
public void deposit(double amount) throws RemoteException
{
// Method implementation
throw new RemoteException();
}

注意:

  • 如果在子类中覆盖了超类的一个方法,子类方法中声明的受查异常不能比超类方法中声明的异常更通用。如果超类
    方法没有抛出任何受查异常,子类也不会抛出任何受查异常。
  • 一旦方法抛出异常,这个方法就不可能返回到调用者

创建异常类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//继承检查异常,抛出需要检查
public class MyException extends Exception{

public MyException(String error) {
super(error);
}
}

public static void main(String[] args) {
try {
throw new MyException("出了什么错");
} catch (MyException e) {
e.printStackTrace();
}
}

//继承非受查异常,抛出不需要检查
public class MyException extends RuntimeException{

public MyException(String error) {
super(error);
}
}

public static void main(String[] args) {
throw new MyException("出了什么错");
}

运行结果:

1
2
MyException: 出了什么错
at Main.main(Main.java:6)

捕获异常

使用 try 和 catch 关键字可以捕获异常。try/catch 代码块放在异常可能发生的地方。
try/catch代码块中的代码称为保护代码,使用 try/catch 的语法如下:

1
2
3
4
5
6
7
try
{
// 程序代码
}catch(ExceptionName e1)
{
//当发生异常时,会调用这里的代码
}

多重捕获块

一个 try 代码块后面跟随多个 catch 代码块的情况就叫多重捕获
多重捕获块的语法如下所示:

1
2
3
4
5
6
7
8
9
try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}

可以在 try 语句后面添加任意数量的 catch 块。
如果保护代码中发生异常,异常被抛给第一个 catch 块。
如果抛出异常的数据类型与 ExceptionType1 匹配,它在这里就会被捕获。
如果不匹配,它会被传递给第二个 catch 块。
如此,直到异常被捕获或者通过所有的 catch 块。

java7以后可以放在一个catch中,例如:

1
2
3
4
5
try{
// 程序代码
}catch(异常类型1 |异常类型2 异常的变量名){//当捕获多个变量时,异常变量隐含为final变量,不能为它赋值
// 程序代码
}

注意:只有当捕获的异常类型彼此之间不存在子类关系时才需要这个特性

异常方法

Throwable 类的主要方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public String getMessage()
返回关于发生的异常的详细信息。这个消息在Throwable 类的构造函数中初始化了。

public Throwable getCause()
返回一个Throwable 对象代表异常原因。

public String toString()
使用getMessage()的结果返回类的串级名字。

public void printStackTrace()
打印toString()结果和栈层次到System.err,即错误输出流。

public StackTraceElement [] getStackTrace()
返回一个包含堆栈层次的数组。下标为0的元素代表栈顶,最后一个元素代表方法调用堆栈的栈底。

public Throwable fillInStackTrace()
用当前的调用栈层次填充Throwable 对象栈层次,添加到栈层次任何先前信息中

finally

finally 关键字用来创建在 try 代码块后面执行的代码块。
无论是否发生异常,finally 代码块中的代码总会被执行。
在 finally 代码块中,可以运行清理类型等收尾善后性质的语句。
finally 代码块出现在 catch 代码块最后,语法如下:

1
2
3
4
5
6
7
8
9
try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}finally{
// 程序代码
}

注意:当finally子句包含return语句时,在方法返回前,finally子句的内容
会被执行,这里的return语句的返回值会覆盖原始的返回值,例如:

1
2
3
4
5
6
7
8
public static int f(int n){
try{
int r=n*n;
return r;
}finally{
if(n==2)return 0;
}
}

如果调用f(2),那么try语句块的计算结果为r = 4,并执行return语句。
然而,在方法真正返回时,还要执行finally子句。finally子句使得方法返回
0,覆盖了原来的方法。

带资源的try

语法:

1
2
3
try(Resure res = ....){
//具体代码
}

使用带资源的try语句,不用自己关闭资源。