聊聊BufferedInputStream和BufferedOutputStream的二三事

本文探讨了Java中的BufferedInputStream和BufferedOutputStream如何提高输入输出效率。BufferedInputStream通过内部缓冲区实现数据批量处理,支持标记功能,提供skip和available等方法。BufferedOutputStream同样利用缓冲区提升写入效率,数据会在缓冲区满或调用flush、close方法时写入磁盘。

BufferedInputStream是对InputStream的一种包装,属于设计模式中的装饰器模式。同样的,BufferedOutputStream是对OutputStream的一种包装。

关联现实世界,相当于:

假如使用FileInputStream,就像是一个快递员从仓库取出一件包裹给客户送去,再取第二件包裹给客户送去,如此往复。

BufferedInputStream干了件什么事呢?它先从仓库里把包裹一件一件拿出来以后,先不给客户送过去,只是先用个容器给装起来,等容器装满了或者调用了flush方法或者调用了close方法,才会给客户送过去。这样的话,就提高了效率。

BufferedOutputStream也是同样的道理!


先聊聊BufferedInputStream类吧!

在这里插入图片描述

它和FileInputStream的API差不多,但是也是有区别的,比如:

boolean markSupported()

void reset()

void mark(int readlimit)

我们知道,在FileInputStream中,markSupported()返回的是false!是不支持标记功能的。但是在BufferedInputStream类中是支持的。

public boolean markSupported() {
	return true;
}

那我们就试一试!

public class BufferedInputStreamTestClient {
    public static void main(String[] args) {
        try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("ThreeKind"))) {
            System.out.println("markSupported = " + bufferedInputStream.markSupported());
            System.out.println(bufferedInputStream.read());
            System.out.println(bufferedInputStream.read());
            bufferedInputStream.mark(3);//第三个位置做上标记
            System.out.println(bufferedInputStream.read());
            System.out.println(bufferedInputStream.read());
            bufferedInputStream.reset();//回到标记的位置(第三个位置)
            System.out.println(bufferedInputStream.read());
            System.out.println(bufferedInputStream.read());
        } catch (Exception exception) {
        }
    }
}

ThreeKind文件的内容是:ABCDEFG。运行结果是:

markSupported = true
65
66
67
68
67
68

可以看出,先输出了对mark功能是支持的,返回true。先输出的2个字节分别是 65 66A B。此时我们在C即第三个字节做了标记,继续输出2个字节,分别是C D67 68。紧接着,我们reset()一下,就回到了标记处,再次输出2个字节,又是 67 68C D。这就是mark的功能。

好,继续看下其他的API:

  • int available()

功能类似,获取剩余的字节数。

public class BufferedInputStreamTestClient {
    public static void main(String[] args) {
        try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("ThreeKind"))) {
            int available = bufferedInputStream.available();
            System.out.println("available = " + available);
        } catch (Exception exception) {
        }
    }
}
available = 7
  • long skip(long n)

功能类似,跳过指定的字节数,返回值是实际跳过的字节数。

public class BufferedInputStreamTestClient {
    public static void main(String[] args) {
        try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("ThreeKind"))) {
            long skip = bufferedInputStream.skip(1);
            System.out.println("skip = " + skip);
            int data = bufferedInputStream.read();
            System.out.println("data = " + (char) data);
        } catch (Exception exception) {
        }
    }
}
skip = 1
data = B

跳过了一个字节,本来应该是输出A的,被跳过去了。

接下来就是读的API了,由于和FileInputStream的API很类似,所以就贴下代码,不做解释了。

public class BufferedInputStreamTestClient {
    public static void main(String[] args) {
        try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("ThreeKind"))) {
            int data;
            while ((data = bufferedInputStream.read()) != -1) {
                System.out.print((char) data);
            }
        } catch (Exception exception) {
        }
    }
}
public class BufferedInputStreamTestClient {
    public static void main(String[] args) {
        try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("ThreeKind"))) {
            int len;//读取字节数
            byte[] bytes = new byte[4];
            while ((len = bufferedInputStream.read(bytes)) != -1) {
                System.out.println("len = " + len + "bytes = " + Arrays.toString(bytes));
            }
        } catch (Exception exception) {
        }
    }
}
public class BufferedInputStreamTestClient {
    public static void main(String[] args) {
        try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("ThreeKind"))) {
            int len;//读取字节数
            byte[] bytes = new byte[4];
            while ((len = bufferedInputStream.read(bytes, 1/*起始位置,从0开始*/, 2/*存放字节数组长度*/)) != -1) {
                System.out.println("len = " + len + "bytes = " + Arrays.toString(bytes));
            }
        } catch (Exception exception) {
        }
    }
}

最后,说一下BufferedInputStream的构造方法吧!很简单就2种!

public BufferedInputStream(InputStream in) {
	this(in, DEFAULT_BUFFER_SIZE);
}
public BufferedInputStream(InputStream in, int size) {
	super(in);
	if (size <= 0) {
		throw new IllegalArgumentException("Buffer size <= 0");
	}
	buf = new byte[size];
}

第一个构造方法是调用第二个构造方法的。int size参数给的是DEFAULT_BUFFER_SIZEDEFAULT_BUFFER_SIZE是一个固定值:private static int DEFAULT_BUFFER_SIZE = 8192;

所以,可以看出,默认的缓冲大小是8192字节,就是8kb。日常来说是够用了,当然可以用第②种构造方法来修改。


好,接下来聊一下,BufferedOutputStream,看看它的用法。

在这里插入图片描述

BufferedOutputStream并不是一个字节一个字节的往硬盘上写的,而是有个桶,就是缓冲区,先把要写的数据装进缓冲区里,然后一次性把桶里的数据写出去。

public BufferedOutputStream(OutputStream out) {
	this(out, 8192);
}
public BufferedOutputStream(OutputStream out, int size) {
	super(out);
	if (size <= 0) {
		throw new IllegalArgumentException("Buffer size <= 0");
	}
	buf = new byte[size];
}

看到,this(out, 8192);了吗?这就是调用了第二个构造方法!默认的size就是8192,就是缓冲区,大小是8kb

那么,什么时候会往磁盘中写数据呢?

第①种:缓存区满了,写出去!

第②种:void flush()方法被调用,写出去!

第③种:void close()方法被调用,写出去!因为,close()方法被调用的时候,会先调用flush()方法。

public class BufferedOutputStreamTestClient {
    public static void main(String[] args) {
        try (BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("ThreeKind"), 4)) {
            bufferedOutputStream.write(65);
            bufferedOutputStream.flush();//调用flush()方法会将缓冲区的数据刷入到磁盘中
            bufferedOutputStream.write(66);//flush()方法将缓冲区清空,此语句执行完,缓冲区只有一个字节的数据
            bufferedOutputStream.write(67);
            bufferedOutputStream.write(68);
            bufferedOutputStream.write(69);//此条语句执行完缓冲区刚刚满,但是还未将数据刷入到磁盘中
            bufferedOutputStream.write(70);//缓冲区已经满了先将数据刷入到磁盘中,清空缓冲区,再存入一个字节
            bufferedOutputStream.write(71);
        } catch (Exception exception) {
        }
    }
}

通过调试模式,一步一步调试,进行测试:

由于,构造函数第二个参数是4,所以默认的8192被覆盖了,缓冲区的大小变成了4字节。

write(65)时,文件中没有数据,因为,此时65被写入了缓冲区,还没到磁盘;

flush()方法被调用时,文件中有了A字母。此时的缓冲区被清空了。

接下来的数据都被写入的缓冲区,直到write(70)时,发现缓冲区满了,此时将缓冲区的数据写入磁盘中,并将缓冲区清空,将70写入缓冲区。

当write(71)时,文件中只有ABCDE,即65 66 67 68 69,此时的 7071 还未写入到文件中。

直到,准备跳出try代码块,程序会自动调用close方法,为什么?原因在FileInputStream一文的开始就说了,在这里就不罗嗦了。close方法又会调用flush方法,所以,此时,FG才会写入文件中。

以上就是关于BufferedInputStreamBufferedOutputStream的全部内容!不知客官欣赏的是否尽兴,评论区聊聊

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值