Java IO系统——NIO之Buffer、Channel和Charset类

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zjx86320/article/details/51286372


     从JDK1.4开始,Java提供了一系列改进的输入/输出处理的新功能,这些功能被统称为新IO(NIO),新增了许多用于处理输入/输出的类,这些类都被放在java.nio包以及子包下,并且对原java.io包中的很多类都以NIO为基础进行了改写,新增了满足NIO的功能。


     一、Why


     为什么要使用NIO呢,我们下面从两方面分析:


     1.传统IO的缺点

     ①阻塞式

     使用InputStream的read()方法从流中读取数据时,如果数据源中没有数据,它会阻塞该线程。

     ②效率低

     传统的输入流、输出流都是通过字节的移动来处理的,即使不直接去处理字节流,但底层的实现还是依赖于字节处理,也就是说,面向流的输入/输出系统一次只能处理一个字节,因此面向流的输入/输出系统通常效率不高。

     

     2.NIO的优点

     ①非阻塞

     NIO中提供了用于支持非阻塞式输入/输出的Selector类,希望采用非阻塞方式进行通信的Channel都应该注册到Selector对象。

     ②效率高

     NIO采用内存映射文件的方式来处理输入/输出,NIO将文件或文件的一段区域映射到内存中,这样就可以像访问内存一样类访问文件了,通过这种方式来进行输入/输出要比传统的输入/输出快很多。


     二、Buffer类


     1.简介

     从内部结构上来看,Buffer就像一个数组,它可以保存多个类型相同的数据,Buffer是一个抽象类,对应的基本数据类型都有相应的Buffer类:CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer,可以在底层的字节数组上进行get/set操作。


     2.构造函数

     Buffer类没有提供构造器,荣国如下方法获得:

     static XxxBuffer allocate(int capacity):创建一个容量为capacity的XxxBuffer对象。


     3.示例代码:

public class BufferTest {
	public static void main(String[] args) {
		// 创建Buffer,指定Buffer的容量为8
		CharBuffer buff = CharBuffer.allocate(8);
		System.out.println("capacity: " + buff.capacity());
		System.out.println("limit: " + buff.limit());
		System.out.println("position: " + buff.position());
		// 放入元素
		buff.put('a'); // 2
		buff.put('b'); // 3
		buff.put('c'); // 4

		System.out.println("加入三个元素后,position = " + buff.position());
		// 调用flip()方法,将position设为0,limit设为position的位置
		buff.flip();
		System.out.println("执行flip()后,limit = " + buff.limit());
		System.out.println("position = " + buff.position());
		// 取出第一个元素
		System.out.println("第一个元素(position=0):" + buff.get()); // 6
		System.out.println("取出一个元素后,position = " + buff.position());
		// 调用clear方法,将position设为0,limit设为capacity的位置
		buff.clear(); // 7
		System.out.println("执行clear()后,limit = " + buff.limit());
		System.out.println("执行clear()后,position = " + buff.position());
		System.out.println("执行clear()后,缓冲区内容并没有被清除:" + buff.get(2)); // 8
		System.out.println("执行绝对读取后,position = " + buff.position());
	}
}

        三、Channel类


     1.简介

     Channel类似于传统的流对象,但与传统的流对象有两个主要区别。

     ①Channel可以直接将指定文件的部分或全部直接映射成Buffer.

     ②程序不能直接访问Channel中的数据,包括读取、写入都不行,Channel只能与Buffer进行交互。


     2.分类

     ①支持线程之间通信:Pipe.SinkChannel、Pipe.SourceChannel

     ②支持TCP网络通信:ServerSocketChannel、SocketChannel

     ③支持UDP网络通信:DatagramChannel

     ④支持文件之间通信:FileChannel


     3.获取

     所有的Channel都不应该通过构造器直接创建,而是通过传统节点的getChannel()方法来返回对应的Channel,不同的节点流获得的Channel不一样,例如FileInputStream的getChannel()方法返回的是FileChannel.


     4.示例代码

public class FileChannelTest {
	public static void main(String[] args) {
		FileChannel inChannel = null;
		FileChannel outChannel = null;
		try {
			File f = new File("FileChannelTest.java");
			// 创建FileInputStream,以该文件输入流创建FileChannel
			inChannel = new FileInputStream(f).getChannel();
			// 将FileChannel里的全部数据映射成ByteBuffer
			// MapMode:映射模式,只读和读写两种模式
			// 0,f.length():将Channel对应索引的数据映射到buffer中
			MappedByteBuffer buffer = inChannel.map(
					FileChannel.MapMode.READ_ONLY, 0, f.length());
			// 使用GBK的字符集来创建解码器
			Charset charset = Charset.forName("GBK");
			// 以文件输出流创建FileBuffer,用以控制输出
			outChannel = new FileOutputStream("a.txt").getChannel();
			// 直接将buffer里的数据全部输出
			outChannel.write(buffer);
			// 再次调用buffer的clear()方法,复原limit、position的位置
			buffer.clear();
			// 创建解码器(CharsetDecoder)对象
			CharsetDecoder decoder = charset.newDecoder();
			// 使用解码器将ByteBuffer转换成CharBuffer
			CharBuffer charBuffer = decoder.decode(buffer);
			// CharBuffer的toString方法可以获取对应的字符串
			System.out.println(charBuffer);
		} catch (IOException ex) {
			ex.printStackTrace();
		} finally {
			try {
				if (inChannel != null)
					inChannel.close();
				if (outChannel != null)
					outChannel.close();
			} catch (IOException ex) {
				ex.printStackTrace();
			}
		}
	}
}

        三、CharSet类

     

     计算机底层是没有文本文件、图片文件之分的,它只是忠实地记录每个文件的二进制序列而已。当需要保存文本文件时,程序必须把文件中的每个字符翻译成二进制序列;但需要读取文本文件时,程序必须把二进制序列转换为一个个的字符。

     Java默认使用Unicode字符集,但很多操作系统并不是用Unicode字符集,那么当从系统中读取数据到java程序中时,就可能出现乱码等问题。JDK1.4提供了Charset来处理字节序列和字符序列(字符串)之间的转换关系,该类包含了用于创建解码器和编码器的方法。

     Encode和Decode两个专业术语来自于早期的电报、情报等,当把明文的消息转换成普通人看不懂的电码(或密码)的过程就是Encode,而将电码(或密码)翻译成明文的消息则被称为Decode。后来计算机也采用了这两个概念。


     示例代码:

public class CharsetTransform {
	public static void main(String[] args) throws Exception {
		// 创建简体中文对应的Charset
		Charset cn = Charset.forName("GBK");
		// 获取cn对象对应的编码器和解码器
		CharsetEncoder cnEncoder = cn.newEncoder();
		CharsetDecoder cnDecoder = cn.newDecoder();
		// 创建一个CharBuffer对象
		CharBuffer cbuff = CharBuffer.allocate(3);
		cbuff.put('孙');
		cbuff.put('悟');
		cbuff.put('空');
		cbuff.flip();
		// 将CharBuffer中的字符序列转换成字节序列
		ByteBuffer bbuff = cnEncoder.encode(cbuff);
		// 循环访问ByteBuffer中的每个字节
		for (int i = 0; i < bbuff.capacity(); i++) {
			System.out.print(bbuff.get(i) + " ");
		}
		
		// 将ByteBuffer的数据解码成字符序列
		System.out.println("\n" + cnDecoder.decode(bbuff));
	}
}

     运行结果如下:

     

     

     


     







猜你喜欢

转载自blog.csdn.net/zjx86320/article/details/51286372