对于在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();
}
当注释掉buffer.clear()的时候,输入的文件内容将不停的写入到输出文件中。因为第一次写完之后,position=limit,如果不执行clear的话,下一次再次调用read的时候将无法写入到buffer任何数据,因为position不可能大于limit,所以返回0(和没有内容但是读取成功返回-1不同),所以后续就会一直循环写入到输出文件
绝对方法与相对方法的含义:
相对方法:limit值与position值会在操作时被考虑到
绝对方法:完全忽略掉limit值与position值