Java进阶总结——异常
概念
Java语言中如果某个方法不能按照正常途径处理完任务,则需要通过另外一种路径退出该方法,同时抛出一个封装有对应错误信息的对象。在这种情况下该方法的调用者无法执行这个方法,开始在异常处理器中处理对应异常。
异常体系
从上图我们可以看到Throwable是所有错误或异常的超类,而在Throwable之下一般分为Error和Exception两大类。
1.Error类是指运行时内部错误和资源耗尽错误,程序不会抛出该类对象。
2.Exception类又可分为运行时异常(RuntimeException)和受检异常(CheckedException),其中运行时异常是指在JVM正常运行时抛出的异常,例如NullPointerException,ClassCastException等;受检异常一般是外部错误发生在编译阶段,java编译器会强制捕获此类异常。
异常的处理方式
1.遇到问题不进行处理继续抛给上层(throw,throws)
public class TestEqual {
public static void main(String[] args){
System.out.println(function1(3));
}
public static int function1(int a){
try{
int b = 1;
return function2(b);
}catch(Exception e){
return -1;
}
}
public static int function2(int b) throws Exception {
try{
if(b > 0){
throw new Exception("haha");
}
return 0;
}catch(Exception e){
throw new Exception(e);
}
}
}
2.遇到问题在本层直接处理(try…catch…)
public class TestEqual {
public static void main(String[] args)throws Exception {
System.out.println(function2(3));
}
public static int function2(int b)throws Exception {
try{
if(b >0){
throw new Exception("haha");
}
return 0;
}catch(Exception e){
System.out.println(e);
}
return -1;
}
}
try - catch - finally语法
try 块:用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。
catch 块:用于处理try捕获到的异常。
finally 块:无论是否捕获或处理异常,finally块里的语句都会被执行。
当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。在以下4种特殊情况下,finally块不会被执行:
1)在finally语句块中发生了异常。
2)在前面的代码中用了System.exit()退出程序。
3)程序所在的线程死亡。
4)关闭CPU。
try-catch-finally 规则
-
必须在 try 之后添加 catch 或 finally 块。try 块后可同时接 catch 和 finally 块,但至少有一个块。
-
必须遵循块顺序:若代码同时使用 catch 和 finally 块,则必须将 catch 块放在 try 块之后。
-
catch 块与相应的异常类的类型相关。
-
一个 try 块可能有多个 catch 块。若如此,则执行第一个匹配块。即Java虚拟机会把实际抛出的异常对象依次和各个catch代码块声明的异常类型匹配,如果异常对象为某个异常类型或其子类的实例,就执行这个catch代码块,不会再执行其他的 catch代码块
-
可嵌套 try-catch-finally 结构。
-
在 try-catch-finally 结构中,可重新抛出异常。
-
除了下列情况,总将执行 finally 做为结束:JVM 过早终止(调用 System.exit(int));在 finally 块中抛出一个未处理的异常;计算机断电、失火、或遭遇病毒攻击。
try-catch-finally语句块的执行顺序
1)当try没有捕获到异常时:try语句块中的语句逐一被执行,程序将跳过catch语句块,执行finally语句块和其后的语句;
2)当try捕获到异常,catch语句块里没有处理此异常的情况:当try语句块里的某条语句出现异常时,而没有处理此异常的catch语句块时,此异常将会抛给JVM处理,finally语句块里的语句还是会被执行,但finally语句块后的语句不会被执行;
3)当try捕获到异常,catch语句块里有处理此异常的情况:在try语句块中是按照顺序来执行的,当执行到某一条语句出现异常时,程序将跳到catch语句块,并与catch语句块逐一匹配,找到与之对应的处理程序,其他的catch语句块将不会被执行,而try语句块中,出现异常之后的语句也不会被执行,catch语句块执行完后,执行finally语句块里的语句,最后执行finally语句块后的语句;
异常处理原则
1.具体明确
异常的抛出必须细分到具体的场景,抛出的异常越具体,就越能回答什么地方出了错的问题,例如IOException中异常细分为FileNotFoundException,EOFException等子类,这些子类就是为不同的异常场景设计的。
2.提早抛出
在程序运行时检测到错误立刻抛出异常来迅速实现失败,可以避免掉不必要的对象构造或资源浪费,但是需要在最先出现异常的地方进行打印日志和抛出。
3.延迟捕获
异常一经抛出后,不一定要在本层去处理该异常,可以在上层或者上上层去处理,异常捕获的原则是为用户提供明确信息以及可以根据该异常引导用户从错误中恢复过来,如果方法本身无法完成这样的工作则可以抛给调用方去捕获处理。
优雅的处理异常
以阿里的java开发手册为参考,异常处理过程可以遵循以下原则来进行
1.对大段代码进行 try-catch,这是不负责任的表现。catch 时请分清稳定代码和非稳 定代码,稳定代码指的是无论如何不会出错的代码。对于非稳定代码的 catch 尽可能进行区分 异常类型,再做对应的异常处理。
2.异常不要用来做流程控制,条件控制,因为异常的处理效率比条件分支低。
3.捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请 将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的 内容。
4.不能在 finally 块中使用 return,finally 块中的 return 返回后方法结束执行,不 会再执行 try 块中的 return 语句。
5.finally 块必须对资源对象、流对象进行关闭,有异常也要做 try-catch。
6.捕获异常与抛异常,必须是完全匹配,或者捕获异常是抛异常的父类。
总结
异常是java体系中用来处理发生问题场景的机制,但是不是所有的发生问题的场景都能用异常处理,在处理异常时一定要尽可能的细分场景。
上一篇: 双路低频信号发生分析仪(部分)
下一篇: 进阶SQL总结