JAVA中的NIO(一)

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

http://blog.csdn.net/baple/article/details/12749005

http://www.cnblogs.com/mjorcen/p/3992245.html

posted @ 2016-04-20 21:28  被罚站的树  阅读(179)  评论(0编辑  收藏  举报