IO流的抽象基类分字节输入流(InputStream)、字节输出流(OutputStream)、字符输入流(Reader)、字符输出流(Writer)。它们是IO流中的抽象父类,是Java IO操作的基础。流都实现了Closeable接口,即都需要关闭,输出流则额外实现了一个Flushable方法,主要是通知立刻将数据刷入指定输出位置。
2.1 InputStream
java.io.InputStream
是一个抽象类,是字节输入流的所有类的超类,除了继承自Object的属性和方法,InputStream包含9个方法:
-
abstract int read()
方法是一个虚拟方法,从输入流中读取下一个字节,其子类必须实现这个方法。 -
int read(byte[] b,int off,int len)
方法将流读入byte[]数组中第off个位置开始,读取len个字节。其实现有调用read()
方法进行读取,读取前判断参数是否合理,若读取到-1则退出(流以-1判断是否结束),并返回已经读取的数据个数,若读取的个数为0则返回-1。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; }
-
int read(byte[])
方法用处是将流的输入读入byte[]数组中,其实际上调用的是read(byte[],int,int)
。 -
long skip(long)
,跳过指定个字节不读。其具体实现是:创建了一个跳过的缓冲数据,将读到这里面去,跳过的字节有长度限制,为2048个。通过循环排除IO异常的感染,知道读完或者读满要跳过的,然后返回实际跳过的量。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; }
-
int available()
返回估测的可读的数量,在InputStream中是直接return 0
。这个方法对于所有流的实现来说不都是准确的,所以慎用。这个方法需要被子类覆写。单次读或跳过很多字节不会阻塞,但是可能会读取或跳过少量字节。 -
void close()
,关闭流,但是在InputStream中没有具体实现,会释放操作系统资源。 -
synchronized void mark(int readlimit)
标记读取位置,如果流实现支持,就可以重置位置,下次读取则一模一样。InputStream中什么都没做。使用之前要通过markSupported方法判断一下是否支持。 -
synchronized void reset()
reset到mark方法最后一次调用的位置,InputStream中并没有实现mark方法,所以reset方法直接抛出异常。 -
boolean markSupported()
判断当前流是否支持标记流。
2.2 OutputStream
java.io.OutputStream
此抽象类是表示输出字节流的所有类的超累,输出流接受输出字节并将这些字节发送到某个接收器,除了继承自Object的属性和方法,OutputStream包含5个方法:
-
abstract void write(int b)
,抽象方法,通常是写入一个字节byte,低8位将写入,高24位会被忽略。将指定字节写入输出位置,子类必须实现这个方法。 -
void write(byte b[], int off, int len)
方法,将数组b的起始位置off开始,写入len长度的字节。其实现包含边界判定,最后通过循环调用write
方法实现。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]); } }
-
void write(byte b[])
通过调用方法2:write(b,0,b.length)实现 -
void flush()
刷新此输出流并强制携出所有缓冲的输出字节。flush的常规协定是:如果此输出流的实现已经缓冲了以前写入的任何字节,则调用flush方法指示应将这些字节立即写入它们的预期目标。但是如果此流的预期目标是由基础操作系统提供的一个抽象(如一个文件),则flush只能保证将以前写入到流的字节传递给操作系统进行写入,不保证能立即将这些字节写入到物理设备(如磁盘驱动器)。在OutputStream中该方法不执行任何操作。 -
void close()
关闭输出流并释放与此流有关的所有系统资源。close的常规协定是:该方法将关闭输出流,关闭的流不能执行输出操作,也不能重新发开。OutputStream的close方法不执行人格操作。
2.3 Reader
java.io.Reader
实现了接口Closeable和Readable,其是用于读取字符流的抽象类。
相比于字节输入流InputStream,Reader中不同的有以下几点:
-
lock对象 lock是Object类型的对象,用于同步针对此流的操作的对象。为了提高效率,字符流可以使用自身以外的对象来保护重要部分,因此,子类应使用此字段中的对象而不是this或者同步的方法。构造方法中Reader()所做的是创建一个新的字符流,其重要部分将同步其自身的reader;而Reader(Object lock)则是创建一个新的字符流reader,其重要部分将同步给定的对象。
-
Reader的三个read方法中,
read()
方法和read(char cbuf[])
都是使用read(char cbuf[], int off, int len)
实现的,而子类必须实现read(char cbuf[], int off, int len)
方法,注意:用于支持高效单字符输入的子类必须重写read()
方法/*用于支持高效单字符输入的子类必须重写该方法*/ public int read() throws IOException { char cb[] = new char[1]; if (read(cb, 0, 1) == -1) return -1; else return cb[0]; } public int read(char cbuf[]) throws IOException { return read(cbuf, 0, cbuf.length); } /*子类必须实现该方法*/ abstract public int read(char cbuf[], int off, int len) throws IOException;
-
read(CharBuffer target)
方法:该方法试图将字符读入指定的字符缓冲区。缓冲区可照鸳鸯用作字符的存储库:所做的唯一改变时put操作的结果。不对缓冲区执行翻转或重绕操作。public int read(java.nio.CharBuffer target) throws IOException { int len = target.remaining(); char[] cbuf = new char[len]; int n = read(cbuf, 0, len); if (n > 0) target.put(cbuf, 0, n); return n; }
-
*ready()方法** 该方法在Reader中是直接返回flase的,其用来判断是否准备读取此流。如果保证下一个read()不阻塞输入,则返回True,否则返回false。注意,返回false并不保证下一次读取。
-
skip()方法的长度限制是8192
2.4 Writer
java.io.Writer
实现了
Writer相比于字节输出流OutputStream有更多的不同。
-
lock对象
-
write方法 Writer提供了5个write方法,其中子类需要实现的是
void write(char cbuf[], int off, int len)
。/*该方法使用write(char cbuf[], int off, int len)方法实现*/ public void write(int c) throws IOException { synchronized (lock) { if (writeBuffer == null){ writeBuffer = new char[WRITE_BUFFER_SIZE]; } writeBuffer[0] = (char) c; write(writeBuffer, 0, 1); } } /*该方法使用write(char cbuf[], int off, int len)方法实现*/ public void write(char cbuf[]) throws IOException { write(cbuf, 0, cbuf.length); } /*子类必须实现该方法*/ abstract public void write(char cbuf[], int off, int len) throws IOException; /*该方法使用write(String str, int off, int len)方法实现*/ public void write(String str) throws IOException { write(str, 0, str.length()); } /*该方法使用write(char cbuf[], int off, int len)方法实现*/ public void write(String str, int off, int len) throws IOException { synchronized (lock) { char cbuf[]; if (len <= WRITE_BUFFER_SIZE) { if (writeBuffer == null) { writeBuffer = new char[WRITE_BUFFER_SIZE]; } cbuf = writeBuffer; } else { // Don't permanently allocate very large buffers. cbuf = new char[len]; } str.getChars(off, (off + len), cbuf, 0); write(cbuf, 0, len); } }
3.append方法都来自于其实现的Appendable接口,用于将指定字符或字符序列添加到此writer。
-
public Writer append(CharSequence csq) throws IOException { if (csq == null) write("null"); else write(csq.toString()); return this; } public Writer append(CharSequence csq, int start, int end) throws IOException { CharSequence cs = (csq == null ? "null" : csq); write(cs.subSequence(start, end).toString()); return this; } public Writer append(char c) throws IOException { write(c); return this; }