java IO相关API探索之Closeable和InputStream接口

<span style="font-family:Comic Sans MS;font-size:18px;">import java.io.IOException;

/**
 * A {@code Closeable} is a source or destination of data that can be closed.
 * The close method is invoked to release resources that the object is
 * holding (such as open files).
 *
 * @since 1.5
 */
public interface Closeable extends AutoCloseable {

    /**
     * Closes this stream and releases any system resources associated
     * with it. If the stream is already closed then invoking this
     * method has no effect.
     *
     * <p> As noted in {@link AutoCloseable#close()}, cases where the
     * close may fail require careful attention. It is strongly advised
     * to relinquish the underlying resources and to internally
     * <em>mark</em> the {@code Closeable} as closed, prior to throwing
     * the {@code IOException}.
     *
     * @throws IOException if an I/O error occurs
     */
    public void close() throws IOException;
}</span>

<span style="font-family:Comic Sans MS;font-size:18px;">接口Closeable继承自AutoCloseable,同时重写了关于 close关闭流的方法。Closeable 是一个能够被关闭的数据源,close方法被调用区释放对象持有的资源例如打开的文件。</span>

关闭stream流和与之相关的资源,如果流已经关闭了,那么再次调用这个方法是没有影响的。与AutoCloseable中的close方法一样,如果关闭操作失败,那么就要格外注意了。
当IO异常发生时该方法会抛出IOExce异常

上面主要是关于Closeable的介绍,而InputStream则是Closeable的一个实现,而InputStream是一个抽象类,所谓抽象类就是该类不能被实例化,只能实例化它的非抽象子类,我看的是jdk1.8的实现。

public abstract class InputStream implements Closeable
MAX_SKIP_BUFFER_SIZE is used to determine the maximum buffer size to
use when skipping.
 private static final int MAX_SKIP_BUFFER_SIZE = 2048;

类中定义了一个static final类型的变量,用来指定InputStream流一次能够跳过的字节的最大值(忽略字节),后面的一个方法skip会用到这个常量

<span style="font-family:Comic Sans MS;font-size:18px;">public abstract int read() throws IOException;</span>
这个read方法待实现,主要是从流中读取一个字节作为输出,Reads the next byte of data from the input stream,因为0-255能够表示所有的字节,所以如果read方法返回-1,则说明这个流已经到了末尾。则不再读取即可.在while中直接break就OK了。

<span style="font-family:Comic Sans MS;font-size:18px;">public int read(byte b[]) throws IOException {
        return read(b, 0, b.length);
}</span>

read(byte b[])方法读取流中的b.length个字节到字节数组b[]中。返回值表示读取到字节数组中的字节数。下面我们看一下关于read(b, 0, b.length)方法怎么实现的。

<span style="font-family:Comic Sans MS;font-size:18px;">public int read(byte b[], int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }
        int c = read();
        if (c == -1) {
            return -1;
        }
        b[off] = (byte)c;

        int i = 1;
        try {
            for (; i < len ; i++) {
                c = read();
                if (c == -1) {
                    break;
                }
                b[off + i] = (byte)c;
            }
        } catch (IOException ee) {
        }
        return i;
    }</span>

(off表示开始位置,len表示读取流的最长字节)如果b字节数组为null的话,则会抛出空指针异常,如果开始位置off 小于0或者len小于0或者len > b.length - off(表示从开头off + 要读取的字节长度 len) 大于字节数组的length,则抛出数组越界异常。

如果len == 0,那么则直接返回0,表示读入到字节数组中的字节数为0;向后读取一个字节,如果返回为-1,则表示已经到达流的末尾,返回-1即可。b[off] = (byte)c;从这句代码就可以知道read(b, off, len)是把文件流读入到b字节数组中,读入数组中的位置从off索引开始,并且最长能够读取的就是填到b的末尾了,但是由于是从字节的off索引开始填充的,所以最长只能够读取b.length - off个字节。后面一个for循环,一个字节一个字节的读入到字节数组中off+i索引的位置。如果读到-1,那么跳出循环,返回读入到数组中的字节个数。

下面这个方法就是用到了最开始定义的最长能够跳过的字节数

<span style="font-family:Comic Sans MS;font-size:18px;">public long skip(long n) throws IOException {

        long remaining = n;
        int nr;

        if (n <= 0) {
            return 0;
        }

        int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
        byte[] skipBuffer = new byte[size];
        while (remaining > 0) {
            nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
            if (nr < 0) {
                break;
            }
            remaining -= nr;
        }

        return n - remaining;
    }</span>

跳过Math.min(MAX_SKIP_BUFFER_SIZE, remaining),这么长的字节,所谓的跳过,就是把这么多字节读入到另外一个不处理的字节数组中,然后函数结束的时候会自动释放,这样就跳过了一些流中的字节。比如要跳过3000字节,那么在调用Math.min方法的时候就会设置size=2048,然后调用read方法把2048个字节读入到skipBuffer字节数组中,假设文件流中只剩下了1000个字节,那么根据上面的方法介绍,则第一次进入while循环时,nr=1000,remaining=3000-1000=2000,因为n=3000,所以该函数返回3000-2000=1000,所以该函数返回的数据是跳过的字节数。

<span style="font-family:Comic Sans MS;font-size:18px;">public int available() throws IOException {
        return 0;
}</span>

代码中是这么解释返回值的:返回一个可能被读取的流中的字节数或者能够跳过的流中字节数,该方法要其子类来实现,其实就是返回流中的字节数.

<span style="font-family:Comic Sans MS;font-size:18px;">public void close() throws IOException {}</span>

这个方法就不用多说了,关闭文件流操作,不过具体实现要待子类中实现了

publicsynchronizedvoid mark(intreadlimit) {}

同步操作,mark就像书签一样,在这个BufferedReader对应的buffer里作个标记,以后再调用reset时就可以再回到这个mark过的地方。mark方法有个参数,通过这个整型参数,你告诉系统,希望在读出这么多个字符之前,这个mark保持有效。读过这么多字符之后,系统可以使mark不再有效,而你不能觉得奇怪或怪罪它。这跟buffer有关,如果你需要很长的距离,那么系统就必须分配很大的buffer来保持你的mark。       
    //reader       is       a       BufferedReader     
    reader.mark(50);//要求在50个字符之内,这个mark应该保持有效,系统会保证buffer至少可以存储50个字符     
    int       a       =       reader.read();//读了一个字符     
    int       b       =       reader.read();//又读了一个字符     
    //做了某些处理,发现需要再读一次     
    reader.reset();     
    reader.read();//读到的字符和a相同     
    reader.read();//读到的字符和b相同

对应的使用reset则使得文件流指针又指向了原来mark的位置,读取相同数据

<span style="font-family:Comic Sans MS;font-size:18px;">public synchronized void reset() throws IOException {
        throw new IOException("mark/reset not supported");
}</span>

该方法待子类实现。这里只抛出了不支持mark或者reset方法。

<span style="font-family:Comic Sans MS;font-size:18px;">public boolean markSupported() {
        return false;
    }</span>

测试这个文件流是否支持mark以及reset方法。

下面是测试代码:

<span style="font-family:Comic Sans MS;">// TODO Auto-generated method stub
		//文件中的内容为:helloworld
		File file = new File("/users/terrylmay/desktop/test.txt");
		FileInputStream fis = new FileInputStream(file);
		BufferedInputStream br = new BufferedInputStream(fis);
		//br.skip(2);跳过两个字节,那么流中就只剩下了lloworld
		br.skip(2);
		//设置在读取的5个字节内标识有效,并且标记当前位置
		br.mark(5);
		//读取两个字节a = 'l' b='l'
		int a = br.read();
		int b = br.read();
		//打印
		System.out.println(""+(char)a+(char)b);
		//使得复位
		br.reset();
		//再次读取流中的字节
		a = br.read();
		b = br.read();
		//打印
		System.out.println(""+(char)a+(char)b);
		//返回流中的剩余字节
		int length = br.available();
		System.out.println(length);</span>

打印结果为:

ll

ll

6









  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值