Java NIO系列教程一

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
可观测可视化 Grafana 版,10个用户账号 1个月
简介: 今天主要给大家介绍的是NIO的基本的概念以及Channel中常用的FileChannel的基本的用法,算是对Channel有一个简单的介绍。下一篇文章我们将详细的为大家介绍其他的常用Channel。

一、 java NIO概述

1.1 NIO的基本作用

  • 替代java io的一个操作
  • 面向缓冲区也可以基于通道操作
  • 更高效的进行文件的读写操作

1.2 阻塞 IO

读或者写数据的时候,会阻塞直到数据能够正常的读或者写入在传统的方法中,服务器为客户端建立一个线程,这种模式如果线程增加,大量线程会造成服务器的开销,为了解决这种问题,采用了线程池,并设置线程池的上限,但超出线程池的上限的线程就会访问不上

1.3 非阻塞 IO(NIO)

​ 非阻塞指的是 IO 事件本身不阻塞,是获取 IO 事件的 select()方法是需要阻塞等待的,区别是阻塞的 IO 会阻塞在 IO 操作上, NIO 阻塞在事件获取上,没有事件就没有 IO,select()阻塞的时候 IO 还没有发生,何谈 IO 的阻塞。本质是延迟io操作,真正发生io的时候才执行,而不是发生的时候再阻塞。用Selector负责去监听多个通道,注册感兴趣的特定 I/O 事件,之后系统进行通知.

当有读或写等任何注册的事件发生时,可以从 Selector 中获得相应的 SelectionKey,同时从 SelectionKey 中可以找到发生的事件和该事件所发生的具体的 SelectableChannel,以获得客户端发送过来的数据。

IO NIO
面向流 面向缓冲区
阻塞IO 非阻塞IO
选择器

1.4 NIO 概述

java NIO 由以下几个核心部分组成,还有其他组件(pipe、filelock)

  • Channel(双向的,既可以用来进行读操作,又可以用来进行写操作)
    主要有如下:
    FileChannel(IO)、DatagramChannel(UDP )、
    SocketChannel (TCP中Server )和 ServerSocketChannel(TCP中Client)
  • Buffer
    主要有如下:
    ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer,
    IntBuffer, LongBuffer, ShortBuffer
  • Selector(处理多个 Channel)

二、Channel

  • 可以进行读取和写入,或者进行读写操作,全双工
  • 操作的数据源可以多种,比如文件、网络socket
  • Channel 用于在字节缓冲区和位于通道另一侧的实体(通常是一个文件或套接字)之间有效地传输数据
  • 从通道读取数据到缓冲区,从缓冲区写入数据到通道

Java NIO 的通道类似流,但又有些不同

  • 既可以从通道中读取数据,又可以写数据到通道。但流的读写通常是单向的。
  • 通道可以异步地读写。
  • 通道中的数据总是要先读到一个 Buffer,或者总是要从一个 Buffer 中写入

主要是接口实现,不同操作系统不同接口实现,通过代码也可以看到其代码为接口

public interface Channel extends Closeable {
   
   

    /**
     * Tells whether or not this channel is open.
     *
     * @return {@code true} if, and only if, this channel is open
     */
    public boolean isOpen();

    /**
     * Closes this channel.
     *
     * <p> After a channel is closed, any further attempt to invoke I/O
     * operations upon it will cause a {@link ClosedChannelException} to be
     * thrown.
     *
     * <p> If this channel is already closed then invoking this method has no
     * effect.
     *
     * <p> This method may be invoked at any time.  If some other thread has
     * already invoked it, however, then another invocation will block until
     * the first invocation is complete, after which it will return without
     * effect. </p>
     *
     * @throws  IOException  If an I/O error occurs
     */
    public void close() throws IOException;

}

实现接口主要有以下几个常用类:

  • FileChannel 从文件中读写数据。
  • DatagramChannel 能通过 UDP 读写网络中的数据。
  • SocketChannel 能通过 TCP 读写网络中的数据。
  • ServerSocketChannel 可以监听新进来的 TCP 连接,像 Web 服务器那样。对每一个新进来的连接都会创建一个 SocketChannel

2.1 FileChannel

主要是文件IO也是最常用的一个类,以下是FileChannel类的核心方法和主要的作用:

Buffer 通常的操作

  1. 将数据写入缓冲区
  2. 调用 buffer.flip() 反转读写模式
  3. 从缓冲区读取数据
  4. 调用 buffer.clear() 或 buffer.compact() 清除缓冲区内容

部分步骤代码展示:

  1. 先打开文件,无法直接打开一个

    FileChannel,需要通过使用一个 InputStream、OutputStream 或RandomAccessFile 来获取一个 FileChannel

    //创建FileChannel
    RandomAccessFile aFile = new RandomAccessFile("b://1.txt","rw");
    FileChannel channel = aFile.getChannel();
    
  2. 创建Buffer

    ByteBuffer buf = ByteBuffer.allocate(1024);
    
  3. 从 FileChannel 读取数据

    read()方法返回的 int 值表示了有多少字节被读到了 Buffer 中。如果返回-1,表示到了文件末尾

    int bytesRead = channel.read(buf);
    
  4. FileChannel.write()方法向 FileChannel 写数据,该方法的参数是一个 Buffer。在 while 循环中调用的。因为无法保证 write()方法一次能向 FileChannel 写入多少字节,因此需要重复调用 write()方法,直到 Buffer 中已经没有尚未写入通道的字节。

读数据主要的代码思路步骤是

  1. 创建一个FileChannel
  2. 创建一个数据缓冲区
  3. 读取数据到缓冲区中
  4. 判断数据是否有,如果有,则取出,判断的依据是获取到的数据是否为-1,取出的数据要先反转读写操作,之后如果数据缓冲区还有,则取出,最后清除数据缓冲区后在判断是否缓冲区还有数据。关闭FileChannel

完整代码展示

public class FileChannelDemo1 {
   
   
    //FileChannel读取数据到buffer中
    public static void main(String[] args) throws Exception {
   
   
        //创建FileChannel
        RandomAccessFile aFile = new RandomAccessFile("d://opencoder.txt","rw");
        FileChannel channel = aFile.getChannel();

        //创建Buffer
        ByteBuffer buf = ByteBuffer.allocate(1024);

        //读取数据到buffer中
        int bytesRead = channel.read(buf);
        while(bytesRead != -1) {
   
   
            System.out.println("读取了:"+bytesRead);
            buf.flip();
            while(buf.hasRemaining()) {
   
   
                System.out.println((char)buf.get());
            }
            buf.clear();
            bytesRead = channel.read(buf);
        }
        aFile.close();
        System.out.println("结束了");
    }
}

写数据主要代码思路是:

  1. 创建一个FileChannel
  2. 创建一个数据缓冲区
  3. 创建要写入的数据对象,以及清空以下缓冲区(防止出错)
  4. 要写入的数据写入到缓冲区中
  5. 缓冲区读写反转
  6. 判断缓冲区是否有数据,将数据一个一个写入到FileChannel
  7. 关闭FileChannel

完整代码展示:

//FileChanne写操作
public class FileChannelDemo2 {
   
   

    public static void main(String[] args) throws Exception {
   
   
        // 打开FileChannel
        RandomAccessFile aFile = new RandomAccessFile("d://opencoder.txt","rw");
        FileChannel channel = aFile.getChannel();

        //创建buffer对象
        ByteBuffer buffer = ByteBuffer.allocate(1024);

        String newData = "manongyanjiuseng";
        buffer.clear();

        //写入内容
        buffer.put(newData.getBytes());

        buffer.flip();

        //FileChannel完成最终实现
        while (buffer.hasRemaining()) {
   
   
            channel.write(buffer);
        }

        //关闭
        channel.close();
    }
}

2.2 其他常用方法

  1. position方法

    需要在 FileChannel 的某个特定位置进行数据的读/写操作。可以通过调用position()方法获取 FileChannel 的当前位置。也可以通过调用 position(long pos)方法设置 FileChannel 的当前位置

    注意这样设置会造成两个后果:
    位置如果设置在文件结束符之后,读取数据的文件结束标志返回-1,而且写入数据的时候前面会有间隙,导致文件空洞

    long pos = channel.position();
    channel.position(pos +404);
    
  2. size 方法

    返回该实例所关联文件的大小

  3. truncate 方法

    截取一个文件。截取文件时,文件将中指定长度,后面的部分将被删除,而且截取的数据长度是以字节截取

  4. force 方法

    尚未写入磁盘的数据强制写到磁盘上

  5. transferTo 和 transferFrom 方法

    进行通道之间的传输注意一个To与From的区别,一个主动一个被动。

    以下是transferFrom 的完整代码:

    //通道之间数据传输
    public class FileChannelDemo3 {
         
         
    
        //transferFrom()
        public static void main(String[] args) throws Exception {
         
         
            // 创建两个fileChannel
            RandomAccessFile aFile = new RandomAccessFile("d://opencoder.txt","rw");
            FileChannel fromChannel = aFile.getChannel();
    
            RandomAccessFile bFile = new RandomAccessFile("d://opencoder2.txt","rw");
            FileChannel toChannel = bFile.getChannel();
    
            //fromChannel 传输到 toChannel
            long position = 0;
            long size = fromChannel.size();
            toChannel.transferFrom(fromChannel,position,size);
    
            aFile.close();
            bFile.close();
            System.out.println("over!");
        }
    }
    

    以下是transferTo的完整代码:

    //通道之间数据传输
    public class FileChannelDemo4 {
         
         
    
        //transferTo()
        public static void main(String[] args) throws Exception {
         
         
            // 创建两个fileChannel
            RandomAccessFile aFile = new RandomAccessFile("d://opencoder1.txt","rw");
            FileChannel fromChannel = aFile.getChannel();
    
            RandomAccessFile bFile = new RandomAccessFile("d://opencoder2.txt","rw");
            FileChannel toChannel = bFile.getChannel();
    
            //fromChannel 传输到 toChannel
            long position = 0;
            long size = fromChannel.size();
            fromChannel.transferTo(0,size,toChannel);
    
            aFile.close();
            bFile.close();
            System.out.println("over!");
        }
    }
    

    总结

    ​ 今天主要给大家介绍的是NIO的基本的概念以及Channel中常用的FileChannel的基本的用法,算是对Channel有一个简单的介绍。下一篇文章我们将详细的为大家介绍其他的常用Channel。

目录
相关文章
|
16小时前
|
前端开发 测试技术 Python
《手把手教你》系列技巧篇(四十一)-java+ selenium自动化测试 - 处理iframe -上篇(详解教程)
【5月更文挑战第5天】本文介绍了HTML中的`iframe`标签,它用于在网页中嵌套其他网页。`iframe`常用于加载外部内容或网站的某个部分,以实现页面美观。文章还讲述了使用Selenium自动化测试时如何处理`iframe`,通过`switchTo().frame()`方法进入`iframe`,完成相应操作,然后使用`switchTo().defaultContent()`返回主窗口。此外,文章提供了一个包含`iframe`的HTML代码示例,并给出了一个简单的自动化测试代码实战,演示了如何在`iframe`中输入文本。
10 3
|
1天前
|
JavaScript 前端开发 Java
《手把手教你》系列技巧篇(四十)-java+ selenium自动化测试-JavaScript的调用执行-下篇(详解教程)
【5月更文挑战第4天】本文介绍了如何使用JavaScriptExecutor在自动化测试中实现元素高亮显示。通过创建并执行JS代码,可以改变元素的样式,例如设置背景色和边框,以突出显示被操作的元素。文中提供了一个Java示例,展示了如何在Selenium中使用此方法,并附有代码截图和运行效果展示。该技术有助于跟踪和理解测试过程中的元素交互。
7 0
|
2天前
|
Web App开发 JavaScript 前端开发
《手把手教你》系列技巧篇(三十九)-java+ selenium自动化测试-JavaScript的调用执行-上篇(详解教程)
【5月更文挑战第3天】本文介绍了如何在Web自动化测试中使用JavaScript执行器(JavascriptExecutor)来完成Selenium API无法处理的任务。首先,需要将WebDriver转换为JavascriptExecutor对象,然后通过executeScript方法执行JavaScript代码。示例用法包括设置JS代码字符串并调用executeScript。文章提供了两个实战场景:一是当时间插件限制输入时,用JS去除元素的readonly属性;二是处理需滚动才能显示的元素,利用JS滚动页面。还给出了一个滚动到底部的代码示例,并提供了详细步骤和解释。
26 10
|
3天前
|
JavaScript 前端开发 测试技术
《手把手教你》系列技巧篇(三十八)-java+ selenium自动化测试-日历时间控件-下篇(详解教程)
【5月更文挑战第2天】在自动化测试过程中,经常会遇到处理日期控件的点击问题。宏哥之前分享过一种方法,但如果输入框是`readonly`属性,这种方法就无法奏效了。不过,通过修改元素属性,依然可以实现自动化填写日期。首先,定位到日期输入框并移除`readonly`属性,然后使用`sendKeys`方法输入日期。这样,即使输入框设置了`readonly`,也能成功处理日期控件。
23 1
|
4天前
|
Java 测试技术 Python
《手把手教你》系列技巧篇(三十七)-java+ selenium自动化测试-日历时间控件-上篇(详解教程)
【5月更文挑战第1天】该文介绍了使用Selenium自动化测试网页日历控件的方法。首先,文章提到在某些Web应用中,日历控件常用于选择日期并筛选数据。接着,它提供了两个实现思路:一是将日历视为文本输入框,直接输入日期;二是模拟用户交互,逐步选择日期。文中给出了JQueryUI网站的一个示例,并展示了对应的Java代码实现,包括点击日历、选择日期等操作。
19 0
|
10天前
|
Java 测试技术 Python
《手把手教你》系列技巧篇(三十六)-java+ selenium自动化测试-单选和多选按钮操作-番外篇(详解教程)
【4月更文挑战第28天】本文简要介绍了自动化测试的实战应用,通过一个在线问卷调查(&lt;https://www.sojump.com/m/2792226.aspx/&gt;)为例,展示了如何遍历并点击问卷中的选项。测试思路包括找到单选和多选按钮的共性以定位元素,然后使用for循环进行点击操作。代码设计方面,提供了Java+Selenium的示例代码,通过WebDriver实现自动答题。运行代码后,可以看到控制台输出和浏览器的相应动作。文章最后做了简单的小结,强调了本次实践是对之前单选多选操作的巩固。
23 0
|
10天前
|
Java 测试技术 项目管理
Java基础教程(22)-构建工具Maven的基本使用
【4月更文挑战第22天】Maven是Java项目管理及构建工具,简化构建、测试、打包和部署等任务。遵循约定优于配置原则,核心是`pom.xml`配置文件,用于管理依赖和项目信息。安装涉及下载、解压、配置环境变量。在IDEA中使用Maven创建项目,通过`pom.xml`添加依赖和管理版本。常用命令包括`clean`、`compile`、`test`、`package`、`install`和`deploy`。IDEA支持直接执行这些命令。
|
10天前
|
NoSQL Java 关系型数据库
Java基础教程(21)-Java连接MongoDB
【4月更文挑战第21天】MongoDB是开源的NoSQL数据库,强调高性能和灵活性。Java应用通过MongoDB Java驱动与之交互,涉及MongoClient、MongoDatabase、MongoCollection和Document等组件。连接MongoDB的步骤包括:配置连接字符串、创建MongoClient、选择数据库和集合。伪代码示例展示了如何建立连接、插入和查询数据。
|
11天前
|
存储 前端开发 测试技术
《手把手教你》系列技巧篇(三十五)-java+ selenium自动化测试-单选和多选按钮操作-下篇(详解教程)
【4月更文挑战第27天】本文介绍了使用Java+Selenium进行Web自动化测试时,如何遍历和操作多选按钮的方法。文章分为两个部分,首先是一个本地HTML页面的示例,展示了多选按钮的HTML代码和页面效果,并详细解释了遍历多选按钮的思路:找到所有多选按钮的共同点,通过定位这些元素并放入list容器中,然后使用for循环遍历并操作。 第二部分介绍了在JQueryUI网站上的实战,给出了被测网址,展示了代码设计,同样使用了findElements()方法获取所有多选按钮并存储到list中,然后遍历并进行点击操作。最后,文章对整个过程进行了小结,并推荐了作者的其他自动化测试教程资源。
19 0
|
11天前
|
Java 关系型数据库 MySQL
Java基础教程(20)-Java连接mysql数据库CURD
【4月更文挑战第19天】MySQL是流行的关系型数据库管理系统,支持SQL语法。在IDEA中加载jar包到项目类路径:右击项目,选择“Open Module Settings”,添加库文件。使用JDBC连接MySQL,首先下载JDBC驱动,然后通过`Class.forName()`加载驱动,`DriverManager.getConnection()`建立连接。执行CRUD操作,例如创建表、插入数据和查询,使用`Statement`或`PreparedStatement`,并确保正确关闭数据库资源。