• java NIO


    目录

    NIO

    NIO基础

    NIO和多路复用的区别

    Buffer的使用

    写文件操作 

     读文件操作

     Buffer类的常用方法

     Channel类的常用方法

     写入数据

     selector

    基本用法

    Chatset字符集

    Charset类 


    NIO


     从JDK1.4开始Java引入了一系列改进的输入/输出处理的新功能,统称为NIO,即新IO,新增了许多用于 处理输入输出的类,新IO采用内存映射文件的方式处理输入输出,新IO将文件或文件的一段区域映射到内存中,这样就可以像访问内存一样来访问文件,这种方式进行输入输出比传统的输入输出快的多

    NIO基础

    Channel通道和Buffer缓冲是NIO中的两个核心对象

    • Chanel是对传统输入输出系统的模拟,通过map方法可以将一块数据映射到内存中
    • Buffer本质是一个数组,发送到Channel中的所有对象都必须先放到Buffer中,而从Channel中读 取的数据必须先放到Buffer中

    NIO和多路复用的区别

    IO模型

    • 同步阻塞IO(Blocking IO):即传统的IO模型
    • 同步非阻塞IO(Non-blocking IO):默认创建的socket都是阻塞的,非阻塞IO要求socket被设置 为NONBLOCK。注意这里所说的NIO并非Java的NIO(New IO)库
    • 多路复用IO(IO Multiplexing):即经典的Reactor设计模式,有时也称为异步阻塞IO,Java中的 Selector和Linux中的epoll都是这种模型
    • 异步IO(Asynchronous IO):即经典的Proactor设计模式,也称为异步非阻塞IO。

    java NIO就是采用多路复用IO模式

    在多路复用IO模型中,会有一个线程(Java中的Selector)不断去轮询多个socket的状态,只有当 socket真正有读写事件时,才真正调用实际的IO读写操作。因为在多路复用IO模型中,只需要使用一个 线程就可以管理多个socket,系统不需要建立新的进程或者线程,也不必维护这些线程和进程,并且只 有在真正有socket读写事件进行时,才会使用IO资源,所以它大大减少了资源占用。

    Buffer的使用

    Buffer是一个抽象类,主要作用是用于装入数据,然后输出数据

    • 最常见的子类ByteBuffer可以在底层字节数组上进行get/set操作
    • 其它基本数据类型都有对应的Buffer类:CharBuffer、 ShortBuffer、 IntBuffer、 LongBuffer、 FloatBuffer、 DoubleBuffer

    静态方法static XxxBuffer allocate(int capacity)创建一个容量为capacity的XxxBuffer对象 Buffer中有3个重要概念:容量capacity、界限limit和位置position

    • 容量capacity表示该Buffer的最大数据容量,创建后则不能改变
    • 界限limit,位于limit后的数据既不可被读,也不可被写
    • 位置position用于指明下一个可以被读出的或者写入缓冲区的位置索引
    • 标记mark位置

    Buffer的position为0,limit为capacity,程序可以通过put方法向Buffer写入一些数据,每放入一些数 据,Buffer的position响应的向后移动。

    写文件操作 

    1. String fileName = "nioFile";
    2. try (FileOutputStream fos = new FileOutputStream(new File(fileName))) {
    3. FileChannel channel = fos.getChannel();
    4. ByteBuffer byteBuffer = StandardCharsets.UTF_8.encode("你好你好..");
    5. int length = 0;
    6. while ((length = channel.write(byteBuffer)) != 0) {
    7. System.out.println("写入长度:" + length);
    8. }
    9. } catch (IOException e) {
    10. e.printStackTrace();
    11. }

     读文件操作

    1. String fileName = "C:\IODemo\nioFile";
    2. try (
    3. FileInputStream fis = new FileInputStream(new File(fileName));
    4. FileChannel channel = fis.getChannel()) {
    5. int capacity = 100;
    6. ByteBuffer byteBuffer = ByteBuffer.allocate(capacity);
    7. int length = -1;
    8. while ((length = channel.read(byteBuffer)) != -1) {
    9. byteBuffer.clear();
    10. byte[] array = byteBuffer.array();
    11. System.out.write(array, 0, length);
    12. System.out.println();
    13. }
    14. } catch (FileNotFoundException e) {
    15. e.printStackTrace();
    16. }

     Buffer类的常用方法

    • capacity():int返回Buffer的容量大小
    • hasRemaining():boolean判断是否还有元素可以进行处理
    • remaining():int返回当前位置和界限之间的元素个数
    • position():int返回当前操作的位置
    • mark():Buffer设置Buffer的标记位置,只能在0和position之间做标记
    • reset():Buffer将位置position转到mark所在的位置
    • rewind():Buffer将位置position设置到0,取消设置的mark
    • put(obj)用于向Buffer中放入数据
    • get()用于从Buffer中取出数据
    1. // 创建对应的buffer对象,其中最大容积值为10
    2. CharBuffer buffer = CharBuffer.allocate(10);
    3. System.out.println(buffer.capacity()); // 获取buffer的容积值
    4. capacity=10,position=0,limit=10
    5. System.out.println(buffer.position());
    6. System.out.println(buffer.limit());
    7. buffer.put('中');
    8. System.out.println(buffer.capacity()); // 10
    9. System.out.println(buffer.position()); // 1
    10. System.out.println(buffer.limit()); // 10
    11. String ss="中国人民解放军";
    12. for(int i=0;i
    13. buffer.put(ss.charAt(i));
    14. buffer.flip(); // 将limit设置到position,并且把position设置为0。相当于是将buffer中
    15. 没有数据的存储位置封印起来,从而避免读取时读到不合法的数据
    16. System.out.println(buffer.capacity()); // 10
    17. System.out.println(buffer.position()); // 0
    18. System.out.println(buffer.limit()); //7
    19. char cc=buffer.get();//获取position对应的字符
    20. System.out.println(cc); //中
    21. System.out.println(buffer.capacity()); // 10
    22. System.out.println(buffer.position()); // 1
    23. System.out.println(buffer.limit()); //7
    24. buffer.clear(); //清空buffer中的数据,并重置position和limit
    25. System.out.println(buffer.capacity()); // 10
    26. System.out.println(buffer.position()); // 0
    27. System.out.println(buffer.limit()); //10
    28. ss="中国人民解放军";
    29. for(int i=0;i
    30. buffer.put(ss.charAt(i));
    31. cc=buffer.get(2); //按照下标位置获取对应的数据,并不会操作position
    32. System.out.println(cc);
    33. System.out.println(buffer.position()); //7

     Channel类的常用方法

    Channel可以直接将文件的部分或者全部直接映射成Buffer

    注意:不能直接访问Channel中的数据,包括读取、写入都不行。Channel只能与Buffer进行交互

    • 所有Channel不应该通过构造器来直接创建,而是通过传统的节点InputStream、OutputStream 的getChannel方法来返回对应的Channel
    • 常用的是FileInputStream、FileOutputStream的getChannel()返回的FileChannel

    Channel中最常用的三个方法是map()、read()和write()

    1. File file = new File("T1.java");
    2. FileChannel inChannel = new FileInputStream(file).getChannel();
    3. ByteBuffer buffer = ByteBuffer.allocate(256);
    4. while (inChannel.read(buffer) != -1) {//多次读取数据的方式从文件中获取内容
    5. buffer.flip(); //将没有数据的区域封印起来
    6. Charset charset = Charset.forName("GBK");//使用GBK的字符集创建解码器
    7. CharsetDecoder decoder = charset.newDecoder();
    8. CharBuffer cb = decoder.decode(buffer);//使用解码器将ByteBuffer转换为
    9. CharBuffer
    10. System.out.println(cb);
    11. buffer.clear();//将position设置为0,为下一次读取数据做准备
    12. }

     异常:java.nio.charset.MalformedInputException一般是编码转换时由于编码字符集错误导致的,可 以修改Charset中编码字符集名称解决。例如GBK、UTF-8等

    map()方法将Channel对应的部分或全部数据映射成ByteBuffer 

    1. File f = new File("T1.java");
    2. FileChannel in = new FileInputStream(f).getChannel();
    3. FileChannel out = new FileOutputStream("a.txt").getChannel();
    4. MappedByteBuffer buffer = in.map(FileChannel.MapMode.READ_ONLY, 0,
    5. f.length());//参数1为执行映射时的模式,有只读、读写模式;参数2和3用于设置哪些数据执行映
    6. 射。可以将FileChannel中全部数据映射为ByteBuffer
    7. out.write(buffer);
    8. buffer.clear();
    9. Charset charset = Charset.forName("GBK");
    10. CharsetDecoder decoder = charset.newDecoder();
    11. CharBuffer cb = decoder.decode(buffer);
    12. System.out.println(cb);

     写入数据

    1. File file = new File("data/a1.txt");
    2. int len=(int)file.length();
    3. RandomAccessFile raf = new RandomAccessFile(file, "rw");
    4. FileChannel channel = raf.getChannel();// 返回的channel是只读还是读写,取决于
    5. RandomAccessFile文件对象的打开模式
    6. ByteBuffer buffer = channel.map(MapMode.READ_ONLY, 0, len);//buffer支持可读,将
    7. 文件中的所有内容映射到buffer中
    8. channel.position(len); //移动指针到内容末尾
    9. channel.write(buffer); //重新写出buffer中的内容,实际上就是将文件内容拷贝

     selector

    是Java NIO核心组件中的一个,用于检查一个或多个NIO Channel通道的状态是否处于可读、可写。如 此可以实现单线程管理多个channels也就是可以管理多个网络链接。

    使用Selector的好处在于: 使用更少的线程来就可以来处理通道了, 相比使用多个线程,避免了线程上 下文切换带来的开销

    • FileChannel不能切换为非阻塞模式,更准确的来说是因为FileChannel没有继承 SelectableChannel
    • 多用于网络应用编程中

    基本用法

    1. Selector的创建。通过调用Selector.open()方法创建一个Selector对象 

    Selector selector = Selector.open();

    2. 注册Channel到Selector

    channel.configureBlocking(false); SelectionKey key = channel.register(selector, Selectionkey.OP_READ); 

    Channel必须是非阻塞的。

    3. 轮询方式获取选择器上的状态值 

    1. while(selector.select()>0){
    2. Iterator it=selector.selectedKeys().iterator();
    3. ...
    4. }

    Chatset字符集

    所有文件在底层都是二进制文件,字符文件是系统将底层的二进制序列转换为字符,这里会涉及编码 Encoder和解码Decoder

    • 将明文的字符序列转换为计算机理解的二进制序列称为编码
    • 将二进制序列转换为明文字符串称为解码

    Charset类 

    availableCharsets():SortedMap 获取当前JDK所支持的所有字符集

    字符串别名

    • GBK简体中文
    • ISO-8859-1拉丁文
    • UTF-8是8位UCS转换格式 

    Charset c=Charset.forName("GBK"); 

    Java7新增StandardCharsets类,其中包含了ISO_8859_1、UTF_8、UTF-16等类变量,这些类变量 代表了最常见的字符集对应的Charset对象

    • newDecoder():CharsetDecoder获取该编码字符集对应的解码器

            decode(ByteBuffer):CharBuffer方法可以将字节序列ByteBuffer转换为CharBuffer字符序列

    • newEncoder():CharsetEncoder获取该编码字符集对应的编码器

            encode(CharBuffer):ByteBuffer方法可以将字符序列CharBuffer转换为ByteBuffer字节序列 

    1. Charset c1 = Charset.forName("GBK");
    2. CharsetEncoder encoder = c1.newEncoder();
    3. CharsetDecoder decoder = c1.newDecoder();
    4. CharBuffer cb = CharBuffer.allocate(8);
    5. cb.put('孙');
    6. cb.put('误');
    7. cb.put('空');
    8. cb.flip();
    9. ByteBuffer bb=encoder.encode(cb);//将CharBuffer转换为ByteBuffer
    10. for(int i=0;i<6;i++) {
    11. System.out.println(bb.get(i)+" ");
    12. }
    13. System.out.println("====================");
    14. System.out.println(decoder.decode(bb));//将byteBuffer转换为charBuffer

     

  • 相关阅读:
    领悟《信号与系统》之 非周期信号的傅里叶变换及性质
    ubuntu 22.04 更新NVIDIA显卡驱动,重启后无网络图标等系统奇奇怪怪问题
    无状态编程, lambda 表达式中传入的局部变量,为什么需要是不可变的(final)
    ​力扣解法汇总640-求解方程
    apt和dpkg的源码下载链接
    程序开机自启动(基于linux)
    讯飞输入法怎么用密语模式
    千梦网创:你现在赚的钱是三年前选择的结果
    第五章:Ajax高级
    Oracle DB 体系机构
  • 原文地址:https://blog.csdn.net/weixin_50518344/article/details/126293516