配色: 字号:
Java中正确的异常处理
2015-08-26 | 阅:  转:  |  分享 
  
看到很多朋友用Java的时候异常处理非常随便,因此把我写的书的节选发过来。节选自杨

中科《J2EE开发全程实录》异常是Java语言在语法层面提供的面向对象的例外处理机制,

属于核心的Java特性,正确、有效的使用异常机制将会大大提高系统的开发效率和稳定性。

8.1.1.异常处理的方式:

当程序进入某种不被期望的状态时,异常就被抛出了。Java中的异常分为两种:受查异常

(checkedexception)与非受查异常(uncheckedexception)。非受查异常一般直接或者间接的从

RuntimeException继承,其他的从Exception类继承而非从RuntimeException继承的即被称为

受查异常。受查异常是必须被处理的:或者在代码中处理或者将异常向上抛出。具体来说处

理方式有如下几种:

u吃掉异常

try

{







}

catch(ClassNotFoundExceptione)

{





//donothing

}

异常是系统中的非正常行为,如果将异常吃掉,将会使这种非正常行为得不到暴露,有可能

会使得系统进入某种非预期状态。异常一旦发生了就一定要暴露出来,哪怕这个异常不需要

再次抛出,不需要额外的处理代码,那把异常打印出来也是有益的。

u打印异常

这种处理方式比上一种好一些,因为它打印出了异常,能使我们观察到这个异常的存在,这

在很多试验性代码甚至是正式运行的代码中经常看到的。

try

{







}

catch(ClassNotFoundExceptione)

{





e.printStackTrace();

}

这种异常处理方式在试验性代码中出现是可以的,但是如果在正式运行的代码中出现则不能

被接受。e.printStackTrace()尽管很方便,但开销巨大。在磁盘I/O期间,e.printStackTrace()

对I/O处理进行同步,这极大降低了吞吐量。在缺省情况下,堆栈跟踪被记录到控制台。

但是,在生产系统中,浏览控制台以查看异常跟踪是行不通的。而且不能保证堆栈跟踪会显

示在生产系统中,如果把应用程序服务器作为NT服务运行甚至不会有控制台。即使把控制

台日志重定向到一个输出文件,当产品应用程序服务器重新启动时,这个文件很可能也将被

重写。

如果异常确实不用处理,应该用日志系统将异常输出的日志中去。在Java领域目前有两个

日志工具非常流行:一个是Log4J,它是来自Apache的Jakarta的一个开放源代码的项目;

另一个是J2SE1.4捆绑提供的。以Log4j为例:

try

{







}

catch(ClassNotFoundExceptione)

{





Logger.getLogger(getClass()).error(e.getMessage,e);

}

这种输出异常的方式一般只适用于开发人员认为此异常不应该暴露给上层的情况,而对于其

他的则不应该使用此处理方式。

u将异常处理后重新抛出

try

{







}

catch(IOExceptione)

{





found=false;





throwe;

}

u将异常转换成其他受查异常重新抛出

try

{



testException();

}catch(LoadPluginClassExceptione)

{



e.printStackTrace();

}

privatestaticvoidtestException()throwsLoadPluginClassException

{



try



{





Classclass1=Class.forName("com.cownew.test.Class1");





Classclass2=Class.forName("com.cownew.test.Class2");



}catch(ClassNotFoundExceptione)



{





thrownewLoadPluginClassException("PluginClassnotfound");



}

}

运行结果:

com.LoadPluginClassException:PluginClassnotfound

atcom.Main.testException(Main.java:54)

atcom.Main.main(Main.java:37)

从打印出的异常堆栈中看不到到底是加载Class1的时候发生异常还是加载Class2的时候发

生异常。这种无法定位到异常源头的处理方式无论是对开发人员还是对正式运行的系统来说

都是很恼火的事情。

为什么在异常堆栈中看不到在哪里抛出的ClassNotFoundException呢?因为在Java看来这

个LoadPluginClassException是从它被throw的位置抛出的,发生系统异常的地方也就是在这

个地方。为了能让Java把原始的异常也打印出来,可以将原始异常做为根异常传递给新抛

出的异常即可。

try

{



testException();

}catch(LoadPluginClassExceptione)

{



e.printStackTrace();

}

privatestaticvoidtestException()throwsLoadPluginClassException

{



try



{





Classclass1=Class.forName("com.cownew.test.Class1");





Classclass2=Class.forName("com.cownew.test.Class2");



}catch(ClassNotFoundExceptione)



{





thrownewLoadPluginClassException("PluginClassnotfound",e);



}

}

运行结果:

com.LoadPluginClassException:PluginClassnotfound



atcom.Main.testException(Main.java:54)



atcom.Main.main(Main.java:37)

Causedby:java.lang.ClassNotFoundException:com.cownew.test.Class1



atjava.net.URLClassLoader$1.run(UnknownSource)



atjava.security.AccessController.doPrivileged(NativeMethod)



atjava.net.URLClassLoader.findClass(UnknownSource)



atjava.lang.ClassLoader.loadClass(UnknownSource)



atsun.misc.Launcher$AppClassLoader.loadClass(UnknownSource)



atjava.lang.ClassLoader.loadClass(UnknownSource)



atjava.lang.ClassLoader.loadClassInternal(UnknownSource)



atjava.lang.Class.forName0(NativeMethod)



atjava.lang.Class.forName(UnknownSource)



atcom.Main.testException(Main.java:50)



...1more

对原始异常进行包装,无论异常抛到哪里,我们都能清晰的看到异常的源头。

u将异常转化为非受查异常抛出

与C#、Delphi等语言不同,Java在异常处理方面更加严谨,Java的受查异常要求开发人员必

须处理。这在增强了系统的逻辑正确性的同时,也使得代码异常复杂,到处充满着异常抛出、

异常声明、异常捕获的代码。开发人员对于自己无法处理的异常有两种方式,一种懒人是直

接吃掉异常,另一种追求严谨的开发人员会把异常继续向上抛出,比如:

privatestaticvoidfooBar()throwsInstantiationException,







IllegalAccessException,ClassNotFoundException

{



Objectobj=Class.forName("com.test.MyClass").newInstance();



inti=Integer.parseInt(obj.toString());



System.out.println(i);

}

这么一个简单的方法竟然抛出如此多的异常,而且调用者也许也不明白为什么这个方法要抛

出ClassNotFoundException,所以调用者也继续向上抛,代码中就充斥着大量的异常声明。

越来越多的人发现过多的使用受查异常给代码带来的坏处,所以非受查异常也逐渐被越来越

多的人接受,最突出的就是在Spring提供的一些JDBC、ORM等框架中很多地方使用了非受

查异常,这样应用程序对其关心的非受查异常可以有选择的进行捕获处理。案例系统中提供

了一个将受查异常转换为通用非受查异常的方法:即定义在

com.cownew.ctk.common.ExceptionUtils类中的publicstaticCTKRunTimeException

toRuntimeException(Throwablee)方法:

publicstaticCTKRunTimeExceptiontoRuntimeException(Throwablee)

{



CTKRunTimeExceptionre=newCTKRunTimeException(e);



re.setStackTrace(e.getStackTrace());



Logger.getLogger(ExceptionUtils.class).error(e.getMessage(),e);



returnre;

}

其中CTKRunTimeException类的定义如下:

publicclassCTKRunTimeExceptionextendsRuntimeException

{



publicCTKRunTimeException(Throwablecause)



{





super(cause);



}

}

使用示例:

try

{



inti=Integer.parseInt(“123”);

}

catch(NumberFormatExceptionfe)

{



throwExceptionUtils.toRuntimeException(fe);

}

转化为非受查异常的这种方式不能滥用,应该有选择的使用,这种方式一般适用于一下几种

情况:

u这种异常极少发生;

u这种异常不应该让调用者知道且不能转化为其他受查异常;

u方法是覆盖父类的方法,父类的方法签名中没有声明抛出异常;

u在static初始化块中;

不要在基础性的API中滥用非受查异常;

献花(0)
+1
(本文系如鹏网在线...首藏)