觉得很不错,就转载了, 作者: Paul Lin

首先贴一段Apache commons IO官网上的介绍,来对这个著名的开源包有一个基本的了解:
Commons IO is a library of utilities to assist with developing IO functionality. There are four main areas included:
●Utility classes - with static methods to perform common tasks ●Filters - various implementations of file filters
●Comparators - various implementations of java.util.Comparator for files ●Streams - useful stream, reader and writer implementations

[tr=none]

[tr=none]

[tr=none]

[tr=none]

[tr=none]

[tr=none]

Packages
org.apache.commons.io This package defines utility classes for working with streams, readers, writers and files.
org.apache.commons.io.comparator This package provides various Comparator implementations for Files.
org.apache.commons.io.filefilter This package defines an interface (IOFileFilter) that combines both FileFilter and FilenameFilter.
org.apache.commons.io.input This package provides implementations of input classes, such as InputStream and Reader.
org.apache.commons.io.output This

【二】org.apache.comons.io.input包介绍
这个包针对SUN JDK IO包进行了扩展,实现了一些功能简单的IO类,主要包括了对字节/字符输入流接口的实现

附件: 你需要登录才可以下载或查看附件。没有帐号? 注册

这个包针对java.io.InputStream和Reader进行了扩展,其中比较实用的有以下几个:
●AutoCloseInputStream
Proxy stream that closes and discards the underlying stream as soon as the end of input has been reached or when the stream is explicitly closed. Not even a reference to the underlying stream is kept after it has been closed, so any allocated in-memory buffers can be freed even if the client application still keeps a reference to the proxy stream
This class is typically used to release any resources related to an open stream as soon as possible even if the client application (by not explicitly closing the stream when no longer needed) or the underlying stream (by not releasing resources once the last byte has been read) do not do that.
这个输入流是一个底层输入流的代理,它能够在数据源的内容被完全读取到输入流后,后者当用户调用close()方法时,立即关闭底层的输入流。释放底层的资源(例如文件的句柄)。这个类的好处就是避免我们在代码中忘记关闭底层的输入流而造成文件处于一直打开的状态。
我们知道对于某些文件,只允许由一个进程打开。如果我们使用后忘记关闭那么该文件将处于一直“打开”的状态,其它进程无法读写。例如下面的例子:

  1. new BufferedInputStream(new FileInputStream(FILE))

复制代码

里面的FileInputStream(FILE)在打开后不能被显式关闭,这将导致可能出现的问题。如果我们使用了 AutoCloseInputStream,那么当数据读取完毕后,底层的输入流会被自动关闭,迅速地释放资源。

  1. new BufferedInputStream(new AutoClosedInputStream(new FileInputStream));

复制代码 

 
那么这个类是如何做到自动关闭的呢?来看看这个非常简单的类的代码吧

  1. package org.apache.commons.io.input;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. public class AutoCloseInputStream extends ProxyInputStream {
  5. public AutoCloseInputStream(InputStream in) {
  6. super(in);
  7. }
  8. public void close() throws IOException {
  9. in.close();
  10. in = new ClosedInputStream();
  11. }
  12. public int read() throws IOException {
  13. int n = in.read();
  14. if (n == -1) {
  15. close();
  16. }
  17. return n;
  18. }
  19. public int read(byte[] b) throws IOException {
  20. int n = in.read(b);
  21. if (n == -1) {
  22. close();
  23. }
  24. return n;
  25. }
  26. public int read(byte[] b, int off, int len) throws IOException {
  27. int n = in.read(b, off, len);
  28. if (n == -1) {
  29. close();
  30. }
  31. return n;
  32. }
  33. protected void finalize() throws Throwable {
  34. close();
  35. super.finalize();
  36. }
  37. }
  38. public class ClosedInputStream extends InputStream {
  39. /** *//**
  40. * A singleton.
  41. */
  42. public static final ClosedInputStream CLOSED_INPUT_STREAM = new ClosedInputStream();
  43. /** *//**
  44. * Returns -1 to indicate that the stream is closed.
  45. *
  46. * @return always -1
  47. */
  48. public int read() {
  49. return -1;
  50. }
  51. }

复制代码

可以看到这个类通过两个途径来保证底层的流能够被正确地关闭: ①每次调用read方法时,如果底层读到的是-1,立即关闭底层输入流。返回一个ClosedInputStream ②当这个类的对象被回收时,确保关闭底层的输入流
●TeeInputStream
InputStream proxy that transparently writes a copy of all bytes read from the proxied stream to a given OutputStream. The proxied input stream is closed when the close() method is called on this proxy. It is configurable whether the associated output stream will also closed.
可以看到这个类的作用是把输入流读入的数据原封不动地传递给输出流。这一点和JDK中提供的PipedInputStream的理念有些类似。在实际使用中可以非常方便地做到像:将从远程URL读入的数据写到输出流,保存到文件之类的动作。当输入流被关闭时,输出流不一定被关闭。可以依然保持打开的状态。
下面是这个类的部分源码

  1. public int read(byte[] bts, int st, int end) throws IOException {
  2. int n = super.read(bts, st, end);
  3. if (n != -1) {
  4. branch.write(bts, st, n);
  5. }
  6. return n;
  7. }

复制代码

 
 
 
 
 
  TOP
   

 
UID
1
精华
19
拼元
1320699 元
贡献
16939.62 点
信誉
0 分
来自
拼吾爱
 
 

cobra

striver

  • 组别管理员
  • 性别
    保密
  • 积分3026078
  • 帖子3638
  • 注册时间 2007-04-09
[发布日期: 2010-03-08 22:02] [只看楼主] 真皮板凳

字体大小: t T
 
 
 
●CharSequenceReader
Reader implementation that can read from String, StringBuffer, StringBuilder or CharBuffer.
这个类可以看成是对StringReader的一个扩展,用于从内存中读取字符。
●NullReader
A functional, light weight Reader that emulates a reader of a specified size.
This implementation provides a light weight object for testing with an Reader where the contents don't matter.
One use case would be for testing the handling of large Reader as it can emulate that scenario without the overhead of actually processing large numbers of characters - significantly speeding up test execution times.
从上面的文字描述来看,这个类显然是用来做测试辅助的,它的目标对象是“对读入内容不关心”的需求。它并不传递真正的数据,而是模拟这个过程。来看看下面的源代码

  1. /** *//**
  2. * Read the specified number characters into an array.
  3. *
  4. * @param chars The character array to read into.
  5. * @param offset The offset to start reading characters into.
  6. * @param length The number of characters to read.
  7. * @return The number of characters read or <code>-1</code>
  8. * if the end of file has been reached and
  9. * <code>throwEofException</code> is set to <code>false</code>.
  10. * @throws EOFException if the end of file is reached and
  11. * <code>throwEofException</code> is set to <code>true</code>.
  12. * @throws IOException if trying to read past the end of file.
  13. */
  14. public int read(char[] chars, int offset, int length) throws IOException {
  15. if (eof) {
  16. throw new IOException("Read after end of file");
  17. }
  18. if (position == size) {
  19. return doEndOfFile();
  20. }
  21. position += length;
  22. int returnLength = length;
  23. if (position > size) {
  24. returnLength = length - (int)(position - size);
  25. position = size;
  26. }
  27. processChars(chars, offset, returnLength);
  28. return returnLength;
  29. }
  30. /** *//**
  31. * Return a character value for the  <code>read()</code> method.
  32. * <p>
  33. * This implementation returns zero.
  34. *
  35. * @return This implementation always returns zero.
  36. */
  37. protected int processChar() {
  38. // do nothing - overridable by subclass
  39. return 0;
  40. }
  41. /** *//**
  42. * Process the characters for the <code>read(char[], offset, length)</code>
  43. * method.
  44. * <p>
  45. * This implementation leaves the character array unchanged.
  46. *
  47. * @param chars The character array
  48. * @param offset The offset to start at.
  49. * @param length The number of characters.
  50. */
  51. protected void processChars(char[] chars, int offset, int length) {
  52. // do nothing - overridable by subclass
  53. }

复制代码

知道它是怎么模拟的了吗?呵呵~~。原来它只是模拟计数的过程,根本不传递、处理、存储任何数据。数组始终都是空的。

 
 
 
 
 
  TOP
   

 
UID
1
精华
19
拼元
1320699 元
贡献
16939.62 点
信誉
0 分
来自
拼吾爱
 
 

cobra

striver

  • 组别管理员
  • 性别
    保密
  • 积分3026078
  • 帖子3638
  • 注册时间 2007-04-09
[发布日期: 2010-03-08 22:02] [只看楼主] 真皮地板

字体大小: t T
 
 
 
【三】org.apache.commons.io.output包介绍

附件: 你需要登录才可以下载或查看附件。没有帐号? 注册

和input包类似,output包也实现/继承了部分JDK IO包的类、接口。这里需要特别注意的有3个类,他们分别是:
①ByteArrayOutputStream ②FileWriterWithEncoding ③LockableFileWriter
●ByteArrayOutputStream
This class implements an output stream in which the data is written into a byte array. The buffer automatically grows as data is written to it.
The data can be retrieved using toByteArray() and toString().
Closing a ByteArrayOutputStream has no effect. The methods in this class can be called after the stream has been closed without generating an IOException.
This is an alternative implementation of the java.io.ByteArrayOutputStream class. The original implementation only allocates 32 bytes at the beginning. As this class is designed for heavy duty it starts at 1024 bytes. In contrast to the original it doesn't reallocate the whole memory block but allocates additional buffers. This way no buffers need to be garbage collected and the contents don't have to be copied to the new buffer. This class is designed to behave exactly like the original. The only exception is the deprecated toString(int) method that has been ignored.
从上面的文档中,我们看到Apache commons io的ByteArrayOutputString比起SUN自带的ByteArrayOutputStream更加高效,原因在于:
①缓冲区的初始化大小比原始的JDK自带的ByteArrayOutputStream要大很多(1024:32) ②缓冲区的大小可以无限增加。当缓冲不够时动态增加分配,而非清空后再重新封闭 ③减少write方法的调用次数,一次性将多个一级缓冲数据写出。减少堆栈调用的时间
那么为什么这个类可以做到这些呢?来看看他的源码吧:

  1. /** *//** The list of buffers, which grows and never reduces. */
  2. private List buffers = new ArrayList();
  3. /** *//** The current buffer. */
  4. private byte[] currentBuffer;

复制代码

  1. /** *//**
  2. * Creates a new byte array output stream. The buffer capacity is
  3. * initially 1024 bytes, though its size increases if necessary.
  4. */
  5. public ByteArrayOutputStream() {
  6. this(1024);
  7. }

复制代码

而JDK自带的Buffer则只有简单的一个byte[]

  1. /** *//**
  2. * The buffer where data is stored.
  3. */
  4. protected byte buf[];

复制代码

  1. /** *//**
  2. * Creates a new byte array output stream. The buffer capacity is
  3. * initially 32 bytes, though its size increases if necessary.
  4. */
  5. public ByteArrayOutputStream() {
  6. this(32);
  7. }

复制代码

原来Apache commons 的io是采用了二级缓冲:首先一级缓冲是一个byte[],随着每次写出的数据不同而不同。二级缓冲则是一个无限扩充的ArrayList,每次从 byte[]中要写出的数据都会缓存到这里。当然效率上要高很多了。那么这个类是如何做到动态增加缓冲而不需要每次都回收已有的缓冲呢?

  1. /** *//**
  2. * Makes a new buffer available either by allocating
  3. * a new one or re-cycling an existing one.
  4. *
  5. * @param newcount  the size of the buffer if one is created
  6. */
  7. private void needNewBuffer(int newcount) {
  8. if (currentBufferIndex < buffers.size() - 1) {
  9. //Recycling old buffer
  10. filledBufferSum += currentBuffer.length;
  11. currentBufferIndex++;
  12. currentBuffer = getBuffer(currentBufferIndex);
  13. } else {
  14. //Creating new buffer
  15. int newBufferSize;
  16. if (currentBuffer == null) {
  17. newBufferSize = newcount;
  18. filledBufferSum = 0;
  19. } else {
  20. newBufferSize = Math.max(
  21. currentBuffer.length << 1,
  22. newcount - filledBufferSum);
  23. filledBufferSum += currentBuffer.length;
  24. }
  25. currentBufferIndex++;
  26. currentBuffer = new byte[newBufferSize];
  27. buffers.add(currentBuffer);
  28. }
  29. }

复制代码

在初始化的情况下,currentBuffer == null,于是第一个一级缓冲区byte[]的大小就是默认的1024或者用户指定的值。然后filledBufferSum、 currentBufferIndex分别进行初始化。创建第一个一级缓存区,添加到二级缓冲区buffers中。

 
 
 
 
 
  TOP
   

 
UID
1
精华
19
拼元
1320699 元
贡献
16939.62 点
信誉
0 分
来自
拼吾爱
 
 

cobra

striver

  • 组别管理员
  • 性别
    保密
  • 积分3026078
  • 帖子3638
  • 注册时间 2007-04-09
[发布日期: 2010-03-08 22:03] [只看楼主] 5#

字体大小: t T
 
 
 
当后续的缓冲请求到来后,根据剩下的缓冲大小和尚存的缓冲进行比较,然后选择较大的值作为缓冲扩展的大小。再次创建一个新的一级缓冲byte[],添加到二级缓冲中。
相比于JDK自带的方法,这个类多了一个write(InputStream in)的方法,看看下面的源代码

  1. public synchronized int write(InputStream in) throws IOException {
  2. int readCount = 0;
  3. int inBufferPos = count - filledBufferSum;
  4. int n = in.read(currentBuffer, inBufferPos, currentBuffer.length - inBufferPos);
  5. while (n != -1) {
  6. readCount += n;
  7. inBufferPos += n;
  8. count += n;
  9. if (inBufferPos == currentBuffer.length) {
  10. needNewBuffer(currentBuffer.length);
  11. inBufferPos = 0;
  12. }
  13. n = in.read(currentBuffer, inBufferPos, currentBuffer.length - inBufferPos);
  14. }
  15. return readCount;
  16. }

复制代码

可以看到每次从InputStream读取当前一级缓冲剩余空间大小的字节,缓冲到剩下的空间。如果缓冲满了则继续分配新的一级缓冲。直至数据读完。对于写出到另外的输出流,则是:

  1. public synchronized void writeTo(OutputStream out) throws IOException {
  2. int remaining = count;
  3. for (int i = 0; i < buffers.size(); i++) {
  4. byte[] buf = getBuffer(i);
  5. int c = Math.min(buf.length, remaining);
  6. out.write(buf, 0, c);
  7. remaining -= c;
  8. if (remaining == 0) {
  9. break;
  10. }
  11. }
  12. }

复制代码

由于每次write的时候一次性地写出一级缓冲,而且是将二级缓冲全部写出,减少了调用的次数,所以提高了效率。
●FileWriterWithEncoding
从这个类的名称已经可以很清楚的知道它的作用了。在JDK自带的FileWriter中,是无法设置encoding的,这个类允许我们采用默认或者指定的encoding,以字符的形式写到文件。为什么这个类可以改变字符嗯?
原理很简单:无非使用了OutputStreamWriter。而且这个类并不是继承与FileWriter,而是直接继承于Writer。

  1. OutputStream stream = null;
  2. Writer writer = null;
  3. try {
  4. stream = new FileOutputStream(file, append);
  5. if (encoding instanceof Charset) {
  6. writer = new OutputStreamWriter(stream, (Charset)encoding);
  7. } else if (encoding instanceof CharsetEncoder) {
  8. writer = new OutputStreamWriter(stream, (CharsetEncoder)encoding);
  9. } else {
  10. writer = new OutputStreamWriter(stream, (String)encoding);
  11. }

复制代码

剩下的各种write方法,无非就是decorator模式而已。
●LockableFileWriter
使用“文件锁”而非“对象锁”来限制多线程环境下的写动作。这个类采用在JDK默认的系统临时目录下写文件:java.io.tmpdir属性。而且允许我们设置encoding。

  1. /** *//**
  2. * Constructs a LockableFileWriter with a file encoding.
  3. *
  4. * @param file  the file to write to, not null
  5. * @param encoding  the encoding to use, null means platform default
  6. * @param append  true if content should be appended, false to overwrite
  7. * @param lockDir  the directory in which the lock file should be held
  8. * @throws NullPointerException if the file is null
  9. * @throws IOException in case of an I/O error
  10. */
  11. public LockableFileWriter(File file, String encoding, boolean append,
  12. String lockDir) throws IOException {
  13. super();
  14. // init file to create/append
  15. file = file.getAbsoluteFile();
  16. if (file.getParentFile() != null) {
  17. FileUtils.forceMkdir(file.getParentFile());
  18. }
  19. if (file.isDirectory()) {
  20. throw new IOException("File specified is a directory");
  21. }
  22. // init lock file
  23. if (lockDir == null) {
  24. lockDir = System.getProperty("java.io.tmpdir");
  25. }
  26. File lockDirFile = new File(lockDir);
  27. FileUtils.forceMkdir(lockDirFile);
  28. testLockDir(lockDirFile);
  29. lockFile = new File(lockDirFile, file.getName() + LCK);
  30. // check if locked
  31. createLock();
  32. // init wrapped writer
  33. out = initWriter(file, encoding, append);
  34. }

复制代码

首先创建一个用于存放lock文件的目录,位于系统临时目录下。
接下来创建一个位于该目录下的名为xxxLCK的文件(锁文件)。
然后创建锁
最后则是初始化writer
显然我们关心的是如何创建这个锁,以及如何在写期间进行锁。首先来看创建锁的过程

  1. private void createLock() throws IOException {
  2. synchronized (LockableFileWriter.class) {
  3. if (!lockFile.createNewFile()) {
  4. throw new IOException("Can't write file, lock " +
  5. lockFile.getAbsolutePath() + " exists");
  6. }
  7. lockFile.deleteOnExit();
  8. }
  9. }

复制代码

注意这里的deleteOnExit()方法很重要,它告诉JVM:当JV退出是要删除该文件。否则磁盘上将有无数无用的临时锁文件。
下面的问题则是如何实现锁呢?呵呵~~。还是回到这个上面这个类的构造方法吧,我们看到在构造这个LockableFileWriter时,会调用 createLock()这个方法,而这个方法如果发现文件已经创建/被其它流引用时,会抛出一个IOException。于是创建不成功,也就无法继续后续的write操作了。
那么如果第一个进程创建了锁之后就不释放,那么后续的进程岂不是无法写了,于是在这个类的close方法中有这样一句代码:

  1. public void close() throws IOException {
  2. try {
  3. out.close();
  4. } finally {
  5. lockFile.delete();
  6. }
  7. }

复制代码

每个进程在完成数据的写动作后,必须调用close()方法,于是锁文件被删除,锁被解除。相比于JDK中自带Writer使用的object锁 (synchronized(object)),这个方法确实要更加简便和高效。这个类当初就是被设计来替换掉原始的FileWriter的。

IO与文件读写---使用Apache commons IO包提高读写效率的更多相关文章

  1. apache commons io包基本功能

    1. http://jackyrong.iteye.com/blog/2153812 2. http://www.javacodegeeks.com/2014/10/apache-commons-io ...

  2. Apache Commons IO入门教程(转)

    Apache Commons IO是Apache基金会创建并维护的Java函数库.它提供了许多类使得开发者的常见任务变得简单,同时减少重复(boiler-plate)代码,这些代码可能遍布于每个独立的 ...

  3. [转]Apache Commons IO入门教程

    Apache Commons IO是Apache基金会创建并维护的Java函数库.它提供了许多类使得开发者的常见任务变得简单,同时减少重复(boiler-plate)代码,这些代码可能遍布于每个独立的 ...

  4. apache commons io入门

    原文参考  http://www.javacodegeeks.com/2014/10/apache-commons-io-tutorial.html    Apache Commons IO 包绝对是 ...

  5. Java (三)APACHE Commons IO 常规操作

    上一篇:Java (二)基于Eclipse配置Commons IO的环境 例1:查看文件.文件夹的长度(大小). 1 import java.io.File; 2 3 import org.apach ...

  6. 使用Apache Commons IO组件读取大文件

    Apache Commons IO读取文件代码如下: Files.readLines(new File(path), Charsets.UTF_8); FileUtils.readLines(new ...

  7. Java (四)APACHE Commons IO 复制文件

    上一篇:Java (三)APACHE Commons IO 常规操作 例1:复制文件 1 import java.io.File; 2 import java.io.IOException; 3 4 ...

  8. apache.commons.io.FileUtils的常用操作

    至于相关jar包可以到官网获取 http://commons.apache.org/downloads/index.html package com.wz.apache.fileUtils; impo ...

  9. Caused by: java.lang.ClassNotFoundException: org.apache.commons.io.FileUtils

    1.错误叙述性说明 警告: Could not create JarEntryRevision for [jar:file:/D:/MyEclipse/apache-tomcat-7.0.53/web ...

随机推荐

  1. OC运行时和方法机制笔记

    在OC当中,属性是对字段的一种特殊封装手段. 在编译期,编译器会将对字段的访问替换为内存偏移量,实质是一种硬编码. 如果增加一个字段,那么对象的内存排布就会改变,需要重新编译才行. OC的做法是,把实 ...

  2. nginx源代码分析--配置文件解析

    ngx-conf-parsing 对 Nginx 配置文件的一些认识: 配置指令具有作用域,分为全局作用域和使用 {} 创建其他作用域. 同一作用域的不同的配置指令没有先后顺序:同一作用域能否使用同样 ...

  3. 关于在用Swift开发iOS时如何隐藏NavigationBar和TabBar

    举个例子:如果我有一个页面需要进入时同时隐藏NavigationBar和TabBar,那么我就在那个页面的ViewController的代码里加上下面的代码.就可以实现了.接下来告诉大家每一块要注意的 ...

  4. 开发汉澳即时通信网,2006年上线,QQ死期到了

    为汉澳sinox用户打造即时通信网让大家用上即时通信软件 近期腾讯关闭了linuxQQ登录,汉澳 sinox也登陆不上.非windows用户再也不能用上即时通信软件了! 这是多么可悲的事,可是我们必须 ...

  5. 轻量级C语言实现的minixml解析库入门教程

    svn上的minixml源码下载.  svn co http://svn.msweet.org/mxml/tags/release-2.7/ 按照下载回来的源代码进行编译和安装.本教程只针对新手做一个 ...

  6. javaScript 工作必知(三) String .的方法从何而来?

    String 我们知道javascript 包括:number,string,boolean,null,undefined 基本类型和Object 类型. 在我的认知中,方法属性应该是对象才可以具有的 ...

  7. Sizzle一步步实现所有功能(层级选择)

    第二步:实现Sizzle("el,el,el..."),Sizzle("el > el"),Sizzle("el el"),Sizzl ...

  8. MSDN地址,记录下来,以防以后使用

    MSDN在线官网:https://msdn.microsoft.com/zh-cn/default.aspx 以备学习时候使用.

  9. c++多线程同步使用的对象

    线程的同步 Critical section(临界区)用来实现“排他性占有”.适用范围是单一进程的各线程之间.它是: ·         一个局部性对象,不是一个核心对象. ·         快速而 ...

  10. MySQL AUTO_INCREMENT 简介

    可使用复合索引在同一个数据表里创建多个相互独立的自增序列,具体做法是这样的:为数据表创建一个由多个数据列组成的PRIMARY KEY OR UNIQUE索引,并把AUTO_INCREMENT数据列包括 ...