前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java的NIO入门

Java的NIO入门

作者头像
半月无霜
发布2023-03-03 15:04:33
2490
发布2023-03-03 15:04:33
举报
文章被收录于专栏:半月无霜半月无霜

Java的NIO入门

一、介绍

Java NIO是从Java 1.4版本开始引入的一个新的IO ,在传统的IO模型中,使用的是同步阻塞IO,也就是blocking IO

NIO指的是New IO,代指新IO模型。有些博客指的是not blocking IO,非阻塞IO,叫哪种都行,都是NIO

NIO中,最重要的两个东西就是缓冲Buffer和通道Channel了。继续往下看!

二、Buffer

缓冲区Buffer,可以理解成是一个含数组的容器对象,该对象提供了一组方法,可以更轻松地使用其中的数据。该对象记录了一些状态值,能够跟踪和记录缓冲区的状态变化情况。

后续的通道Channel的读取、写入操作都经过缓冲。

Buffer是一个抽象类,它的实现类有很多,但我们最常用的还是ByteBuffer,因为要和字节打交道嘛

image-20220701212258190
image-20220701212258190

它里面有四个最重要的状态值,分别是

  1. mark:标记
  2. position:当前读取或存储数据的索引位置,位置
  3. limit:当前缓冲最大可以写入或读取到的位置,极限
  4. capacity:当前缓冲的容量,容量

其中,mark<= position<=limit<=capacity,具体是什么作用,稍等看看演示代码,建议打开java的api文档来同步进行查看

1)初识缓冲

建议DEBUG,进入后查看上面的四个状态值的变化

代码语言:javascript
复制
package com.banmoon.test;

import java.nio.IntBuffer;

public class BufferTest01 {

    public static void main(String[] args) {
        // 创建容量大小为5的一个int缓冲
        IntBuffer buffer = IntBuffer.allocate(5);
        // 放入数据,初始化时position=0,每put一次,position++,不能大于limit
        buffer.put(1);
        buffer.put(2);
        buffer.put(3);
        buffer.put(4);// position=4, limit=5

        // 写入完毕,反转进行读取,反转过后,position=0,limit=4
        buffer.flip();

        // 获取数据,每获取一次,position++,不能大于limit
        System.out.println(buffer.get());
        System.out.println(buffer.get());
        System.out.println(buffer.get());
        System.out.println(buffer.get());// position=4,limit=4

        // 如果还想写入数据,清空此缓冲,再进行写入,重置position和limit
        buffer.clear();
    }
}

2)BufferUnderFlowException

BufferUnderFlowException异常,存的类型和取的类型不一致所导致的异常。

不同的类型的存储大小空间不同,所以会导致读取的异常

代码语言:javascript
复制
package com.banmoon.test;

import java.nio.ByteBuffer;

public class BufferTest02 {

    public static void main(String[] args) {
        // 创建容量大小为5的一个byte缓冲
        ByteBuffer buffer = ByteBuffer.allocate(100);
        // 放入数据
        buffer.putShort((short) 1);
        buffer.putInt(1);
        buffer.putLong(100);

        // 反转
        buffer.flip();

        // 取出数据
        System.out.println(buffer.getLong());
        System.out.println(buffer.getFloat());
        System.out.println(buffer.getLong());
    }
}
image-20220701220545046
image-20220701220545046

3)只读缓冲

可以将一个缓冲设置为只读,也就是说在缓冲有数据后,可以得到一个只读的缓冲,此缓冲不再支持写入。

代码语言:javascript
复制
package com.banmoon.test;

import java.nio.ByteBuffer;

public class BufferTest03 {

    public static void main(String[] args) {
        // 创建容量大小为5的一个缓冲
        ByteBuffer buffer = ByteBuffer.allocate(5);
        // 放入数据
        for (int i = 0; i < buffer.capacity(); i++) {
            buffer.put((byte) i);
        }

        // 反转
        buffer.flip();
        // 获取只读缓冲
        ByteBuffer readOnlyBuffer = buffer.asReadOnlyBuffer();

        // 循环读取
        while (readOnlyBuffer.hasRemaining()) {
            System.out.println(readOnlyBuffer.get());
        }

        // 写入只读异常会抛出ReadOnlyBufferException
//        readOnlyBuffer.put(1);
    }
}
image-20220701221840278
image-20220701221840278

三、Channel

Channel,称为通道,类似流,但与流有下面几点区别

  • 通道可以同时进行读写,而流只能读或者写
  • 通道可以实现异步读写数据
  • 通道可以从缓冲读数据,可以写入数据到缓冲

Channel是一个接口,常用的实现类如下

  • FileChannelImpl:文件相关的通道
  • ServerSocketChannel:类似BIO中的ServerSocket,用于TCP的连接
  • SocketChannel:类似BIO中的Socket,用于TCP的连接
  • DatagramChannel:用于UDP数据的读写

多说无益,先来看看他们的使用好吧,建议打开java的api文档来同步进行查看

1)写入输出文件

代码语言:javascript
复制
package com.banmoon.test;

import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class ChannelTest01 {

    public static void main(String[] args) throws IOException {
        String str = "你好,半月无霜";
        // 创建一个输出流
        FileOutputStream outputStream = new FileOutputStream("E:\\repository\\test.txt");
        // 通过输出流获取通道
        FileChannel channel = outputStream.getChannel();
        // 将文字放入字节缓冲
        ByteBuffer byteBuffer = ByteBuffer.wrap(str.getBytes());
        // 将字节缓冲写入到通道
        channel.write(byteBuffer);
        // 关闭输出流
        outputStream.close();
    }

}

2)读取指定文件

代码语言:javascript
复制
package com.banmoon.test;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class ChannelTest02 {

    public static void main(String[] args) throws IOException {
        File file = new File("E:\\repository\\test.txt");
        // 获取文件输入流
        FileInputStream inputStream = new FileInputStream(file);
        // 通过输入流获取通道
        FileChannel channel = inputStream.getChannel();
        // 创建字节缓冲
        ByteBuffer byteBuffer = ByteBuffer.allocate((int) file.length());
        // 将通道中的数据读取到字节缓冲
        channel.read(byteBuffer);
        // 查看数据
        System.out.println(new String(byteBuffer.array()));
    }

}
image-20220630235736421
image-20220630235736421

3)拷贝文件

拷贝文件,也就是使用同一个Buffer完成读写,首先我们先准备好一个文件,我们重新创建一个文件hello.txt,如下

代码语言:javascript
复制
你好,半月无霜!
1、飞流直下三千尺,不及汪伦送我情。
2、醒时同交欢,儿女忽成行。
3、路漫漫其修远兮,壮士一去兮不复返!
4、后宫佳丽三千人,铁杵磨成绣花针。
5、问世间情为何物,两岸猿声啼不住。

开始拷贝啦

代码语言:javascript
复制
package com.banmoon.test;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class ChannelTest03 {

    public static void main(String[] args) throws IOException {
        // 获取文件输入流
        FileInputStream inputStream = new FileInputStream("E:\\repository\\hello.txt");
        // 通过输入流获取通道
        FileChannel channel01 = inputStream.getChannel();
        // 获取文件输出流
        FileOutputStream outputStream = new FileOutputStream("E:\\repository\\hello_copy.txt");
        // 通过输出流获取通道
        FileChannel channel02 = outputStream.getChannel();

        // 创建字节缓冲,故意设置小一点,可以多次进入循环,大家可以DEBUG感受一下是如何运行的
        ByteBuffer byteBuffer = ByteBuffer.allocate(16);

        // 循环对文件进行读取,读取后进行写入
        while (true) {
            // 清空缓冲,清空上次循环的数据
            byteBuffer.clear();

            // 读取到缓冲
            int read = channel01.read(byteBuffer);
            // -1代表读取结束,退出循环
            if (read == -1)
                break;

            // 反转缓冲
            byteBuffer.flip();

            // 写入通道
            channel02.write(byteBuffer);
        }

        // 关闭流
        inputStream.close();
        outputStream.close();
    }

}

4)快速拷贝文件

拷贝文件,与上面不同的是,上面是自己写缓冲进行读写,这一次直接使用channelapi进行拷贝,方便快捷。

代码语言:javascript
复制
package com.banmoon.test;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;

public class ChannelTest04 {

    public static void main(String[] args) throws IOException {
        // 获取文件输入流
        FileInputStream inputStream = new FileInputStream("E:\\repository\\hello.txt");
        // 通过输入流获取通道
        FileChannel channel01 = inputStream.getChannel();
        // 获取文件输出流
        FileOutputStream outputStream = new FileOutputStream("E:\\repository\\hello_copy.txt");
        // 通过输出流获取通道
        FileChannel channel02 = outputStream.getChannel();

        // 转移复制通道
        channel01.transferTo(0, channel01.size(), channel02);
        // 或者可以这样写
//        channel02.transferFrom(channel01, 0, channel01.size());
        
        // 关闭流
        inputStream.close();
        outputStream.close();
    }

}

四、最后

NIO在上面的入门示例中,完全没有展现出NIO的同步非阻塞的特点与优势,后续会开单章补上。

先简简单单入个门吧,最主要的NIO就是缓冲和通道。

我是半月,祝你幸福!!!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-07-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Java的NIO入门
    • 一、介绍
      • 二、Buffer
        • 1)初识缓冲
        • 2)BufferUnderFlowException
        • 3)只读缓冲
      • 三、Channel
        • 1)写入输出文件
        • 2)读取指定文件
        • 3)拷贝文件
        • 4)快速拷贝文件
      • 四、最后
      相关产品与服务
      容器服务
      腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档