Exception best practice

http://www.wikijava.org/wiki/10_best_practices_with_Exceptions

 

 

10 best practices with Exceptions

From WikiJava

这篇文章列举了十个Java里使用Exception的最佳实践

Exception是一个能打断程序正常逻辑的event. Exception不一定代表一个错误,但是JAVA里一般用Exception代表一个错误

如果你不知道Exception是神马东西,看Sun公司提供的这个文档: tutorial about exceptions.

Relevancy

函数只应该跑出和他本身有关系的Exception

看这个构造函数

java.io.FileInputStream.FileInputStream(String name) throws FileNotFoundException

这个函数打开一个文件,读取数据,所以它抛出FileNotFoundException就很合理. 如果它抛出IndexOutOfBoundsException那就不合理了

Encapsulation

Relevancy后面就得提这个:Encapsulation. 就是说把异常包装成另外一个异常.

也就是说如果你的函数调用了另一个函数,而那个函数抛出了一个异常,而且那个异常是一个很general的异常,那你可能就得考虑把这个异常包装一下再抛出去

try {
  return new FileOutputStream(fileName);
} catch (FileNotFoundException e) {
  throw new ActionException(e);
}

这个函数接受了一个FileOutputStream 异常, 然后把它包装成ActionException, 然后抛出.

这样做有好处:

  • 符合Relevancy的原则,也就是说我这个函数可没抛出那个很general的异常
  • Debug的时候也能多一些信息(因为包装过了么)

Reason

异常必须说明自己被抛出的原因,有两种方式:

  • 每次都抛出不同种类的异常
  • 创建一个很general的异常然后用字符串描述之

前者要求你创建好多异常类,后者Catch到以后不好处理. 怎么做好呢,就是折中.

如果你想用不同的方法来处理异常那你就创建新的异常类, 反之你就考虑用一个比较一般的异常.

Exception names

异常类的名字必须有意义, 比如java.io.FileNotFoundException.

java.lang.Exception 是最一般的异常类,它有它的用处,但是你别直接抛出它,因为它的名字不能提供任何其他特定信息. 作者的习惯是即使是一般的异常类,也自己创建一个,给自己的特定代码使用.这样你看到这个异常你就知道你的那部分代码有问题了.

Balance what you catch

catch 也有两种写法:

  • 一个catch抓住所有类型的异常
} catch (Throwable e) {
   throw new CommandExecutorException(e);
}
  • 每个异常都写一个catch
} catch (ClassCastException e1) {
   ...
} catch (FileNotFoundException e) {
   ...	
} catch (IOException e) {
   ...
}

这个问题其实不难, 关键就看你是不是需要对每种异常区别处理

Scoping

try 包含的代码多长呢, 你可以整个函数就写一个try,也可以每行可能跑异常的代码都写一个try. 两者当然各有优缺点.

有一些指导原则:

可以用try-blocks来规范变量的作用域
如果你有一个变量只是在一下段代码里使用,可以把它放在try里面
用try来分隔你的代码
把一大段代码用try分隔成几个逻辑小块, 这样代码易读性好
用多个try能使得你处理异常的粒度比较小
这种情况下你可以用很多try block. 如果你用一个大的try block,你的代码就不容易读到是哪块代码抛出的哪个异常
考虑代码的大小
如果你有一个函数很大,一屏幕也装不下(应该避免这种), 那么用try block分割代码也不错
抛出的异常的数量
如果你有一个很大的try block,那么这一大块抛出的异常数量可能就比较多,那你就得弄多个catch, catch连起来也很难看

Use Finally

Finally用来清理资源什么的很有用

FileWriter output= null;
try {
    output = new FileWriter(file);
    output.write(stringBuffer.toString());
    System.out.println("file " + path + filename + " written");
} catch (IOException e) {
    e.printStackTrace();
} finally {
    try {
      output.close();
    } catch (Throwable e) {
      e.printStackTrace();
    }
}

上面这里看到finally里又有try catch, 发生了嵌套. 如果你这样写,那就有点危险了

try {
   output.close();
} catch (IOException e) {
   e.printStackTrace();
}

因为这个catch和外面那个catch (IOException e) 有冲突 (为毛?我不懂,谁给我解释解释)

Throw only Exceptions

JAVA里面只要继承java.lang.Throwable,你就能抛出它. 但是记住throwable就是用来抛出错误和异常的,如果你用throwable做其他目的,那么别人看你的代码就会一头雾水了,所以你最好别这么干

Throw early catch late

这是异常处理里面最著名的定律. 意思是如果有一个异常你就应该尽早抛出,然后尽量晚,直到你得到所有信息能处理这个异常的时候才处理它.

这里的意思是你应该在底层的函数里就抛出异常,比如检查null或什么东西. 然后让这个异常爬到栈的顶层去处理,比如在GUI层让用户决定如何操作,甚至是在main里面让程序退出之类的

Add validation

检查输入(参数什么的),如果不对就抛出异常.

if ( null == variable || variable.isEmpty()){
   throw new blahException("the variable cannot be null at this point");
}

不过也不是每个参数都必须检查,这个要具体问题具体分析.

 

posted @ 2012-06-27 16:14  大兵八世  阅读(309)  评论(0)    收藏  举报