在网上看到一篇关于关闭资源的正确方式:http://blog.csdn.net/bornforit/article/details/6896775

该博文中的总结:

(1)使用finally块来关闭物理资源(非托管资源),保证关闭操作始终会被执行;

(2)关闭每个资源之前首先保证引用该资源的引用变量不为null;

(3)为每个物理资源使用单独的trycatch块关闭资源,保证关闭资源时引发的异常不会影响其他资源的关闭。

在资源过多的时候,我们要在finally块中写很多的非空判断、以及try-catch块。如果没有关闭非托管资源,比如Connection,即使你给它赋值为null, 它也会常驻内存,必须要手动关闭非托管资源。不管是托管资源还是非托管资源,我们都希望能用相同的方式关闭。为了防止某些时候忘了给资源做非空判断、对每个资源都使用一个try-catch块关闭而导致资源/内存泄漏(), 我们自制一些关闭资源的工具类。而且使用工具类可以减少代码冗余、使代码可读性更好。

下面是我写的工具类:

CloseResources.java

  1. public class CloseResources {
  2. private static final Object[] PARAMS = new Object[0];//使用反射调用方法时传入的参数,可以当作null
  3. private static final ExceptionHandle[] HANDLERS = new ExceptionHandle[0];//空的异常处理器,可以认为是null
  4.  
  5. private static boolean invokeCloseMethod(Object o, Method[] methods) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {//用反射执行无参的close()方法
  6. for (Method method : methods) {
  7. //System.out.println(method.getName());
  8. if (method.getName().equals("close") && method.getParameterTypes().length == 0) {
  9. System.out.println("############调用close() " + o.getClass());
  10. method.invoke(o, PARAMS);
  11. return true;
  12. }
  13. }
  14. return false;
  15. }
  16. public static void close(Object o) {
  17. close(o, null);
  18. }
  19. public static void close(Object o, ExceptionHandle handle) {//ExceptionHandle是我自己定义的
  20. if (o == null) {//如果为空就不调用close()了,免得空指针异常
  21. return;
  22. }
  23. Class<?> clazz = o.getClass();
  24. try {
  25. if (!invokeCloseMethod(o, clazz.getDeclaredMethods())) {//如果重写了close(),就调用重写的
  26. invokeCloseMethod(o, clazz.getMethods());//如果没有重写close(),就调用继承的close()
  27. }
  28. } catch (SecurityException e) {
  29. if (handle != null) {
  30. handle.handleSecurityException(e);
  31. }
  32. } catch (IllegalAccessException e) {
  33. if (handle != null) {
  34. handle.handleIllegalAccessException(e);
  35. }
  36. } catch (IllegalArgumentException e) {
  37. if (handle != null) {
  38. handle.handleIllegalArgumentException(e);
  39. }
  40. } catch (InvocationTargetException e) {
  41. if (handle != null) {
  42. handle.handleInvocationTargetException(e);
  43. }
  44. } catch (Exception e) {
  45. if (handle != null) {
  46. handle.handleException(e);
  47. }
  48. }
  49. }
  50. public static void closeCollection(Collection<?> collection) {//关闭collection,比如List
  51. closeCollection(collection, HANDLERS);
  52. }
  53. public static void closeCollection(Collection<?> collection, ExceptionHandle ... handles) {
  54. if (collection == null) {
  55. return;
  56. }
  57. int i = 0;
  58. for (Object o : collection) {
  59. if (handles == null) {
  60. close(o);
  61. } else if (i < handles.length) {
  62. close(o, handles[i++]);
  63. } else {
  64. close(o);
  65. }
  66. }
  67. collection.clear();
  68. }
  69. public static <K, V> void closeMap(Map<K, V> map) {//关闭map
  70. closeMap(map, HANDLERS);
  71. }
  72. public static <K, V> void closeMap(Map<K, V> map, ExceptionHandle ... handles) {
  73. if (map == null) {
  74. return;
  75. }
  76. Set<K> keys = map.keySet();
  77. int i = 0;
  78. for (K key : keys) {
  79. if (handles == null) {
  80. close(map.get(key));
  81. } else if (i < handles.length) {
  82. close(map.get(key), handles[i++]);
  83. } else {
  84. close(map.get(key));
  85. }
  86. }
  87. map.clear();
  88. }
  89. }

CloseResources工具类的几个方法:

  1. public static void close(Object o):关闭o,不做任何处理
  2. public static void close(Object o, ExceptionHandle handle): 用自定义处理异常的方式关闭o
  3. public static void closeCollection(Collection<?> collection): 关闭collection中的所有资源,并清空collection
  4. public static void closeCollection(Collection<?> collection, ExceptionHandle ... handles): 用自定义异常处理的方式分别关闭collection中的所有资源,并清空collection
    putlic static <K, V> void closeMap(Map<K, V> map): 关闭map中的所有资源,并清空map
  5. putlic static <K, V> void closeMap(Map<K, V> map, ExceptionHandle ... handles): 用自定义异常处理的方式分别关闭map中的所有资源,并清空map

如果资源o为null,那么CloseResources的close方法会直接返回,不做处理

使用CloseResources的close方法时还可以传入ExceptionHandle类来接受关闭资源时抛出的异常,并做处理

ExceptionHandle.java

  1. public class ExceptionHandle {
  2. public void handleSecurityException(SecurityException e) {}
  3.  
  4. public void handleIllegalAccessException(IllegalAccessException e) {}
  5.  
  6. public void handleIllegalArgumentException(IllegalArgumentException e) {}
  7.  
  8. public void handleInvocationTargetException(InvocationTargetException e) {}
  9.  
  10. public void handleException(Exception e) {}
  11. }

MyResource.java:

我在close方法中强行抛出IOException,这样方便等会儿使用自定义异常处理的方式关闭资源

  1. public class MyResource implements Closeable {
  2. @Override
  3. public void close() throws IOException {
  4. throw new IOException("调用MyResource的close()时强行抛出IOException");
  5. }
  6. }

主类:

  1. public class Main {
  2. public static void main(String[] args) throws Exception {
  3. BufferedInputStream bis = null;
  4. BufferedOutputStream bos = null;
  5.  
  6. List<Object> list = new ArrayList<Object>();
  7. try {
  8. FileInputStream fis = new FileInputStream("c:/1.txt");
  9. bis = new BufferedInputStream(fis);
  10. bos = new BufferedOutputStream(new FileOutputStream("c:/2.txt"));
  11. list.add(bis);
  12. list.add(bos);
  13. } catch (FileNotFoundException e) {
  14. e.printStackTrace();
  15. } finally {
  16. CloseResources.closeCollection(list);//可以关闭collection中的资源,并清空collection
  17. CloseResources.close(new MyResource(), new ExceptionHandle() {
    @Override
  18. public void handleInvocationTargetException(InvocationTargetException e) {//使用自定义异常处理的方式关闭MyResource
  19. System.out.println("ok");
  20. Throwable t = e.getCause();
  21. t.printStackTrace();
  22. }
  23. });
  24. }
  25. }
  26. }

运行程序的结果:

在主方法中我写了这几句话:

  1. @Override
  2. public void handleInvocationTargetException(InvocationTargetException e) {
  3. System.out.println("ok");
  4. Throwable t = e.getCause();
  5. t.printStackTrace();
  6. }

可以看见,我打印的是InvocationTargetException的Cause,如果直接打印e,会包含InvocationTargetException的信息,实际上我们只需要close()抛出的IOException,因此我先获得Cause。如果不想用这种方式,而是直接把Cause: IOException传到handleException,不分出那么多的自定义异常处理方法,可以自己修改一下代码。在实际场景,不应该仅仅只调用printStackTrace(),而是把异常写入日志、尝试修复异常、做一些业务。

应该指出:这个工具类还很不完善,例如,只能执行资源无参的close()方法,还没有经过足够多的实际场景去验证它的性能、鲁棒性

java关闭资源,自制关闭资源工具类的更多相关文章

  1. java里poi操作excel的工具类(兼容各版本)

    转: java里poi操作excel的工具类(兼容各版本) 下面是文件内具体内容,文件下载: import java.io.FileNotFoundException; import java.io. ...

  2. Java判断不为空的工具类总结

    1.Java判断是否为空的工具类,可以直接使用.包含,String字符串,数组,集合等等. package com.bie.util; import java.util.Collection; imp ...

  3. Java字符串转16 进制工具类Hex.java

    Java字符串转16 进制工具类Hex.java 学习了:https://blog.csdn.net/jia635/article/details/56678086 package com.strin ...

  4. Java中的AES加解密工具类:AESUtils

    本人手写已测试,大家可以参考使用 package com.mirana.frame.utils.encrypt; import com.mirana.frame.constants.SysConsta ...

  5. java.util.Arrays----操作数组的工具类

    java.util.Arrays操作数组的工具类,里面定义了很多操作数组的方法 1.boolean equals(int[] a,int[] b):判断两个数组是否相等. 2.String toStr ...

  6. Java中的4个并发工具类 CountDownLatch CyclicBarrier Semaphore Exchanger

    在 java.util.concurrent 包中提供了 4 个有用的并发工具类 CountDownLatch 允许一个或多个线程等待其他线程完成操作,课题点 Thread 类的 join() 方法 ...

  7. Java学习笔记49(DBUtils工具类二)

    上一篇文章是我们自己模拟的DBUtils工具类,其实有开发好的工具类 这里使用commons-dbutils-1.6.jar 事务的简单介绍: 在数据库中应用事务处理案例:转账案例 张三和李四都有有自 ...

  8. Java学习笔记48(DBUtils工具类一)

    上一篇的例子可以明显看出,在增删改查的时候,很多的代码都是重复的, 那么,是否可以将增删改查封装成一个类,方便使用者 package demo; /* * 实现JDBC的工具类 * 定义方法,直接返回 ...

  9. java分页的实现(后台工具类和前台jsp页面)

    1.首先,新建一个类Page.java public class Page implements Serializable { private static final long serialVers ...

  10. Java并发之Semaphore和Exchanger工具类简单介绍

    一.Semaphore介绍 Semaphore意思为信号量,是用来控制同时访问特定资源的线程数数量.它的本质上其实也是一个共享锁.Semaphore可以用于做流量控制,特别是公用资源有限的应用场景.例 ...

随机推荐

  1. javascript工具--控制台详解

    一.显示信息的命令 console.log();  //控制台输入 网页中不会输出 console.info();  //一般信息 console.debug();  //除错信息 console.w ...

  2. tab栏切换

    最简单的tab栏切换 html部分 <ul class="tab"> <li class="item">待支付(1)</li> ...

  3. dedecmsV5.7自定义图片字段调用方法

    正常情况下,在列表页(也就是 {dede:list}标签)调用附加的图片类型字段则会出现Fatal error: Call to a member function GetInnerText() on ...

  4. libcudnn (R5) not found in library path

    环境:Ubuntu 18.04 +  Torch7 + cuda10 在运行使用cudnn的lua程序的时候产生错误: /home/majiabiao/torch/: /home/majiabiao/ ...

  5. mysql 密码的破解

    现在的主流的数据库一般是mysql  ,sql  server ,  oracle. 有的时候我们忘记了数据库密码的时候我们要怎么办,破解别人的数据库的密码的时候我们要怎么搞  忘记密码是一件很头痛的 ...

  6. c 和 指针读书笔记(1)

    1.字符串常量:出现在表达式中,其值是一个指针 "abc" + 1; //b 2.回掉函数:就是把函数的作为参数传入父函数,函数本身就是一个地址,传址肯定没问题.至于父函数是想用函 ...

  7. mariadb-增删改查怎么用

    MariaDB 数据类型 MariaDB数据类型可以分为数字,日期和时间以及字符串值. 使用数据类型的原则:够用就行,尽量使用范围小的,而不用大的 常用的数据类型 整数:int, (bit比整数还要小 ...

  8. 带入gRPC:对 RPC 方法做自定义认证

    带入gRPC:对 RPC 方法做自定义认证 原文地址:带入gRPC:对 RPC 方法做自定义认证项目地址:https://github.com/EDDYCJY/go... 前言 在前面的章节中,我们介 ...

  9. python购物车系统

    购物车系统模拟:product_list = [ ('java',100), ('python',200), ('键盘',500), ('电脑',4000), ('mac Book',7000),]S ...

  10. POJ 题目3321 Apple Tree(线段树)

    Apple Tree Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 21566   Accepted: 6548 Descr ...