Java NIO

1、NIO概述

  使用Java新的I/O技术(NIO)要比面向流的I/O效率要高,因为它使用的是内存映射文件的方式来处理I/O,Java NIO的类都放在java.nio包及其子包下,相关的类有:Buffer(缓冲)、Channel(通道)、Charset、Selector等。

  Channel通过节点流的getChannel()方法来获得,成员map()用来将其部分或全部数据映射为Buffer,成员read()、write()方法来读写数据,而且只能通过Buffer作为缓冲来读写Channel关联的数据。

  Buffer是一个缓冲区,它是一个抽象类,常用的子类有ByteBuffer、CharBuffer、IntBuffer、MappedByteBuffer等,通过它可以用来装入数据和输出数据。

  Charset可以将Unicode字符串(CharBuffer)和字节序列(ByteBuffer)相互转化。

  Selector是支持非阻塞I/O的类。

2、Buffer

  Buffer没有构造器,使用类方法allocate()来创建对应的Buffer对象,当向Buffer写入数据后,在读取Buffer中数据之前应该调用flip()方法来设置Buffer中的数据位置信息,读取Buffer中数据之后应该调用clear()方法来清空原来的数据位置信息。

  以下为Buffer内部示意图:

    Java NIO_第1张图片

Java NIO_第2张图片

  Java NIO_第3张图片

  以下为Buffer中的一些成员方法:

  put():向Buffer存入数据,带索引参数的版本不会移动位置position。
  get():从Buffer取出数据,带索引参数的版本不会移动位置position。
  capacity():获得Buffer的大小capacity。
  hasRemaining():判断当前位置position和界限limit之间是否还有元素可供处理。
  remaining():获得当前位置position和界限limit之间元素的个数。
  limit():获得或者设置Buffer的界限limit的位置。
  position():获得或者设置Buffer的位置position。
  mark():设置Buffer的mark位置。
  reset():将位置positon转到mark所在的位置。
  rewind()将位置position设为0,取消设置的mark。

3、Channel
  Channel接口下有用于文件IO的FileChannel,用于UDP通信的DatagramChannel,用于TCP通信的SocketChannel、ServerSocketChannel,用于线程间通信的Pipe.SinkChannel、Pipe.SourceChannel等实现类。Channel也不是通过构造器来创建对象,而是通过节点流的getChannel()方法来获得,如通过FileInputStream、FileOutputStream、RandomAccessFile的getChannel()获得对应的FileChannel。

  Channel中常用的方法有map()、read()、write(),map用来将Channel对应的部分或全部数据映射成MappedByteBuffer(ByteBuffer的子类),read/write用于对Buffer读写数据。下面为使用RandomAccessFile对应的Channel将文件a.txt中全部数据映射到Buffer后再复制该Buffer中数据到文件末尾:

import java.io.*;
import java.nio.*;
import java.nio.channels.*;

public class RandomFileChannelTest
{
	public static void main(String[] args)
		throws IOException
	{
		File f = new File("a.txt");
		try(
			RandomAccessFile raf = new RandomAccessFile(f, "rw");
			FileChannel randomChannel = raf.getChannel())
		{
			// 将Channel中所有数据映射成ByteBuffer
			ByteBuffer buffer = randomChannel.map(FileChannel.MapMode.READ_ONLY, 0 , f.length());
			// 把Channel的记录指针移动到最后
			randomChannel.position(f.length());
			// 将buffer中所有数据输出到Channel
			randomChannel.write(buffer);
		}
	}
}

  FileChannel中提供lock()/tryLock()方法来防止其他进程并发修改同一个文件,lock()返回FileLock类型的对象,其release()方法用来释放文件锁。需要注意的是在某些平台下关闭FileChannel时,会释放java虚拟机在该文件上的所有锁。

4、Charset

  Java中默认使用Unicode字符集,可以通过Charset来处理字节序列和字符序列(字符串)之间的转换,其availableCharsets()静态方法可以获得当前JDK支持的所有字符集。调用Charset的静态方法forName()可以获得指定字符集对应的Charset对象,调用该对象的newEncoder()、newDecoder()可以获得对应的编码器、解码器,调用编码器的encode()可以将CharBuffer或String转换为ByteBuffer,调用解码器的decode()可以将ByteBuffer转换为CharBuffer。

  下面的例子没有使用map来将文件映射到内存,而是使用Channel和Buffer每次只从文件test.data读取256字节的内容,然后将读取到的Byte数据通过Charset转码为GBK格式的charBuffer:

import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;

public class ReadFile
{
	public static void main(String[] args)
		throws IOException
	{
		try(
			FileInputStream fis = new FileInputStream("test.data");
			FileChannel fcin = fis.getChannel())
		{
			ByteBuffer bbuff = ByteBuffer.allocate(256);
			while( fcin.read(bbuff) != -1 ) //从Channel读取数据后保存到Buffer
			{
				// 锁定Buffer的空白区:向Buffer写入数据后调整数据位置指针,为从Buffer读取数据做准备
				bbuff.flip();
				
				// 创建Charset对象
				Charset charset = Charset.forName("GBK");
				// 创建解码器(CharsetDecoder)对象
				CharsetDecoder decoder = charset.newDecoder();
				// 将ByteBuffer的内容转码
				CharBuffer cbuff = decoder.decode(bbuff);
				System.out.print(cbuff);
				
				// 将Buffer初始化:从Buffer读取数据后调整数据位置指针,为下一次保存数据做准备
				bbuff.clear();
			}
		}
	}
}

 

你可能感兴趣的:(Java,SE)