1、IO与NIO
IO就是普通的IO,或者说原生的IO。特点:阻塞式、内部无缓冲,面向流。
NIO就是NEW IO,比原生的IO要高效。特点:非阻塞、内部有缓存,面向缓冲。
要实现高效的IO操作,尤其是服务器端程序时,需要使用NIO进行开发。
2、NIO的理解
NIO就是中引入了通道和缓冲器的概念。我们可以把文件想想成一个水池,以前是我们使用普通IO时是直接从池中取水。现在的NIO就相当于在水池中引出来一个水管,并将水管的另一端放在一个跟小的蓄水池中。当我们需要水时直接从蓄水池中取水。NIO中蓄水池就是Buffer类,我们可以设置这个类的大小,而这个蓄水池也只能放基本数据类型。而所谓的水管就是Channel了。
具体buffer类:
- ByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
具体的channel类
- FileChannel
- DatagramChannel
- SocketChannel
- ServerSocketChannel
2、NIO的使用
1.buffer的三大属性
- capacity 缓冲区的最大容量
- limit 读写的限制,比如读的时候缓冲区就只有5个字节,那么limit就等于4。当写缓冲区时,limit一般指向缓冲区的末尾
- position 当前读写的位置
package com.dy.xidian; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class GetChannel { public static final int BSIZE = 1024; @SuppressWarnings("resource") public static void main(String[] args) throws IOException { FileChannel fc = new FileOutputStream("E:/html/utf-8.php").getChannel(); // 将传入的数组作为ByteBuffer的储存器,就是所谓的缓冲区 fc.write(ByteBuffer.wrap("some text".getBytes())); fc.close(); fc = new RandomAccessFile("E:/html/utf-8.php", "rw").getChannel(); // 通过position更改管道的位置,我们可以认为水池中水管的位置是不固定 fc.position(fc.size()); fc.write(ByteBuffer.wrap("Some more".getBytes())); fc.close(); fc = new FileInputStream("E:/html/utf-8.php").getChannel(); ByteBuffer buff = ByteBuffer.allocate(BSIZE); fc.read(buff);
// 重置缓冲区,令limit=position,position=0,read之后必须的操作 buff.flip(); while (buff.hasRemaining()) System.out.println((char) buff.get()); } }
2.文件copy
package com.dy.xidian; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class FileCopy { public static final int BSIZE = 1024; @SuppressWarnings("resource") public static void main(String[] args) throws IOException { if (args.length != 2) { System.out.println("arguments: sourcefile destfile"); System.exit(1); } FileChannel in = new FileInputStream(args[0]).getChannel(); FileChannel out = new FileOutputStream(args[1]).getChannel(); ByteBuffer buffer = ByteBuffer.allocate(BSIZE); while (in.read(buffer) != -1) { //limit=position, position=0 buffer.flip(); out.write(buffer); //position=0,limit=capacity buffer.clear(); } } }
改进:将两个管道直接连接起来
package com.dy.xidian; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.channels.FileChannel; public class GetChannel { public static final int BSIZE = 1024; @SuppressWarnings("resource") public static void main(String[] args) throws IOException { if (args.length != 2) { System.out.println("arguments: sourcefile destfile"); System.exit(1); } FileChannel in = new FileInputStream(args[0]).getChannel(); FileChannel out = new FileOutputStream(args[1]).getChannel();
//0:源文件的起始位置,in.size():数据量,out目的文件 in.transferTo(0, in.size(), out); } }
3.转换数据
package com.dy.xidian; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.Charset; public class BufferToText { private static final int BSIZE = 1024; @SuppressWarnings("resource") public static void main(String[] args) throws IOException { /************** 写操作 ****************/ FileChannel fc = new FileOutputStream("data2.txt").getChannel(); // ByteBuffer为字节缓冲器,我们向缓冲器中输入数据时应对其进行编码 // 从缓冲器中读数据时应该进行解码 fc.write(ByteBuffer.wrap("Some text".getBytes("utf-8"))); fc.close(); /************** 读操作 ****************/ fc = new FileInputStream("data2.txt").getChannel(); ByteBuffer buff = ByteBuffer.allocate(BSIZE); fc.read(buff); buff.flip(); System.out.println(buff.asCharBuffer()); // position=0 buff.rewind(); //设置字符集,解决乱码问题 String encoding = System.getProperty("file.encoding"); System.out.println("Decoded using " + encoding + ": " + Charset.forName(encoding).decode(buff)); //通过charBuffer向ByteBuffer中写入 fc = new FileOutputStream("data2.txt").getChannel(); buff = ByteBuffer.allocate(24); buff.asCharBuffer().put("Some text"); fc.write(buff); fc.close(); fc = new FileInputStream("data2.txt").getChannel(); buff.clear(); fc.read(buff); buff.flip(); System.out.println(buff.asCharBuffer()); } }
4.获取基本数据类型
package com.dy.xidian; import java.nio.ByteBuffer; public class GetData { private static final int BSIZE = 1024; public static void main(String[] args) { ByteBuffer bb = ByteBuffer.allocate(BSIZE); int i = 0; // 缓冲区自动被初始化为0 while (i++ < bb.limit()) // limit不变,position++ if (bb.get() != 0) System.out.println("nonzero!"); System.out.println("i = " + i); // position = 0 bb.rewind(); /* 以字符的方式向缓冲区写 */ bb.asCharBuffer().put("Howdy"); char c; while ((c = bb.getChar()) != 0) System.out.print(c + " "); System.out.println(""); bb.rewind(); /* 以short类型向缓冲区写 */ bb.asShortBuffer().put((short) 471); System.out.println(bb.getShort()); bb.rewind(); /* 以int类型向缓冲区写 */ bb.asIntBuffer().put(99471142); System.out.println(bb.getInt()); bb.rewind(); /* 以long类型向缓冲区写 */ bb.asLongBuffer().put(99471142); System.out.println(bb.getLong()); bb.rewind(); /* 以float类型向缓冲区写 */ bb.asFloatBuffer().put(99471142); System.out.println(bb.getFloat()); bb.rewind(); /* 以double类型向缓冲区写 */ bb.asDoubleBuffer().put(99471142); System.out.println(bb.getDouble()); bb.rewind(); } }
代码中使用到了视图缓冲器(asIntBuffer、asFloatBuffer...),通过视图缓冲器来操作底层的ByteBuffer使得编程更加方便。不同的视图缓冲器对底层数组的影响是不同的,比如char视图会一次读两个字节,position则会移动两位,这点需要注意。ByteBuffer是以大端(低地址存高数据位)的方式存储数据。
4.相邻字符交换
package com.dy.xidian; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.CharBuffer; public class Exchange { public static void main(String[] args) throws UnsupportedEncodingException { char[] data = "usingbuffers".toCharArray(); System.out.println("char[] data = " + data.length); ByteBuffer bb = ByteBuffer.allocate(data.length * 2); System.out.println("bytebuffer.capacity = " + bb.capacity()); System.out.println("bytebuffer.limit = " + bb.limit()); CharBuffer cb = bb.asCharBuffer(); cb.put(data); System.out.println("charBuffer.limit = " + cb.limit()); char c1, c2; cb.rewind(); while (cb.hasRemaining()) { cb.mark(); c1 = cb.get(); c2 = cb.get(); cb.reset(); cb.put(c2).put(c1); } cb.rewind(); System.out.println(cb); } }
3、参考文章
http://www.iteye.com/magazines/132-Java-NIO#579
http://blog.csdn.net/linxcool/article/details/7771952