PushbackInputstream类是Java IO中的一个较特别的类,因为它可以将读出的字节再推回流中。这也就是它的名字中Pushback的由来。下面首先介绍一下它的基本用法,再分析一下它的源代码。
使用方法
首先需要构造一个PushbackInputStream对象,这个对象需要传入一个InputStream实例作为参数。之后从该InputStream绑定的数据源(例如一个文件、或者网络流)读入数据,然后将读出的数据再推回PushBackInputStream中。
如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class PushbackInputStreamTest { public static void main(String[] args) throws Exception{ PushbackInputStream inputStream = new PushbackInputStream( new FileInputStream("input.txt")); int data = inputStream.read(); System.out.println(data); inputStream.unread(data); data = inputStream.read(); System.out.println(data); } }
|
文件中保存着一个字符串abcdefg。输出如下:
Read before push back: 97
Read after push back: 97
可见,我们读出了一个字符a(它的ascii码是97),之后又成功推回了流中。
如果我们需要一次推回多个字符,则需要在构造PushbackInputStream实例时传入一个整型参数,用于指定每次推回多最大字节数。
如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class PushbackInputStreamTest { public static void main(String[] args) throws Exception{ PushbackInputStream input = new PushbackInputStream( new FileInputStream("input.txt"), 10); byte[] bytes = new byte[10]; int r = input.read(bytes); for (byte b : bytes) { System.out.println(b); } input.unread(bytes); r = input.read(bytes); for (byte b : bytes) { System.out.println(b); } } }
|
输出:
Read before push back:
97
98
99
100
101
102
103
0
0
0
Read after push back:
97
98
99
100
101
102
103
0
0
0
可见,我们可以一次读入最多10个字节(因为数据只有7个字节,所以剩余字节为0.
如果推回多字节大于构造函数中指定的最大字节数,则会发生异常:
java.io.IOException: Push back buffer is full
下面分析一下PushbackInputStream的源代码
成员变量
1 2 3 4 5 6 7 8 9 10
| * 缓冲区,被推回的字节将会保存在这里 */ protected byte[] buf; * 从缓冲区读取数据时的位置,也就是读取被推回的数据时的下标。当缓冲区为空时,pos为buf.length(即没有推回数据,故不能从缓冲区读取);当缓冲区所有字节均有数据时,则pos为0 * */ protected int pos;
|
构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| * 通过指定size的缓冲数组以及一个InputStream实例,来构造一个PushbackInputStream对象 */ public PushbackInputStream(InputStream in, int size) { super(in); if (size <= 0) { throw new IllegalArgumentException("size <= 0"); } this.buf = new byte[size]; this.pos = size; } * 若不指定缓冲区大小,则默认为1,即每次只能推回一个字节 */ public PushbackInputStream(InputStream in) { this(in, 1); }
|
读取数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| * 读取数据,若缓冲区有数据,则从缓冲区的pos位置开始读取;若缓冲区无数据,则调用超类的read方法读取数据 */ public int read() throws IOException { ensureOpen(); if (pos < buf.length) { return buf[pos++] & 0xff; } return super.read(); } * 将数据读入一个byte数组中,参数off是目标数组中的起始偏移量,参数len是读入数据的最大字节数 */ public int read(byte[] b, int off, int len) throws IOException { ensureOpen(); 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 avail = buf.length - pos; if (avail > 0) { if (len < avail) { avail = len; } System.arraycopy(buf, pos, b, off, avail); pos += avail; off += avail; len -= avail; } if (len > 0) { len = super.read(b, off, len); if (len == -1) { return avail == 0 ? -1 : avail; } return avail + len; } return avail; }
|
推回数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| * 推回一个字节数据,即将它拷贝到本类的缓冲数组中 */ public void unread(int b) throws IOException { ensureOpen(); if (pos == 0) { throw new IOException("Push back buffer is full"); } buf[--pos] = (byte)b; } * 推回字节数组的一部分到缓冲数组中, */ public void unread(byte[] b, int off, int len) throws IOException { ensureOpen(); if (len > pos) { throw new IOException("Push back buffer is full"); } pos -= len; System.arraycopy(b, off, buf, pos, len); }
|
以上就是这个类的主要方法。我们可以看出,当单个推回数据时,是逆序插入缓冲数组,同时读取数据时,是正序读出。所以是后入先出。