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

来源:互联网 发布:iphone6s还原网络设置 编辑:程序博客网 时间:2024/04/27 17:50


     从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的容量为8CharBuffer 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'); // 2buff.put('b'); // 3buff.put('c'); // 4System.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()); // 6System.out.println("取出一个元素后,position = " + buff.position());// 调用clear方法,将position设为0,limit设为capacity的位置buff.clear(); // 7System.out.println("执行clear()后,limit = " + buff.limit());System.out.println("执行clear()后,position = " + buff.position());System.out.println("执行clear()后,缓冲区内容并没有被清除:" + buff.get(2)); // 8System.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,以该文件输入流创建FileChannelinChannel = 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转换成CharBufferCharBuffer 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 {// 创建简体中文对应的CharsetCharset 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));}}

     运行结果如下:

     

     

     


     







0 0
原创粉丝点击