Java基础——Java NIO详解(一)

一、基本概念


1、I/0简介

       I/O即输入输出,是计算机与外界世界的一个借口。IO操作的实际主题是操作系统。在java编程中,一般使用流的方式来处理IO,所有的IO都被视作是单个字节的移动,通过stream对象一次移动一个字节。流IO负责把对象转换为字节,然后再转换为对象。

       关于Java IO相关知识请参考我的另一篇文章:Java IO 详解


2、什么是NIO

       NIO即New IO,这个库是在JDK1.4中才引入的。NIO和IO有相同的作用和目的,但实现方式不同,NIO主要用到的是块,所以NIO的效率要比IO高很多。

在Java API中提供了两套NIO,一套是针对标准输入输出NIO,另一套就是网络编程NIO,本篇文章重点介绍标NIO,关于网络编程NIO请见Java NIO详解(二)


3、流与块的比较

       NIO和IO最大的区别是数据打包和传输方式。IO是以的方式处理数据,而NIO是以的方式处理数据。

       面向流的IO一次一个字节的处理数据,一个输入流产生一个字节,一个输出流就消费一个字节。为流式数据创建过滤器就变得非常容易,链接几个过滤器,以便对数据进行处理非常方便而简单,但是面向流的IO通常处理的很慢。

       面向块的IO系统以块的形式处理数据。每一个操作都在一步中产生或消费一个数据块。按块要比按流快的多,但面向块的IO缺少了面向流IO所具有的有雅兴和简单性。


二、NIO基础


       BufferChannel是标准NIO中的核心对象(网络NIO中还有个Selector核心对象),几乎每一个IO操作中都会用到它们。

Channel是对原IO中流的模拟,任何来源和目的数据都必须通过一个Channel对象。一个Buffer实质上是一个容器对象,发给Channel的所有对象都必须先放到Buffer中;同样的,从Channel中读取的任何数据都要读到Buffer中。


1、关于Buffer

       Buffer是一个对象,它包含一些要写入或读出的数据。在NIO中,数据是放入buffer对象的,而在IO中,数据是直接写入或者读到Stream对象的。应用程序不能直接对 Channel 进行读写操作,而必须通过 Buffer 来进行,即 Channel 是通过 Buffer 来读写数据的。

       在NIO中,所有的数据都是用Buffer处理的,它是NIO读写数据的中转池。Buffer实质上是一个数组,通常是一个字节数据,但也可以是其他类型的数组。但一个缓冲区不仅仅是一个数组,重要的是它提供了对数据的结构化访问,而且还可以跟踪系统的读写进程。

使用 Buffer 读写数据一般遵循以下四个步骤:

       1)写入数据到 Buffer;

       2)调用 flip() 方法;

       3)从 Buffer 中读取数据;

       4)调用 clear() 方法或者 compact() 方法。

       当向 Buffer 写入数据时,Buffer 会记录下写了多少数据。一旦要读取数据,需要通过 flip() 方法将 Buffer 从写模式切换到读模式。在读模式下,可以读取之前写入到 Buffer 的所有数据。

       一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。有两种方式能清空缓冲区:调用 clear() 或 compact() 方法。clear() 方法会清空整个缓冲区。compact() 方法只会清除已经读过的数据。任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面。

Buffer主要有如下几种:



3、关于Channel

Channel是一个对象,可以通过它读取和写入数据。可以把它看做IO中的流。但是它和流相比还有一些不同:

       1)Channel是双向的,既可以读又可以写,而流是单向的

       2)Channel可以进行异步的读写

       3)对Channel的读写必须通过buffer对象

       正如上面提到的,所有数据都通过Buffer对象处理,所以,您永远不会将字节直接写入到Channel中,相反,您是将数据写入到Buffer中;同样,您也不会从Channel中读取字节,而是将数据从Channel读入Buffer,再从Buffer获取这个字节。

       因为Channel是双向的,所以Channel可以比流更好地反映出底层操作系统的真实情况。特别是在Unix模型中,底层操作系统通常都是双向的。


在Java NIO中Channel主要有如下几种类型:

  • FileChannel:从文件读取数据的
  • DatagramChannel:读写UDP网络协议数据
  • SocketChannel:读写TCP网络协议数据
  • ServerSocketChannel:可以监听TCP连接


三、从理论到实践:NIO中的读和写


       IO中的读和写,对应的是数据和Stream,NIO中的读和写,则对应的就是通道和缓冲区。NIO中从通道中读取:创建一个缓冲区,然后让通道读取数据到缓冲区。NIO写入数据到通道:创建一个缓冲区,用数据填充它,然后让通道用这些数据来执行写入。


1、从文件中读取

       我们已经知道,在NIO系统中,任何时候执行一个读操作,您都是从Channel中读取,而您不是直接从Channel中读取数据,因为所有的数据都必须用Buffer来封装,所以您应该是从Channel读取数据到Buffer。

因此,如果从文件读取数据的话,需要如下三步:

       1)从FileInputStream获取Channel

       2)创建Buffer

       3)从Channel读取数据到Buffer

下面我们看一下具体过程: 

第一步:获取通道

FileInputStream fin = new FileInputStream( "readandshow.txt" );
FileChannel fc = fin.getChannel(); 

第二步:建立缓冲区

ByteBuffer buffer = ByteBuffer.allocate( 1024 );
第三步:将数据从通道读到缓冲区
fc.read( buffer );


2、写入数据到文件
类似于从文件读数据

第一步:获取一个通道

FileOutputStream fout = new FileOutputStream( "writesomebytes.txt" );
FileChannel fc = fout.getChannel();
第二步:创建缓冲区,将数据放入缓冲区

ByteBuffer buffer = ByteBuffer.allocate( 1024 );for (int i=0; i<message.length; ++i) {buffer.put( message[i] );
}
buffer.flip();
第三步:把缓冲区数据写入通道中

fc.write( buffer );


3、读写结合
       CopyFile是一个非常好的读写结合的例子,我们将通过CopyFile这个实力让大家体会NIO的操作过程。CopyFile执行三个基本的操作:创建一个Buffer,然后从源文件读取数据到缓冲区,然后再将缓冲区写入目标文件。

/*** 用java NIO api拷贝文件* @param src* @param dst* @throws IOException*/
public static void copyFileUseNIO(String src,String dst) throws IOException{//声明源文件和目标文件FileInputStream fi=new FileInputStream(new File(src));FileOutputStream fo=new FileOutputStream(new File(dst));//获得传输通道channelFileChannel inChannel=fi.getChannel();FileChannel outChannel=fo.getChannel();//获得容器bufferByteBuffer buffer=ByteBuffer.allocate(1024);while(true){//判断是否读完文件int eof =inChannel.read(buffer);if(eof==-1){break;  }//重设一下buffer的position=0,limit=positionbuffer.flip();//开始写outChannel.write(buffer);//写完要重置buffer,重设position=0,limit=capacitybuffer.clear();}inChannel.close();outChannel.close();fi.close();fo.close();
}     


四、需要注意的点

上面程序中有三个地方需要注意


1、检查状态

       当没有更多的数据时,拷贝就算完成,此时 read() 方法会返回 -1 ,我们可以根据这个方法判断是否读完。

int r= fcin.read( buffer );
if (r==-1) {break;
}


2、Buffer类的flip、clear方法

控制buffer状态的三个变量

       1)position:跟踪已经写了多少数据或读了多少数据,它指向的是下一个字节来自哪个位置

       2)limit:代表还有多少数据可以取出或还有多少空间可以写入,它的值小于等于capacity。

       3)capacity:代表缓冲区的最大容量,一般新建一个缓冲区的时候,limit的值和capacity的值默认是相等的。

flip、clear这两个方法便是用来设置这些值的。


flip方法

我们先看一下flip的源码:

public final Buffer flip() {limit = position;position = 0;mark = -1;return this;}

       在上面的FileCopy程序中,写入数据之前我们调用了 buffer.flip();方法,这个方法把当前的指针位置position设置成了limit,再将当前指针position指向数据的最开始端,我们现在可以将数据从缓冲区写入通道了。 position 被设置为 0,这意味着我们得到的下一个字节是第一个字节。 limit 已被设置为原来的 position,这意味着它包括以前读到的所有字节,并且一个字节也不多。


clear方法

先看一下clear的源码:

public final Buffer clear() {position = 0;limit = capacity;mark = -1;return this;
}

       在上面的FileCopy程序中,写入数据之后也就是读数据之前,我们调用了 buffer.clear();方法,这个方法重设缓冲区以便接收更多的字节。上图显示了在调用 clear() 后缓冲区的状态。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/446978.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MAC上Git安装与GitHub基本使用

参考链接 MAC上Git安装与GitHub基本使用

Java基础——深入理解Java线程池

简介 我们使用线程的时候就去创建一个线程&#xff0c;这样实现起来非常简便&#xff0c;但是就会有一个问题&#xff1a; 如果并发的线程数量很多&#xff0c;并且每个线程都是执行一个时间很短的任务就结束了&#xff0c;这样频繁创建线程就会大大降低系统的效率&#xff0c;…

密码机项目安装软件时候出现的问题以及对应的解决办法

Could NOT find Boost (missing: locale) (found version "1.65.1") 使用命令 apt-get install libboost-locale-dev 进行安装 解决普通用户cmake版本11&#xff0c;而root用户版本15&#xff0c;clion对于版本兼容的问题 修改clion里面的toolchain&#xff0c;将其…

Java基础——线程及并发机制

前言 在Java中&#xff0c;线程是一个很关键的名词&#xff0c;也是很高频使用的一种资源。那么它的概念是什么呢&#xff0c;是如何定义的&#xff0c;用法又有哪些呢&#xff1f;为何说Android里只有一个主线程呢&#xff0c;什么是工作线程呢。线程又存在并发&#xff0c;并…

密码机 密钥管理项目安装配置 从零开始

安装gcc 更新sudo apt-get update下载gcc sudo apt-get install gcc参考链接 不推荐 安装g 下载g sudo apt-get install g 安装make sudo apt -get install make参考链接 安装cmake 下载地址参考链接 安装ssh sudo apt-get install ssh 安装git和配置 sudo apt-get inst…

Androud 如何有效减少重复代码

前言 重复的代码一直都是可维护性的大敌&#xff0c;重构的重要任务之一也就是要去除掉重复的代码&#xff0c;有效的减少重复代码&#xff0c;可以大大提高软件的扩展性。 在Android开发中&#xff0c;很容易产生重复的代码。因为Android是组件&#xff0c;模板式开发&#xf…

解决在sample文件夹里面写代码,在测试的时候因为virtual原因,make编译报错

代码的结构 错误显示 解决办法 添加一句话&#xff0c;具体的cpp依据情况而定set_source_files_properties(${PROJECT_SOURCE_DIR}/src/sample_storage_test.cpp COMPILE_FLAGS "-Wno-unused-parameter")

Android SharedPreferences总结及优化

一、SharedPreferences简介 Android 中的 SharedPreferences&#xff08;后续简称SP&#xff09;是轻量级的数据存储方式&#xff0c;能够保存简单的数据类型&#xff0c;比如 String、int、boolean 值等。应用场合主要是数据比较少的配置信息。其内部是以 XML 结构保存在 /dat…

Java基础——深入理解ReentrantLock

一、简介在Java中通常实现锁有两种方式&#xff0c;一种是synchronized关键字&#xff0c;另一种是Lock。二者其实并没有什么必然联系&#xff0c;但是各有各的特点&#xff0c;在使用中可以进行取舍的使用。二、ReentrantLock与synchronized的比较相同点&#xff1a; &#xf…

使用开源的openssl的md5头文件,实现对于文件的md5代码

需要安装openssl的库 sudo apt-get install opensslsudo apt-get install libssl-dev参考链接 代码 #include "openssl/md5.h" #include <iostream> #include <fstream> #include <iomanip>//#define MAX_DATA_BUFF 1024; //#define MD5_LENGTH…

Android 多进程开发

前言正常情况下&#xff0c;一个apk启动后只会运行在一个进程中&#xff0c;其进程名为AndroidManifest.xml文件中指定的应用包名&#xff0c;所有的基本组件都会在这个进程中运行。但是如果需要将某些组件&#xff08;如Service、Activity等&#xff09;运行在单独的进程中&am…

clion中链接openssl库

错误显示 前提条件 apt-get install opensslapt-get install openssl-dev 解决办法 在CMakeLists.txt文件中加入如下命令link_libraries(crypto) 参考链接 无法将openssl库链接到CLion C 程序c - 无法将openssl库链接到CLion C程序

Java中String、StringBuffer、StringBuilder三者的区别

一、简介String、StringBuffer、StringBuilder三个类之间的区别主要是在两个方面&#xff1a;运行速度和线程安全。二、区别1、运行速度&#xff0c;或者说是执行速度在这方面运行速度快慢为&#xff1a;StringBuilder > StringBuffer > String StringString为字符串常量…

Ubuntu环境下,使用clion编译器,使用开源opensll的对称AES算法对于文件进行加密,C++代码

前提准备条件 需要安装openssl需要安装openssl-dev需要配置CMakeLists.txt文件集体内容可以参考我提供的相关参考链接 AES_file.h #include <openssl/aes.h> #include <iostream> #include <fstream> #include <cstring>#define RELEASE_ARRAY(P) if…

Java提高篇 —— Java关键字之static的四种用法

一、前言 在java的关键字中&#xff0c;static和final是两个我们必须掌握的关键字。不同于其他关键字&#xff0c;他们都有多种用法&#xff0c;而且在一定环境下使用&#xff0c;可以提高程序的运行性能&#xff0c;优化程序的结构。下面我们先来了解一下static关键字及其用法…

C++ 使用move来删除用户指定的文件

代码 #include <iostream>bool remove_file(std::string path){if (remove(path.c_str())0){std::cout << "success!" << std::endl;}else{std::cout << "False!" << std::endl;} } int main() {std::string path "/…

Java提高篇 —— Java关键字之final的几种用法

一、前言 在java的关键字中&#xff0c;static和final是两个我们必须掌握的关键字。不同于其他关键字&#xff0c;他们都有多种用法&#xff0c;而且在一定环境下使用&#xff0c;可以提高程序的运行性能&#xff0c;优化程序的结构。下面我们来了解一下final关键字及其用法。 …

使用C++的方式实现AES算法

aes_file.h #include <iostream> #include <fstream> #include <bitset> #include <string> using namespace std; typedef bitset<8> byte; typedef bitset<32> word;const int Nr 10; // AES-128需要 10 轮加密 const int Nk 4; /…

Java提高篇 —— Java三大特性之封装

一、封装 封装从字面上来理解就是包装的意思&#xff0c;专业点就是信息隐藏&#xff0c;是指利用抽象数据类型将数据和基于数据的操作封装在一起&#xff0c;使其构成一个不可分割的独立实体&#xff0c;数据被保护在抽象数据类型的内部&#xff0c;尽可能地隐藏内部的细节&am…

sqlite3的backup和restore函数的使用

参考代码 第一段这个亲测可以使用 #include <sqlite3.h> #include <iostream> /* ** Perform an online backup of database pDb to the database file named ** by zFilename. This function copies 5 database pages from pDb to ** zFilename, then unlocks pD…