Java字节流:BufferedInputStream BufferedOutputStream
-----------------------------------------------------------------------------------
BufferedInputStream
类声明:public class BufferedInputStream extends FilterInputStream
位于java.io包下
官方对其说明:
A BufferedInputStream adds functionality to another input stream-namely, the ability to buffer the input and to support the mark and reset methods. When the BufferedInputStream is created, an internal buffer array is created. As bytes from the stream are read or skipped, the internal buffer is refilled as necessary from the contained input stream, many bytes at a time. The mark operation remembers a point in the input stream and the reset operation causes all the bytes read since the most recent mark operation to be reread before new bytes are taken from the contained input stream.
(简单翻译:BufferedInputStream为另一个输入流添加一些功能,即缓冲输入以及支持mark和reset方法的能力,在创建BufferedInputStream时,会创建一个内部缓冲区数组。在读取或跳过流中的字节时,可根据需要从包含的输入流再次填充该内部缓冲区,一次填充多个字节。mark 操作记录输入流中的某个点,reset 操作使得在从包含的输入流中获取新字节之前,再次读取自最后一次 mark 操作后读取的所字节。)
主要字段:
protected byte[] buf;//存储数据的内部缓冲区数组
protected int count;//缓冲区中有效字节的个数
protected int marklimit;//调用mark方法后,在后续调用reset方法失败之前允许的最大提前读取量
protected int markpos;//最后一次调用mark方法时pos字段的值
protected int pos;//缓冲区中的当前位置
构造方法:
BufferedInputStream(InputStream in)
创建一个BufferedInputStream并保存其参数,即输入流in,以便将来使用。
BufferedInputStream(InputStream in,int size)
创建具有指定缓冲区大小的BufferedInputStream并保存其参数,即输入流in,以便将来使用。
主要方法:
- int available(): 返回缓存字节输入流中可读取的字节数
- void close(): 关闭此缓存字节输入流并释放与该流有关的系统资源.
- void mark(int readlimit): 在流中标记位置
- boolean markSupported(): 测试该输入流是否支持mark和reset方法
- int read(): 从缓冲输入流中读取一个字节数据
- int read(byte[] b,int off,int len): 从缓冲输入流中将最多len个字节的数据读入到字节数组b中
- long skip(long n): 从缓冲输入流中跳过并丢弃n个字节的数据
首先我们要明白BufferedInputStream的思想,它的作用就是为其它输入流提供缓冲功能。创建BufferedInputStream时我们会通过它的构造函数指定某个输入流为参数,BufferedInputStream会将该输入流数据分批读取,每次读取一部分到缓冲区中,操作完缓冲区中的数据后,再次从输入流中读取下一部分的数据。
BufferedInputStream 缓冲字节输入流,它作为FilterInputStream的一个子类,为传入的底层字节输入流提供缓冲功能,通过底层字节输入流(in)读取字节到自己的buffer中(内置缓存字节数组),然后程序调用BufferedInputStream的read方法将buffer中的字节读取到程序中,当buffer中的字节被读取完之后,BufferedInputStream会从in中读取下一批数据块到buffer中,直到in中的数据被读取完毕,这样做的好处是提高读取的效率和减少打开存储介质的链接次数。
下面就通过构造函数来创建一个BufferedInputStream实例
重点查看read()和fill()方法
bis.txt文件的内容为:qwertyuiopasdfghjklzxcvbnm
//指定输入流为FileInputStream、 缓冲区大小为10
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt"),10);
此时BufferedInputStream在内存中的情况如下图:
此时只是创建出一个BufferedInputStream的实例bis, 其缓冲区中并没有任何的数据,下面可以通过执行read()方法将输入流in中的输入读取到缓冲区中。
先来看看read()方法的源代码:
public synchronized int read() throws IOException {
if (pos >= count) {
fill();//调用fill()方法从输入流in中将数据读取到缓冲区中
if (pos >= count)
return -1;
}
return getBufIfOpen()[pos++] & 0xff;
}
当第一次执行bis.read()方法时,因为属性pos=0、count=0,所以一定会去执行fill()方法,下面我们就先转到fill()方法去看看。
fill()方法的源代码如下:
private void fill() throws IOException {
byte[] buffer = getBufIfOpen();
if (markpos < 0)
pos = 0; /* no mark: throw away the buffer */
else if (pos >= buffer.length) /* no room left in buffer */
if (markpos > 0) { /* can throw away early part of the buffer */
int sz = pos - markpos;
System.arraycopy(buffer, markpos, buffer, 0, sz);
pos = sz;
markpos = 0;
} else if (buffer.length >= marklimit) {
markpos = -1; /* buffer got too big, invalidate mark */
pos = 0; /* drop buffer contents */
} else { /* grow buffer */
int nsz = pos * 2;
if (nsz > marklimit)
nsz = marklimit;
byte nbuf[] = new byte[nsz];
System.arraycopy(buffer, 0, nbuf, 0, pos);
if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
// Can't replace buf if there was an async close.
// Note: This would need to be changed if fill()
// is ever made accessible to multiple threads.
// But for now, the only way CAS can fail is via close.
// assert buf == null;
throw new IOException("Stream closed");
}
buffer = nbuf;
}
count = pos;
int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
if (n > 0)
count = n + pos;
}
当此时从read()方法中调用fill()方法时,因为属性markpos=-1,所以可以把fill()方法不会执行的else部分先去掉:
private void fill() throws IOException {
byte[] buffer = getBufIfOpen();
if (markpos < 0)
pos = 0; /* no mark: throw away the buffer */
count = pos;
int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
if (n > 0)
count = n + pos;
}
看上面简化版的fill()方法就很清楚了,会通过文件输入流的read(byte[] b, int off, int len)方法将字节输入读取到bis的缓冲区中,执行完fill()方法后,bis在内存中的情况如下:
可以看出实例bis中的各个属性值的情况。由此我们可以知道fill()方法从输入流中读取字节数据到bis实例的缓冲区中。
第一次调用read()方法时,内部会去调用fill()方法(fill方法的执行效果如上图所示),read()方法执行完后返回 113,bis在内存中的情况:
属性count=10、pos=1
第二次、第三次、第四次、第五次、第六次、第七次、第八次、第九次调用read()方法时,因为:pos的值都小于count,所以read()方法只会执行下面的代码:
return getBufIfOpen()[pos++] & 0xff;
执行完9次read()方法后,此时bis在内存中的情况如下:属性pos=10,这样下一次去调用read()方法时又会去执行fill()方法了。
当执行第10次read()方法时,因为pos的值为10,所以又会去调用fill()方法,此时因为markpost的值还是为-1,所以fill()方法简化后如下:
private void fill() throws IOException {
byte[] buffer = getBufIfOpen();
if (markpos < 0)
pos = 0; /* no mark: throw away the buffer */
count = pos;
int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
if (n > 0)
count = n + pos;
}
执行完fill()方法后,bis在内存中的情况如下:
从上图可以看出,pos的值被修改为0,buf数组中存储的值为in输入流中下10个字节.由此我们就可以知道:BufferedInputStream类在其内部提供了一个缓冲区来存储从输入流中读取的数据,每次读取一批数据到缓冲区中供程序使用,当缓冲区中的数据使用完了以后,再次从输入流中读取下一批,直到in输入流的末尾。
上面我们分析了BufferedInputStream中的fill()和read()方法,但是在属性markpos>=0的情况下还没有分析,要修改markpos的值 需要调用mark(int readlimit)方法:
public synchronized void mark(int readlimit) {
marklimit = readlimit;
markpos = pos;
}
根据实例bis中的不同属性值,fill方法会有如下5个执行流程:
流程1:当if (pos >= count)并且markpos的值为-1时:
程序执行流程如下:
(1)执行read()方法,转到到fill()方法
(2)fill()方法中,执行if(markpos < 0) 这个分支
简化后的代码如下:
private void fill() throws IOException {
byte[] buffer = getBufIfOpen();
if (markpos < 0)
pos = 0; /* no mark: throw away the buffer */
count = pos;
int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
if (n > 0)
count = n + pos;
}
这种流程发生的情况是 ---->输入流中有很多的数据,我们每次从中读取一部分数据到缓冲区buffer中进行操作。每次当我们读取完buffer中的数据之后,并且此时输入流没有被标记;那么,就接着从输入流中读取下一部分的数据到buffer中。
其中,判断是否读完buffer中的数据,是通过 if (pos >= count) 来判断的;
判断输入流有没有被标记,是通过 if (markpos < 0) 来判断的。
理解这个思想之后,我们再对这种情况下的fill()的代码进行分析,就特别容易理解了。
(1) if (markpos < 0) 它的作用是判断“输入流是否被标记”。若被标记,则markpos大于/等于0;否则markpos等于-1。
(2) 在这种情况下:通过getInIfOpen()获取输入流,然后接着从输入流中读取buffer.length个字节到buffer中。
(3) count = n + pos; 这是根据从输入流中读取的实际数据的多少,来更新buffer中数据的实际大小。
流程2:当if (pos >= count)、markpos>0、if (pos >= buffer.length)时:
程序执行流程如下:
(1) read() 函数中调用 fill()
(2) fill() 中的 else if (pos >= buffer.length) ...
(3) fill() 中的 if (markpos > 0) ...
简化后的代码如下:
private void fill() throws IOException {
byte[] buffer = getBufIfOpen(); if (pos >= buffer.length) /* no room left in buffer */
if (markpos > 0) { /* can throw away early part of the buffer */
int sz = pos - markpos;
System.arraycopy(buffer, markpos, buffer, 0, sz);
pos = sz;
markpos = 0;
}
count = pos;
int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
if (n > 0)
count = n + pos;
}
这种流程发生的情况是 ----> 输入流中有很多的数据,我们每次从中读取一部分数据到buffer中进行操作。当我们读取完buffer中的数据之后,并且此时输入流存在标记时;那么,就发生流程2。此时,我们要保留“被标记位置”到“buffer末尾”的数据,然后再从输入流中读取下一部分的数据到buffer中。
其中,判断是否读完buffer中的数据,是通过 if (pos >= count) 来判断的;
判断输入流有没有被标记,是通过 if (markpos < 0) 来判断的。
判断buffer中没有多余的空间,是通过 if (pos >= buffer.length) 来判断的。
理解这个思想之后,我们再对这种情况下的fill()代码进行分析,就特别容易理解了。
(1) int sz = pos - markpos; 作用是“获取‘被标记位置’到‘buffer末尾’”的数据长度。
(2) System.arraycopy(buffer, markpos, buffer, 0, sz); 作用是“将buffer中从markpos开始的数据”拷贝到buffer中(从位置0开始填充,填充长度是sz)。接着,将sz赋值给pos,即pos就是“被标记位置”到“buffer末尾”的数据长度。
(3) int n = getInIfOpen().read(buffer, pos, buffer.length - pos); 从输入流中读取出“buffer.length - pos”的数据,然后填充到buffer中。
(4) 通过第(02)和(03)步组合起来的buffer,就是包含了“原始buffer被标记位置到buffer末尾”的数据,也包含了“从输入流中新读取的数据”。
注意:执行过流程2之后,markpos的值由“大于0”变成了“等于0”!
流程3:当if (pos >= count)、if(pos >= buffer.length)、if(buffer.length >= marklimit)时:
程序执行流程如下:
(1) read() 函数中调用 fill()
(2) fill() 中的 else if (pos >= buffer.length)
(3) fill() 中的 else if (buffer.length >= marklimit)
简化后的代码如下:
private void fill() throws IOException {
byte[] buffer = getBufIfOpen();
if (pos >= buffer.length) /* no room left in buffer */
if (buffer.length >= marklimit) {
markpos = -1; /* buffer got too big, invalidate mark */
pos = 0; /* drop buffer contents */
}
count = pos;
int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
if (n > 0)
count = n + pos;
}
说明:这种情况的处理非常简单。首先,就是“取消标记”,即 markpos = -1;然后,设置初始化位置为0,即pos=0;最后,再从输入流中读取下一部分数据到buffer中。
流程4:当if (pos >= count)、if(pos >= buffer.length)、markpos=0时:
程序执行流程如下:
(1) read() 函数中调用 fill()
(2) fill() 中的 else if (pos >= buffer.length)
(3) fill() 中的 else { int nsz = pos * 2}
简化后的代码如下:
private void fill() throws IOException {
byte[] buffer = getBufIfOpen(); if (pos >= buffer.length){ /* no room left in buffer */
/* grow buffer */
int nsz = pos * 2;
if (nsz > marklimit)
nsz = marklimit;
byte nbuf[] = new byte[nsz];
System.arraycopy(buffer, 0, nbuf, 0, pos);
if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
// Can't replace buf if there was an async close.
// Note: This would need to be changed if fill()
// is ever made accessible to multiple threads.
// But for now, the only way CAS can fail is via close.
// assert buf == null;
throw new IOException("Stream closed");
}
buffer = nbuf;
}
count = pos;
int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
if (n > 0)
count = n + pos;
}
说明:
这种情况的处理非常简单。
(1) 新建一个字节数组nbuf。nbuf的大小是“pos*2”和“marklimit”中较小的那个数。
(2) 接着,将buffer中的数据拷贝到新数组nbuf中。通过System.arraycopy(buffer, 0, nbuf, 0, pos)
(3) 最后,从输入流读取部分新数据到buffer中。通过getInIfOpen().read(buffer, pos, buffer.length - pos);
注意:在这里,我们思考一个问题,“为什么需要marklimit,它的存在到底有什么意义?”我们结合“情况2”、“情况3”、“情况4”的情况来分析。
假设,marklimit是无限大的,而且我们设置了markpos。当我们从输入流中每读完一部分数据并读取下一部分数据时,都需要保存markpos所标记的数据;这就意味着,我们需要不断执行情况4中的操作,要将buffer的容量扩大……随着读取次数的增多,buffer会越来越大;这会导致我们占据的内存越来越大。所以,我们需要给出一个marklimit;当buffer>=marklimit时,就不再保存markpos的值了。
流程5:除了上面4种情况之外的流程:
执行流程如下:
(1)read()函数中调用fill()方法
(2)fill()中的count = pos
简化后的代码如下:
private void fill() throws IOException {
byte[] buffer = getBufIfOpen(); count = pos;
int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
if (n > 0)
count = n + pos;
}
说明:这种情况的处理很简单,就是直接从输入流读取部分数据到buffer中.
BufferedInputStream类中的其它方法都很简单,直接查看源代码就好了。
BufferedInputStream源代码如下:
package java.io;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; /**
* A <code>BufferedInputStream</code> adds
* functionality to another input stream-namely,
* the ability to buffer the input and to
* support the <code>mark</code> and <code>reset</code>
* methods. When the <code>BufferedInputStream</code>
* is created, an internal buffer array is
* created. As bytes from the stream are read
* or skipped, the internal buffer is refilled
* as necessary from the contained input stream,
* many bytes at a time. The <code>mark</code>
* operation remembers a point in the input
* stream and the <code>reset</code> operation
* causes all the bytes read since the most
* recent <code>mark</code> operation to be
* reread before new bytes are taken from
* the contained input stream.
*
* @author Arthur van Hoff
* @since JDK1.0
*/
public
class BufferedInputStream extends FilterInputStream { private static int defaultBufferSize = 8192; /**
* The internal buffer array where the data is stored. When necessary,
* it may be replaced by another array of
* a different size.
*/
protected volatile byte buf[]; /**
* Atomic updater to provide compareAndSet for buf. This is
* necessary because closes can be asynchronous. We use nullness
* of buf[] as primary indicator that this stream is closed. (The
* "in" field is also nulled out on close.)
*/
private static final
AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater =
AtomicReferenceFieldUpdater.newUpdater
(BufferedInputStream.class, byte[].class, "buf"); /**
* The index one greater than the index of the last valid byte in
* the buffer.
* This value is always
* in the range <code>0</code> through <code>buf.length</code>;
* elements <code>buf[0]</code> through <code>buf[count-1]
* </code>contain buffered input data obtained
* from the underlying input stream.
*/
protected int count; /**
* The current position in the buffer. This is the index of the next
* character to be read from the <code>buf</code> array.
* <p>
* This value is always in the range <code>0</code>
* through <code>count</code>. If it is less
* than <code>count</code>, then <code>buf[pos]</code>
* is the next byte to be supplied as input;
* if it is equal to <code>count</code>, then
* the next <code>read</code> or <code>skip</code>
* operation will require more bytes to be
* read from the contained input stream.
*
* @see java.io.BufferedInputStream#buf
*/
protected int pos; /**
* The value of the <code>pos</code> field at the time the last
* <code>mark</code> method was called.
* <p>
* This value is always
* in the range <code>-1</code> through <code>pos</code>.
* If there is no marked position in the input
* stream, this field is <code>-1</code>. If
* there is a marked position in the input
* stream, then <code>buf[markpos]</code>
* is the first byte to be supplied as input
* after a <code>reset</code> operation. If
* <code>markpos</code> is not <code>-1</code>,
* then all bytes from positions <code>buf[markpos]</code>
* through <code>buf[pos-1]</code> must remain
* in the buffer array (though they may be
* moved to another place in the buffer array,
* with suitable adjustments to the values
* of <code>count</code>, <code>pos</code>,
* and <code>markpos</code>); they may not
* be discarded unless and until the difference
* between <code>pos</code> and <code>markpos</code>
* exceeds <code>marklimit</code>.
*
* @see java.io.BufferedInputStream#mark(int)
* @see java.io.BufferedInputStream#pos
*/
protected int markpos = -1; /**
* The maximum read ahead allowed after a call to the
* <code>mark</code> method before subsequent calls to the
* <code>reset</code> method fail.
* Whenever the difference between <code>pos</code>
* and <code>markpos</code> exceeds <code>marklimit</code>,
* then the mark may be dropped by setting
* <code>markpos</code> to <code>-1</code>.
*
* @see java.io.BufferedInputStream#mark(int)
* @see java.io.BufferedInputStream#reset()
*/
protected int marklimit; /**
* Check to make sure that underlying input stream has not been
* nulled out due to close; if not return it;
*/
private InputStream getInIfOpen() throws IOException {
InputStream input = in;
if (input == null)
throw new IOException("Stream closed");
return input;
} /**
* Check to make sure that buffer has not been nulled out due to
* close; if not return it;
*/
private byte[] getBufIfOpen() throws IOException {
byte[] buffer = buf;
if (buffer == null)
throw new IOException("Stream closed");
return buffer;
} /**
* Creates a <code>BufferedInputStream</code>
* and saves its argument, the input stream
* <code>in</code>, for later use. An internal
* buffer array is created and stored in <code>buf</code>.
*
* @param in the underlying input stream.
*/
public BufferedInputStream(InputStream in) {
this(in, defaultBufferSize);
} /**
* Creates a <code>BufferedInputStream</code>
* with the specified buffer size,
* and saves its argument, the input stream
* <code>in</code>, for later use. An internal
* buffer array of length <code>size</code>
* is created and stored in <code>buf</code>.
*
* @param in the underlying input stream.
* @param size the buffer size.
* @exception IllegalArgumentException if size <= 0.
*/
public BufferedInputStream(InputStream in, int size) {
super(in);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
} /**
* Fills the buffer with more data, taking into account
* shuffling and other tricks for dealing with marks.
* Assumes that it is being called by a synchronized method.
* This method also assumes that all data has already been read in,
* hence pos > count.
*/
private void fill() throws IOException {
byte[] buffer = getBufIfOpen();
if (markpos < 0)
pos = 0; /* no mark: throw away the buffer */
else if (pos >= buffer.length) /* no room left in buffer */
if (markpos > 0) { /* can throw away early part of the buffer */
int sz = pos - markpos;
System.arraycopy(buffer, markpos, buffer, 0, sz);
pos = sz;
markpos = 0;
} else if (buffer.length >= marklimit) {
markpos = -1; /* buffer got too big, invalidate mark */
pos = 0; /* drop buffer contents */
} else { /* grow buffer */
int nsz = pos * 2;
if (nsz > marklimit)
nsz = marklimit;
byte nbuf[] = new byte[nsz];
System.arraycopy(buffer, 0, nbuf, 0, pos);
if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
// Can't replace buf if there was an async close.
// Note: This would need to be changed if fill()
// is ever made accessible to multiple threads.
// But for now, the only way CAS can fail is via close.
// assert buf == null;
throw new IOException("Stream closed");
}
buffer = nbuf;
}
count = pos;
int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
if (n > 0)
count = n + pos;
} /**
* See
* the general contract of the <code>read</code>
* method of <code>InputStream</code>.
*
* @return the next byte of data, or <code>-1</code> if the end of the
* stream is reached.
* @exception IOException if this input stream has been closed by
* invoking its {@link #close()} method,
* or an I/O error occurs.
* @see java.io.FilterInputStream#in
*/
public synchronized int read() throws IOException {
if (pos >= count) {
fill();
if (pos >= count)
return -1;
}
return getBufIfOpen()[pos++] & 0xff;
} /**
* Read characters into a portion of an array, reading from the underlying
* stream at most once if necessary.
*/
private int read1(byte[] b, int off, int len) throws IOException {
int avail = count - pos;
if (avail <= 0) {
/* If the requested length is at least as large as the buffer, and
if there is no mark/reset activity, do not bother to copy the
bytes into the local buffer. In this way buffered streams will
cascade harmlessly. */
if (len >= getBufIfOpen().length && markpos < 0) {
return getInIfOpen().read(b, off, len);
}
fill();
avail = count - pos;
if (avail <= 0) return -1;
}
int cnt = (avail < len) ? avail : len;
System.arraycopy(getBufIfOpen(), pos, b, off, cnt);
pos += cnt;
return cnt;
} /**
* Reads bytes from this byte-input stream into the specified byte array,
* starting at the given offset.
*
* <p> This method implements the general contract of the corresponding
* <code>{@link InputStream#read(byte[], int, int) read}</code> method of
* the <code>{@link InputStream}</code> class. As an additional
* convenience, it attempts to read as many bytes as possible by repeatedly
* invoking the <code>read</code> method of the underlying stream. This
* iterated <code>read</code> continues until one of the following
* conditions becomes true: <ul>
*
* <li> The specified number of bytes have been read,
*
* <li> The <code>read</code> method of the underlying stream returns
* <code>-1</code>, indicating end-of-file, or
*
* <li> The <code>available</code> method of the underlying stream
* returns zero, indicating that further input requests would block.
*
* </ul> If the first <code>read</code> on the underlying stream returns
* <code>-1</code> to indicate end-of-file then this method returns
* <code>-1</code>. Otherwise this method returns the number of bytes
* actually read.
*
* <p> Subclasses of this class are encouraged, but not required, to
* attempt to read as many bytes as possible in the same fashion.
*
* @param b destination buffer.
* @param off offset at which to start storing bytes.
* @param len maximum number of bytes to read.
* @return the number of bytes read, or <code>-1</code> if the end of
* the stream has been reached.
* @exception IOException if this input stream has been closed by
* invoking its {@link #close()} method,
* or an I/O error occurs.
*/
public synchronized int read(byte b[], int off, int len)
throws IOException
{
getBufIfOpen(); // Check for closed stream
if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
} int n = 0;
for (;;) {
int nread = read1(b, off + n, len - n);
if (nread <= 0)
return (n == 0) ? nread : n;
n += nread;
if (n >= len)
return n;
// if not closed but no bytes available, return
InputStream input = in;
if (input != null && input.available() <= 0)
return n;
}
} /**
* See the general contract of the <code>skip</code>
* method of <code>InputStream</code>.
*
* @exception IOException if the stream does not support seek,
* or if this input stream has been closed by
* invoking its {@link #close()} method, or an
* I/O error occurs.
*/
public synchronized long skip(long n) throws IOException {
getBufIfOpen(); // Check for closed stream
if (n <= 0) {
return 0;
}
long avail = count - pos; if (avail <= 0) {
// If no mark position set then don't keep in buffer
if (markpos <0)
return getInIfOpen().skip(n); // Fill in buffer to save bytes for reset
fill();
avail = count - pos;
if (avail <= 0)
return 0;
} long skipped = (avail < n) ? avail : n;
pos += skipped;
return skipped;
} /**
* Returns an estimate of the number of bytes that can be read (or
* skipped over) from this input stream without blocking by the next
* invocation of a method for this input stream. The next invocation might be
* the same thread or another thread. A single read or skip of this
* many bytes will not block, but may read or skip fewer bytes.
* <p>
* This method returns the sum of the number of bytes remaining to be read in
* the buffer (<code>count - pos</code>) and the result of calling the
* {@link java.io.FilterInputStream#in in}.available().
*
* @return an estimate of the number of bytes that can be read (or skipped
* over) from this input stream without blocking.
* @exception IOException if this input stream has been closed by
* invoking its {@link #close()} method,
* or an I/O error occurs.
*/
public synchronized int available() throws IOException {
int n = count - pos;
int avail = getInIfOpen().available();
return n > (Integer.MAX_VALUE - avail)
? Integer.MAX_VALUE
: n + avail;
} /**
* See the general contract of the <code>mark</code>
* method of <code>InputStream</code>.
*
* @param readlimit the maximum limit of bytes that can be read before
* the mark position becomes invalid.
* @see java.io.BufferedInputStream#reset()
*/
public synchronized void mark(int readlimit) {
marklimit = readlimit;
markpos = pos;
} /**
* See the general contract of the <code>reset</code>
* method of <code>InputStream</code>.
* <p>
* If <code>markpos</code> is <code>-1</code>
* (no mark has been set or the mark has been
* invalidated), an <code>IOException</code>
* is thrown. Otherwise, <code>pos</code> is
* set equal to <code>markpos</code>.
*
* @exception IOException if this stream has not been marked or,
* if the mark has been invalidated, or the stream
* has been closed by invoking its {@link #close()}
* method, or an I/O error occurs.
* @see java.io.BufferedInputStream#mark(int)
*/
public synchronized void reset() throws IOException {
getBufIfOpen(); // Cause exception if closed
if (markpos < 0)
throw new IOException("Resetting to invalid mark");
pos = markpos;
} /**
* Tests if this input stream supports the <code>mark</code>
* and <code>reset</code> methods. The <code>markSupported</code>
* method of <code>BufferedInputStream</code> returns
* <code>true</code>.
*
* @return a <code>boolean</code> indicating if this stream type supports
* the <code>mark</code> and <code>reset</code> methods.
* @see java.io.InputStream#mark(int)
* @see java.io.InputStream#reset()
*/
public boolean markSupported() {
return true;
} /**
* Closes this input stream and releases any system resources
* associated with the stream.
* Once the stream has been closed, further read(), available(), reset(),
* or skip() invocations will throw an IOException.
* Closing a previously closed stream has no effect.
*
* @exception IOException if an I/O error occurs.
*/
public void close() throws IOException {
byte[] buffer;
while ( (buffer = buf) != null) {
if (bufUpdater.compareAndSet(this, buffer, null)) {
InputStream input = in;
in = null;
if (input != null)
input.close();
return;
}
// Else retry in case a new buf was CASed in fill()
}
}
}
-----------------------------------------------------------------------------------
BufferedOutputStream
类声明:public class BufferedOutputStream extends FilterOutputStream
明白了BufferedInputStream后就很好理解BufferedOutputStream了,在BufferedOutputStream内部也提供了一个缓冲区,当缓冲区中的数据满了以后或者直接调用flush()方法就会把缓冲区中的数据写入到输出流。直接查看源代码就明白了。
package java.io; /**
* The class implements a buffered output stream. By setting up such
* an output stream, an application can write bytes to the underlying
* output stream without necessarily causing a call to the underlying
* system for each byte written.
*
* @author Arthur van Hoff
* @since JDK1.0
*/
public
class BufferedOutputStream extends FilterOutputStream {
/**
* The internal buffer where data is stored.
*/
protected byte buf[]; /**
* The number of valid bytes in the buffer. This value is always
* in the range <tt>0</tt> through <tt>buf.length</tt>; elements
* <tt>buf[0]</tt> through <tt>buf[count-1]</tt> contain valid
* byte data.
*/
protected int count; /**
* Creates a new buffered output stream to write data to the
* specified underlying output stream.
*
* @param out the underlying output stream.
*/
public BufferedOutputStream(OutputStream out) {
this(out, 8192);
} /**
* Creates a new buffered output stream to write data to the
* specified underlying output stream with the specified buffer
* size.
*
* @param out the underlying output stream.
* @param size the buffer size.
* @exception IllegalArgumentException if size <= 0.
*/
public BufferedOutputStream(OutputStream out, int size) {
super(out);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
} /** Flush the internal buffer */
private void flushBuffer() throws IOException {
if (count > 0) {
out.write(buf, 0, count);
count = 0;
}
} /**
* Writes the specified byte to this buffered output stream.
*
* @param b the byte to be written.
* @exception IOException if an I/O error occurs.
*/
public synchronized void write(int b) throws IOException {
if (count >= buf.length) {
flushBuffer();
}
buf[count++] = (byte)b;
} /**
* Writes <code>len</code> bytes from the specified byte array
* starting at offset <code>off</code> to this buffered output stream.
*
* <p> Ordinarily this method stores bytes from the given array into this
* stream's buffer, flushing the buffer to the underlying output stream as
* needed. If the requested length is at least as large as this stream's
* buffer, however, then this method will flush the buffer and write the
* bytes directly to the underlying output stream. Thus redundant
* <code>BufferedOutputStream</code>s will not copy data unnecessarily.
*
* @param b the data.
* @param off the start offset in the data.
* @param len the number of bytes to write.
* @exception IOException if an I/O error occurs.
*/
public synchronized void write(byte b[], int off, int len) throws IOException {
if (len >= buf.length) {
/* If the request length exceeds the size of the output buffer,
flush the output buffer and then write the data directly.
In this way buffered streams will cascade harmlessly. */
flushBuffer();
out.write(b, off, len);
return;
}
if (len > buf.length - count) {
flushBuffer();
}
System.arraycopy(b, off, buf, count, len);
count += len;
} /**
* Flushes this buffered output stream. This forces any buffered
* output bytes to be written out to the underlying output stream.
*
* @exception IOException if an I/O error occurs.
* @see java.io.FilterOutputStream#out
*/
public synchronized void flush() throws IOException {
flushBuffer();
out.flush();
}
}
Java字节流:BufferedInputStream BufferedOutputStream的更多相关文章
- Java API —— IO流( FileInputStream & FileOutputStream & BufferedInputStream & BufferedOutputStream )
1.IO流概述 · IO流用来处理设备之间的数据传输 · 上传文件和下载文件 · Java对数据的操作是通过流的方式 · Java用于操作流的对象都在IO包中 2.IO ...
- Java 字节流操作
在java中我们使用输入流来向一个字节序列对象中写入,使用输出流来向输出其内容.C语言中只使用一个File包处理一切文件操作,而在java中却有着60多种流类型,构成了整个流家族.看似庞大的体系结构, ...
- 关于java字节流的read()方法返回值为int的思考
我们都知道java中io操作分为字节流和字符流,对于字节流,顾名思义是按字节的方式读取数据,所以我们常用字节流来读取二进制流(如图片,音乐 等文件).问题是为什么字节流中定义的read()方法返回值为 ...
- java 字节流和字符流的区别 转载
转载自:http://blog.csdn.net/cynhafa/article/details/6882061 java 字节流和字符流的区别 字节流与和字符流的使用非常相似,两者除了操作代码上的不 ...
- java 字节流和字符流的区别
转载自:http://blog.csdn.net/cynhafa/article/details/6882061 java 字节流和字符流的区别 字节流与和字符流的使用非常相似,两者除了操作代码上的不 ...
- Java进阶(四十五)java 字节流与字符流的区别
java 字节流与字符流的区别(转载) 字节流与和字符流的使用非常相似,两者除了操作代码上的不同之外,是否还有其他的不同呢? 实际上字节流在操作时本身不会用到缓冲区(内存),是文件本身直接操作 ...
- 使用文件流与使用缓冲流完成文件的复制操作性能对比,文件流 FileInputStream FileOutputStream 缓冲流: BufferedInputStream BufferedOutputStream
package seday06; import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOExc ...
- 字节缓冲流 ( BufferedInputStream / BufferedOutputStream)
package com.sxt.reader; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; imp ...
- 使用Java字节流拷贝文件
本文给出使用Java字节流实现文件拷贝的例子 package LearnJava; import java.io.*; public class FileTest { public static vo ...
随机推荐
- oracle sqlplus 连接不正常
场景描述:在开始--运行--输入SQLPLUS 登陆不了报警:“WINDOWS找不到文件‘SQLPLUS’. 原因分析:一般出现这种情况可能的原因: 1.文件名有问题 2.路径有问题 3.安装有问题 ...
- Windows Sqlserver Automatic Log Audit Via C/C++
catalog . 数据库日志审计产品 . Mysql日志审计 . SQLServer日志审计 1. 数据库日志审计产品 Relevant Link: http://enterprise.huawei ...
- .Net和C#的理解
.Net是一个微软出品的开发平台,不仅仅是适用于Windows操作系统,还适用于mono,Linux等等, .Net库包括两部分:部分库定义了一些基本类型.CTS(common Type System ...
- VS2010生成安装包
项目的第一个版本出来了,要做个安装包,之前没有做过,网上看看贴,写了一个,总结下,根据本项目的需要,没有写的太复杂,可能还不是很完善,仅作参考. 首先在打开 VS2010 > 文件 & ...
- iOS “智慧气象”APP中用到的第三方框架汇总
“智慧气象”是我最近在公司接手的项目,已经完成最新版本的更新并上架,在此分享下其中用到的第三方框架的使用. 应用地址:APP商店搜索“智慧气象” MJRefresh(下拉刷新)业界知名下拉刷新框架就不 ...
- (原)String、StringBuilder、StringBuffer作为形参
今天在刷一道算法题时,突然遇到StringBuilder作为形参和String作为形参时,最终得出来的结果不同.故尝试了几个demo看看它们之间的区别. 当String类型作为参数时, public ...
- 【转载】Linux 与 BSD 有什么不同?
原创:Linux中国 https://linux.cn/article-3186-1.html 原创:LCTT https://linux.cn/article-3186-1.html 本文地址:ht ...
- ecshop 远程图片本地化
define('IN_ECS', true); require(dirname(__FILE__) . '/includes/init.php'); $smarty->assign('siteD ...
- CSS3-实现单选框radio的小动画
在微信上看到一个教程文,觉得制作的小动画还是很有意思的,自己也试验了一下.一开始动画怎么都不执行(我用的HB),因为内置浏览器对css3的不兼容.加上各种浏览器前缀后就好了.但是旋转那个效果,在HB里 ...
- iOS开发-二维码
二维码 从ios7开始集成了二维码的生成和读取功能 此前被广泛使用的zbarsdk目前不支持64位处理器 生成二维码的步骤: 倒入CoreImage框架 通过滤镜CIFilter生成二维码 二维码的内 ...