处理流之Buffer缓冲流
[源码级]
【BIS,BOS / BR,BW】缓冲流[增强]概述
分为两类字节、字符型,与抽象基类一致,但BIS,BOS与BR,BW的直接父类有很大区别
1.字符型:BufferedReader,BufferedWriter
2.字节型:BufferedInputStream,BufferedOutputStream
注:只需要关闭包装流/高级流/处理流,内部的低级流会被高级流close()函数内部关闭
父类(FilterIS,OS/R W)子类(LNR)
字节输入输出流
直接父类为包装/过滤/装饰流FilterIS,FilterOS实现类,而Fitler流的直接父类是抽象基类IS,OS
①BufferedInputStream
- SuperClass:FilterInputStream
②BufferedOutputStream
- SuperClass:FilterOutputStream
字符输入输出流
直接父类为抽象基类R,W
③BufferedReader
- SuperClass:Reader
- Subclasses:LineNumberReader
④BufferedWriter
- SuperClass:Writer
构造器(2型*2重载)
读·字节、字符
BufferedInputStream | BufferedReader |
---|---|
BufferedInputStream(InputStream in) | BufferedReader(Reader in) |
BufferedInputStream(InputStream in, int size) | BufferedReader(Reader in, int sz) |
sz是缓冲byte[]长度 | sz是缓冲char[]长度 |
写·字节、字符
BufferedOutputStream | BufferedWriter |
---|---|
BufferedOutputStream(OutputStream out) | BufferedWriter(Writer out) |
BufferedOutputStream(OutputStream out, int size) | BufferedWriter(Writer out, int sz) |
sz是缓冲byte[]长度 | sz是缓冲char[]长度 |
全部函数
读·字节、字符
BufferedInputStream | BufferedReader | 描述 |
---|---|---|
void close() | void close() | |
void mark(int readlimit) | void mark(int readAheadLimit) | |
boolean markSupported() | boolean markSupported() | |
void reset() | void reset() | |
long skip(long n) | long skip(long n) | |
int read() | int read() | 读一个byte/char |
int read(byte[] b, int off, int len) | int read(char[] cbuf, int off, int len) | 读取长度len至数组指定部分off |
String readLine() | 读取一行字符 | |
boolen ready() | 当前流是否可进行下次调用 | |
Stream<String> lines() | 返回行数 | |
int available() | 返回可读字节数的估计值 |
注:lines()使用
①在执行终端流操作期间,不能对BufferedReader进行操作。否则,终端流操作的结果是undefined的。
②lines()调用后,输入流可能会被关闭。不保证BufferedReader将位于读取下一个字符或行所需的特定位置。
写·字节、字符
BufferedOutputStream | BufferedWriter | 描述 |
---|---|---|
void close() | BW复写父类方法 | |
void flush() | void flush() | 二者均复写父类方法 |
void newLine() | 输出一个换行符 | |
void write(int b) | void write(int c) | 写入一个byte/char |
void write(byte[] b, int off, int len) | void write(char[] cbuf, int off, int len) | 写入数组的指定部分 |
void write(String s, int off, int len) | 写入String指定部分 |
注:其余函数继承父类函数。
※字符·读写文件实例
注意:如果是音频文件,必须更换 字符读写 为 字节读写,因为音频文件非字符文件。
//Buffered包装流读写实例
//注意!write发送后,要newline,而readline是要求换行的
@Test
public void test7() {
//1.Steam
FileReader reader = null;
FileWriter writer = null;
BufferedReader br = null;
BufferedWriter bw = null;
//2.try catch finally
//先读后写【已存在hello.txt内容】
try {
reader = new FileReader("hello.txt");
writer = new FileWriter("hello2.txt");
br = new BufferedReader(reader);
bw = new BufferedWriter(writer);
//3.Process Data
String string = br.readLine();
while (string != null) {
bw.write(string);
bw.newLine();
string = br.readLine();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
.
源码1 (拆)
字节输入输出缓冲流
主要函数关系
BIS
BOS
BufferedInputStream
public
class BufferedInputStream extends FilterInputStream {
//1.DeclaredField
//缓冲数组buffer[]默认长度 8kb
private static int DEFAULT_BUFFER_SIZE = 8192;
//最大缓冲长度限制
private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
//字节缓冲数组buf[],使用volatile关键字修饰:只要更改就会通知所有线程重新从内存读取副本
protected volatile byte buf[];
//数组内字节个数计数器
protected int count;
//缓冲数组中的当前位置 / 从buf数组中读取下一个字符的索引。
protected int pos;
//最后一次mark函数调用后标记的位置
//如果markpos不是-1,那么从位置 buf[markpos]到 buf[pos-1]的所有字节必须保留在buf[]数组中
//(尽管它们可以移动到缓冲区数组中的另一个位置,并适当调整 count,pos和markpos);
//除非 pos - markpos > marklimit,否则它们不会被丢弃。
protected int markpos = -1;
//mark最远距离,超过marklimit会导致mark失效
protected int marklimit;
// 摘自 知乎:梅子酒青木马牛
// 缓冲数组的原子更新器
// 该成员变量与buf数组的volatile关键字共同组成了buf数组的原子更新功能实现。
// 即在多线程中操作BufferedInputStream对象时
// buf和bufUpdater都具有原子性(不同的线程访问到的数据都是相同的)
private static final
AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater =
AtomicReferenceFieldUpdater.newUpdater
(BufferedInputStream.class, byte[].class, "buf");
//2.Constructor
//单参数IS对象,使用的仍是双参数重载构造器
public BufferedInputStream(InputStream in) {
this(in, DEFAULT_BUFFER_SIZE);
}
//重载双参数IS对象及缓冲数组buf[]的int长度
public BufferedInputStream(InputStream in, int size) {
super(in);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
}
//super
class FilterInputStream extends InputStream{
protected volatile InputStream in;
protected FilterInputStream(InputStream in) {
//BIS继承自FilterIS的InputStream in输入流对象
this.in = in;
}
}
//总结:也就是BufferedInputStream缓冲流对象构造器只作了2件事情
//① new一个byte[size]数组,② 将构造器参数IS对象赋值给 本类继承父类的 IS类型成员对象
//3.DeclaredMethod
//① read*3重载函数【均为阻塞调用】
//注:单参重装是直接继承父类FilterIS,未做重写
//重载1:空参read,读一个字节byte
public synchronized int read() throws IOException {
if (pos >= count) {
//先对buffer数组进行填充
fill();
if (pos >= count)
return -1;
}
//返回buffer[pos]处的一个byte对象
//& 0xff 确保byte与int相互转换时的二进制补码的一致性
return getBufIfOpen()[pos++] & 0xff;
}
//getBufIfOpen【获取BIS对象内部属性buffer[]对象,并检测是否非空,若空代表流关闭】
private byte[] getBufIfOpen() throws IOException {
byte[] buffer = buf;
if (buffer == null)
throw new IOException("Stream closed");
return buffer;
}
//fill【如果数组满了临时扩容出mark标记字节流的空间,如果数组没满对mark标记字节流进行前移保存】
private void fill() throws IOException {
byte[] buffer = getBufIfOpen();
//如果没有mark标记则初始化pos为数组的起始位置0.【第一类情况】
if (markpos < 0)
pos = 0; /* no mark: throw away the buffer */
//如果buffer[]当前索引pos已经 >= buffer的长度 【第二大类情况】
else if (pos >= buffer.length) /* no room left in buffer */
//第二大类 情况1:如果有mark点
if (markpos > 0) { /* can throw away early part of the buffer */
//前移保存mark点与pos间的字节对象
int sz = pos - markpos;
System.arraycopy(buffer, markpos, buffer, 0, sz);
pos = sz;
markpos = 0;
}
//第二大类 情况2:如果超过了设定的marklimit极限【无效mark】
else if (buffer.length >= marklimit) {
//清除mark点及pos当前点,需要注意内容此时仍存在buffer[]中
markpos = -1; /* buffer got too big, invalidate mark */
pos = 0; /* drop buffer contents */
}
//第二大类 情况3:如果超过了buffer[]最大长度,直接抛出异常
else if (buffer.length >= MAX_BUFFER_SIZE) {
throw new OutOfMemoryError("Required array size too large");
}
//第二大类 情况4:数组没有多余空间,对无空间的 原buffer[]数组扩容成新buffer[]
else { /* grow buffer */
//取(pos*2,marklimit,MAX_BUFFER_SIZE)三者中最小的数为new size
int nsz = (pos <= MAX_BUFFER_SIZE - pos) ?
pos * 2 : MAX_BUFFER_SIZE;
if (nsz > marklimit)
nsz = marklimit;
//创建新的扩容数组nbuf[]
byte nbuf[] = new byte[nsz];
//调用系统方法arraycopy进行原buffer[]复制到新buffer[]操作
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;
}
//【第三类情况】pos处于buffer[](未满)内部某处 或者 已经扩完容量的新buffer[]
//计数buffer[]中的已有byte个数
count = pos;
//调用抽象基类 InputStream public int read(byte b[], int off, int len)方法
int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
//尽可能的一次read()装填满buffer[]数组
if (n > 0)
count = n + pos;
}
//getInIfOpen【检测输入流对象是否存在,否代表当前流关闭】
private InputStream getInIfOpen() throws IOException {
InputStream input = in;
if (input == null)
throw new IOException("Stream closed");
return input;
}
//重载2:3参数read,读指定字节,读入传入的b[]指定位置
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();
}
//如果len参数为0,直接返回0【返回值为读到的byte个数】
else if (len == 0) {
return 0;
}
//初始化返回值 n 为0
int n = 0;
//循环获取
for (;;) {
//nread为底层read1函数的返回值=读入byte数组b[]的字节个数,read1处理mark标记流
int nread = read1(b, off + n, len - n);
//只要nread<=0,就返回0
if (nread <= 0)
return (n == 0) ? nread : n;
//将底层read1返回值nread付给本层返回值n
n += nread;
if (n >= len)
return n;
// if not closed but no bytes available, return
InputStream input = in;
//如果流仍然存在,但输入流可获取的字节数<=0则视为输入流终止返回n值
if (input != null && input.available() <= 0)
return n;
}
}
//read1【处理mark标记流】
private int read1(byte[] b, int off, int len) throws IOException {
//计算当前是否有mark标记流,如果有需要向前存储的标记流,会导致count < pos
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. */
//加速机制:如果没有mark/reset操作,则直接从输入源读入传入的b[]数组
//不走BIS的内部数组
if (len >= getBufIfOpen().length && markpos < 0) {
return getInIfOpen().read(b, off, len);
}
//对内部数组buffer[]进行扩容并将mark标记流向前存储(整理)
fill();
avail = count - pos;
//如果扩容整理后,内部buffer[]仍然计数器与pos不匹配,
//则返回-1 ?为什么会有-1发生什么了?
if (avail <= 0) return -1;
}
//取avail与len的最小值,此时avail应该>=0
int cnt = (avail < len) ? avail : len;
//调用系统方法arraycopy,将输入源从pos到pos+cnt字节们复制到b[]从off开始共cnt个
System.arraycopy(getBufIfOpen(), pos, b, off, cnt);
//调整当前索引值为pos+cnt
pos += cnt;
//返回复制的字节数量
return cnt;
}
//标记流
//mark标记
public synchronized void mark(int readlimit) {
//将参数(限制标记长度)
marklimit = readlimit;
//将当前pos索引保存到markpos中
markpos = pos;
}
//重置mark标记
public synchronized void reset() throws IOException {
//检测buffer[]是否存在/不存在==流关闭的标志
getBufIfOpen(); // Cause exception if closed
//如果没有调用过mark()抛出异常
if (markpos < 0)
throw new IOException("Resetting to invalid mark");
//将标记点
pos = markpos;
}
//跳过并丢弃输入流指定长度long n
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;
}
//本类是否支持标记
public boolean markSupported() {
return true;
}
//可获取的字节数
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;
}
//关闭BufferedIntputStream流(无需自己手动关闭底层流,高级流已关闭底层流)
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()
}
}
}
FilterInputStream(BIS父类)
作用:装饰输入流,为一个输入流对象而不是为整个IS类添加一些新的功能函数
特点:灵活,不需要继承IS类,可以想一下,希望不同情况下IS类有不同的处理能力
如果使用Java单继承,会造成IS子类处理函数的大爆炸
也就是最后一个子类——将会拥有各自情况下的特适处理函数,谁都不想自己写的类大爆炸对吧?
使用:继承FilterIS类,并可选择覆盖IS对象的某些方法,也可为IS对象新增其他方法和字段。
实例:BIS继承FilterIS类,选择覆盖(增强)FilterIS的read函数的无参及三参重载等等函数
//过滤——装饰设计模式——装饰/包装——Inputstream流
public class FilterInputStream extends InputStream {
//Field
protected volatile InputStream in;
//Constructor
protected FilterInputStream(InputStream in) {
this.in = in;
}
//Method
//核心函数read*3重载
//无参read
public int read() throws IOException {
return in.read();
}
//单参重载read 传入字节数组
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
//三参重载read
public int read(byte b[], int off, int len) throws IOException {
return in.read(b, off, len);
}
//其他函数,与上面都一样,均是调用传入FilterIS输入流对象 in 的编译时类方法
BufferedOutputStream
public class BufferedOutputStream extends FilterOutputStream {
//Field
//内部缓冲数组buf[]
protected byte buf[];
//buf[]数组的实际字节计数器
protected int count;
//Constructor
//单参重载构造器
public BufferedOutputStream(OutputStream out) {
//调用双参构造器,默认8k
this(out, 8192);
}
//双参重载构造器
public BufferedOutputStream(OutputStream out, int size) {
super(out);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
//构造器只是为内部缓冲数组创建对象
buf = new byte[size];
}
//super
public class FilterOutputStream extends OutputStream {
protected OutputStream out;
public FilterOutputStream(OutputStream out) {
//BOS继承自FilterOS的OutputStream out输出流对象
this.out = out;
}
}
//Method
//核心函数write()*3重载
//注意单参数byte[]重载未被BOS重写,直接继承
//①writer单参重载,写入一个字节
public synchronized void write(int b) throws IOException {
//如果计数器 >= buf[]的长度,将满的buf[]内容write并清0计数器
if (count >= buf.length) {
flushBuffer();
}
//将传入参数b写入缓冲数组buf[]
buf[count++] = (byte)b;
}
//flushBuffer【将缓冲数组buf[]的内容全部写入文件,并将count清0】
private void flushBuffer() throws IOException {
if (count > 0) {
out.write(buf, 0, count);
count = 0;
}
}
//②writer三参重载,写入len个字节至b[]字节数组的指定位置从off开始,阻塞方法
public synchronized void write(byte b[], int off, int len) throws IOException {
//情况1:要求写入的长度len大于内部缓冲数组buf[]的长度,
//则直接将内部buf[]已有的字节写入,并跳过内部buf[]直接将传入数组b[]内容写入输出源
if (len >= buf.length) {
flushBuffer();
out.write(b, off, len);
return;
}
//情况2: buf.length - count < len < buf.length
//将buf[]内部已存的字节写入输入流——清空内部buf[]内容,为新来的腾地
if (len > buf.length - count) {
flushBuffer();
}
//调用系统数组复制,将b[]从off位置开始个数为len个字节复制进入buf[]从count位置开始
System.arraycopy(b, off, buf, count, len);
count += len;
}
//其他函数
//刷新方法,注意它是一个阻塞方法。
public synchronized void flush() throws IOException {
//将内部buf[]数组中的内容写入输出源out.write
flushBuffer();
//再调用IS对象out自己的flush方法
out.flush();
}
//其他方法全部直接继承FilterOS类
FilterOutputStream(BOS父类)
public class FilterOutputStream extends OutputStream {
//Field
//包装/装饰的 输出流对象out
protected OutputStream out;
//Constructor
public FilterOutputStream(OutputStream out) {
this.out = out;
}
//Method
//核心函数write*3重载
//单参数int b重载
public void write(int b) throws IOException {
out.write(b);
}
//单参数byte[] b重载
public void write(byte b[]) throws IOException {
write(b, 0, b.length);
}
//3参数write()重载
public void write(byte b[], int off, int len) throws IOException {
if ((off | len | (b.length - (len + off)) | (off + len)) < 0)
throw new IndexOutOfBoundsException();
for (int i = 0 ; i < len ; i++) {
write(b[off + i]);
}
}
//flush()调用 out IS编译时类方法
public void flush() throws IOException {
out.flush();
}
//close()实质就是flush
public void close() throws IOException {
try (OutputStream ostream = out) {
flush();
}
}