Java中的异常处理模板
正确的错误处理很繁琐
正确的异常处理代码编写起来可能很乏味。尝试捕获块还会使代码混乱,并使代码更难阅读。看下面的例子:
Input input = null;
IOException processException = null;
try{
input = new FileInputStream(fileName);
//...process input stream...
} catch (IOException e) {
processException = e;
} finally {
if(input != null){
try {
input.close();
} catch(IOException e){
if(processException != null){
throw new MyException(processException, e,
"Error message..." +
fileName);
} else {
throw new MyException(e,
"Error closing InputStream for file " +
fileName;
}
}
}
if(processException != null){
throw new MyException(processException,
"Error processing InputStream for file " +
fileName;
}
在此示例中,不会丢失任何异常。如果在try块中引发了异常,而在finally块中的input.close()调用中引发了另一个异常,则这两个异常都保留在MyException实例中,并在调用堆栈中传播。
那就是在不丢失任何异常的情况下处理输入流所需的代码量。实际上,它甚至只捕获IOExceptions。如果input.close()调用也引发异常,则不会保留从try块引发的RuntimeException。是不是很丑?不难理解实际情况吗?我们还记得每次处理输入流时都要编写所有这些代码吗?
幸运的是,有一个简单的设计模式,即Template Method,可以每次都正确地处理异常,而无需在代码中看到或者编写它。好吧,也许我们将不得不编写一次,仅此而已。
我们要做的是将所有异常处理代码放入模板中。该模板只是一个普通的类。这是上述输入流异常处理的模板:
public abstract class InputStreamProcessingTemplate {
public void process(String fileName){
IOException processException = null;
InputStream input = null;
try{
input = new FileInputStream(fileName);
doProcess(input);
} catch (IOException e) {
processException = e;
} finally {
if(input != null){
try {
input.close();
} catch(IOException e){
if(processException != null){
throw new MyException(processException, e,
"Error message..." +
fileName);
} else {
throw new MyException(e,
"Error closing InputStream for file " +
fileName;
}
}
}
if(processException != null){
throw new MyException(processException,
"Error processing InputStream for file " +
fileName;
}
}
//override this method in a subclass, to process the stream.
public abstract void doProcess(InputStream input) throws IOException;
}
所有异常处理都封装在InputStreamProcessingTemplate类内。注意process()方法如何在try-catch块内调用doProcess()方法。我们将通过子类化模板并覆盖doProcess()方法来使用模板。为此,我们可以编写:
new InputStreamProcessingTemplate(){
public void doProcess(InputStream input) throws IOException{
int inChar = input.read();
while(inChar !- -1){
//do something with the chars...
}
}
}.process("someFile.txt");
本示例创建InputStreamProcessingTemplate类的匿名子类,实例化该子类的实例,然后调用其process()方法。
这更容易编写,也更容易阅读。在代码中只有域逻辑可见。编译器将检查我们是否正确扩展了InputStreamProcessingTemplate。通常,在编写IDE时,我们也会从IDE的代码完成中获得更多帮助,因为IDE会识别doProcess()和process()方法。
现在,我们可以在代码中需要处理文件输入流的任何地方重用InputStreamProcessingTemplate。我们可以轻松地修改模板以使其适用于所有输入流,而不仅仅是文件。
使用接口而不是子类化
可以将其重写为采用InputStreamProcessor接口的实例,而不是对InputStreamProcessingTempate进行子类化。这是它的外观:
public interface InputStreamProcessor {
public void process(InputStream input) throws IOException;
}
public class InputStreamProcessingTemplate {
public void process(String fileName, InputStreamProcessor processor){
IOException processException = null;
InputStream input = null;
try{
input = new FileInputStream(fileName);
processor.process(input);
} catch (IOException e) {
processException = e;
} finally {
if(input != null){
try {
input.close();
} catch(IOException e){
if(processException != null){
throw new MyException(processException, e,
"Error message..." +
fileName;
} else {
throw new MyException(e,
"Error closing InputStream for file " +
fileName);
}
}
}
if(processException != null){
throw new MyException(processException,
"Error processing InputStream for file " +
fileName;
}
}
}
注意模板的process()方法中的额外参数。这是InputStreamProcessor,它从try块(processor.process(input))内部调用。使用此模板如下所示:
new InputStreamProcessingTemplate()
.process("someFile.txt", new InputStreamProcessor(){
public void process(InputStream input) throws IOException{
int inChar = input.read();
while(inChar !- -1){
//do something with the chars...
}
}
});
它看起来与以前的用法没有太大区别,只是对InputStreamProcessingTemplate.process()方法的调用现在更接近代码的顶部。这可能更容易阅读。
静态模板方法
也可以将模板方法实现为静态方法。这样,我们无需在每次调用模板时都将模板实例化为对象。这是InputStreamProcessingTemplate看起来是静态方法的样子:
public class InputStreamProcessingTemplate {
public static void process(String fileName,
InputStreamProcessor processor){
IOException processException = null;
InputStream input = null;
try{
input = new FileInputStream(fileName);
processor.process(input);
} catch (IOException e) {
processException = e;
} finally {
if(input != null){
try {
input.close();
} catch(IOException e){
if(processException != null){
throw new MyException(processException, e,
"Error message..." +
fileName);
} else {
throw new MyException(e,
"Error closing InputStream for file " +
fileName;
}
}
}
if(processException != null){
throw new MyException(processException,
"Error processing InputStream for file " +
fileName;
}
}
}
将process(...)方法简单地设置为静态。调用该方法的外观如下:
InputStreamProcessingTemplate.process("someFile.txt",
new InputStreamProcessor(){
public void process(InputStream input) throws IOException{
int inChar = input.read();
while(inChar !- -1){
//do something with the chars...
}
}
});
注意,对模板的process()方法的调用现在是一个静态方法调用。
概括
异常处理模板是一种简单而强大的机制,可以提高代码的质量和可读性。由于我们无需编写太多的代码,也不必担心,因此它也可以提高工作效率。异常由模板处理。并且,如果我们需要在开发过程的后期改进异常处理,则只需更改一下即可:异常处理模板。
模板方法设计模式可以用于除异常处理之外的其他目的。输入流的迭代也可以放入模板中。 JDBC中ResultSet的迭代可以放在模板中。可以将正确执行JDBC中的事务放入模板中。

