Error和Exception都是继承了Throwable类,Java里面只有Throwable实例才可以抛出(throw)和捕捉(catch)异常。Error和Exception是对不同异常情况的分类,
1.Error:
1) 是指在正常情况下,不大可能出现的情况,绝大部分的 Error 都会导致程序(比如 JVM自身)处于非正常的、不可恢复状态。既然是非正常情况,所以不便于也不需要捕获,常见的比如 OutOfMemoryError 之类,都是 Error 的子类。
2)Error就是Java运行时内部错误与资源耗尽错误,这种内部错误一旦出现,除了告知用户并使程序安全终止之外,在无能为力,断电。
2.Exception(程序出错):
Exception 又分为受查(checked)异常和不受查(unchecked)异常,可检查异常在源代码里必须显式地进行捕获处理,这是编译期检查的一部分。前面我介绍的不可查的 Error,是Throwable 不是 Exception。不检查异常就是所谓的运行时异常,类似 NullPointerException、ArrayIndexOutOfBoundsException 之类,通常是可以编码避免的逻辑错误,具体根据需要来判断是否需要捕获,并不会在编译期强制要求
Exception是程序正常运行过程中,可以预料的异常情况,可以对他们进行捕捉,并进行相应的处理
1)? IOException:程序本身没有问题,由于IO处理程序导致出错
2)? ?RuntimeException:经常性出现的错误,可以捕获,并作出处理,可以不捕获,也可以不用抛出。? ? ? ? ? ? ? ? ? ? ?ArrayIndexOutOfBoundsException像:这种异常可以不捕获,为什么呢?在一个程序里,使用很多数组,如果使用一次捕获一次,则很累。
非受查异常:由于继承与Error或RuntimeException(空指针,类型转换)的异常类称为非受查异常
受查异常(必须强制处理):所有继承Exception与IOException的称为受查异常
?
3.异常处理格式
try{
???????可能出现异常的语句
}[catch??( 异常类 对象??) ]??{
????出现异常的操作
}[finaly ]{
????异常的出口(无论是否产生异常,都会走finaly,并且是最后走)
}
try....catch...
try....finaly....
try.....catch....finaly....
无论是否产生异常,无论try ,catch是否有返回语句,最终都会执行finaly块,Lock锁的释放,JDBC里数据库的关闭,
try catch finally的执行流程
1.不管有没有出现异常,finally块中代码都会执行;
2.当try和catch中有return时,finally仍然会执行;当finally里面没有return语句是,执行try 和finally语句之后最后再执行return。?
3.finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,管finally中的代码怎么样,返回的值都不会改变,任然是之前保存的值),所以函数返回值是在finally执行前确定的;
4.finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值。
注意:
仅仅在下面4中情况下不会执行finally语句 :
如果在try 或catch语句中执行了System.exit(0)。
.在执行finally之前jvm崩溃了。
.try语句中执行死循环。
.电源断电。
https://blog.csdn.net/u013309870/article/details/73498860里面讲解了try catch finally的执行流程
throw和throws关键字
1.throws关键字---作用于方法上
在进行方法定义时,如果要明确告诉调用者本方法可能会产生那些异常,可以用throws方法进行声明,表示将异常抛回给回调方,并且当方法出现问题后可以不处理异常
?
2.throw关键字--作用在方法中
throw语句直接写在方法中,表示人为进行异常抛出。一般与自定义异常类搭配使用。
如果现在异常类对象实例化不希望由JVM产生而由用户产 生,就可以使用throw来完成
断言assert
assert是一个布尔表达式,是一个Error异常
?
知识扩展:
1.分析下面代码的问题
try {
// 业务代码
// …
Thread.sleep(1000L);
} catch (Exception e) {
// Ignore it
}
第一个问题:它捕捉的是Exception异常,这是异常的父类,他应该捕捉特定的异常,而不是通用的异常。
这里Thread.sleep() 抛出的 InterruptedException异常,其他情况下也会有类型转换异常,空指针异常,数组越界异常等。所以捕捉的异常尽量要细,而不是抛出一个Exception。这样做的目的就是能够直接的体现出更多而信息,而Exception却隐藏了这些信息,同时我们也要保证程序不会捕捉到我们不希望捕捉的异常,比如RuntimeException。
一般情况下不要捕捉Throwable和Error,因为很难保证我们能够正确的处理OutOfMemoryError,
?
第二个问题:不要生吞异常,这是异常处理中要特别注意的事情,因为很可能会导致非常难以诊断的诡异情况。
生吞异常(掩盖问题):往往是基于假设这段代码可能不会发生,或者感觉忽略异常是无所谓的,但是千万不要在产品代码做这种假设!
如果我们不把异常抛出来,或者也没有输出到日志(Logger)之类,程序可能在后续代码以不可控的方式结束。没人能够轻易判断究竟是哪里抛出了异常,以及是什么原因产生了异常。这回导致发现异常和处理异常变得非常困难。
?
2.分析该段代码的问题
e.printStackTrace();
一个catch可能解决不了所有的问题,所以用catch(Exception e){e.PrintStackTrace}
try {
// 业务代码
// …
} catch (IOException e) {
e.printStackTrace();
}
在printStackTrace()的文档,开头就是“Prints this throwable and its backtraceto the standard error stream”。问题就在这里,在稍微复杂一点的生产系统中,标准出错(STERR)不是个合适的输出选项,因为你很难判断出到底输出到哪里去了。
尤其是对于分布式系统,如果发生异常,但是无法找到堆栈轨迹(stacktrace),这纯属是为诊断设置障碍。所以,最好使用产品日志,详细地输出到日志系统里。
从性能角度来审视一下 Java 的异常处理机制,这里有两个可能会相对昂贵的地方:
try-catch 代码段会产生额外的性能开销,或者换个角度说,它往往会影响 JVM 对代码进行优化,所以建议仅捕获有必要的代码段,尽量不要一个大的 try 包住整段的代码;与此同时,利用异常控制代码流程,也不是一个好主意,远比我们通常意义上的条件语句(if/else、switch)要低效
Java 每实例化一个 Exception,都会对当时的栈进行快照,这是一个相对比较重的操作。如果发生的非常频繁,这个开销可就不能被忽略,所以,对于部分追求极致性能的底层类库,有种方式是尝试创建不进行栈快照的 Exception。这本身也存在争议,因为这样做的假设在于,我创建异常时知道未来是否需要堆栈.问题是,实际上可能吗?小范围或许可能,但是在大规模项目中,这么做可能不是个理智的选择.如果需要堆栈,但又没有收集这些信息,在复杂情况下,尤其是类似微服务这种分布式系统,这会大大增加诊断的难。当我们的服务出现反应变慢、吞吐量下降的时候,检查发生最频繁的 Exception 也是一种思路。
Throw early, catch late 原则
public void readPreferences(String fileName){
//...perform operations...
InputStream in = new FileInputStream(fileName);
//...read the preferences file...
}
这里如果fileName 是 null,那么程序就会抛出 NullPointerException,但是由于没有第一时间暴露出问题,堆栈信息可能非常令人费解,往往需要相对复杂的定位。这个 NPE 只是作为例子,实际产品代码中,可能是各种情况,比如获取配置失败之类。在发现问题的时候,第一时间抛出,能够更加清晰地反映问题。
所以,修改代码,即throw early
public void readPreferences(String filename) {
Objects. requireNonNull(filename);
//...perform other operations...
InputStream in = new FileInputStream(filename);
//...read the preferences file..
}
关于“catch late”,其实经常苦恼的问题,是捕获异常后,需要怎么处理呢?最差的处理方式,就是我前面提到的“生吞异常”,本质上其实是掩盖问题。如果实在不知道如何处理,可以选择保留原有异常的 cause 信息,直接再抛出或者构建新的异常抛去。在更高层面,因为有了清晰的(业务)逻辑,往往会更清楚合适的处理方式是什么。
?
自定义异常需要考虑的问题
是否需要定义成 Checked Exception,因为这种类型设计的初衷更是为了从异常情况恢复,作为异常设计者,应该具有充足信息进行分类。
在保证诊断信息足够的同时,也要考虑避免包含敏感信息,因为那样可能导致潜在的安全问题。如果我们看 Java 的标准类库,你可能注意到类似 java.net.ConnectException,出错信息是类似“ Connection refused (Connection refused)”,而不包含具体的机器名、IP、端口等,一个重要考量就是信息安全。类似的情况在日志中也有,比如,用户数据一般是不可以输出到日志里面
当然这里面也由两个问题:
Checked Exception 的假设是我们捕获了异常,然后恢复程序.但是,其实大多数情况下,根本就不可能恢复.Checked Exception 的使用,已经大大偏离了最初的设计目的.
Checked Exception 不兼容 functional 编程,如果你写过 Lambda/Stream 代码,相信深有体
-!