NIO学习系列:核心概念及基本读写

1.    引言 
   I/O流或者输入/输出流指的是计算机与外部世界或者一个程序与计算机的其余部分的之间的接口。新的输入/输出(NIO)库是在JDK 1.4中引入的。NIO弥补了原来的I/O的不足,它在标准Java代码中提供了高速的、面向块的I/O。
   原来的I/O库与NIO最重要的区别是数据打包和传输的方式的不同,原来的 I/O 以流 的方式处理数据,而 NIO 以块 的方式处理数据。 
   面向流的I/O系统一次一个字节地处理数据。一个输入流产生一个字节的数据,一个输出流消费一个字节的数据。为流式数据创建过滤器非常容易。链接几个过滤器,以便每个过滤器只负责单个复杂处理机制的一部分,这样也是相对简单的。不利的一面是,面向流的I/O通常相当慢。 
   NIO与原来的I/O有同样的作用和目的,但是它使用块I/O的处理方式。每一个操作都在一步中产生或者消费一个数据块。按块处理数据比按(流式的)字节处理数据要快得多。但是面向块的I/O缺少一些面向流的I/O所具有的优雅性和简单性。

2.    从一个例子开始 
   下面我们从一个简单的使用IO和NIO读取一个文件中的内容为例,来进入NIO的学习之旅。
   使用IO来读取指定文件中的前1024字节并打印出来:

Java代码  

  1. /**
  2. * 使用IO读取指定文件的前1024个字节的内容。
  3. * @param file 指定文件名称。
  4. * @throws java.io.IOException IO异常。
  5. */
  6. public void ioRead(String file) throws IOException {
  7. FileInputStream in = new FileInputStream(file);
  8. byte[] b = new byte[1024];
  9. in.read(b);
  10. System.out.println(new String(b));
  11. }
  12. /**
  13. * 使用NIO读取指定文件的前1024个字节的内容。
  14. * @param file 指定文件名称。
  15. * @throws java.io.IOException IO异常。
  16. */
  17. public void nioRead(String file) throws IOException {
  18. FileInputStream in = new FileInputStream(file);
  19. FileChannel channel = in.getChannel();
  20. ByteBuffer buffer = ByteBuffer.allocate(1024);
  21. channel.read(buffer);
  22. byte[] b = buffer.array();
  23. System.out.println(new String(b));
  24. }

从上面的例子中可以看出,NIO以通道Channel和缓冲区Buffer为基础来实现面向块的IO数据处理。下面将讨论并学习NIO 库的核心概念以及从高级的特性到底层编程细节的几乎所有方面。

3.    核心概念:通道和缓冲区 
   1)    概述: 
   通道和缓冲区是NIO中的核心对象,几乎在每一个I/O操作中都要使用它们。 
   通道Channel是对原I/O包中的流的模拟。到任何目的地(或来自任何地方)的所有数据都必须通过一个Channel对象。
   缓冲区Buffer实质上是一个容器对象。发送给一个通道的所有对象都必须首先放到缓冲区中;同样地,从通道中读取的任何数据都要读到缓冲区中。

2)    缓冲区: 
   Buffer是一个容器对象,它包含一些要写入或者刚读出的数据。在NIO中加入Buffer对象,体现了新库与原I/O的一个重要区别。在面向流的I/O中,您将数据直接写入或者将数据直接读到Stream对象中。 
   在NIO库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的。在写入数据时,它是写入到缓冲区中的。任何时候访问NIO中的数据,您都是将它放到缓冲区中。 
   缓冲区实质上是一个数组。通常它是一个字节数组,但是也可以使用其他种类的数组。但是一个缓冲区不仅仅是一个数组。缓冲区提供了对数据的结构化访问,而且还可以跟踪系统的读/写进程。 
   最常用的缓冲区类型是ByteBuffer。 一个ByteBuffer可以在其底层字节数组上进行get/set操作(即字节的获取和设置)。 
   ByteBuffer不是NIO中唯一的缓冲区类型。事实上,对于每一种基本Java类型都有一种缓冲区类型: 
   ByteBuffer 
   CharBuffer 
   ShortBuffer 
   IntBuffer 
   LongBuffer 
   FloatBuffer 
   DoubleBuffer 
   每一个Buffer类都是Buffer接口的一个实例。 除了ByteBuffer, 每一个Buffer类都有完全一样的操作,只是它们所处理的数据类型不一样。因为大多数标准I/O操作都使用ByteBuffer,所以它具有所有共享的缓冲区操作以及一些特有的操作。 
   下面的UseFloatBuffer列举了使用类型化的缓冲区FloatBuffer的一个应用例子:

Java代码  

  1. /**
  2. * 使用 float 缓冲区。
  3. * @version 1.00 2010-5-19, 10:30:59
  4. * @since 1.5
  5. * @author ZhangShixi
  6. */
  7. public class UseFloatBuffer {
  8. public static void main(String[] args) {
  9. // 分配一个容量为10的新的 float 缓冲区
  10. FloatBuffer buffer = FloatBuffer.allocate(10);
  11. for (int i = 0; i < buffer.capacity(); i++) {
  12. float f = (float) Math.sin((((float) i) / 10) * (2 * Math.PI));
  13. buffer.put(f);
  14. }
  15. // 反转此缓冲区
  16. buffer.flip();
  17. // 告知在当前位置和限制之间是否有元素
  18. while (buffer.hasRemaining()) {
  19. float f = buffer.get();
  20. System.out.println(f);
  21. }
  22. }
  23. }

3)    通道: 
   Channel是对原I/O包中的流的模拟,可以通过它读取和写入数据。拿NIO与原来的I/O做个比较,通道就像是流。 
   正如前面提到的,所有数据都通过Buffer对象来处理。您永远不会将字节直接写入通道中,相反,您是将数据写入包含一个或者多个字节的缓冲区。同样,您不会直接从通道中读取字节,而是将数据从通道 读入缓冲区,再从缓冲区获取这个字节。 
通道与流的不同之处在于通道是双向的。而流只是在一个方向上移动(一个流必须是InputStream或者OutputStream的子类), 而通道可以用于读、写或者同时用于读写。 
   因为它们是双向的,所以通道可以比流更好地反映底层操作系统的真实情况。特别是在UNIX模型中,底层操作系统通道是双向的。

4.    从理论到实践:NIO中的读和写 
   1)    概述: 
   读和写是I/O的基本过程。从一个通道中读取很简单:只需创建一个缓冲区,然后让通道将数据读到这个缓冲区中。写入也相当简单:创建一个缓冲区,用数据填充它,然后让通 道用这些数据来执行写入操作。

2)    从文件中读取: 
如果使用原来的I/O,那么我们只需创建一个FileInputStream并从它那里读取。而在NIO中,情况稍有不同:我们首先从FileInputStream获取一个FileChannel对象,然后使用这个通道来读取数据。 
   在NIO系统中,任何时候执行一个读操作,您都是从通道中读取,但是您不是直接从通道读取。因为所有数据最终都驻留在缓冲区中,所以您是从通道读到缓冲区中。 
   因此读取文件涉及三个步骤:
   (1) 从FileInputStream获取Channel。
   (2) 创建Buffer。
   (3) 将数据从Channel读到Buffer 中。 
   现在,让我们看一下这个过程。

Java代码  

  1. // 第一步是获取通道。我们从 FileInputStream 获取通道:
  2. FileInputStream fin = new FileInputStream( "readandshow.txt" );
  3. FileChannel fc = fin.getChannel();
  4. // 下一步是创建缓冲区:
  5. ByteBuffer buffer = ByteBuffer.allocate( 1024 );
  6. // 最后,需要将数据从通道读到缓冲区中:
  7. fc.read( buffer );

您会注意到,我们不需要告诉通道要读多少数据到缓冲区中。每一个缓冲区都有复杂的内部统计机制,它会跟踪已经读了多少数据以及还有多少空间可以容纳更多的数据。我们将在缓冲区内部细节中介绍更多关于缓冲区统计机制的内容。

3)    写入文件: 
   在 NIO 中写入文件类似于从文件中读取。

Java代码  

  1. // 首先从 FileOutputStream 获取一个通道:
  2. FileOutputStream fout = new FileOutputStream( "writesomebytes.txt" );
  3. FileChannel fc = fout.getChannel();
  4. // 下一步是创建一个缓冲区并在其中放入一些数据,这里,用message来表示一个持有数据的数组。
  5. ByteBuffer buffer = ByteBuffer.allocate( 1024 );
  6. for (int i=0; i<message.length; ++i) {
  7. buffer.put( message[i] );
  8. }
  9. buffer.flip();
  10. // 最后一步是写入缓冲区中:
  11. fc.write( buffer );

注意在这里同样不需要告诉通道要写入多数据。缓冲区的内部统计机制会跟踪它包含多少数据以及还有多少数据要写入。

4)    读写结合: 
   下面的示例将展示使用读写结合,将一个文件的所有内容拷贝到另一个文件中。

Java代码  

  1. /**
  2. * 将一个文件的所有内容拷贝到另一个文件中。
  3. *
  4. * CopyFile.java 执行三个基本操作:
  5. * 首先创建一个 Buffer,然后从源文件中将数据读到这个缓冲区中,然后将缓冲区写入目标文件。
  6. * 程序不断重复 — 读、写、读、写 — 直到源文件结束。
  7. *
  8. * @version 1.00 2010-5-19, 10:49:46
  9. * @since 1.5
  10. * @author ZhangShixi
  11. */
  12. public class CopyFile {
  13. public static void main(String[] args) throws Exception {
  14. String infile = "C:\\copy.sql";
  15. String outfile = "C:\\copy.txt";
  16. // 获取源文件和目标文件的输入输出流
  17. FileInputStream fin = new FileInputStream(infile);
  18. FileOutputStream fout = new FileOutputStream(outfile);
  19. // 获取输入输出通道
  20. FileChannel fcin = fin.getChannel();
  21. FileChannel fcout = fout.getChannel();
  22. // 创建缓冲区
  23. ByteBuffer buffer = ByteBuffer.allocate(1024);
  24. while (true) {
  25. // clear方法重设缓冲区,使它可以接受读入的数据
  26. buffer.clear();
  27. // 从输入通道中将数据读到缓冲区
  28. int r = fcin.read(buffer);
  29. // read方法返回读取的字节数,可能为零,如果该通道已到达流的末尾,则返回-1
  30. if (r == -1) {
  31. break;
  32. }
  33. // flip方法让缓冲区可以将新读入的数据写入另一个通道
  34. buffer.flip();
  35. // 从输出通道中将数据写入缓冲区
  36. fcout.write(buffer);
  37. }
  38. }
  39. }
时间: 2024-10-14 18:08:50

NIO学习系列:核心概念及基本读写的相关文章

Facebook币Libra学习-1.核心概念

Libra区块链是一个基于Libra协议的加密认证的分布式数据库.本文将简略介绍Libra协议的核心概念.其详细说明请参阅Libra技术白皮书. Libra区块链由分布式的Validator节点网络维护, 或简称为Validator.Validator集体遵循共识协议 决定区块链中交易的进行次序. Libra测试网络是Libra区块链项目早期原型,即Libra Core的Demo . 交易和状态 Libra协议的两个核心基本概念为交易和状态在任一时间点,区块链都有一个所谓的状态.状态(或成为分布

Java NIO学习系列五:I/O模型

前面总结了很多IO.NIO相关的基础知识点,还总结了IO和NIO之间的区别及各自适用场景,本文会从另一个视角来学习一下IO,即IO模型.什么是IO模型?对于不同人.在不同场景下给出的答案是不同的,所以先限定一下本文的上下文:Linux环境下的network IO. 本文会从如下几个方面展开: 一些基础概念 I/O模型 总结 1. 一些基础概念 IO模型这个概念属于比较基础的底层概念,在此之前容我再先简单介绍一些涉及到的更底层的概念,帮助对I/O模型的理解: 1.1 用户空间与内核空间 现在操作系

ZooKeeper 系列(一)—— ZooKeeper核心概念详解

一.Zookeeper简介 二.Zookeeper设计目标 三.核心概念 ????????3.1 集群角色 ????????3.2 会话 ????????3.3 数据节点 ????????3.4 节点信息 ????????3.5 Watcher ????????3.6 ACL 四.ZAB协议 ????????4.1 ZAB协议与数据一致性 ????????4.2 ZAB协议的内容 五.Zookeeper的典型应用场景 ????????5.1数据的发布/订阅 ????????5.2 命名服务 ??

[转帖]Zookeeper学习系列【一】 教会你Zookeeper的一些基础概念

Zookeeper学习系列[一] 教会你Zookeeper的一些基础概念 https://segmentfault.com/a/1190000018927058 前言 最近加入了部门的技术兴趣小组,被分配了Zookeeper的研究任务.在研究过程当中,发现Zookeeper由于其开源的特性和其卓越的性能特点,在业界使用广泛,有很多的应用场景,而这些不同的应用场景实际上底层的原理都是差不多的,只要你真正理解了Zookeeper的一些基础概念和机制,就能够触类旁通. 于是乎,在第一次和项目小组内成员

kubernetes系列教程(五)初识核心概念pod

写在前面 前面的系列文章已介绍kubernetes架构,安装,升级和快速入门,读者通过文章的实操已对kubernetes已有初步的认识和理解,从本章开始逐步介绍kubernetes中的基础概念概念和核心概念,基础概念包括:namespace,labels,annotations,pods,volumes等:核心概念包含kubernetes中各种controller,包含以下几种: 应用副本控制器有:Deployments,ReplicaSets,DaemonSets,StatefulSets:

ElasticSearch学习笔记-01 简介、安装、配置与核心概念

一.简介 ElasticSearch是一个基于Lucene构建的开源,分布式,RESTful搜索引擎.设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便.支持通过HTTP使用JSON进行数据索引. Lucene只是一个框架,要利用它的功能,需要使用JAVA,并且在程序中集成Lucene.更糟的是,Lucene非常复杂,需要做很多的学习了解,才能明白它是如何运行的. Elasticsearch使用Lucene作为内部引擎,但是在使用它做全文搜索时,只需要使用统一开发好的API即可,

Ext JS 6学习文档–第2章–核心概念

核心概念 在下一章我们会构建一个示例项目,而在这之前,你需要学习一些在 Ext JS 中的核心概念,这有助于你更容易理解示例项目.这一章我们将学习以下知识点: 类系统,创建和扩展类 事件 Ext JS 对象的查询 容器 布局 转载请注明出处:http://www.jeeboot.com/archives/1217.html class system(类系统) Ext JS 提供了很多功能,使得它创建和处理类变得简单.以下是在 Ext JS 6 的类系统中的几大组成类: Ext Base Clas

hibernate学习系列-----(2)hibernate核心接口和工作机制

在上一篇文章hibernate学习系列-----(1)开发环境搭建中,大致总结了hibernate的开发环境的搭建步骤,今天,我们继续了解有关hibernate的知识,先说说这篇文章的主要内容吧: Configuration类 SessionFactory接口 Session接口 Transaction接口 Query和Criteria接口 下面就进行分类别地详细说明: Configuration类 功能描述:Configuration类负责管理Hibernate的配置信息作并根据配置信息启动H

Elasticsearch学习笔记(六)核心概念和分片shard机制

一.核心概念 1.近实时(Near Realtime NRT) (1)从写入数据到数据可以被搜索到有一个小延迟(大概1秒): (2)基于es执行搜索和分析可以达到秒级 2.集群(Cluster) 一个集群下有多个节点.集群名称,默认是elasticsearch 3.节点(Node) 集群中的一个节点,节点也有一个名称(默认是随机分配的),节点名称很重要(在执行运维管理操作的时        候),默认节点会去加入一个名称为"elasticsearch"的集群,如果直接启动一堆节点,那么