异常的概念

异常是由在程序执行时由于程序的逻辑错误而导致的错误。一般的编译错误比较容易发现和改正,而逻辑错误导致的异常则不然。
比如执行以下程序:

package 实验用;

public class Test {

	public static void main(String[] args) {
		int x = 10;
		int y = 10;
		
		System.out.println(x/y);
	}
}

输出结果:
1

程序没有产生异常,所以程序会按照逻辑顺序运行完毕。但是如果有异常产生,程序就会中断执行:

package 实验用;

public class Test {

	public static void main(String[] args) {
		int x = 10;
		int y = 0;//将除数改为0
		
		System.out.println(x/y);
	}
}

输出结果:
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at 实验用.Test.main(Test.java:9)

只要异常没被处理,程序就会在异常处中断。为了让程序在产生异常后也能继续执行,就需引入异常处理语句。

异常处理

异常处理主要使用三个关键字:try,catch,finally。
异常处理的格式:

try {
			//有可能出现异常的语句
		} catch(异常类型 对象) {
			// 异常处理;
		} catch(异常类型 对象) {
			// 异常处理;
		} catch(异常类型 对象) {
			// 异常处理;
		} finally {
			;//不管是否出现异常,都执行的代码
		}

异常处理语句使用时,不一定三个关键字都要使用,可以有这些使用格式:try…catch…finally,try…catch,try…finally。

比如将异常处理语句写入前文简单程序:

package 实验用;

public class Test {

	public static void main(String[] args) {
		int x = 10;
		int y = 0;
		
		try {
			System.out.println(x/y);//有可能出现异常的语句
		} catch(ArithmeticException z) {//捕捉算数异常
			System.out.println("catch内语句输出:"+ z);// 异常处理;
		} finally {
			System.out.println("finally内语句执行");//不管是否出现异常,都执行的语句
		}
		
		System.out.println("异常被处理后执行的语句");
	}
}

输出结果:
catch内语句输出:java.lang.ArithmeticException: / by zero
finally内语句执行
异常被处理后执行的语句

找出异常,就是为了解决异常,但有时输出的信息不够以处理我们处理异常,所以需要使用printStackTrace()方法进行异常信息的完整输出,来帮助我们进行处理:

package 实验用;

public class Test {

	public static void main(String[] args) {
		int x = 10;
		int y = 0;
		
		try {
			System.out.println(x/y);//有可能出现异常的语句
		} catch(ArithmeticException z) {
			z.printStackTrace();// 异常处理;
		} finally {
			System.out.println("finally内语句执行");//不管是否出现异常,都执行的代码
		}
		
		System.out.println("异常被处理后执行的语句");
	}
}

输出结果:
java.lang.ArithmeticException: / by zero
	at 实验用.Test.main(Test.java:10)    //catch内方法printStackTrace()执行后的输出结果
finally内语句执行
异常被处理后执行的语句

如果要处理多个异常可以使用多个catch语句进行异常处理。但catch语句中要求写明异常类型,如果每次异常处理都要考虑全部的异常类型就会很麻烦。那么怎么解决这个问题呢?

我们来看一下异常类的关系:

java捕获进程关闭 java捕获异常_Test


可以发现所有的异常类最高的继承类是Throwable,并且通过上图可以发现Throwable下有两个子类:

Error:JVM错误,这个时候的程序并没有执行,无法处理

Exception:程序运行中产生的异常,用户可以使用异常处理格式处理

按照对象的引用原则,子类可以自动向父类转型,所以实际上所有的异常都可以使用Exception来处理。

package 实验用;

public class Test {

	public static void main(String[] args) {
		int x = 10;
		int y = 0;
		
		try {
			System.out.println(x/y);//有可能出现异常的语句
		} catch(Exception z) {
			z.printStackTrace();// 异常处理;
		} finally {
			System.out.println("finally内语句执行");//不管是否出现异常,都执行的代码
		}
		
		System.out.println("异常被处理后执行的语句");
	}
}

输出结果:
java.lang.ArithmeticException: / by zero
	at 实验用.Test.main(Test.java:10)
finally内语句执行
异常被处理后执行的语句

但是处理多个异常的时候,需要注意如果要同时处理捕获范围大的异常类型和比其捕获范围小的异常类型时,则捕获范围小的异常要放在捕获范围大的异常之前处理,因为如果捕获范围大的异常在捕获范围小的之前处理,则捕获范围大的异常被处理时会将小的异常一起处理掉,程序就会发生错误。

throw,throws关键字

throw:在代码块中使用,手动抛出异常
throws:在方法定义中使用,表示将此方法中可能产生的异常明确告诉调用处,由调用处进行处理

例子:
throw关键字:

package 实验用;

public class Test {

	public static void main(String[] args) {
		int x = 10;
		int y = 0;
		
		try {
			throw new Exception("手动抛出异常");
		} catch(Exception z) {
			z.printStackTrace();
		}
	}
}

输出结果:
java.lang.Exception: 手动抛出异常
	at 实验用.Test.main(Test.java:10)

throws关键字:

package 实验用;

class Math{
	public static int jisuan(int x,int y) throws Exception{
		return x / y;
	}
}

public class Test {

	public static void main(String[] args) {
		int x = 10;
		int y = 0;
		
		try {
			System.out.println(Math.jisuan(10, 0));
		} catch(Exception z) {
			z.printStackTrace();
		}
	}
}

输出结果:
java.lang.ArithmeticException: / by zero
	at 实验用.Math.jisuan(Test.java:5)
	at 实验用.Test.main(Test.java:16)