`
wen866595
  • 浏览: 267115 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

java program idiom之 资源初始化与清理

    博客分类:
  • java
 
阅读更多

源的初始化与关闭是非常常见的操作,也是很容易出错的地方。Java里一般使用try-catch-finally来处理这个问题,在JDK7增加了try-with-resource。

1.1.  try-catch-finally

下面是个有错误的举例:

public void copy_error(File src, File dst) throws IOException {
    FileInputStream fin = new FileInputStream(src);
    FileOutputStream fout = new FileOutputStream(dst);
    try {
        // do copy
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        fin.close();
        fout.close();
    }
}

 
上面的代码存在的问题有:

A、 fin、 fout 的初始化没有放入 try - catch 语句,如果 fin 初始化成功,而 fout 初始化时抛出异常,则会导致 fin 不会关闭,因为此时还没有进入 try 语句块,所以 finally 块是不会执行的。

B、 在finally 中的 fin 、 fout 的关闭错误。如果 fin.close() 执行时抛出异常,则不会执行 fout.close 。正确的做法是,由于 Input/OutputStream 实现了 Closeable 接口,可以同等对待,借助可变参数的特点,可以实现这样一个工具方法:( IoUtils.close )

public static void close(Closeable... closeables) {
    for (Closeable closeable : closeables) {
        if (closeable != null) {
            try {
                closeable.close();
            } catch (Exception ignore) {
            }
        }
    }
}

 
关闭流时只需调用IoUtils.close(fin, fout); ,就算只处理单一的输入输出流,这个方法也可用,因为它的参数是可变的。正确的代码大致如下:

public void copy_better(File src, File dst) throws FileNotFoundException {
    FileInputStream fin = null;
    FileOutputStream fout = null;
    try {
        fin = new FileInputStream(src);
        fout = new FileOutputStream(dst);
        // do copy
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        IoUtils.close(fin, fout);
    }
}

 
涉及SQL 操作也可以先实现一个工具方法:

public static void close(ResultSet rs, Statement stmt, Connection conn) {
    if (rs != null) {
        try {
            rs.close();
        } catch (Exception e) {
        }
    }

    if (stmt != null) {
        try {
            stmt.close();
        } catch (Exception e) {
        }
    }

    if (conn != null) {
        try {
            conn.close();
        } catch (Exception e) {
        }
    }
}

 
代码模板大致是这样:

public void doSql() {
    Connection conn = null;
    Statement stmt = null;
    ResultSet rs = null;
    try {
        // conn = ...;
        // stmt = ...;
       
        // rs = stmt.executeQuery("");
        // do sql operation
    } finally {
        Utils.close(rs, stmt, conn);
    }
}

 
所以在传统的try-catch-finally 执行资源有关的操作的模板是:

1) 、在try 语句块 前 声明变量;

2) 、在try 语句块 内 初始化变量;

3) 、在finally 语句块中执行关闭操作,且必须确保每个资源的关闭操作得到执行。

1.2.  JDK7 try-with-resources

JDK7中提供了 AutoCloseable 接口和 try-with-resource( 也称为 ARM (Automatic Resource Management)

)来减少资源泄露的情况。 下面以SQL 操作为例:

try (Connection conn = DriverManager.getConnection("url");
        Statement stmt = conn.createStatement()) {
    // do sql operation
} catch (SQLException e) {
    e.printStackTrace();
}

 

我们不需要对在try() 里声明的实现了 AutoCloseable 的资源类进行显式的关闭,新的特性可以确保try() 的小括号里 声明 的资源得到关闭。

但有可能误用而导致资源泄露,下面是个示例:

try  (BufferedReader br =  new  BufferedReader( new  FileReader(path))) {
     br.readLine();
}

 

问题出在: new  BufferedReader( new  FileReader(path)) ,如果 FileReader 构建成功,但在构建 BufferedReader 出错,则 br是 null ,不会执行关闭操作;而构建出来的 FileReader 只是 BufferedReader 构造函数的一个参数,ARM 不会对它执行关闭,从而导致资源泄露。避免这种 bug 的做法可以这样:

try  (FileReader reader =  new  FileReader(path)) {

     BufferedReader br =  new  BufferedReader(reader);

     br.readLine();

}

 

其实这个陷阱在传统的 try-catch-finally 里也存在,只是在 ARM 里更容易误解,因为 ARM 号称自动管理资源。

这个 陷阱 在使用装饰者模式的资源有关的类库里应该容易出现。 JDK 的 io 库在设计时使用了装饰者模式,这样可以动态地扩展功能,比如 InputStream ,基础的字节输入流,被 BufferedInputStream 包装后,又具有了缓冲的功能,再被 DataInputStream 包装后,具有了读取 Java基本数据类型的能力 。 DataInputStream 、 BufferedInputStream 称为装饰者,它们都实现了被装饰者 InputStream的接口。因为它们在类型上一致性,所以可能这样构建一个资源:

new  DataInputStream( new   BufferedInputStream ( new  FileInputStream(path)));

从而导致上面的 陷阱 。

对任何使用装饰者模式实现的与资源有关的类库都应该留心这个 陷阱 。

关于这个 陷阱 还可以查看这篇博客:

http://java.dzone.com/articles/common-try-resources-idiom

分享到:
评论

相关推荐

    idiom翻译软件

    "idiom翻译软件"是一款专为用户提供了高效翻译服务的应用程序。它被设计用来帮助用户理解和传达不同语言之间的意义,确保跨文化交流的准确性和流畅性。这款软件在经过实际测试后,被认为是非常实用且功能完备的。 ...

    English Phrasal Verb Dictionary&English-Idiom-Expression

    useEnglish 资源E nglish Phrasal Verb & Idiom

    PyPI 官网下载 | china_idiom-0.0.2-py3-none-any.whl

    资源来自pypi官网。 资源全名:china_idiom-0.0.2-py3-none-any.whl

    idiom4.sql

    四字成语mysql数据30810条

    pimpl-idiom.rar_pimpl_site:www.pudn.com

    1. **定义接口类(Public Class)**:公开接口类声明一个指向私有实现类的指针,并在构造函数和析构函数中进行初始化和清理。 ```cpp class MyClass { public: MyClass(); ~MyClass(); // 公共接口 void ...

    Idiom WorldServer Desktop Workbench 8.1.0.208 使用指南

    2. **界面定制**:允许自定义Idiom主窗口中字体、字形和大小,满足个性化需求。 3. **标记与实体处理**:了解如何正确处理文件中的标记和实体,特别是在重组语序或跨段落翻译时,保持文本结构和意义的完整性。 ###...

    亲测可用成语大全三万条tb_idiom.sql.zip。解释、拼音、出处、典故、难易程度,褒贬色彩,英语日语俄语翻译等

    iom` (`id`, `guid`, `idiom`, `full_pinyin`, `pinyin`, `az`, `description`, `source`, `idiom_traditional`, `spinyin`, `idiom_phonetic`, `idiom_correct_pronunciation`, `idiom_distinguish`, `idiom_...

    Idiom-看图猜成语程序

    "Idiom-看图猜成语程序"是一款基于Python语言开发的应用,旨在提供一种娱乐与学习相结合的方式,让用户通过观察图片来猜测对应的成语。这个程序可能是为教育或休闲目的设计的,利用Python的图像处理和人机交互功能,...

    01 设计模式之单例模式.pdf

    这种实现方式依赖于Java的类加载机制,即一个类的内部类在首次被使用时才进行加载和初始化。这种方式既保证了线程安全,又实现了懒加载。 5. 枚举(Enum Singleton) Java枚举的特性使得它天然的线程安全,并且实现...

    idiom:基于上下文的翻译系统

    成语Idiom 是 AngularJS 的新翻译系统,它允许您在当前范围内阅读以提供基于上下文的翻译 - 没有更多的东西,他/她,您实际上知道您在写什么。 #用法向您的项目添加一个文件夹,其中包含每种语言的一个 json 文件...

    Android利用SQLite制作最简单成语小词典

    `onCreate()`用于首次创建数据库时执行的初始化操作,`onUpgrade()`则是在数据库版本升级时调用。 例如: ```java public class DatabaseHelper extends SQLiteOpenHelper { private static final String DATABASE...

    猜成语小程序数据库表ims_yf_chengyu_idiom

    【标题】"猜成语小程序数据库表ims_yf_chengyu_idiom"涉及到的是一个与成语相关的微信小程序的数据存储结构。这个数据库表很可能是用来管理小程序内部的成语信息,包括成语的ID、成语内容、含义、出处等关键数据,以...

    C#的四个基本技巧.txt

    懒加载是一种优化策略,用于延迟资源的初始化直到实际需要时。这在性能敏感的应用中特别有用,可以避免不必要的计算或数据加载,从而节省时间和资源。在C#中,你可以通过属性和虚拟(virtual)或抽象(abstract)...

    AI-Algorithm-Idiom-in-Prolog-Lisp-Java:《Prolog、Lisp 和 Java 中的 AI、Alogirhtm、数据结构和习语》一书的代码

    AI-Algorithm-Idiom-in-Prolog-Lisp-Java 《Prolog、Lisp 和 Java 中的 AI、Alogirhtm、数据结构和习语》一书的代码

    File_实用案例_实现文件拷贝_FileCopy.java

    This is a common I/O programming idiom. while((bytes_read = from.read(buffer)) != -1) // Read until EOF to.write(buffer, 0, bytes_read); // write } // Always close the streams, even if exceptions...

    java 8 lambda 教程

    java 8 lambda Concise syntax – More succinct and clear than anonymous inner classes • Deficiencies with anonymous inner classes – Bulky, confusion re “this” and naming in general, no nonfinal var...

    Java 80 道面试题及答案.docx

    Java 面试题及答案解析 Java 中能创建 volatile 数组吗?答案是可以,Java 中可以创建 volatile 类型数组,不过只是一个指向数组的引用,而不是整个数组。我的意思是,如果改变引用指向的数组,将会受到 volatile ...

    中华新华字典数据库idiom.json

    自然语言处理相关的分词数据

    看图猜成语程序使用说明

    4. **Idiom\weapp-idiom\pages\guess\guess.js**:这是微信小程序(WeChat Mini Program)中的一个页面脚本,用于实现“猜成语”页面的逻辑,包括处理用户输入、显示成语图片、验证答案等功能。 5. **Idiom\weapp-...

Global site tag (gtag.js) - Google Analytics