java编程常见错误七[转帖]_Android, Python及开发编程讨论区_Weblogic技术|Tuxedo技术|中间件技术|Oracle论坛|JAVA论坛|Linux/Unix技术|hadoop论坛_联动北方技术论坛  
网站首页 | 关于我们 | 服务中心 | 经验交流 | 公司荣誉 | 成功案例 | 合作伙伴 | 联系我们 |
联动北方-国内领先的云技术服务提供商
»  游客             当前位置:  论坛首页 »  自由讨论区 »  Android, Python及开发编程讨论区 »
总帖数
1
每页帖数
101/1页1
返回列表
0
发起投票  发起投票 发新帖子
查看: 4298 | 回复: 0   主题: java编程常见错误七[转帖]        下一篇 
lynda
注册用户
等级:上尉
经验:570
发帖:49
精华:0
注册:2012-8-7
状态:离线
发送短消息息给lynda 加好友    发送短消息息给lynda 发消息
发表于: IP:您无权察看 2012-8-8 17:20:40 | [全部帖] [楼主帖] 楼主

不使用finally块释放资源

错误的写法:

  1. public void save(File f) throws IOException {   
  2.  OutputStream out = new BufferedOutputStream(new FileOutputStream(f));   
  3.  out.write(...);   
  4.  out.close();   
  5. }   
  6. public void load(File f) throws IOException {   
  7.  InputStream in = new BufferedInputStream(new FileInputStream(f));   
  8.  in.read(...);   
  9.  in.close();   

上面的代码打开一个文件输出流, 操作系统为其分配一个文件句柄, 但是文件句柄是一种非常稀缺的资源, 必须通过调用相应的close方法来被正确的释放回收. 而为了保证在异常情况下资源依然能被正确回收, 必须将其放在finally block中. 上面的代码中使用了BufferedInputStream将file stream包装成了一个buffer stream, 这样将导致在调用close方法时才会将buffer stream写入磁盘. 如果在close的时候失败, 将导致写入数据不完全. 而对于FileInputStream在finally block的close操作这里将直接忽略。

如果BufferedOutputStream.close()方法执行顺利则万事大吉, 如果失败这里有一个潜在的bug(http://bugs.sun.com/view_bug.do?bug_id=6335274): 在close方法内部调用flush操作的时候, 如果出现异常, 将直接忽略. 因此为了尽量减少数据丢失, 在执行close之前显式的调用flush操作。

下面的代码有一个小小的瑕疵: 如果分配file stream成功, 但是分配buffer stream失败(OOM这种场景), 将导致文件句柄未被正确释放. 不过这种情况一般不用担心, 因为JVM的gc将帮助我们做清理。

  1. // code for your cookbook   
  2. public void save() throws IOException {   
  3.  File f = ...   
  4.  OutputStream out = new BufferedOutputStream(new FileOutputStream(f));   
  5.  try {   
  6.  out.write(...);   
  7.  out.flush(); // don't lose exception by implicit flush on close   
  8.  } finally {   
  9.  out.close();   
  10. }   
  11. }   
  12. public void load(File f) throws IOException {   
  13.  InputStream in = new BufferedInputStream(new FileInputStream(f));   
  14.  try {   
  15.  in.read(...);   
  16.  } finally {   
  17. try { in.close(); } catch (IOException e) { }   
  18. }   

数据库访问也涉及到类似的情况:

  1. Car getCar(DataSource ds, String plate) throws SQLException {   
  2.  Car car = null;   
  3.  Connection c = null;   
  4.  PreparedStatement s = null;   
  5.  ResultSet rs = null;   
  6.  try {   
  7.  c = ds.getConnection();   
  8.  s = c.prepareStatement("select make, color from cars where plate=?");   
  9.  s.setString(1, plate);   
  10.  rs = s.executeQuery();   
  11.  if (rs.next()) {   
  12.  car = new Car();   
  13.  car.make = rs.getString(1);   
  14.  car.color = rs.getString(2);   
  15.  }   
  16.  } finally {   
  17. if (rs != null) try { rs.close(); } catch (SQLException e) { }   
  18. if (s != null) try { s.close(); } catch (SQLException e) { }   
  19. if (c != null) try { c.close(); } catch (SQLException e) { }   
  20. }   
  21. return car;   

finalize方法误用

错误的写法:

  1. public class FileBackedCache {   
  2.  private File backingStore;   
  3.  
  4.  ...   
  5.  
  6.  protected void finalize() throws IOException {   
  7.  if (backingStore != null) {   
  8.  backingStore.close();   
  9.  backingStore = null;   
  10.  }   
  11.  }   

这个问题Effective Java这本书有详细的说明. 主要是finalize方法依赖于GC的调用, 其调用时机可能是立马也可能是几天以后, 所以是不可预知的. 而JDK的API文档中对这一点有误导:建议在该方法中来释放I/O资源。

正确的做法是定义一个close方法, 然后由外部的容器来负责调用释放资源。

  1. public class FileBackedCache {   
  2.  private File backingStore;   
  3.  
  4.  ...   
  5.  
  6.  public void close() throws IOException {   
  7.  if (backingStore != null) {   
  8.  backingStore.close();   
  9.  backingStore = null;   
  10.  }   
  11.  }   

在JDK 1.7 (Java 7)中已经引入了一个AutoClosable接口. 当变量(不是对象)超出了try-catch的资源使用范围, 将自动调用close方法。

  1. try (Writer w = new FileWriter(f)) { // implements Closable   
  2.  w.write("abc");   
  3.  // w goes out of scope here: w.close() is called automatically in ANY case   
  4. } catch (IOException e) {   
  5.  throw new RuntimeException(e.getMessage(), e);   

Thread.interrupted方法误用

错误的写法:

  1. try {   
  2.  Thread.sleep(1000);   
  3. } catch (InterruptedException e) {   
  4.  // ok   
  5. }   
  6. or   
  7. while (true) {   
  8.  if (Thread.interrupted()) break;   

这里主要是interrupted静态方法除了返回当前线程的中断状态, 还会将当前线程状态复位。

正确的写法:

  1. try {   
  2.  Thread.sleep(1000);   
  3. } catch (InterruptedException e) {   
  4.  Thread.currentThread().interrupt();   
  5. }   
  6. or   
  7. while (true) {   
  8.  if (Thread.currentThread().isInterrupted()) break;   

在静态变量初始化时创建线程

错误的写法:

  1. class Cache {   
  2.  private static final Timer evictor = new Timer();   

Timer构造器内部会new一个thread, 而该thread会从它的父线程(即当前线程)中继承各种属性。比如context classloader, threadlocal以及其他的安全属性(访问权限)。 而加载当前类的线程可能是不确定的,比如一个线程池中随机的一个线程。如果你需要控制线程的属性,最好的做法就是将其初始化操作放在一个静态方法中,这样初始化将由它的调用者来决定。

正确的做法:

  1. class Cache {   
  2.  private static Timer evictor;   
  3.  public static setupEvictor() {   
  4.  evictor = new Timer();   
  5.  }   

已取消的定时器任务依然持有状态

错误的写法:

  1. final MyClass callback = this;   
  2. TimerTask task = new TimerTask() {   
  3.  public void run() {   
  4.  callback.timeout();   
  5.  }   
  6. };   
  7. timer.schedule(task, 300000L);   
  8. try {   
  9.  doSomething();   
  10. } finally {   
  11. task.cancel();   

上面的task内部包含一个对外部类实例的应用, 这将导致该引用可能不会被GC立即回收. 因为Timer将保留TimerTask在指定的时间之后才被释放. 因此task对应的外部类实例将在5分钟后被回收。

正确的写法:

  1. TimerTask task = new Job(this);   
  2. timer.schedule(task, 300000L);   
  3. try {   
  4.  doSomething();   
  5. } finally {   
  6. task.cancel();   
  7. }   
  8. static class Job extends TimerTask {   
  9.  private MyClass callback;   
  10.  public Job(MyClass callback) {   
  11.  this.callback = callback;   
  12.  }   
  13.  public boolean cancel() {   
  14.  callback = null;   
  15.  return super.cancel();   
  16.  }   
  17.  public void run() {   
  18.  if (callback == null) return;   
  19.  callback.timeout();   
  20.  }   




赞(0)    操作        顶端 
总帖数
1
每页帖数
101/1页1
返回列表
发新帖子
请输入验证码: 点击刷新验证码
您需要登录后才可以回帖 登录 | 注册
技术讨论