Java-Buffer直接缓冲区与非直接缓冲区/ByteBuffer/通道

本文详细解释了JavaNIO框架中缓冲区(如ByteBuffer和BufferedOutputStream)的概念,以及它们在文件I/O操作中的作用,特别强调了flush方法与Buffer的关系。此外,还介绍了通道(Channel)在内存管理和网络编程中的角色,以及直接缓冲区(directbuffer)与间接缓冲区的区别。

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

本文讨论Java的NIO框架中,缓冲区与通道(Channel)的概念

缓冲区(Buffer)

下面的代码会在桌面上生成一个叫A.txt的文件,并且文件里面有两行我爱武汉汉阳的妹子,这说明wirte()方法可以直接操作文件

public static void main(String[] args) throws Exception {
    FileOutputStream f = new FileOutputStream("C:\\Users\\admin\\Desktop\\A.txt");
    for (int i = 0; i < 2; i++) {
        f.write("我爱武汉汉阳的妹子\n".getBytes(StandardCharsets.UTF_8));
    }
}

我们将上面的代码装饰一下(java.io框架是经典的装饰者模式),变成下面这个样子,则A.txt文件中不会有任何内容(注意运行前先删除A.txt里的内容),BufferedOutputStream的具体方式可以查看这里

FileOutputStream f = new FileOutputStream("C:\\Users\\admin\\Desktop\\A.txt");
BufferedOutputStream buffer = new BufferedOutputStream(f);
for (int i = 0; i < 2; i++) {
    buffer.write("我爱武汉汉阳的妹子\n".getBytes(StandardCharsets.UTF_8));
}

我们将上面的代码增加一行buffer.flush();,变成下面这个样子,则A.txt文件中的内容会变得正确

FileOutputStream f = new FileOutputStream("C:\\Users\\admin\\Desktop\\A.txt");
BufferedOutputStream buffer = new BufferedOutputStream(f);
for (int i = 0; i < 2; i++) {
    buffer.write("我爱武汉汉阳的妹子\n".getBytes(StandardCharsets.UTF_8));
}
buffer.flush();// 多了一行这个

综上所述,本文截止到这里,可以得出下面这个结论:flush()是给Buffer使用的,如果一个类不具有缓冲区功能,那么它的flush()方法是没有任何用的

那么什么是buffer呢?

从Java的I/O框架中的类结构来说,Buffer是个抽象类,定义在java.nio包,其中ByteBuffer类是它最常用的实现,而在上述的示例中,BufferedOutputStream并没有继承Buffer类,但它依然是一个Buffer,虽然没有继承,但这并不影响 flush()是给Buffer使用的 这个结论

从内存管理的角度来说,Buffer是一块内存区域,当这块内存区域满了或者调用flush方法的时候,则会将这块内存中的所有数据一次性写入到硬盘

到此位置,我们应该知道了FileOutputStream与BufferedOutputStream的区别,区别就是FileOutputStream在调用write的时候,是直接将数据从堆中,写到硬盘上的,而BufferedOutputStream调用write的时候,是将数据写到缓冲区中暂存起来,也正是因为这个区别,下面的代码m1的性能要远远远远高于m2

public static void main(String[] args) throws Exception {
    m1();
    m2();
}

public static void m1() throws Exception {
    long t1 = System.currentTimeMillis();
    FileOutputStream f = new FileOutputStream("C:\\Users\\admin\\Desktop\\A.txt");
    BufferedOutputStream buffer = new BufferedOutputStream(f);
    for (int i = 0; i < 10000000; i++) {
        buffer.write("我爱武汉汉阳的妹子".getBytes(StandardCharsets.UTF_8));
    }
    buffer.flush();
    buffer.close();
    long t2 = System.currentTimeMillis();
    System.out.println("m1:" + (t2 - t1));
}

public static void m2() throws Exception {
    long t1 = System.currentTimeMillis();
    FileOutputStream f = new FileOutputStream("C:\\Users\\admin\\Desktop\\B.txt");
    for (int i = 0; i < 10000000; i++) {
        f.write("我爱武汉汉阳的妹子".getBytes(StandardCharsets.UTF_8));
    }
    long t2 = System.currentTimeMillis();
    System.out.println("m2:" + (t2 - t1));
}

结论: Buffer利用一块内存区域将数据暂存起来,调用flush的时候(或者buffer满了的时候),将这些数据一次性写入硬盘,如果不用Buffer,则需要循环和硬盘交互(分批写入),这个速度是非常慢的,主要就是性能上的差异,至于说Buffer能避免OOM,则占比不是很大,所以需要注意flush方法是和Buffer息息相关的,如果这个类不是Buffer,则flush方法是一个空方法,这点从OutputStream的源码中就能看出来

通道(Channel)
从类结构上来说Channel是个接口,定义在java.nio.channels
从内存管理的角度来说
1.当从硬盘上的一个文件中读数据的时候,channel先从文件中读取数据,然后存到存到buffer,我们写的代码再从buffer中拿数据
2.当往一个文件中写数据的时候,我们自己写的代码先往buffer中写数据,然后buffer再写入channel,最后由channel写到硬盘上

我们再来说Buffer,本文截至到现在,可知Buffer占据了一片内存区域,这片内存区域是存在于堆中的,这种Buffer叫做non-direct buffers,还有一种Buffer区域,叫做direct buffers(直接缓冲区),它不在堆中,是在堆外面一块独有的内存区域,下面的代码在堆的外面创建了一个1024字节大小的direct buffers(直接缓冲区),然后将这个缓冲区里的内容写到文件上

public static void main(String[] args) throws Exception {
    byte[] msg = "我爱武汉汉阳的妹子".getBytes(StandardCharsets.UTF_8);
    FileOutputStream out = new FileOutputStream("C:\\Users\\admin\\Desktop\\C.txt");
    // 其中ByteBuffer是NIO中非常重要的类
    ByteBuffer buffer = ByteBuffer.allocateDirect(msg.length);
    buffer.put(msg);
    buffer.flip();
    FileChannel channel = out.getChannel();
    channel.write(buffer);
    channel.close();
    buffer.clear();
}

本文就到这里结束了,主要想说缓冲区与通道的关系,文章开头只是为了强调一下flush是buffer才有的东西,不是buffer是没有flush的,后文说道通道,主要是为了讲述NIO中的概念,网络编程的时候这个概念是比较重要的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值