java-nio详细介绍

对于在java中传统I/O与Nio的I/O操作,两者面向的编程对象不同,操作的流程也大不相同

java.io中最为核心的一个概念是流(Stream),是一种面向流的编程,并且一个流只能是输入流或者是输出流,不可能同时是输入流和输出流。

java.nio中拥有3个核心概念:Selector,Channel与Buffer,在java.noi中,面向的是快(block)或者是缓冲区,(buffer)编程的。Buffer底层就是一块内存,实际上是由数组组成。数据的读和写都是通过Buffer来实现的。

除了数组之外,Buffer还提供来对于数据的结构化访问方式,并且可以追踪到系统的读写过程(当前读写到的位置,可以读写到的位置…)。java中的7种原生数据类型都有格子对应的Buffer类型,如:IntBuffer,LongBuffer… (没有booleanBuffer)

Channel指得是可以向其写入或者读取数据的对象,它类似于java.io中的Stream,所有的数据读写都是通过Buffer来进行的,永远不会出现直接向Channel写入数据或者读取数据的情况。与Stream不同的是,Channel是双向的,因此更能够反映出底层操作系统的真是情况:在Linux系统中,底层操作系统的通道就是双向的。

对于简单变量:

public static void main(String[] args) {
        IntBuffer buffer = IntBuffer.allocate(10);
        for (int i = 0; i < buffer.capacity(); i++) {
            int randomNumber = new SecureRandom().nextInt(20);
            buffer.put(randomNumber);
        }

        buffer.flip();
        while (buffer.hasRemaining()) {
            System.out.println(buffer.get());
        }
    }

buffer.flip()之上的代码是向buffer中写入数据,当需要向buffer中读取数据的时候需要调用flip来“反转“。

通过Channel对文件流进行读写的操作实现:

public static void main(String[] args) throws Exception {
        FileInputStream fileInputStream = new FileInputStream("NioTest2.txt");
        FileChannel fileChannel = fileInputStream.getChannel();

        ByteBuffer byteBuffer = ByteBuffer.allocate(512);
        fileChannel.read(byteBuffer);

        byteBuffer.flip();

        while (byteBuffer.remaining() > 0) {
            byte b = byteBuffer.get();
            System.out.println("Character: " + (char)b);
        }

        fileInputStream.close();
    }
public static void main(String[] args) throws Exception{
        FileOutputStream fileOutputStream = new FileOutputStream("NioTest2.txt");
        FileChannel fileChannel = fileOutputStream.getChannel();

        ByteBuffer byteBuffer = ByteBuffer.allocate(512);

        byte[] message = "hello world , welcome".getBytes();

        for (int i = 0; i < message.length; i++) {
            byteBuffer.put(message[i]);
        }

        byteBuffer.flip();

        fileChannel.write(byteBuffer);

        fileOutputStream.close();
    }

Buffer类的底层,有几个位置标示来完成相应的Buffer的操作。
position:下一个读或写元素的位置

A buffer’s capacity is the number of elements it contains. The capacity of a buffer is never negative and never changes.

limit:下一个遇到的不能够被读或者写的元素的位置

A buffer’s limit is the index of the first element that should not be read or written. A buffer’s limit is never negative and is never

capacity:Buffer初始化就生成好的固定长度,不会被改变

A buffer’s capacity is the number of elements it contains. The capacity of a buffer is never negative and never changes.

marking and resetting:
mark不一定需要,如果设置了,当调用reset方法是position会重置到此mark标记的位置。mark的位置不能大于position的位置,否则会抛错,同样的,当没有使用mark却直接调用reset方法的时候也会抛错。

mark:

public final Buffer mark() {
        mark = position;
        return this;
    }

reset:

public final Buffer reset() {
        int m = mark;
        if (m < 0)
            throw new InvalidMarkException();
        position = m;
        return this;
    }

对于上述的位置应该有一下范围限制:

0 <= mark <= position <= limit <= capacity

初始之后的Buffer总是有一个为0的position,和一个为定义的mark,初始的limit可能是0或者一个其他值,这个和Buffer的类型和构造的方式有关。

clear:position设置为0,limit设置成capacity.

public final Buffer clear() {
        position = 0;
        limit = capacity;
        mark = -1;
        return this;
    }

flip:将limit设置为position,设置position为0

public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }

rewind:将position设置为0,同时limit位置不变

public final Buffer rewind() {
        position = 0;
        mark = -1;
        return this;
    }

代码实例:

public static void main(String[] args) {
        IntBuffer buffer = IntBuffer.allocate(10);

        System.out.println("capacity: " + buffer.capacity());

        for (int i = 0; i < 3; i++) {
            int randomNumber = new SecureRandom().nextInt(20);
            buffer.put(randomNumber);
        }

        System.out.println("before flip limit: " + buffer.limit());

        buffer.flip();

        System.out.println("after flip limit: " + buffer.limit());

        System.out.println("enter while loop");

        while (buffer.hasRemaining()) {
            System.out.println("position: " + buffer.position());
            System.out.println("limit: " + buffer.limit());
            System.out.println("capacity: " + buffer.capacity());

            System.out.println(buffer.get());

            System.out.println("position after: " + buffer.position());
        }
    }

返回结果:

capacity: 10
before flip limit: 10
after flip limit: 3
enter while loop
position: 0
limit: 3
capacity: 10
12
position after: 1
position: 1
limit: 3
capacity: 10
2
position after: 2
position: 2
limit: 3
capacity: 10
12
position after: 3

需要注意的是position的起始位置时0,而每次get一个元素之后position才会前进一个。


通过nio的操作将一个文件内容写入到另一个文件:

public static void main(String[] args) throws Exception {
        FileInputStream inputStream = new FileInputStream("input.txt");
        FileOutputStream outputStream = new FileOutputStream("output.txt");

        FileChannel inputChannel = inputStream.getChannel();
        FileChannel outputChannel = outputStream.getChannel();

        ByteBuffer buffer = ByteBuffer.allocate(512);

        while (true) {
            buffer.clear();

            int read = inputChannel.read(buffer);

            System.out.println("read:" + read);

            if (-1 == read) {
                break;
            }

            buffer.flip();

            outputChannel.write(buffer);
        }

        inputChannel.close();
        outputChannel.close();

    }
  1. 从FileInputStream获取到FileChannel对象
  2. 创建Buffer
  3. 将数据从Channel读取到Buffer中

当注释掉buffer.clear()的时候,输入的文件内容将不停的写入到输出文件中。因为第一次写完之后,position=limit,如果不执行clear的话,下一次再次调用read的时候将无法写入到buffer任何数据,因为position不可能大于limit,所以返回0(和没有内容但是读取成功返回-1不同),所以后续就会一直循环写入到输出文件


绝对方法与相对方法的含义:
相对方法:limit值与position值会在操作时被考虑到
绝对方法:完全忽略掉limit值与position值

你可能感兴趣的:(java,java,nio,io)