JAVA NIO简介-- Buffer、Channel、Charset 、直接缓冲区、分散和聚集、文件锁
IO
是主存和外部设备 ( 硬盘、终端和网络等 ) 拷贝数据的过程。 IO 是操作系统的底层功能实现,底层通过 I/O 指令进行完成。
Java标准io回顾
在Java1.4之前的I/O系统中,提供的都是面向流的I/O系统。 InputStream\OutputStream( 字节流 ) :一次传送一个字节。 Reader\Writer( 字符流 ) :一次一个字符。
NIO
nio 是java nonblocking(非阻塞) IO 的简称(还有种解释是 New IO),在jdk1.4 里提供的新API。Sun 官方标榜的特性如下: 为所有的原始类型提供(Buffer)缓存支持。字符集编码解码解决方案。 Channel :一个新的原始I/O 抽象。支持锁和内存映射文件的文件访问接口。 提供多路(non-bloking) 非阻塞式的高伸缩性网络I/O 。
在NIO中有几个核心对象需要掌握:缓冲区(Buffer)、通道(Channel)、选择器(Selector)。
Buffer
缓冲区实际上是一个容器对象,更直接的说,其实就是一个数组,在NIO库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的; 在写入数据时,它也是写入到缓冲区中的。
一个 buffer 主要由 position,limit,capacity 三个变量来控制读写的过程。
position:指定了下一个将要被写入或者读取的元素位置。
limit:指定还有多少数据需要取出,或者还有多少空间可以放入数据,是最后的位置,不是大小。
capacity:指定了可以存储在缓冲区中的最大数据容量,实际上,它指定了底层数组的大小。
三者的关系:0 <= position <= limit <= capacity 。 初始的时候limit ==capacity。
Buffer 常见方法:
flip(): 重设此缓冲区,将limit设置为当前position,然后将当前position设置为0 。读写缓冲区都会改变position和limit的位置。
clear(): 置为初始化状态 (position 变成 0 , limit 变成 capacity) 。
asReadOnlyBuffer():返回一个只读的缓冲区,和原始缓冲区共用同一块内存,只是只读。修改会抛异常。不能将只读的缓冲区转换为可写的缓冲区。
get():返回当前位置的数据,同时将 position+=1。
put():加入元素,position+=增加的元素个数。
remaining():返回剩余的数据的数量, (即limit - position)。
hasRemaining():是否还有剩余数据,循环的时候用。
rewind(): 将 position 重置为 0 ,一般用于重复读。
limit(int newLimit),position(int newPosition):修改这两个值。
compact(): 将未读取的数据拷贝到 buffer 的头部位。
mark()、reset(): mark 可以标记一个位置, reset 可以重置到该位置。
每个非布尔基本类型都有一个缓冲区类。Buffer 常见类型: ByteBuffer 、 MappedByteBuffer 、 CharBuffer 、 DoubleBuffer 、 FloatBuffer 、 IntBuffer 、 LongBuffer 、 ShortBuffer 。
//创建Buffer:
ByteBuffer bf = ByteBuffer.allocate(1024);
ByteBuffer bfd = ByteBuffer.allocateDirect(1024); // ByteBuffer 才有这个方法 //使用字节数组创建ByteBuffer
byte[] bytes = new byte[10];
ByteBuffer buffer = ByteBuffer.wrap(bytes); //使用ByteBuffer存储字符串
ByteBuffer buffer = ByteBuffer.allocate(100);
CharBuffer cBuffer = buffer.asCharBuffer();
cBuffer.put("Hello World");
cBuffer.flip();
String result = cBuffer.toString();
注意:ByteBuffer转成CharBuffer后,缓冲区总的字节数还是100,但是CharBuffer加入的每个字符都是占用2个字节,所以它的capacity变成了 100/2=50;put("Hello World")中每个字符都是2个字节的。 另,对CharBuffer的操作不会影响ByteBuffer的position、limit等字段的值。
Channel:
可以通过它读取和写入数据。拿 NIO 与原来的 I/O 做个比较,通道就像是Stream,而且是面向缓冲区的。所有数据都通过 Buffer 对象来处理。不会将字节直接写入通道中,相反,是将数据写入包含一个或者多个字节的缓冲区。同样,不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。
通道与流的不同之处在于通道是双向的(但是某一个channel可能只支持读或写)。而流只是在一个方向上移动(一个流必须是 InputStream 或者 OutputStream 的子类), 而 通道 可以用于读、写或者同时用于读写。因为它们是双向的,所以通道可以比流更好地反映底层操作系统的真实情况。特别是在 UNIX 模型中,底层操作系统通道是双向的。
//Channel直接可以用下面两个方法直接传递数据:
long transferFrom(ReadableByteChannel src, long position, long count)
long transferTo(long position, long count, WritableByteChannel target)
//文件拷贝的例子
FileInputStream fin = new FileInputStream("D:/work/test.txt");
FileOutputStream fout = new FileOutputStream("D:/work/output.txt");
FileChannel inChannel = fin.getChannel();
FileChannel outChannel = fout.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
while(true){
int ret = inChannel.read(buffer);
if( ret == -1)
break;
buffer.flip(); //该方法为父类Buffer的方法
outChannel.write(buffer);
buffer.clear(); //该方法为父类Buffer的方法
}
往Channel中写入数据:
int write(ByteBuffer src); // 从buffer当前position开始读取数据,写入到channel。position+=实际读取写出的数据大小,limit不变。write返回实际写入的数据量。
基于write的机制,有这种写法:
while(buf.hasRemaining()) {
channel.write(buf);
}
nio.charset
字符编码解码 : 字节本身只是一些数字,放到正确的上下文中被正确的解析。向 ByteBuffer 中存放数据时需要考虑字符集的编码方式,读取展示 ByteBuffer 数据时涉及对字符集解码。
Java.nio.charset 提供了编码解码一套解决方案。
Charset charset = Charset.forName("GBK");// 创建GBK字符集
ByteBuffer bf = charset.encode("hello world我是中国人");
CharBuffer cf = charset.decode(bf); // 每调用一次都会创建一个CharsetDecoder 对象
String name = charset.displayName();
String ret = cf.toString();
CharsetDecoder decoder = ch.newDecoder();
CharsetEncoder encoder = ch.newEncoder();
直接缓冲区
是为加快I/O速度,使用一种特殊方式为其分配内存的缓冲区,JDK文档中的描述为:给定一个直接字节缓冲区,Java虚拟机将尽最大努 力直接对它执行本机I/O操作。也就是说,它会在每一次调用底层操作系统的本机I/O操作之前(或之后),尝试避免将缓冲区的内容拷贝到一个中间缓冲区中 或者从一个中间缓冲区中拷贝数据。
要分配直接缓冲区,需要调用allocateDirect()方法,而不是allocate()方法,使用方式与普通缓冲区并无区别。还可以用内存映射文件创建直接缓冲区。
分散和聚集
分散/聚集 I/O 是使用多个而不是单个缓冲区来保存数据的读写方法。
一个分散的读取就像一个常规通道读取,只不过它是将数据读到一个缓冲区数组中而不是读到单个缓冲区中。同样地,一个聚集写入是向缓冲区数组而不是向单个缓冲区写入数据。
分散/聚集 I/O 对于将数据流划分为单独的部分很有用,这有助于实现复杂的数据格式。
ScatteringByteChannel、GatheringByteChannel 是两个interface,而 FileChannel 和 SocketChannel 等实现了这两个接口。
ScatteringByteChannel 是一个具有两个附加读方法的通道:
long read( ByteBuffer[] dsts );
long read( ByteBuffer[] dsts, int offset, int length );
这些 long read() 方法很像标准的 read 方法,只不过它们不是取单个缓冲区而是取一个缓冲区数组。
在 分散读取 中,通道依次填充每个缓冲区。填满一个缓冲区后,它就开始填充下一个,在移动下一个buffer前,必须填满当前的buffer,这也意味着它不适用于动态消息(译者注:消息大小不固定)。在某种意义上,缓冲区数组就像一个大缓冲区。
GatheringByteChannel聚集写入 类似于分散读取,只不过是用来写入。它也有接受缓冲区数组的方法:
long write( ByteBuffer[] srcs );
long write( ByteBuffer[] srcs, int offset, int length );
分散/聚集的应用
分散/聚集 I/O 对于将数据划分为几个部分很有用。例如,您可能在编写一个使用消息对象的网络应用程序,每一个消息被划分为固定长度的头部和固定长度的正文。您可以创建一个刚好可以容纳头部的缓冲区和另一个刚好可以容难正文的缓冲区。当您将它们放入一个数组中并使用分散读取来向它们读入消息时,头部和正文将整齐地划分到这两个缓冲区中。我们从缓冲区所得到的方便性对于缓冲区数组同样有效。因为每一个缓冲区都跟踪自己还可以接受多少数据,所以分散读取会自动找到有空间接受数据的第一个缓冲区。在这个缓冲区填满后,它就会移动到下一个缓冲区。聚集写对于把一组单独的缓冲区中组成单个数据流很有用。为了与上面的消息例子保持一致,您可以使用聚集写入来自动将网络消息的各个部分组装为单个数据流,以便跨越网络传输消息。
文件锁
文件大小
RandomAccessFile只能打开文件,不能是文件夹,否则会抛异常
RandomAccessFile rFile = new RandomAccessFile("/sdcard/update.zip", "rw");
FileChannel fChannel = rFile.getChannel();
File file = new File("/sdcard/update.zip");
long chaneelSize = fChannel.size();
long rSize = rFile.length();
long fSize = file.length();
//得到的三个大小值是一样的
Pipe管道
Java nio 管道是2个线程之间的单向数据连接。Pipe有一个source通道和一个sink通道。数据会被写到sink通道,从source通道读取。

//创建管道
Pipe pipe = Pipe.open(); //写数据到管道,需要访问sink通道
Pipe.SinkChannel sinkChannel = pipe.sink();
sinkChannel.write(buf); //从读取管道的数据,需要访问source通道
ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = inChannel.read(buf);
JAVA NIO简介-- Buffer、Channel、Charset 、直接缓冲区、分散和聚集、文件锁的更多相关文章
- Java NIO之Buffer(缓冲区)
Java NIO中的缓存区(Buffer)用于和通道(Channel)进行交互.数据是从通道读入缓冲区,从缓冲区写入到通道中的. 缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存.这 ...
- java NIO简介
1)java nio简介 nio 是 java New IO 的简称,在 jdk1.4 里提供的新 api . Sun 官方标榜的特性如有:为所有的原始类型提供 (Buffer) 缓存支持:字符集编码 ...
- Java NIO系列教程(一)java NIO简介
这个系列的文章,我们开始玩一玩IO方面的知识,对于IO和NIO,我们经常会接触到,了解他们的基本内容,对于我们的工作会有特别大的帮助.这篇博文我们仅仅是介绍IO和NIO的基本概念,以及一些关键词. 基 ...
- JAVA NIO 简介 (netty源码死磕1.1)
[基础篇]netty 源码死磕1.1: JAVA NIO简介 1. JAVA NIO简介 Java 中 New I/O类库 是由 Java 1.4 引进的异步 IO.由于之前老的I/O类库是阻塞I/ ...
- 5. 彤哥说netty系列之Java NIO核心组件之Channel
你好,我是彤哥,本篇是netty系列的第五篇. 简介 上一章我们一起学习了如何使用Java原生NIO实现群聊系统,这章我们一起来看看Java NIO的核心组件之一--Channel. 思维转变 首先, ...
- (一:NIO系列)JAVA NIO 简介
出处:JAVA NIO 简介 Java 中 New I/O类库 是由 Java 1.4 引进的异步 IO.由于之前老的I/O类库是阻塞I/O,New I/O类库的目标就是要让Java支持非阻塞I/O, ...
- Java NIO 之 Buffer
Java NIO 之 Buffer Java NIO (Non Blocking IO 或者 New IO)是一种非阻塞IO的实现.NIO通过Channel.Buffer.Selector几个组件的协 ...
- JAVA基础知识之NIO——Buffer.Channel,Charset,Channel文件锁
NIO机制 NIO即NEW IO的意思,是JDK1.4提供的针对旧IO体系进行改进之后的IO,新增了许多新类,放在java.nio包下,并对java.io下许多类进行了修改,以便使用与nio. 在ja ...
- Java NIO:Buffer、Channel 和 Selector
Buffer 一个 Buffer 本质上是内存中的一块,我们可以将数据写入这块内存,之后从这块内存获取数据. java.nio 定义了以下几个 Buffer 的实现,这个图读者应该也在不少地方见过了吧 ...
随机推荐
- java文件编译及运行
1 配置环境变量 使用鼠标右击“我的电脑”->属性->高级->环境变量 系统变量->新建->变量名:JAVA_HOME 变量值:C:\Program Files (x86 ...
- Linux的课程总结
Linux的课程总结 20125133 马国祥 通过16周的学习明白学好linux不是一件一蹴而就的事,一定要能坚持使用它,特别是在使用初期,由于在linux中,用户权限很大,做任何事情都很自由,所以 ...
- python 字符串编码转换
import chardetdef CheckCode(filename): adchar=chardet.detect(filename) if adchar['encoding']=='utf-8 ...
- 《Android深度探索HAL与驱动开发》第一章阅读心得
首先了解到Android系统架构是由四层构成:其中第一层是Linux内核,他的作用是负责Linux的驱动程序以及内存.进程.电源等管理操作:第二层是C/C++代码库,也就是Linux下.so的文件:第 ...
- Python实例3
3.一个整数,它加上100后是一个完全平方数,再加上268又是一个完全平方数,请问该数是多少? 正解: 源码: #!/usr/bin/python # -*- coding: UTF-8 -*- im ...
- 第六篇T语言实例开发,多点找色应用
---恢复内容开始--- 多点找色应用 文字,图形特征的获取 多点找色 功能原型 窗口多点找色(窗口句柄,x1,y1,x2,y2,颜色值,色点组,相似度,方向,返回x,返回y) 功能说明 根据指定的多 ...
- java 存储到什么地方
下面的内容主要来源于<Thinging in Java> 这本书的第22页讲到的,有5个不同的地方可以存储数据: 1).寄存器 这是最快的存储区,因为它位于处理器内部(没错,如果学过计算机 ...
- 如何修改WAMP中mysql默认空密码
WAMP安装好后,mysql密码是为空的,那么要如何修改呢?其实很简单,通过几条指令就行了,下面我就一步步来操作. 首先,通过WAMP打开mysql控制台. 提示输入密码,因为现在是空,所以直接按 ...
- mysql coalesce函数
COALESCE函数从值列表中返回第一个非NULL的值,当遇到NULL值时将其替换为0. coalesce(str1,str2....); e.g. 需要在表中查出所有比'WARD'提成(COMM ...
- 前端学习 第四弹: HTML(一)
前端学习 第四弹: HTML(一) 元素分类:块元素 内联元素 块级元素在浏览器显示时,通常会以新行来开始(和结束). 例子:<h1>, <p>, <ul>, &l ...