以下是学习《Practical Java(重点版)》的笔记。
1. 认识异常控制流(exception control flow)机制。当异常发生时,会转入到以下三种的处理方式之一:
a).catch block(捕获区段)
b).finally block(终结区段)
c).calling method(调用端)
这三种方式都是异常表现的goto行为。
如果程序在try段抛出异常会发生以下事情:
a).如果同时存在catch和finally段的话,则控制流先转向catch,然后转向finally。
b).如果没有catch的话,则转向finally段。
如果程序正常退出或者异常退出,则该方法创建的对象都将会自动解决引用。
2. 绝对不可轻忽异常(Never ignore an exception)。如果一个线程的异常发生了,但是你未捕获处理的话,那么该线程将被中断。当异常发生的时候,我们可以做的事情如下:
1). 捕捉并处理它,防止它进一步传播(propagate)。
2). 捕捉并再次抛出它,这么一来它会被传播给调用端。
3). 捕捉它,然后抛出一个新异常给调用端。
4) .不捕捉这个异常,听任它传播给调用端。
对捕获的异常,最好是记录到日志中,采用printStackTrace()的方式将其写到日志中,方便今后的查找问题。异常最好是捕获,不能够什么都不做。
3. 千万不要隐藏异常。
public class HideExectionTest {
static void hide() throws Exception {
try {
throw new Exception("hide");//1
} catch (Exception e) {
throw new Exception("catch");//2
} finally {
throw new Exception("Finally");//3
}
}
public static void main(String[] args) {
try {
hide();
} catch (Exception e) {
System.out.println("In main : cathc exception " + e.getMessage());
}
}
}
这种情况就隐藏了异常,输出结果:
In main : cathc exception Finally
原因:1的异常被2覆盖了,而2的异常又被3覆盖了,所以只返回了3的处理异常。在这个时候可以自定义一个异常处理类,然后收集处理的异常信息,最后再将该异常抛出。这样就避免了隐藏异常。
4. throws子句的缺点
throws(抛掷)子句是一种语言机制,用来列出「可从某个函数传至外界」的所有可能异常。编译器强迫你必须在函数中捕捉这些被列出的异常,否则就得在该函数的throws子句中声明它们。throws子句用来向函数调用者发出预警,告知将会产生哪些异常。这是一项非常有用的语言特性,为函数用户提供非常有价值的信息。然而这个特性也有副作用,你必须心理有数。
5. 全面理解throws子句
提供throws 子句的用意在于,提醒函数调用者,告知可能发生的异常。如果父类方法throws了异常,那么子类在覆写此方法时候:可以不throws异常;可以throws出父类方法异常类的派生异常;如果在子类中出现了其他异常,则必须在子类中捕获处理,不能够传递。
如下:
class Test{
public void key() throws FileNotFoundException{
}
}
子类:
class SubTest1 extends Test{
public void key() throws FileNotFoundException{
}
}
class SubTest2 extends Test{
public void key(){
}
}
class SubTest3 extends Test{
public void key() throws FileNotFoundException{
try {
…
} catch (NumberFormatException e) {
}
}
}
6. 使用finally避免资源泄露
无论是否发生异常,finally中的内容始终会执行。
7. 不要在try段中返回
在try finally搭配中,最好不在try中使用return,break,continue,会导致代码阅读性不好。
以下是个例子:
public class TryFinally {
static int test1() {
try {
return 1;
} catch (Exception e) {
return 2;
}
}
static int test2() {
try {
return 3;
} catch (Exception e) {
return 0;
} finally {
return 4;
}
}
public static void main(String[] args) {
System.out.println(test1());
System.out.println(test2());
}
}
输出结果:1
4
8. 将try/catch段设置于循环之外
如果客户使用无JIT能力的JVM时候,将try/catch放到循环中,会导致效率下降。不启动JIT可以减少内存使用。
如果你想在不启动JIT 的情况下执行名为Test.class 的文件,请下这个命令
Java - Djava.compiler=NONE Test
这会使得在执行程序Test 时,JVM 的JIT 能力被抑制。
9. 不要将异常用于流程控制
10.不要每逢出错就使用异常
11. 在构造方法(constructors)中抛出异常
构造方法支持throws子句。
12. 抛出异常之前先将对象恢复为有效状态(valid state)
和事务有很大的相似之处,都是万一失败的话,以前提交的动作该如何处理。