异常,说起来,就是一张图,5个关键字。
一张图,
5个关键字
- try
- catch
- finally
- throw
- throws
Java的异常捕获机制是怎么处理异常的?
要出捕获,要么抛出
一、异常
异常是程序运行过程中出现的错误。
Java把异常当作对象来处理,并定义一个基类java.lang.Throwable作为所有异常的父类。
异常主要分类两类,一类是 Error,另一类是 Exception
Exception有两个子类,一个是 RuntimeException,另一种是 非运行时异常,比如IOException。
接下来就说说说上面出现的这几个东西
Throwable
异常和错误的爹,错的都是他Error
Error是程序无法处理的错误,比如OutOfMemoryError、ThreadDeath等。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。
就好像,你走路走着走着,前方的路塌了,你能怎么办,无计可施。
-
Exception
Exception是程序本身可以处理的异常,这种异常分两大类运行时异常和非运行时异常。- 运行时异常 RuntimeException
运行时异常都是RuntimeException类及其子类。叫运行时异常嘛,运行时发生的异常,RuntimeException是我们程序开发中需要重点处理的异常。
如NullPointerException、 IndexOutOfBoundsException等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引 起的,程序应该从逻辑角度尽可能避免这类异常的发生。
- 运行时异常 RuntimeException
非运行时异常/检查性异常/检查异常/编译异常
(非运行时异常,叫法有点多)
一个异常,如果不是运行异常,那么他就是 非运行时异常。
比如如IOException、SQLException等以及用户自定义的Exception异常。这些异常在编译时就会报错,我们必须处理,想不处理都不行。
二、5个关键字
Java异常处理涉及到五个关键字,分别是:try、catch、finally、throw、throws。
我们先来看一下
try{
//(尝试运行的)程序代码
}catch(异常类型 异常的变量名){
//异常处理代码
}
// finally 不是必须的
finally{
//异常发生,方法返回之前,总是要执行的代码
}
}
1、try
try语句块,表示要尝试运行代码,try语句块中代码受异常监控,其中代码发生异常时,会抛出异常对象。
2、catch
- catch语句块会捕获try代码块中发生的异常并在其代码块中做异常处理,catch语句带一个Throwable类型的参数,表示可捕获异常类型。
- 当 try中出现异常时,catch会捕获到发生的异常,并和自己的异常类型匹配,若匹配,则执行catch块中代码,并将catch块参数指向所抛的异常对象。
- catch语句可以有多个,用来匹配多个中的一个异常,一旦匹配上后,就不再尝试匹配别的catch块了。
通过异常对象可以获取异常发生时完整的 JVM堆栈信息,以及异常信息和异常发生的原因等。
3、finally
finally语句块是紧跟catch语句后的语句块,这个语句块总是会在方法返回前执行,而不管是否try语句块是否发生异常。
比如我们tr……catch IO输入输出的异常,那么我们可以在finally里面把流给关闭。
目的是给程序一个补救的机会。这样做也体现了Java语言的健壮性。
示例代码1 运行时异常
我们先来看一段代码,用0做除数,会抛异常
public class ThrowableTest {
public static void main(String[] args) {
int a = 3;
int b = 0;
// 0如果做除数会抛异常
System.out.println("result: "+a/b);
}
}
运行抛会异常
Exception in thread "main" java.lang.ArithmeticException: / by zero
at TrowTest.ThrowableTest.main(ThrowableTest.java:8)
这里抛出的异常是运行时异常,在编译的时候不会出现。必须我们程序员自己在编码做预防处理,比如用异常捕获机制try……catch
.
.
示例代码2 异常捕获机制
public class ThrowableTest {
public static void main(String[] args) {
int a = 3;
int b = 0;
try {
// 0如果做除数会抛异常
System.out.println("result: " + a / b);
} catch (Exception e) {
System.out.println("捕获到异常: " + e.toString());
}
}
}
运行结果:
捕获到异常: java.lang.ArithmeticException: / by zero
异常被我们捕获了,程序没奔溃,正常运行。
.
.
try,catch和finally的使用注意点
-
1、try、catch、finally三个语句块均不能单独使用
三者可以组成- try...catch...finally、
- try...catch、
- try...finally
三种结构。
catch语句可以有一个或多个,finally语句最多一个。
2、try、catch、finally三个代码块中变量的作用域为代码块内部,分别独立而不能相互访问。如果要在三个块中都可以访问,则需要将变量定义到这些块的外面。
- 3、多个catch块时候,只会匹配其中一个异常类并执行catch块代码,而不会再执行别的catch块,并且匹配catch语句的顺序是由上到下。
throws
throws表示什么
- 当一个方法参数列表之后throws Exception,用来声明方法可能会抛出某些异常,那么调用该方法的地方就必须try……catch捕获异常,否则编译不通过。
throws什么时候用呢
如果一个方法中某些代码有可能造成某种异常,但是并不确定怎么处置,那么这个方法应该声明抛出异常,表示该方法不对这些异常进行处理,有调用者进行着。
如果调用者也无法处理这些异常,那么应该继续抛出throws,如果一层层往上throws,那么这个异常会最终达到main方法,到了main就终止了,JVM肯定会让你处理,这样编译就可以通过了。
(如果你异常一致背向上抛到了main方法,你main方法从语法上其实也可以也throws Exception,da你是这么做就没意义了,跑出来干嘛,一点意思都没有了,一旦异常就奔溃了)当方法的调用者无力处理该异常的时候,应该继续抛出,而不是随随便便在catch块中打印一下堆栈信息做个勉强处理。当然,有能力处理的应该及时处理。
throw
throw关键字是用于方法体内部(语句抛出),用来抛出一个Throwable类型的异常。
- 如果我们在方法里面的语句throw一个异常,那么分两种情况
- 1、在方法内部对应 throw 异常的语句我们进行try……catch,那么异常就会在当前方法被处理,这没什么问题。
- 2、在方法内部对应 throw 异常的语句我们没有进行try……catch,那么在当前方法我们用throws Exception,不然抛出这个异常没人接,就不好了。
(只throw,不try……catch也不throws特可以,但是这样没意思一点)
- 如果throw的是自定义异常,那么就必须 内部捕获异常 或者 throw Exception
.
.
示例代码 throws
public class Test {
static String abc = null;
public static void main(String[] args) {
try {// 因为show() throws Exception,所以调用者 try……catch,不然编译不通过
show(abc);
} catch (Exception e) {
e.printStackTrace();
}
}
private static void show(String s) throws Exception{
//这句代码会引发 java.lang.NullPointerException
s.toString();
}
}
输出
java.lang.NullPointerException
at TrowTest.Test.show(Test.java:14)
at TrowTest.Test.main(Test.java:7)
如上,我们在show方法 throws Exceptionthrows Exception了,所以调用者就艺try……catch
.
.
代理示例 throw 出 运行时异常
public class ThrowableTest {
public static void main(String[] args) {
int a = 3;
int b = 0;
System.out.println("result: "+divisionNum(a, b));
}
private static int divisionNum(int n1,int n2){
try {
if (n2 == 0) {
throw new ArithmeticException("在方法内的语句 throw 抛出运行时异常");
}
} catch (Exception e) {
System.out.println("捕获异常 "+e.toString());
}
return n1/n2;
}
}
运行结果,运行失败抛异常
Exception in thread "main" 捕获异常 java.lang.ArithmeticException: 在方法内的语句 throw 抛出运行时异常
java.lang.ArithmeticException: / by zero
at TrowTest.ThrowableTest.divisionNum(ThrowableTest.java:18)
at TrowTest.ThrowableTest.main(ThrowableTest.java:7)
.
.
自定义异常
ToMinException
public class ToMinException extends Exception {
public ToMinException(String msg)
{
super(msg);
}
}
.
.
ToMaxException
public class ToMaxException extends Exception {
public ToMaxException(String msg)
{
super(msg);
}
}
.
.
Test
public class Test {
public static void main(String[] args) {
Test test = new Test();
try {
test.simpleAdd10to20(8, 17);
} catch (ToMinException e) {
e.printStackTrace();
System.out.println("捕获到的异常信息 toString() :"+e.toString());
System.out.println("捕获到的异常信息:getMessage() "+e.getMessage());
}catch(ToMaxException e){
e.printStackTrace();
System.out.println("捕获到的异常信息 toString() :"+e.toString());
System.out.println("捕获到的异常信息:getMessage() "+e.getMessage());
}
//getMessage() //输出异常的信息
// printStackTrace() //输出导致异常更为详细的信息
}
// 这个方法只允许大于10,和小于20的数字,我们违反规则的数抛异常(仅为演示)
private int simpleAdd10to20(int a,int b) throws ToMinException,ToMaxException{
if(a>10 || b>10){
throw new ToMinException("不能有小于10 的参数");
}
if(a>20 || b>20){
throw new ToMaxException("不能有大于20 的参数");
}
return a+b;
}
}
输出:
TrowTest.ToMinException: 不能有小于10 的参数
at TrowTest.Test.simpleAdd10to20(Test.java:27)
at TrowTest.Test.main(Test.java:8)
捕获到的异常信息 toString() :TrowTest.ToMinException: 不能有小于10 的参数
捕获到的异常信息:getMessage() 不能有小于10 的参数
以上演示完自定义异常了。
最后我们顺便说一下几个关于异常的方法:
e.printStackTrace();
是打印异常的堆栈信息,指明错误原因,其实当发生异常时,通常要处理异常,这是编程的好习惯,所以e.printStackTrace()可以方便你调试程序!
e.toString()
具体哪个异常类哪个方法,具体异常提示是什么
e.getMessage()
最简要的异常内容提示