接上篇Java常见问题汇总(一)重复包装RuntimeException错误的写法: try { doStuff(); } catch(Exception e) { throw new RuntimeException(e); } 正确的写法: try { doStuff(); } catch(RuntimeException e) { throw e; } catch(Exception e) { throw new RuntimeException(e.getMessage(), e); } try { doStuff(); } catch(IOException e) { throw new RuntimeException(e.getMessage(), e); } catch(NamingException e) { throw new RuntimeException(e.getMessage(), e); } 不正确的传播异常错误的写法: try { } catch(ParseException e) { throw new RuntimeException(); throw new RuntimeException(e.toString()); throw new RuntimeException(e.getMessage()); throw new RuntimeException(e); } 主要是没有正确的将内部的错误信息传递给调用者. 第一个完全丢掉了内部错误信息, 第二个错误信息依赖toString方法, 如果没有包含最终的嵌套错误信息, 也会出现丢失, 而且可读性差. 第三个稍微好一些, 第四个跟第二个一样。 正确的写法: try { } catch(ParseException e) { throw new RuntimeException(e.getMessage(), e); } 用日志记录异常错误的写法: try { ... } catch(ExceptionA e) { log.error(e.getMessage(), e); throw e; } catch(ExceptionB e) { log.error(e.getMessage(), e); throw e; } 一般情况下在日志中记录异常是不必要的, 除非调用方没有记录日志。 异常处理不彻底错误的写法: try { is = new FileInputStream(inFile); os = new FileOutputStream(outFile); } finally { try { is.close(); os.close(); } catch(IOException e) { /* we can't do anything */ } } is可能close失败, 导致os没有close 正确的写法: try { is = new FileInputStream(inFile); os = new FileOutputStream(outFile); } finally { try { if (is != null) is.close(); } catch(IOException e) {/* we can't do anything */} try { if (os != null) os.close(); } catch(IOException e) {/* we can't do anything */} } 捕获不可能出现的异常错误的写法: try { ... do risky stuff ... } catch(SomeException e) { // never happens } ... do some more ... 正确的写法: try { ... do risky stuff ... } catch(SomeException e) { // never happens hopefully throw new IllegalStateException(e.getMessage(), e); // crash early, passing all information } ... do some more ... transient的误用错误的写法: public class A implements Serializable { private String someState; private transient Log log = LogFactory.getLog(getClass()); public void f() { log.debug('enter f'); ... } } 这里的本意是不希望Log对象被序列化. 不过这里在反序列化时, 会因为log未初始化, 导致f()方法抛空指针, 正确的做法是将log定义为静态变量或者定位为具备变量。 正确的写法: public class A implements Serializable { private String someState; private static final Log log = LogFactory.getLog(A.class); public void f() { log.debug('enter f'); ... } } public class A implements Serializable { private String someState; public void f() { Log log = LogFactory.getLog(getClass()); log.debug('enter f'); ... } } 不必要的初始化错误的写法: public class B { private int count = 0; private String name = null; private boolean important = false; } 这里的变量会在初始化时使用默认值:0, null, false, 因此上面的写法有些多此一举。 正确的写法: public class B { private int count; private String name; private boolean important; } 最好用静态final定义Log变量private static final Log log = LogFactory.getLog(MyClass.class); 这样做的好处有三:
选择错误的类加载器错误的代码: Class clazz = Class.forName(name); Class clazz = getClass().getClassLoader().loadClass(name); 这里本意是希望用当前类来加载希望的对象, 但是这里的getClass()可能抛出异常, 特别在一些受管理的环境中, 比如应用服务器, web容器, Java WebStart环境中, 最好的做法是使用当前应用上下文的类加载器来加载。 正确的写法: ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (cl == null) cl = MyClass.class.getClassLoader(); // fallback Class clazz = cl.loadClass(name); 反射使用不当错误的写法: Class beanClass = ... if (beanClass.newInstance() instanceof TestBean) ... 这里的本意是检查beanClass是否是TestBean或是其子类, 但是创建一个类实例可能没那么简单, 首先实例化一个对象会带来一定的消耗, 另外有可能类没有定义默认构造函数. 正确的做法是用Class.isAssignableFrom(Class) 方法。 正确的写法: Class beanClass = ... if (TestBean.class.isAssignableFrom(beanClass)) ... 不必要的同步错误的写法: Collection l = new Vector(); for (...) { l.add(object); } Vector是ArrayList同步版本。 正确的写法: Collection l = new ArrayList(); for (...) { l.add(object); } 错误的选择List类型根据下面的表格数据来进行选择
HashMap size陷阱错误的写法: Map map = new HashMap(collection.size()); for (Object o : collection) { map.put(o.key, o.value); } 这里可以参考guava的Maps.newHashMapWithExpectedSize的实现. 用户的本意是希望给HashMap设置初始值, 避免扩容(resize)的开销. 但是没有考虑当添加的元素数量达到HashMap容量的75%时将出现resize。 正确的写法: Map map = new HashMap(1 (int) (collection.size() / 0.75)); ASO推荐 |
|