首页 > 代码库 > Java异常处理

Java异常处理

1.捕获异常

当我们程序遇到异常时,程序会被终止。
如果我们不希望程序被终止怎么办,很简单,可以把有可能发生异常的代码块放进try语句里面,然后在catch语句里面捕获可能发生的异常类型。
try-catch语句:要捕获所有可能发生的异常类型,同一种类型的异常只用一个catch就可以了。

try{
//可能发生异常的代码块
}
catch(Exception1 e){
捕获Exception1类型的异常
}
catch(Exception2 e){
捕获Exception2类型的异常
}
catch(Exception3 e){
捕获Exception3类型的异常
}
//etc...

PS:try语句和catch语句都必须加花括号({}),即使里面只有一条语句。try语句不能单独存在,后面必须接catch语句或者finally语句
try-finally语句:希望程序发生异常时,仍然执行某一段代码时可以用try-finally语句

try{
//可能发生异常的代码块
}
finally{
//不管try中代码块中是否产生异常,finally中的代码总是被执行
}

另外还有try-catch-finally语句,它相当于上面两种语句的结合。此时同样是在catch语句中捕获异常,不管try中语句是否产生异常或者异常是否被捕获,finally语句总是被执行。

2.抛出异常

2.1throws抛出异常

用throws声明抛出异常时,表示将异常交给上一级调用者处理。throws抛出的是异常类,放在方法签名的后面。

void play(){
	try {
		pain();
	} catch (Throwable e) {
		e.printStackTrace();//打印异常跟踪信息
	}
}
void pain()throws Throwable{
        //产生异常代码
}

play()方法调用pain()方法,pain()方法产生异常,但是没有处理,向上抛给调用它的方法,所以在play()方法中要处理这个异常。
那么如果在main()方法中有可能异常,我们也用throws抛出异常会怎么样?

public class MyException {
	public static void main(String[] args) throws Throwable{
			String str="hello";
			str.charAt(10);
	}
}

输出结果:
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 10
at java.lang.String.charAt(Unknown Source)
at com.t.MyException.main(MyException.java:6)

我们清楚地看到程序报错了。这是因为此时main()方法没有上级调用者,产生的异常会交给Java编译器或虚拟机处理。非运行时异常编译器处理,不让程序编译通过。运行时异常Java虚拟机处理,JVM对异常的对异常的处理方法是,打印异常的跟踪栈信息,并终止程序的运行。

PS:如果上一级调用者,没有处理被调用抛出的异常时,程序也会被终止。

2.2throw抛出异常

用throw抛出异常,抛出的不是一个异常类,而是一个异常实例。在程序中的含义是自动产生一个异常。

public class MyException {
	public static void main(String[] args) {
		throw new StringIndexOutOfBoundsException();
	}
}

程序输出结果是:
Exception in thread "main" java.lang.StringIndexOutOfBoundsException
at com.t.MyException.main(MyException.java:5)
这说明此时程序产生了一个StringIndexOutOfBoundsException的异常。

3.异常丢失

下面我们来看看这个程序。

public class MyException {
	public void play() throws Throwable{
		try{
			String str="lavor_zl";
			str.charAt(10);
			pain();
		}catch(StringIndexOutOfBoundsException e){
			throw new StringIndexOutOfBoundsException();
		}catch(ArrayIndexOutOfBoundsException e){
			throw new ArrayIndexOutOfBoundsException();
		}
	}
	public void pain() throws ArrayIndexOutOfBoundsException{
		try{
			int[] array=new int[10];
			array[12]=2014;
		}catch(ArrayIndexOutOfBoundsException e){
			throw new ArrayIndexOutOfBoundsException();
		}
	}
	public static void main(String[] args) {
		MyException my=new MyException();
		try {
			my.play();
		} catch (Throwable e) {
			e.printStackTrace();
		}
	}
}

输出结果:
java.lang.StringIndexOutOfBoundsException
at com.t.MyException.play(MyException.java:10)
at com.t.MyException.main(MyException.java:26)
调用play()方法明明会产生两个异常,为什么输出结果只打印了一个异常的跟踪信息。其实,只要把前面讲的弄清楚了,就知道为什么了。
我现在来分析一下原因。当我们运行到"str.charAt(10);"这行代码的时候,catch捕获到一个StringIndexOutOfBoundsException异常,同时抛出一个StringIndexOutOfBoundsException异常的实例,这时程序本应该被终止,因为play()方法中没有捕获这个自动产生的异常。
但是由于play()方法用了throws Throwable抛出异常,所以程序没有被终止,而是把异常交给play()方法的上级调用者main()方法处理。虽然程序没有被终止,但是由于play()方法产生了异常没有处理,而是抛给了它的上级调用者main()方法。那么产生异常时,play()方法中本该在异常之后执行的代码就不会被执行了。

上面那个程序弄清楚了后,这个程序也会很容易明白,是一个典型的异常丢失的情况。

public class MyException {
	public void play() throws Throwable{
		try{
			pain();
		}catch(ArrayIndexOutOfBoundsException e){
			StringIndexOutOfBoundsException c=new StringIndexOutOfBoundsException();
			throw c;
		}
	}
	public void pain() throws ArrayIndexOutOfBoundsException{
		try{
			int[] array=new int[10];
			array[12]=2014;
		}catch(ArrayIndexOutOfBoundsException e){
			throw new ArrayIndexOutOfBoundsException();
		}
	}
	public static void main(String[] args) {
		MyException my=new MyException();
		try {
			my.play();
		} catch (Throwable e) {
			e.printStackTrace();
		}
	}
}

输出结果:
java.lang.StringIndexOutOfBoundsException
at com.t.MyException.play(MyException.java:8)
at com.t.MyException.main(MyException.java:23)
结果只打印了一个异常的跟踪信息,这是一个典型的异常丢失的情况,我们经常用它来引出异常链,因为它和异常连最接近。

4.异常链

我们来看看下面这个程序吧,这个程序用到了异常链。

public class MyException {
	public void play() throws Throwable{
		try{
			pain();
		}catch(ArrayIndexOutOfBoundsException e){
			StringIndexOutOfBoundsException c=new StringIndexOutOfBoundsException();
			c.initCause(e);//异常链
			throw c;
		}
	}
	public void pain() throws ArrayIndexOutOfBoundsException{
		try{
			int[] array=new int[10];
			array[12]=2014;
		}catch(ArrayIndexOutOfBoundsException e){
			throw new ArrayIndexOutOfBoundsException();
		}
	}
	public static void main(String[] args) {
		MyException my=new MyException();
		try {
			my.play();
		} catch (Throwable e) {
			e.printStackTrace();
		}
	}
}

输出结果:
java.lang.StringIndexOutOfBoundsException
at com.t.MyException.play(MyException.java:8)
at com.t.MyException.main(MyException.java:24)
Caused by: java.lang.ArrayIndexOutOfBoundsException
at com.t.MyException.pain(MyException.java:18)
at com.t.MyException.play(MyException.java:6)
... 1 more
从输出结果可以看到此时打印了两个异常的跟踪信息,这是因为用到了异常链的原因。
我们还发现这个程序仅仅只比上面的程序多一行“c.initCause(e);”,这就是异常链的用法,用原始异常来初始化引起现在异常的原因。
异常链的特性是所有异常均具备的,因为这个initCause()方法是从Throwable继承的。

5.异常的分类

Throwable类是所有异常的父类。
Throwable有两个子类,ERROR(错误)和Exception。
Exception下面有非运行时异常(编译异常)和运行时异常(RuntimeException)。
非运行时异常需要捕获,不然编译器不让通过。运行时异常可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。

6.自定义异常

我们自定义普通异常时应该继承Exception,定义运行时异常要继承RuntimeException。

/*
自定义异常举例
*/
class MyException extends Exception{
	public MyException() {
		super();
	}
}


Java异常处理