Java I/O 25 - BlockDataInputStream

  关于 java.io.ObjectInputStream.BlockDataInputStream 的部分笔记,该类是ObjectInputStream的底层数据读取类,用来完成从流中读取数据的所有操作。本文演示代码段的执行环境基于JDK版本1.7

概述

  BlockDataInputStream有两个读取模式:默认模式中,输入数据按照和DataOutputStream一样的格式被写入,如果在块数据模式中,输入数据被块数据标记包围写入。是否采用缓存支持取决于读取模式,默认模式不会采用缓存机制来获取数据,所有数据直接从底层输入流获取。反之,如果是块数据模式那么数据会被缓存下来供以后读取需要。

继承关系

1
2
3
4
5
// BlockDataInputStream
--java.lang.Object
--java.io.InputStream
--java.io.ObjectInputStream
--java.io.ObjectInputStream.BlockDataInputStream

实现接口

类名 实现接口
BlockDataInputStream Closeable, DataInput, AutoCloseable

BlockDataInputStream

Constructor Summary

public ObjectInputStream(InputStream in)

1
2
3
4
BlockDataInputStream(InputStream in) {
this.in = new PeekInputStream(in);
din = new DataInputStream(this);
}

  根据指定输入流初始化一个底层输入流和一个DataInputStream实例。块数据模式默认被置为false。

部分方法

boolean setBlockDataMode(boolean newmode)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
boolean setBlockDataMode(boolean newmode) throws IOException {
if (blkmode == newmode) {
return blkmode;
}
if (newmode) {
pos = 0;
end = 0;
unread = 0;
} else if (pos < end) {
throw new IllegalStateException("unread block data");
}
blkmode = newmode;
return !blkmode;
}

  设置块数据读取模式,true为on,false为off,并返回设置之前的读取模式值。如果在模式从on到off的切换过程中尚有数据需要读取,那么就抛出IllegalStateException异常。

boolean getBlockDataMode()

1
2
3
boolean getBlockDataMode() {
return blkmode;
}

  获取块数据读取模式。

void skipBlockData()

1
2
3
4
5
6
7
8
void skipBlockData() throws IOException {
if (!blkmode) {
throw new IllegalStateException("not in block data mode");
}
while (end >= 0) {
refill();
}
}

  跳过当前数据块到达当前数据块的结尾位置。如果当前模式不是on,那么抛出异常。refill()方法完成实际的跳过操作。

private void refill()

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
private void refill() throws IOException {
try {
do {
pos = 0;
if (unread > 0) {
int n =
in.read(buf, 0, Math.min(unread, MAX_BLOCK_SIZE));
if (n >= 0) {
end = n;
unread -= n;
} else {
throw new StreamCorruptedException(
"unexpected EOF in middle of data block");
}
} else {
int n = readBlockHeader(true);
if (n >= 0) {
end = 0;
unread = n;
} else {
end = -1;
unread = 0;
}
}
} while (pos == end);
} catch (IOException ex) {
pos = 0;
end = -1;
unread = 0;
throw ex;
}
}

  用块数据重新填充内部缓冲区buffer。在方法被调用时,buffer中存在的数据被认为是消费过的。新的pos,end,unread字段被认为是新的块数据信息。操作流程如图1所示:

图 - 1

private int readBlockHeader(boolean canBlock)

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
51
52
53
54
55
56
57
58
59
60
61
62
63
private int readBlockHeader(boolean canBlock) throws IOException {
if (defaultDataEnd) {
/*
* Fix for 4360508: stream is currently at the end of a field
* value block written via default serialization; since there
* is no terminating TC_ENDBLOCKDATA tag, simulate
* end-of-custom-data behavior explicitly.
*/
return -1;
}
try {
for (;;) {
int avail = canBlock ? Integer.MAX_VALUE : in.available();
if (avail == 0) {
return HEADER_BLOCKED;
}

int tc = in.peek();
switch (tc) {
case TC_BLOCKDATA:
if (avail < 2) {
return HEADER_BLOCKED;
}
in.readFully(hbuf, 0, 2);
return hbuf[1] & 0xFF;

case TC_BLOCKDATALONG:
if (avail < 5) {
return HEADER_BLOCKED;
}
in.readFully(hbuf, 0, 5);
int len = Bits.getInt(hbuf, 1);
if (len < 0) {
throw new StreamCorruptedException(
"illegal block data header length: " +
len);
}
return len;

/*
* TC_RESETs may occur in between data blocks.
* Unfortunately, this case must be parsed at a lower
* level than other typecodes, since primitive data
* reads may span data blocks separated by a TC_RESET.
*/
case TC_RESET:
in.read();
handleReset();
break;

default:
if (tc >= 0 && (tc < TC_BASE || tc > TC_MAX)) {
throw new StreamCorruptedException(
String.format("invalid type code: %02X", tc));
}
return -1;
}
}
} catch (EOFException ex) {
throw new StreamCorruptedException(
"unexpected EOF while reading block data header");
}
}

  尝试读取下一个块数据的头部信息,并返回头部指定的块数据长度。

int currentBlockRemaining()

1
2
3
4
5
6
7
int currentBlockRemaining() {
if (blkmode) {
return (end >= 0) ? (end - pos) + unread : 0;
} else {
throw new IllegalStateException();
}
}

  返回当前数据块中剩余的可读数据长度。如果是块数据模式,那么就返回剩余长度,否则抛出异常。

int peek()

1
2
3
4
5
6
7
8
9
10
int peek() throws IOException {
if (blkmode) {
if (pos == end) {
refill();
}
return (end >= 0) ? (buf[pos] & 0xFF) : -1;
} else {
return in.peek();
}
}

  获取(但不会消费掉)下一个字节的内容。如果是块数据模式,那么尝试从底层缓冲区buffer中取数据,否则直接调用底层输入流获取。

byte peekByte()

1
2
3
4
5
6
7
byte peekByte() throws IOException {
int val = peek();
if (val < 0) {
throw new EOFException();
}
return (byte) val;
}

  获取(但不会消费掉)下一个字节的内容,内容以字节类型返回。

public int read()

1
2
3
4
5
6
7
8
9
10
public int read() throws IOException {
if (blkmode) {
if (pos == end) {
refill();
}
return (end >= 0) ? (buf[pos++] & 0xFF) : -1;
} else {
return in.read();
}
}

  获取下一个字节的内容。如果是块数据模式,那么尝试从底层缓冲区buffer中取数据,否则直接调用底层输入流获取。

public int read(byte[] b, int off, int len)

1
2
3
public int read(byte[] b, int off, int len) throws IOException {
return read(b, off, len, false);
}

  获取字节内容,并存储到字节数组b中自off位置起,长度为len的空间内。返回实际读取的字符内容长度。

int read(byte[] b, int off, int len, boolean copy)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int read(byte[] b, int off, int len, boolean copy) throws IOException {
if (len == 0) {
return 0;
} else if (blkmode) {
if (pos == end) {
refill();
}
if (end < 0) {
return -1;
}
int nread = Math.min(len, end - pos);
System.arraycopy(buf, pos, b, off, nread);
pos += nread;
return nread;
} else if (copy) {
int nread = in.read(buf, 0, Math.min(len, MAX_BLOCK_SIZE));
if (nread > 0) {
System.arraycopy(buf, 0, b, off, nread);
}
return nread;
} else {
return in.read(b, off, len);
}
}

  如果是块数据读取模式,尝试从底层缓冲区buffer中获取数据。如果copy为true,那么首先将读取的数据保存到底层缓冲区buffer中,然后在将数据返回到b中。如果不是快数据读取模式,那么直接从底层输入流获取数据。

public long skip(long len)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public long skip(long len) throws IOException {
long remain = len;
while (remain > 0) {
if (blkmode) {
if (pos == end) {
refill();
}
if (end < 0) {
break;
}
int nread = (int) Math.min(remain, end - pos);
remain -= nread;
pos += nread;
} else {
int nread = (int) Math.min(remain, MAX_BLOCK_SIZE);
if ((nread = in.read(buf, 0, nread)) < 0) {
break;
}
remain -= nread;
}
}
return len - remain;
}

  跳过当前块数据中长度为len的内容。

public int available()

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
public int available() throws IOException {
if (blkmode) {
if ((pos == end) && (unread == 0)) {
int n;
while ((n = readBlockHeader(false)) == 0) ;
switch (n) {
case HEADER_BLOCKED:
break;

case -1:
pos = 0;
end = -1;
break;

default:
pos = 0;
end = 0;
unread = n;
break;
}
}
// avoid unnecessary call to in.available() if possible
int unreadAvail = (unread > 0) ? Math.min(in.available(), unread) : 0;
return (end >= 0) ? (end - pos) + unreadAvail : 0;
} else {
return in.available();
}
}

  返回当前块数据中剩余可读的数据量。

public void close()

1
2
3
4
5
6
7
8
public void close() throws IOException {
if (blkmode) {
pos = 0;
end = -1;
unread = 0;
}
in.close();
}

  关闭当前底层输入流。

primitive data input methods

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/* ----------------- primitive data input methods ------------------ */
public void readFully(byte[] b) throws IOException {
readFully(b, 0, b.length, false);
}

public void readFully(byte[] b, int off, int len) throws IOException {
readFully(b, off, len, false);
}

public void readFully(byte[] b, int off, int len, boolean copy)
throws IOException {
while (len > 0) {
int n = read(b, off, len, copy);
if (n < 0) {
throw new EOFException();
}
off += n;
len -= n;
}
}

public int skipBytes(int n) throws IOException {
return din.skipBytes(n);
}

public boolean readBoolean() throws IOException {
int v = read();
if (v < 0) {
throw new EOFException();
}
return (v != 0);
}

public byte readByte() throws IOException {
int v = read();
if (v < 0) {
throw new EOFException();
}
return (byte) v;
}

public int readUnsignedByte() throws IOException {
int v = read();
if (v < 0) {
throw new EOFException();
}
return v;
}

public char readChar() throws IOException {
if (!blkmode) {
pos = 0;
in.readFully(buf, 0, 2);
} else if (end - pos < 2) {
return din.readChar();
}
char v = Bits.getChar(buf, pos);
pos += 2;
return v;
}

public short readShort() throws IOException {
if (!blkmode) {
pos = 0;
in.readFully(buf, 0, 2);
} else if (end - pos < 2) {
return din.readShort();
}
short v = Bits.getShort(buf, pos);
pos += 2;
return v;
}

public int readUnsignedShort() throws IOException {
if (!blkmode) {
pos = 0;
in.readFully(buf, 0, 2);
} else if (end - pos < 2) {
return din.readUnsignedShort();
}
int v = Bits.getShort(buf, pos) & 0xFFFF;
pos += 2;
return v;
}

public int readInt() throws IOException {
if (!blkmode) {
pos = 0;
in.readFully(buf, 0, 4);
} else if (end - pos < 4) {
return din.readInt();
}
int v = Bits.getInt(buf, pos);
pos += 4;
return v;
}

public float readFloat() throws IOException {
if (!blkmode) {
pos = 0;
in.readFully(buf, 0, 4);
} else if (end - pos < 4) {
return din.readFloat();
}
float v = Bits.getFloat(buf, pos);
pos += 4;
return v;
}

public long readLong() throws IOException {
if (!blkmode) {
pos = 0;
in.readFully(buf, 0, 8);
} else if (end - pos < 8) {
return din.readLong();
}
long v = Bits.getLong(buf, pos);
pos += 8;
return v;
}

public double readDouble() throws IOException {
if (!blkmode) {
pos = 0;
in.readFully(buf, 0, 8);
} else if (end - pos < 8) {
return din.readDouble();
}
double v = Bits.getDouble(buf, pos);
pos += 8;
return v;
}

public String readUTF() throws IOException {
return readUTFBody(readUnsignedShort());
}

public String readLine() throws IOException {
return din.readLine(); // deprecated, not worth optimizing
}

primitive data array input methods

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/* -------------- primitive data array input methods --------------- */
void readBooleans(boolean[] v, int off, int len) throws IOException {
int stop, endoff = off + len;
while (off < endoff) {
if (!blkmode) {
int span = Math.min(endoff - off, MAX_BLOCK_SIZE);
in.readFully(buf, 0, span);
stop = off + span;
pos = 0;
} else if (end - pos < 1) {
v[off++] = din.readBoolean();
continue;
} else {
stop = Math.min(endoff, off + end - pos);
}

while (off < stop) {
v[off++] = Bits.getBoolean(buf, pos++);
}
}
}

void readChars(char[] v, int off, int len) throws IOException {
int stop, endoff = off + len;
while (off < endoff) {
if (!blkmode) {
int span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 1);
in.readFully(buf, 0, span << 1);
stop = off + span;
pos = 0;
} else if (end - pos < 2) {
v[off++] = din.readChar();
continue;
} else {
stop = Math.min(endoff, off + ((end - pos) >> 1));
}

while (off < stop) {
v[off++] = Bits.getChar(buf, pos);
pos += 2;
}
}
}

void readShorts(short[] v, int off, int len) throws IOException {
int stop, endoff = off + len;
while (off < endoff) {
if (!blkmode) {
int span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 1);
in.readFully(buf, 0, span << 1);
stop = off + span;
pos = 0;
} else if (end - pos < 2) {
v[off++] = din.readShort();
continue;
} else {
stop = Math.min(endoff, off + ((end - pos) >> 1));
}

while (off < stop) {
v[off++] = Bits.getShort(buf, pos);
pos += 2;
}
}
}

void readInts(int[] v, int off, int len) throws IOException {
int stop, endoff = off + len;
while (off < endoff) {
if (!blkmode) {
int span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 2);
in.readFully(buf, 0, span << 2);
stop = off + span;
pos = 0;
} else if (end - pos < 4) {
v[off++] = din.readInt();
continue;
} else {
stop = Math.min(endoff, off + ((end - pos) >> 2));
}

while (off < stop) {
v[off++] = Bits.getInt(buf, pos);
pos += 4;
}
}
}

void readFloats(float[] v, int off, int len) throws IOException {
int span, endoff = off + len;
while (off < endoff) {
if (!blkmode) {
span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 2);
in.readFully(buf, 0, span << 2);
pos = 0;
} else if (end - pos < 4) {
v[off++] = din.readFloat();
continue;
} else {
span = Math.min(endoff - off, ((end - pos) >> 2));
}

bytesToFloats(buf, pos, v, off, span);
off += span;
pos += span << 2;
}
}

void readLongs(long[] v, int off, int len) throws IOException {
int stop, endoff = off + len;
while (off < endoff) {
if (!blkmode) {
int span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 3);
in.readFully(buf, 0, span << 3);
stop = off + span;
pos = 0;
} else if (end - pos < 8) {
v[off++] = din.readLong();
continue;
} else {
stop = Math.min(endoff, off + ((end - pos) >> 3));
}

while (off < stop) {
v[off++] = Bits.getLong(buf, pos);
pos += 8;
}
}
}

void readDoubles(double[] v, int off, int len) throws IOException {
int span, endoff = off + len;
while (off < endoff) {
if (!blkmode) {
span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 3);
in.readFully(buf, 0, span << 3);
pos = 0;
} else if (end - pos < 8) {
v[off++] = din.readDouble();
continue;
} else {
span = Math.min(endoff - off, ((end - pos) >> 3));
}

bytesToDoubles(buf, pos, v, off, span);
off += span;
pos += span << 3;
}
}

String readLongUTF()

1
2
3
String readLongUTF() throws IOException {
return readUTFBody(readLong());
}

  读取并返回以长UTF格式写入流中的字符串。长UTF格式有别于标准的UTF格式,因为长UTF格式采用8个比特位作为头部来传输UTF编码长度,而标准UTF只有两个比特位。

private String readUTFBody(long utflen)

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
private String readUTFBody(long utflen) throws IOException {
StringBuilder sbuf = new StringBuilder();
if (!blkmode) {
end = pos = 0;
}

while (utflen > 0) {
int avail = end - pos;
if (avail >= 3 || (long) avail == utflen) {
utflen -= readUTFSpan(sbuf, utflen);
} else {
if (blkmode) {
// near block boundary, read one byte at a time
utflen -= readUTFChar(sbuf, utflen);
} else {
// shift and refill buffer manually
if (avail > 0) {
System.arraycopy(buf, pos, buf, 0, avail);
}
pos = 0;
end = (int) Math.min(MAX_BLOCK_SIZE, utflen);
in.readFully(buf, avail, end - avail);
}
}
}

return sbuf.toString();
}

  读取并返回UTF编码除了头部之外的数据内容。

private long readUTFSpan(StringBuilder sbuf, long utflen)

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
private long readUTFSpan(StringBuilder sbuf, long utflen) throws IOException {
int cpos = 0;
int start = pos;
int avail = Math.min(end - pos, CHAR_BUF_SIZE);
// stop short of last char unless all of utf bytes in buffer
int stop = pos + ((utflen > avail) ? avail - 2 : (int) utflen);
boolean outOfBounds = false;

try {
while (pos < stop) {
int b1, b2, b3;
b1 = buf[pos++] & 0xFF;
switch (b1 >> 4) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7: // 1 byte format: 0xxxxxxx
cbuf[cpos++] = (char) b1;
break;

case 12:
case 13: // 2 byte format: 110xxxxx 10xxxxxx
b2 = buf[pos++];
if ((b2 & 0xC0) != 0x80) {
throw new UTFDataFormatException();
}
cbuf[cpos++] = (char) (((b1 & 0x1F) << 6) |
((b2 & 0x3F) << 0));
break;

case 14: // 3 byte format: 1110xxxx 10xxxxxx 10xxxxxx
b3 = buf[pos + 1];
b2 = buf[pos + 0];
pos += 2;
if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80) {
throw new UTFDataFormatException();
}
cbuf[cpos++] = (char) (((b1 & 0x0F) << 12) |
((b2 & 0x3F) << 6) |
((b3 & 0x3F) << 0));
break;

default: // 10xx xxxx, 1111 xxxx
throw new UTFDataFormatException();
}
}
} catch (ArrayIndexOutOfBoundsException ex) {
outOfBounds = true;
} finally {
if (outOfBounds || (pos - start) > utflen) {
/*
* Fix for 4450867: if a malformed utf char causes the
* conversion loop to scan past the expected end of the utf
* string, only consume the expected number of utf bytes.
*/
pos = start + (int) utflen;
throw new UTFDataFormatException();
}
}

sbuf.append(cbuf, 0, cpos);
return pos - start;
}

private int readUTFChar(StringBuilder sbuf, long utflen)

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
private int readUTFChar(StringBuilder sbuf, long utflen) throws IOException {
int b1, b2, b3;
b1 = readByte() & 0xFF;
switch (b1 >> 4) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7: // 1 byte format: 0xxxxxxx
sbuf.append((char) b1);
return 1;

case 12:
case 13: // 2 byte format: 110xxxxx 10xxxxxx
if (utflen < 2) {
throw new UTFDataFormatException();
}
b2 = readByte();
if ((b2 & 0xC0) != 0x80) {
throw new UTFDataFormatException();
}
sbuf.append((char) (((b1 & 0x1F) << 6) |
((b2 & 0x3F) << 0)));
return 2;

case 14: // 3 byte format: 1110xxxx 10xxxxxx 10xxxxxx
if (utflen < 3) {
if (utflen == 2) {
readByte(); // consume remaining byte
}
throw new UTFDataFormatException();
}
b2 = readByte();
b3 = readByte();
if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80) {
throw new UTFDataFormatException();
}
sbuf.append((char) (((b1 & 0x0F) << 12) |
((b2 & 0x3F) << 6) |
((b3 & 0x3F) << 0)));
return 3;

default: // 10xx xxxx, 1111 xxxx
throw new UTFDataFormatException();
}
}

PeekInputStream

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
private static class PeekInputStream extends InputStream {

/** underlying stream */
private final InputStream in;
/** peeked byte */
private int peekb = -1;

/**
* Creates new PeekInputStream on top of given underlying stream.
*/
PeekInputStream(InputStream in) {
this.in = in;
}

/**
* Peeks at next byte value in stream. Similar to read(), except
* that it does not consume the read value.
*/
int peek() throws IOException {
return (peekb >= 0) ? peekb : (peekb = in.read());
}

public int read() throws IOException {
if (peekb >= 0) {
int v = peekb;
peekb = -1;
return v;
} else {
return in.read();
}
}

public int read(byte[] b, int off, int len) throws IOException {
if (len == 0) {
return 0;
} else if (peekb < 0) {
return in.read(b, off, len);
} else {
b[off++] = (byte) peekb;
len--;
peekb = -1;
int n = in.read(b, off, len);
return (n >= 0) ? (n + 1) : 1;
}
}

void readFully(byte[] b, int off, int len) throws IOException {
int n = 0;
while (n < len) {
int count = read(b, off + n, len - n);
if (count < 0) {
throw new EOFException();
}
n += count;
}
}

public long skip(long n) throws IOException {
if (n <= 0) {
return 0;
}
int skipped = 0;
if (peekb >= 0) {
peekb = -1;
skipped++;
n--;
}
return skipped + skip(n);
}

public int available() throws IOException {
return in.available() + ((peekb >= 0) ? 1 : 0);
}

public void close() throws IOException {
in.close();
}
}

涉及基础知识点

  1. NIL

参考文献

  1. NIL




------------- End of this article, thanks! -------------


  版权声明:本文由N.C.Lee创作和发表,采用署名(BY)-非商业性使用(NC)-相同方式共享(SA)国际许可协议进行许可,转载请注明作者及出处。
  本文作者为 N.C.Lee
  本文标题为 Java I/O 25 - BlockDataInputStream
  本文链接为 https://marcuseddie.github.io/2018/java-BlockDataInputStream.html