小心例外带来的风险(2)
接上文:小心例外带来的风险(1)
不要捕获泛型例外
在复杂的软件中,经常会有一些特定的代码块执行时会抛出多种不同例外的方法。动态装入一个类和实例化一个对象都可能会产生几个不同的例外,包括ClassNotFoundException, InstantiationException, IllegalAccessException, 和 ClassCastException。
一个繁忙的程序员在遇到这种情况时可能简单的把方法调用包在一个只会捕获泛型例外Exception的try/catch块,而不是添加四个不同的catch块到try块后面(看下面的代码清单3)。这看起来似乎无可置否,却会产生一些无意识的副面效果。例如,如果className()是null,那么Class.forName()将会抛出一个NullPointerException例外并在这个方法中被捕获。在这种情况下,catch块将捕获此例外虽然它从没打算去捕获这样一个例外,只是因为NullPointerException是RuntimeException的一个子类,而且RuntimeException又是Exception的一个子类。所以一个普通的catch(Exception e)将会捕获所有RuntimeException的子类,包括NullPointerException, IndexOutOfBoundsException, 和ArrayStoreException。通常,一个程序员并不打算去捕获这些例外。
在代码清单3中,null的className会导致一个NullPointerException例外产生,它告诉在调用的方法中类名无效。
代码清单3
- public SomeInte易做图ce buildInstance(String className) {
- SomeInte易做图ce impl = null;
- try {
- Class clazz = Class.forName(className);
- impl = (SomeInte易做图ce)clazz.newInstance();
- }
- catch (Exception e) {
- log.error("Error creating class: " + className);
- }
-
- return impl;
- }
另一个使用泛型捕获子句的结果是限制日志记录,因为catch不知道到底那一个特殊的例外被捕获。有些程序员在面对这种问题的时候,采取添加检测的手段去查看例外的类型(代码清单4),而这正好与使用catch块的目的相背离。
代码清单4
- catch (Exception e) {
- if (e instanceof ClassNotFoundException) {
- log.error("Invalid class name: " + className + ", " + e.toString());
- }
- else {
- log.error("Cannot create class: " + className + ", " + e.toString());
- }
- }
代码清单5提供一种完整的捕获特殊例外的例子,一些程序员可能会对它感趣。操作符instanceof不是必须的因为这个特殊的例外自会被捕获。每一个被检查的例外(ClassNotFoundException, InstantiationException, IllegalAccessException) 会被捕获和处理。对于一个类装入正确,但是却没有实现SomeInte易做图ce接口这种特殊情况会产生一个ClassCastException例外,这个例外也会被查证。
代码清单5
- public SomeInte易做图ce buildInstance(String className) {
- SomeInte易做图ce impl = null;
- try {
- Class clazz = Class.forName(className);
- impl = (SomeInte易做图ce)clazz.newInstance();
- }
- catch (ClassNotFoundException e) {
- log.error("Invalid class name: " + className + ", " + e.toString());
- }
- catch (InstantiationException e) {
- log.error("Cannot create class: " + className + ", " + e.toString());
- }
- catch (IllegalAccessException e) {
- log.error("Cannot create class: " + className + ", " + e.toString());
- }
- catch (ClassCastException e) {
- log.error("Invalid class type, " + className
- + " does not implement " + SomeInte易做图ce.class.getName());
- }
-
- return impl;
- }
在某些情况下,更好的方法是重新抛出一个已知的例外(或者叫创建一个新的例外)而不是试图去在当前这个方法中处理。这允许调用方法通过放置这个例外到一个已知的上下文中去处理这种错误情形。
下面的代码清单6提供了一个buildInte易做图ce()方法的替换版本。如果在装入和实例化类时发生问题,这个版本会抛出一个ClassNotFoundException例外。在这个例子中,调用方易做图确保得到一个正确的实例化对象或者是一个例外。这样调用方法就不需要去检查返回的对象是否为空了。
注意这个例子使用了Java 1.4的方法来创建一个已经被另外的例外封装的新的例外,以便保存原始的堆栈跟踪信息。否则
补充:软件开发 , Java ,