您的位置:首页 > 编程语言 > Java开发

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

2014-12-01 22:01 375 查看
<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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: