java nio

从JDK1.4开始,引入了新的I/O类库,位于java.nio包中,其目的是在于提高I/O操作的效率。

java.nio包引入了4个关键的数据类型:

1  Buffer:缓冲区,临时存放输入或输出数据

2  Charset:具有把Unicode字符编码转换为其他字符编码,以及把其他字符编码转换为Unicode字符编码的功能。

3  Channel:数据传输通道,能够把Buffer中的数据写到数据汇,或者把数据源的数据读入到Buffer。

4  Selector:支持异步I/O操作,也称为非阻塞I/O操作。一般在编写服务器程序时需要用到它。


新I/O类库主要从两个方面来提高I/O操作的效率

1:利用Buffer缓冲器和Channel通道来提高I/O操作的速度。

2:利用Selector来支持非阻塞I/O操作。



缓冲器Buffer概述

数据输入和输出旺旺是比较耗时的操作。缓冲区从两个方面来提高I/O操作的效率。

1:减少实际的物理读写次数。

2:缓冲区在创建时被分配内存,这块内存区域一直被重用,这可以减少动态分配和回收内存区域的次数。


Buffer的层次结构


Buffer

ByteBuffer

MappedByteBuffer

CharBuffer

DoubleBuffer

FloatBuffer

IntBuffer

LongBuffer

ShortBuffer


所有缓冲区都有以下属性:

1:容量(capacity):表示该缓冲区可以保存多少数据。

2:极限 (limit):表示缓冲区的当前终点,不能对缓冲区中超过极限的数据进行读写操作。极限是可以修改的,这有利于缓冲区的重用。为非负整数。

3:位置(pusiton):表示缓冲区中下一个读写单元的位置,每次读写缓冲区的数据时,都会改变位置值,为下一次读写数据做准备。位置是一个非负整数,不应该大于极限。

以上三个属性的关系为:容器>=极限>=位置>=0


缓冲区提供了用于改变以上3个属性的方法

clear():把极限设为容量值,再把位置设为0。

flip():把极限设为位置值,再把位置设为0。

rewind():不改变极限,把位置设为0。



java.nio.Buffer类是一个抽象类,不能被实例化。共有8个具体的缓冲区类,其中最基本的缓冲区是ByteBuffer,它存放的数据单元是字节。ByteBuffer类并没有提供公开的构造方法,但是提供了两个获得ByteBuffer实例的静态工厂方法。

1   allocate(int capacity):返回一个ByteBuffer对象,参数capacity指定缓冲区的容量。

2   directAllocate(int  capacity):返回一个ByteBuffer对象,参数capacity指定缓冲区的容量。该方法返回的缓冲区称为直接缓冲区,它与当前操作系统能够更好地融合,因此能 进一步提高I/O操作的速度。但是分配直接缓冲区的代价高昂,因此只有在缓冲区较大并且长期存在,或者需要经常重用时,才使用这种缓冲区。


除boolean类型以外,每种基本类型都有对应的缓冲区类,包括CharBuffer,DoubleBuffer,FloatBuffer,IntBuffer,LongBuffer和ShortBuffer。这几个缓冲区类都有一个能够返回自身实例的静态工厂方法------allocate(int  capacity)。在CharBuffer中存放的数据单元为字符,在DoubleBuffer中国存放的数据单元为double,以此类推。

还有一种缓冲区是MappedByteBuffer,它是ByteBuffer的子类。MappedByteBuffer能够使缓冲区和文件的某个区域直接映射。

所有具体缓冲区类都提供了读写缓冲区的方法:

1  get():相对读。从缓冲区的当前位置读取一个单元的数据,读完后把位置值加1。

2  get(int  index):绝对读。从参数index指定的位置读取一个单元的数据。

3  put():相对写。向缓冲区的当前位置写入一个单元的数据,写完后把位置值加1。

4  put(int  index):绝对写。向参数index指定的位置写入一个单元的数据。



通道Channel概述


通道Channel用来连接缓冲区与数据源或数据汇。数据源的数据经过通道到达缓冲区,缓冲区的数据经过通道到达数据汇。

Channel的主要层次结构:

Channel

ReadableByteChannel

ScatteringByteChannel

WritableByteChannel

GatheringByteChanne

ByteChannle是ReadableByteChannel和WritableByteChannel的子接口

以上全是接口

FileChannel是唯一的实现类,它是ByteChannle,ScatteringByteChannel,GatheringByteChannel的实现类。


java.nio.channels.Channel接口只声明了以下两个方法:

1:close():关闭通道。

2:isOpen():判断通道是否打开。

通道在创建时被打开,一旦关闭通道,就不能重新打开它。

Channel接口的两个最重要的子接口是ReadableByteChannel和WritableByteChannel。

ReadableByteChannel接口声明了read(ByteBuffer  dst)方法,该方法把数据源的数据读入到参数指定的ByteBuffer缓冲区中。

WritableByteChannel接口声明了write(ByteBuffer   src)方法,该方法把参数指定的ByteBuffer缓冲区中的数据写到数据汇中。



ScatteringByteChannle和GatheringByteChannel接口(通过缓冲区数组分散的分散读,集中写数据)

ScatteringByteChannle接口扩展了ReadableByteChannel接口,允许分散地读取数据。分散读取数据是指单个读取操作能填充多个缓冲区。(Scattering:分散)

这个接口声明了一个read(ByteBuffer[ ]  dsts)方法,该方法把从数据源读取的数据依次填充到参数指定的ByteBuffer中。

GatheringByteChannel接口扩展了WritableByteChannel接口,允许集中的写入数据。集中写入数据是指单个写操作能把多个缓冲区的数据写到数据汇。(Gathering:集中)

这个接口声明了一个write(ByteBuffer[ ]  srcs)方法,该方法依次把参数指定的ByteBuffer数组的每个ByteBuffer中的数据写到数据汇。

分散读取数据和集中写入数据能够进一步提高输入和输出操作的速度。

FileChannel

FileChannel类是Channel接口的实现类,代表一个与文件相连的通道。该类实现了ByteChannek,ScatteringByteChannle和GatheringByteChannel接口,支持读操作,写操作,分散读操作和集中写操作。FileChannel类没有提供公开的构造方法,因此客户程序不能用new语句来构造它的实例。

不过FileInputStream,FileOutStream和RandomAccessFile类中提供了getChannel()方法,该方法返回相应的FileChannel对象。


字符编码CharSet类概述

java.nio.Charset类的每个实例代表特定的字符编码类型。

CharSet类提供了以下用于字符编码转换的方法:

1  ByteBuffer    encode(String  str):把参数str指定的字符串转换为当前字符编码。(当前字符编码表示当前CharSet对象表示的字符编码)

2  ByteBuffer    encode(CharBuffer   cb):把参数cb指定的字符缓冲区中的字符转换为当前字符编码,把转换后的当前字符编码存放在一个ByteBuffer对象中,并将其返回。 原先在参数cb缓冲区内的字符使用Unicode字符编码。

3  CharBuffer   decode(ByteBuffer  bb): 把参数bb指定的ByteBuffer中的当前字符编码转换为Unicode字符编码,把转换后的Unicode字符编码存放在一个CharBuffer对象中, 将其返回。



用FileChannel读写文件

final int BSIZE=1024;
File file=new File("D:\\test.txt");
//向文件中写数据
FileChannel fc=new FileOutputStream(file).getChannel();
fc.write(ByteBuffer.wrap("你好".getBytes()));
fc.close();

//向文件末尾添加数据
fc=new RandomAccessFile(file, "rw").getChannel();
fc.position(fc.size()); // 定位到文件末尾
fc.write(ByteBuffer.wrap("朋友!".getBytes()));
fc.close();

//读数据
fc=new FileInputStream(file).getChannel();
ByteBuffer buff=ByteBuffer.allocate(BSIZE);
fc.read(buff); //把文件中的数据读入到ByteBuffer中
buff.flip();
Charset cs=Charset.defaultCharset();//获得本地平台的字符编码
cs.decode(buff); //转换为Unicode字符编码
fc.close();


控制缓冲区

final int BSIZE=1024;
//把test.txt文件中的数据拷贝到out.txt中
FileChannel in=new FileInputStream("D:\\test.txt").getChannel();
FileChannel out=new FileOutputStream("D:\\out.txt").getChannel();

ByteBuffer buff=ByteBuffer.allocate(BSIZE);
while(in.read(buff)!=-1){
buff.flip();
out.write(buff);
buff.clear();
}
in.close();
out.close();

在以上程序中,每次执行in.read(buff)方法,都会从文件中读入一些数据,把它们填充到buff缓冲区中。只要文件中还有足够多的数据,该方法就会用数据填满buff缓冲区。

如果文件中已经没有足够多的数据,那么该方法无法填满buff缓冲区。buff.flip()方法确保out.write(buff)方法仅仅操作缓冲区的当前数据。buff.clear()方法把缓冲区的极限设为容量值,为下一次执行in.read(buff)方法向缓冲区内填充尽可能多的数据做准备。

以上例子主要用于演示ByteBuffer类的flip()和clear()方法的用途。另一方面,对于单纯的拷贝文件操作,可以用FileChannel类的静态方法transferTo()或者transferFrom()来实现。以下三段代码是等价的

//1

ByteBuffer  buff=ByteBuffer.allocate(BSIZE);

while(in.read(buff)!=-1){

buff.flip();
out.write(buff);
buff.clear();

}

//2

in.transferTo(0,in.size(),out);

//3

out . transferFrom(in,0,in.size());



字符编码转换


CharBuffer缓冲区内存放的数据单元为Unicode字符。ByteBuffer类的asCharBuffer()方法把ByteBuffer内的数据转换为Unicode字符,把它们存放在一个CharBuffer对象中,并将其返回。

如果ByteBuffer中存放了表示Unicode字符编码的字节,那么asCharBuffer()方法会返回包含正确字符的CharBuffer;否则,asCharBuffer()方法返回的CharBuffer会包含乱码。

在这种情况下,应该利用Charset类来进行Unicode字符编码与其他类型字符编码的转换。



final int BSIZE=1024;
//代码1
ByteBuffer bb=ByteBuffer.wrap("你好".getBytes("UTF-8"));
CharBuffer cb=bb.asCharBuffer();
System.out.println(cb);   //打印??


//代码2
bb=ByteBuffer.wrap("你好".getBytes("UTF-16BE"));
cb=bb.asCharBuffer();
System.out.println(cb); //打印你好

//代码3
bb=ByteBuffer.wrap("你好".getBytes("UTF-8"));
Charset cs=Charset.forName("UTF-8");
cb=cs.decode(bb);
System.out.println(cb); //打印你好

//代码4
cs=Charset.forName("GBK");
bb=cs.encode("你好");
cb=cs.decode(bb);
for(int i=0;i<cb.limit();i++){
System.out.println(cb.get());
}


在以上第一段代码中,ByteBuffer中存放的是UTF-8字符编码,asCharBuffer()方法返回的CharBuffer包含错误的字符串。

在第二段代码中,ByteBuffer中存放的是UTF-16BE字符编码,即Unicode字符编码,asCharBuffer()方法返回的是CharBuffer包含正确的字符串。

第三段代码中,ByteBuffer中存放的是UTF-8字符编码,Charset.forName("UTF-8")方法返回代表UTF-8字符编码的Charset对象,该Charset对象的decode()方法把ByteBuffer参数中的UTF-8字符编码转换为CharBuffer中的Unicode字符编码,因此CharBuffer包含正确的字符串。

第四段代码中,Charset.forName("GBK")返回代表GBK字符编码的Charset对象,该Charset对象的encode()方法把字符串“你好”转换为GBK字符编码,并把它存放在ByteBuffer中。




文件映射缓冲区:MappedByteBuffer

MappedByteBuffer用于创建和修改那些因为太大而不能放入内存的文件。MapperByteBuffer可用来映射文件中的一块区域,所有对MappedByteBuffer的读写操作都会被映射到对文件的物理读写操作。

FileChannel类提供了获得MappedByteBuffer对象的map()方法,如下:

MappedByteBuffer   map(FileChannel. MapMode   mode,long  position,long  size)

参数postion指定文件映射区的起始位置,参数size指定映射区域的大小,参数mode指定映射模式,有3个可选值:

1  MapMode.READ_ONLY :只能对映射区域进行读操作

2  Read/write:可以对映射区域进行读和写操作。

3  MapMode.PRIVATE:对MappedByteBuffer缓冲区中数据的修改不会保存到文件中,并且这种修改对其他程序不可见。

int capacity=0x8000000;//128MB
MappedByteBuffer mb=new RandomAccessFile("D:\\text.txt", "rw").getChannel().map(FileChannel.MapMode.READ_WRITE, 0, capacity);
mb.put("你好啊".getBytes("GBK"));//向文件中写入采用GBK字符编码的字符串"你好啊"
mb.flip();
System.out.println(Charset.forName("GBK").decode(mb));
//MappedByteBuffer继承了ByteBuffer类,因此可以调用MappedByteBuffer的asCharBuffer()或asIntBuffer()等方法来获得CharBuffer或IntBuffer试图。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值