不指定超时时间
错误的代码:
- Socket socket = ...   
 - socket.connect(remote);   
 - InputStream in = socket.getInputStream();   
 - int i = in.read();  
 
这种情况在工作中已经碰到不止一次了。个人经验一般超时不要超过20s。这里有一个问题,connect可以指定超时时间,但是read无法指定超时时间。但是可以设置阻塞(block)时间。
正确的写法:
- Socket socket = ...   
 - socket.connect(remote, 20000); // fail after 20s   
 - InputStream in = socket.getInputStream();   
 - socket.setSoTimeout(15000);   
 - int i = in.read();  
 
另外,文件的读取(FileInputStream, FileChannel, FileDescriptor, File)没法指定超时时间, 而且IO操作均涉及到本地方法调用, 这个更操作了JVM的控制范围,在分布式文件系统中,对IO的操作内部实际上是网络调用。一般情况下操作60s的操作都可以认为已经超时了。为了解决这些问题,一般采用缓存和异步/消息队列处理。
频繁使用计时器
错误代码:
- for (...) {   
 -  long t = System.currentTimeMillis();   
 -  long t = System.nanoTime();   
 -  Date d = new Date();   
 -  Calendar c = new GregorianCalendar();   
 - } 
 
每次new一个Date或Calendar都会涉及一次本地调用来获取当前时间(尽管这个本地调用相对其他本地方法调用要快)。
如果对时间不是特别敏感,这里使用了clone方法来新建一个Date实例。这样相对直接new要高效一些。
正确的写法:
- Date d = new Date();   
 - for (E entity : entities) {   
 -  entity.doSomething();   
 -  entity.setUpdated((Date) d.clone());   
 - } 
 
如果循环操作耗时较长(超过几ms),那么可以采用下面的方法,立即创建一个Timer,然后定期根据当前时间更新时间戳,在我的系统上比直接new一个时间对象快200倍:
- private volatile long time;   
 - Timer timer = new Timer(true);   
 - try {   
 -  time = System.currentTimeMillis();   
 -  timer.scheduleAtFixedRate(new TimerTask() {   
 -  public void run() {   
 -  time = System.currentTimeMillis();   
 -  }   
 -  }, 0L, 10L); // granularity 10ms   
 -  for (E entity : entities) {   
 -  entity.doSomething();   
 -  entity.setUpdated(new Date(time));   
 -  }   
 - } finally {   
 - timer.cancel();   
 - } 
 
捕获所有的异常
错误的写法:
- Query q = ...   
 - Person p;   
 - try {   
 -  p = (Person) q.getSingleResult();   
 - } catch(Exception e) {   
 - p = null;   
 - } 
 
这是EJB3的一个查询操作,可能出现异常的原因是:结果不唯一;没有结果;数据库无法访问,而捕获所有的异常,设置为null将掩盖各种异常情况。
正确的写法:
- Query q = ...   
 - Person p;   
 - try {   
 -  p = (Person) q.getSingleResult();   
 - } catch(NoResultException e) {   
 -  p = null;   
 - } 
 
忽略所有异常
错误的写法:
- try {   
 -  doStuff();   
 - } catch(Exception e) {   
 - log.fatal("Could not do stuff");   
 - }   
 - doMoreStuff(); 
 
这个代码有两个问题, 一个是没有告诉调用者, 系统调用出错了. 第二个是日志没有出错原因, 很难跟踪定位问题。
正确的写法:
- try {   
 -  doStuff();   
 - } catch(Exception e) {   
 - throw new MyRuntimeException("Could not do stuff because: "+ e.getMessage, e);   
 - } 
 
重复包装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);   
 - }