在实际开发中,经常需要在程序中打开一些物理资源,如数据库连接、网络连接、磁盘文件等,打开这些物理资源之后必须显式关闭,否则将会引起资源泄漏。 JVM的垃圾回收机制不会回收这些资源,垃圾回收机制属于Java内存管理的一部分,它只是负责回收堆内存中分配出来的内存,至于程序中打开的物理资源,垃圾回收机制是无能为力的。 为了正常关闭程序中打开的物理资源,应该使用finally块来保证回收。 下面程序示范了常见的数据库资源。 importjava.sql.Connection; importjava.sql.DriverManager; importjava.sql.PreparedStatement; importjava.sql.ResultSet; importjava.sql.SQLException; public class Tester { public static void main(String[] args) { String url = "jdbc:mysql://localhost:3306/test"; String user = "root"; String password = "root"; Connection conn = null; PreparedStatement pst = null; ResultSet rs = null; try { Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection(url,user, password); pst = conn.prepareStatement("select* from test"); rs = pst.executeQuery(); while (rs.next()) { System.out.println(rs.getObject(0)); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } finally { try rs.close(); pst.close(); conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } 虽然程序已经使用finally块来保证资源被关闭,但是这个程序的关闭是不安全的。因为程序开始时指定conn = null;pst = null;rs = null;,完全有可能在程序运行过程中初始化conn之前就引发了异常,那么conn、pst、rs还未来得及初始化,因此它们根本无需关闭。 将上面程序改为如下形式: importjava.sql.Connection; importjava.sql.DriverManager; importjava.sql.PreparedStatement; importjava.sql.ResultSet; importjava.sql.SQLException; public class Tester { public static void main(String[] args) { String url = "jdbc:mysql://localhost:3306/test"; String user = "root"; String password = "root"; Connection conn = null; PreparedStatement pst = null; ResultSet rs = null; try { Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection(url,user, password); pst = conn.prepareStatement("select* from test"); rs = pst.executeQuery(); while (rs.next()) { System.out.println(rs.getObject(0)); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } finally { try { if(rs != null){ rs.close(); } if(pst != null){ pst.close(); } if(conn != null){ conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } } } 程序首先保存rs不为null才关闭,再保证pst不为null才关闭pst,再保证conn不为null才关闭conn。 这样表面看起来安全,实际上并不是这样。假如程序开始已经正常初始化了conn、pst、rs,在关闭rs时出现了异常,那么程序将在关闭rs时非正常退出,这样就会导致pst、conn得不到关闭,从而导致资源泄漏。 为了保证关闭各资源时出现的异常不会相互影响,应该在关闭每个资源时分开使用try...catch块来保证关闭操作不会导致程序非正常退出。 importjava.sql.Connection; importjava.sql.DriverManager; importjava.sql.PreparedStatement; importjava.sql.ResultSet; importjava.sql.SQLException; public class Tester { public static void main(String[] args) { String url = "jdbc:mysql://localhost:3306/test"; String user = "root"; String password = "root"; Connection conn = null; PreparedStatement pst = null; ResultSet rs = null; try { Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection(url,user, password); pst = conn.prepareStatement("select* from test"); rs = pst.executeQuery(); while(rs.next()) { System.out.println(rs.getObject(0)); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } finally { if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (pst != null) { try { pst.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } } 上面程序所示的资源关闭方式才是比较安全的,这种关闭方式主要保证如下3点: 1. 使用finally块来关闭物理资源,保证关闭操作总是会被执行; 2. 关闭每个资源之前首先保证引用该资源的引用变量不为null; 3. 为每个物理资源使用单独try...catch块关闭资源,保证关闭资源时引发的异常不会影响其他资源的关闭。