概述
在 Java API 中,可以从其中读入一个字节序列的对象称做输入流InputStream
,而可以向其中写入一个字节序列的对象称做输出流OutputStream
。
输入流 InputStream
package java.io;
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;
/**
* 从数据中读入一个字节,并返回该字节,或者在遇到输入流结尾时返回-1。
*/
public abstract int read() throws IOException;
/**
* 读入一个字节数组,并返回实际读入的字节数,或者在遇到输入流结尾时返回-1。
* 最多读入b.length个字节
*/
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
/**
* 读入一个字节数组,并返回实际读入的字节数,或者在遇到输入流结尾时返回-1。
* off: 第一个读入字节应该被放置的位置在b中的偏移量。
* len: 读入字节的最大数量。
*/
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;
}
/**
* 返回在不阻塞的情况下可获取的字节数(阻塞意味着当前线程将失去它对资源的占用)。
*/
public int available() throws IOException {
return 0;
}
/**
* 关闭这个输入流
*/
public void close() throws IOException {}
/**
* 在输入流的当前位置打一个标记(并非所有的流都支持这个特性)。
* 如果从输入流中已经读入的字节多于readlimit个,则这个流允许忽略这个标记。
*/
public synchronized void mark(int readlimit) {}
/**
* 返回到最后一个标记,随后对read的调用将重新读入这些字节。
* 如果当前没有任何标记,则这个流不被重置。
*/
public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}
/**
* 如果这个流支持打标记,返回true。
*/
public boolean markSupported() {
return false;
}
}
输出流 OutputStream
package java.io;
public abstract class OutputStream implements Closeable, Flushable {
/**
* 写出一个字节的数据。
*/
public abstract void write(int b) throws IOException;
/**
* 写出所有字节到数组b中。
*/
public void write(byte b[]) throws IOException {
write(b, 0, b.length);
}
/**
* 写出某个范围的字节到数组b中。
* off: 第一个写出字节在b中的偏移量。
* len: 写换粗字节的最大数量。
*/
public void write(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
for (int i = 0 ; i < len ; i++) {
write(b[off + i]);
}
}
/**
* 冲刷输出流,将所有缓冲区中的数据发送到目的地。
*/
public void flush() throws IOException {}
/**
* 冲刷并关闭输出流。
*/
public void close() throws IOException {}
}
注意
read和write方法在执行时都将阻塞,直至字节确实被读入或写出。这就意味着如果流不能被立即访问(通常是因为网络忙),那么当前的线程将被阻塞。这使得在这两个方法等待指定的流变为可用的这段时间里,其他的线程就有机会去执行有用的工作。
当你完成输入/输出流的读写时,应该通过调用close方法关闭它,这个调用会释放掉十分有限的操作系统资源。 如果一个应用程序打开了过多的输入/输出流而没有关闭,那么系统资源将被耗尽。关闭一个输出流的同时还会冲刷用于该输出流的缓冲区:所有被临时置于缓冲区中,以便用更大的包的形式传递的字节在关闭输出流时都将被送出。特别是,如果不关闭文件,那么写出字节的最后一个包可能将永远也得不到传递。当然,我们可以使用flush方法来人为地冲刷这些输出。
参考资料
- Java 核心技术 卷II 第2章 输入与输出