在网上看到一篇关于关闭资源的正确方式:
该博文中的总结:
(1)使用finally块来关闭物理资源(非托管资源),保证关闭操作始终会被执行;
(2)关闭每个资源之前首先保证引用该资源的引用变量不为null;
(3)为每个物理资源使用单独的trycatch块关闭资源,保证关闭资源时引发的异常不会影响其他资源的关闭。
在资源过多的时候,我们要在finally块中写很多的非空判断、以及try-catch块。如果没有关闭非托管资源,比如Connection,即使你给它赋值为null, 它也会常驻内存,必须要手动关闭非托管资源。不管是托管资源还是非托管资源,我们都希望能用相同的方式关闭。为了防止某些时候忘了给资源做非空判断、对每个资源都使用一个try-catch块关闭而导致资源/内存泄漏(), 我们自制一些关闭资源的工具类。而且使用工具类可以减少代码冗余、使代码可读性更好。
下面是我写的工具类:
CloseResources.java
public class CloseResources { private static final Object[] PARAMS = new Object[0];//使用反射调用方法时传入的参数,可以当作null private static final ExceptionHandle[] HANDLERS = new ExceptionHandle[0];//空的异常处理器,可以认为是null private static boolean invokeCloseMethod(Object o, Method[] methods) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { //用反射执行无参的close()方法 for (Method method : methods) { //System.out.println(method.getName()); if (method.getName().equals("close") && method.getParameterTypes().length == 0) { System.out.println("############调用close() " + o.getClass()); method.invoke(o, PARAMS); return true; } } return false; } public static void close(Object o) { close(o, null); } public static void close(Object o, ExceptionHandle handle) { //ExceptionHandle是我自己定义的 if (o == null) { //如果为空就不调用close()了,免得空指针异常 return; } Class clazz = o.getClass(); try { if (!invokeCloseMethod(o, clazz.getDeclaredMethods())) { //如果重写了close(),就调用重写的 invokeCloseMethod(o, clazz.getMethods());//如果没有重写close(),就调用继承的close() } } catch (SecurityException e) { if (handle != null) { handle.handleSecurityException(e); } } catch (IllegalAccessException e) { if (handle != null) { handle.handleIllegalAccessException(e); } } catch (IllegalArgumentException e) { if (handle != null) { handle.handleIllegalArgumentException(e); } } catch (InvocationTargetException e) { if (handle != null) { handle.handleInvocationTargetException(e); } } catch (Exception e) { if (handle != null) { handle.handleException(e); } } } public static void closeCollection(Collection collection) { //关闭collection,比如List closeCollection(collection, HANDLERS); } public static void closeCollection(Collection collection, ExceptionHandle ... handles) { if (collection == null) { return; } int i = 0; for (Object o : collection) { if (handles == null) { close(o); } else if (i < handles.length) { close(o, handles[i++]); } else { close(o); } } collection.clear(); } public staticvoid closeMap(Map map) { //关闭map closeMap(map, HANDLERS); } public static void closeMap(Map map, ExceptionHandle ... handles) { if (map == null) { return; } Set keys = map.keySet(); int i = 0; for (K key : keys) { if (handles == null) { close(map.get(key)); } else if (i < handles.length) { close(map.get(key), handles[i++]); } else { close(map.get(key)); } } map.clear(); }}
CloseResources工具类的几个方法:
public static void close(Object o):关闭o,不做任何处理public static void close(Object o, ExceptionHandle handle): 用自定义处理异常的方式关闭opublic static void closeCollection(Collection collection): 关闭collection中的所有资源,并清空collectionpublic static void closeCollection(Collection collection, ExceptionHandle ... handles): 用自定义异常处理的方式分别关闭collection中的所有资源,并清空collection putlic staticvoid closeMap(Map map): 关闭map中的所有资源,并清空mapputlic static void closeMap(Map map, ExceptionHandle ... handles): 用自定义异常处理的方式分别关闭map中的所有资源,并清空map
如果资源o为null,那么CloseResources的close方法会直接返回,不做处理
使用CloseResources的close方法时还可以传入ExceptionHandle类来接受关闭资源时抛出的异常,并做处理ExceptionHandle.java
public class ExceptionHandle { public void handleSecurityException(SecurityException e) {} public void handleIllegalAccessException(IllegalAccessException e) {} public void handleIllegalArgumentException(IllegalArgumentException e) {} public void handleInvocationTargetException(InvocationTargetException e) {} public void handleException(Exception e) {}}
MyResource.java:
我在close方法中强行抛出IOException,这样方便等会儿使用自定义异常处理的方式关闭资源
public class MyResource implements Closeable { @Override public void close() throws IOException { throw new IOException("调用MyResource的close()时强行抛出IOException"); }}
主类:
public class Main { public static void main(String[] args) throws Exception { BufferedInputStream bis = null; BufferedOutputStream bos = null; List
运行程序的结果:
在主方法中我写了这几句话:
@Overridepublic void handleInvocationTargetException(InvocationTargetException e) { System.out.println("ok"); Throwable t = e.getCause(); t.printStackTrace();}
可以看见,我打印的是InvocationTargetException的Cause,如果直接打印e,会包含InvocationTargetException的信息,实际上我们只需要close()抛出的IOException,因此我先获得Cause。如果不想用这种方式,而是直接把Cause: IOException传到handleException,不分出那么多的自定义异常处理方法,可以自己修改一下代码。在实际场景,不应该仅仅只调用printStackTrace(),而是把异常写入日志、尝试修复异常、做一些业务。
应该指出:这个工具类还很不完善,例如,只能执行资源无参的close()方法,还没有经过足够多的实际场景去验证它的性能、鲁棒性