【JavaSE8 高级编程 IO/NIO】IO入门系列②处理流之Buffer缓冲流(1) 2019_8_17

本文详细解析了Java中缓冲流(BufferedInputStream, BufferedOutputStream, BufferedReader, BufferedWriter)的工作原理与源码级实现,包括构造器、读写操作、缓冲区管理等关键函数。探讨了其在字节与字符流处理中的应用,以及如何通过装饰设计模式增强流的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[源码级]

【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重载)

读·字节、字符

BufferedInputStreamBufferedReader
BufferedInputStream(InputStream in)BufferedReader(Reader in)
BufferedInputStream(InputStream in, int size)BufferedReader(Reader in, int sz)
sz是缓冲byte[]长度sz是缓冲char[]长度

写·字节、字符

BufferedOutputStreamBufferedWriter
BufferedOutputStream(OutputStream out)BufferedWriter(Writer out)
BufferedOutputStream(OutputStream out, int size)BufferedWriter(Writer out, int sz)
sz是缓冲byte[]长度sz是缓冲char[]长度

全部函数

读·字节、字符

BufferedInputStreamBufferedReader描述
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将位于读取下一个字符或行所需的特定位置。

写·字节、字符

BufferedOutputStreamBufferedWriter描述
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();
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值