使用的教材是java核心技术卷1,我将跟着这本书的章节同时配合视频资源来进行学习基础java知识。
day049 使用异常机制的技巧
目前,存在着大量有关如何恰当地使用异常机制的争论。有些程序员认为所有的已检查异常都很令人厌恶;还有一些程序员认为能够拋出的异常量不够。我们认为异常机制(甚至是已检查异常)有其用武之地。下面给出使用异常机制的几个技巧。
1.异常处理不能代替简单的测试
作为一个示例,在这里编写了一段代码,试着上百万次地对一个空栈进行退栈操作。在实施退栈操作之前,首先要查看栈是否为空。
if (!s.empty()) s.pop();
接下来,强行进行退栈操作。然后,捕获EmptyStackException异常来告知我们不能这样做。
try
{
s.pop();
}
catch (EmptyStackException e)
{
}
在测试的机器上,调用isEmpty的版本运行时间为646毫秒。捕获EmptyStackException的版本运行时间为21739毫秒。
可以看出,与执行简单的测试相比,捕获异常所花费的时间大大超过了前者,因此使用异常的基本规则是:只在异常情况下使用异常机制。
2.不要过分地细化异常
很多程序员习惯将每一条语句都分装在一个独立的try语句块中。
PrintStream out;
Stack s;
for (i = 0;i < 100;i++)
{
try
{
n = s.pop();
}
catch (EmptyStackException e)
{
//stack was empty
}
try
{
out.writelnt(n);
}
catch (IOException e)
{
//problem writing to file
}
}
这种编程方式将导致代码量的急剧膨胀。首先看一下这段代码所完成的任务。在这里,希望从栈中弹出100个数值,然后将它们存人一个文件中。(别考虑为什么,这只是一个“玩具”例子。)如果栈是空的,则不会变成非空状态;如果文件出现错误,则也很难给予排除。出现上述问题后,这种编程方式无能为力。因此,有必要将整个任务包装在一个try语句块中,这样,当任何一个操作出现问题时,整个任务都可以取消。
try
{
for (i = 0;i< 100;i++)
{
n =s.pop();
out.writelnt(n);
}
}
catch (IOException e)
{
// problem writing to file
}
catch (EmptyStackException e)
{
//stack was empty
}
这段代码看起来清晰多了。这样也满足了异常处理机制的其中一个目标,将正常处理与错误处理分开。
3.利用异常层次结构
不要只抛出RuntimeException异常。应该寻找更加适当的子类或创建自己的异常类。
不要只捕获Thowable异常,否则,会使程序代码更难读、更难维护。
考虑受查异常与非受查异常的区别。已检查异常本来就很庞大,不要为逻辑错误抛出这些异常。(例如,反射库的做法就不正确。调用者却经常需要捕获那些早已知道不可能发生的异常。)
将一种异常转换成另一种更加适合的异常时不要犹豫。例如,在解析某个文件中的一个整数时,捕获NumberFormatException异常,然后将它转换成IOException或MySubsystemException的子类。
4.不要压制异常
在Java中,往往强烈地倾向关闭异常。如果编写了一个调用另一个方法的方法,而这个方法有可能100年才抛出一个异常,那么,编译器会因为没有将这个异常列在throws表中产生抱怨。而没有将这个异常列在throws表中主要出于编译器将会对所有调用这个方法的方法进行异常处理的考虑。因此,应该将这个异常关闭:
public Image loadImage(String s)
{
try
{
// code that threatens to throw checked exceptions
}
catch (Exception e)
{} // so there
}
现在,这段代码就可以通过编译了。除非发生异常,否则它将可以正常地运行。即使发生了异常也会被忽略。如果认为异常非常重要,就应该对它们进行处理。
5.在检测错误时,“苛刻”要比放任更好
当检测到错误的时候,有些程序员担心抛出异常。在用无效的参数调用一个方法时,返回一个虚拟的数值,还是抛出一个异常,哪种处理方式更好?例如,当栈空时,Stack.pop是返回一个null,还是抛出一个异常?我们认为:在出错的地方抛出一个EmptyStackException异常要比在后面抛出一个NullPointerException异常更好。
6.不要羞于传递异常
很多程序员都感觉应该捕获抛出的全部异常。如果调用了一个抛出异常的方法,例如,FilelnputStream构造器或readLine方法,这些方法就会本能地捕获这些可能产生的异常。其实,传递异常要比捕获这些异常更好:
public void readStuff(String filename) throws IOException // not a sign of shame!
{
InputStreaa in = new FilelnputStream(filename);
...
}
让高层次的方法通知用户发生了错误,或者放弃不成功的命令更加适宜。
规则5、6可以归纳为“早抛出,晚捕获"。