Java NIO
I/O简介
I/O或者输入输出指的是计算机与外部世界或者一个程序与计算机的其余部分之间的接口。它对于任何计算机系统都非常关键,因而所有I/O的主体实际上是内置在操作系统中的。单独的程序一般是让系统为它们完成大部分的工作。
在Java编程中,直到最近一直使用流的方式完成I/O。所有I/O都被视为单个的字节的移动,通过一个称为Stream的对象一次移动一个字节。流I/O用于与外部世界接触。它也在内部使用,用于将对象转换为字节,然后再转换回对象。
NIO与原来的I/O有同样的作用和目的,但是它使用不同的方式:块I/O,其效率要比流I/O高许多。
为什么要用NIO?
NIO的创建目的是为了让java程序员可以实现高速I/O而无需编写自定义的本机代码。NIO将最耗时的I/O操作(即填充和提取缓冲区)转移回操作系统,因而可以极大的提高速度。
流与块的比较
原来的I/O库与NIO最重要的区别是数据打包和传输方式。正如前面提到的,原来的I/O以流的方式处理数据,而NIO以块的方式处理数据。
面向流的I/O
系统一次一个字节地处理数据,一个输入流产生一个字节的数据,一个输出流消费一个字节的数据。为流式数据创建过滤器非常容易。链接几个过滤器,以便每个过滤器只负责单个复杂处理机制的一部分。但不利的一面是,面向流I/O通常相当慢。
面向块的I/O
系统以块的形式处理数据。每一个操作都在一步中产生或者消费一个数据块,按块处理数据的方式要比按流式处理数据的方式要快的多。但相对来说,没有面向流I/O简单。
通道与缓冲区
通道与缓冲区是NIO中的核心对象,mttud每个I/O操作中都要使用它们。
通道是对原I/O包中流的模拟,到任何目的地或者来自任何地方的所有数据都必须通过一个Channel对象。 一个Buffer对象实质上是一个容器对象,发送给一个通道的所有对象都必须首先放到缓冲区中;同样地,从通道中读取的任何数据都要读到缓冲区中。
缓冲区
缓冲区实质上是一个数组。通常它是一个字节数组,但是也可以使用其他种类的数组。但是一个缓冲区不仅仅是一个数组,其还提供了对数据的结构化访问,而且还可以跟踪系统的读写进程。
缓冲区类型
- ByteBuffer
- CharBuffer
- IntBuffer
- LongBuffer
- FloatBuffer
- DoubleBuffer
每一个Buffer类都是Buffer接口一个实例。除了ByteBuffer,每个Buffer类都有一个完全一样的操作,只是它们所处理的数据类型不一样,因为大多数标准I/O操作都要用到ByteBuffer,所以它具有所有共享的缓冲区操作以及一些特有的操作。
通道
channel是一个对象,可以通过它读取或写入数据。正如前面所说,所有数据都通过Buffer对象来处理。你永远不会将字节直接写入通道中,相反,你是将数据写入一个包含一个或多个字节的缓冲区中。同样,你不会直接从通道中读取字节,而是将数据读取到缓冲区中,再从缓冲区中读取这个字节。
通道类型
通道与流的不同之处就在于通道是双向的,而流只是在一个方向上进行移动,而通过可以用于读,写,或者同时读写。
NIO中的读写
读写是I/O的基本过程。从一个通道中读取很简单:只需要创建一个缓冲然后让通道将数据读取到这个缓冲区中。写入也很简单:创建一个缓冲区,用数据填充它,然后让通道用这些数据来执行写入操作。
从文件中读取
读取文件涉及三个步骤:
- 1获得Channel
RandomAccessFile aFile = new RandomAccessFile("testNIO.in", "rw");
FileChannel inChannel = aFile.getChannel();
- 2 创建Buffer
ByteBuffer buf = ByteBuffer.allocate(48);
- 3 将数据从Channel读到到Buffer
inChannel.read(buf);
大家可能会注意到,我们不需要告诉通道要读多少数据到缓冲区,每一个缓冲区都有复杂的内部统计机制,它会跟踪已经读了多少数据以及还有多少空间可以容纳更多的数据。
写入文件
在NIO中写入文件类似于从文件中读取,首先还是获得通道。
RandomAccessFile toFile = new RandomAccessFile("testNIO1.out", "rw");
FileChannel outChannel = toFile.getChannel();
接着创建缓冲区,并把数据写入缓冲区中。
String newData = "New String to write to File!";
ByteBuffer buf = ByteBuffer.allocate(50);
buf.clear();
buf.put(newData.getBytes());
buf.flip();
最后就是将缓冲区中的内容写入通道中。
toChannel.write(buf);
在这里同样,不需要告诉通道要写入多少数据。
读写结合
下面给出一个读写结合的例子,目的是将一个文件中的内容复制到另一个文件中,同样分为三个基本操作:
- 1 创建一个Buffer
ByteBuffer buf = ByteBuffer.allocate(50);
- 2 从源文件中将数据读取到这个缓冲区中
inChannel.read(buf);
- 3 将缓冲区中的数据写入目标文件中
outChannel.write(buf);
注意,这个过程不断重复 读,写,读,写,直到源文件结束。
通道转移
上面的例子给出了普通的复制文件的思路,其实,如果是FileChannel的话,还可以利用transferFrom()和transferTo()方法来完成文件的复制。下面直接给出这两种方法的用法:
/**
* FileChannel的transferFrom()方法可以将数据从源通道传输到FileChannel中
*/
private void channelTransferFrom() {
try {
RandomAccessFile fromFile = new RandomAccessFile("testNIO1.in",
"rw");
FileChannel inChannel = fromFile.getChannel();
RandomAccessFile toFile = new RandomAccessFile("testNIO1.out", "rw");
FileChannel outChannel = toFile.getChannel();
long position = 0;
long count = inChannel.size();
outChannel.transferFrom(inChannel, position, count);
fromFile.close();
toFile.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* transferTo()方法将数据从FileChannel传输到其他的channel中
*/
private void channelTransferTo() {
try {
RandomAccessFile fromFile = new RandomAccessFile("testNIO2.in",
"rw");
FileChannel inChannel = fromFile.getChannel();
RandomAccessFile toFile = new RandomAccessFile("testNIO2.out", "rw");
FileChannel outChannel = toFile.getChannel();
long position = 0;
long count = inChannel.size();
inChannel.transferTo(position, count, outChannel);
fromFile.close();
toFile.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
可以发现两个方法的用法极其相似。
这里只是给出了NIO的一些基础,需要深入了解的可以看:http://ifeve.com/overview/
本文所涉及到的全部代码:
package com.jesson.mianshi.nio;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class NIODemo {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
NIODemo nd = new NIODemo();
// nd.fileChannelRead();
// nd.fileChannelWrite();
// nd.channelTransferFrom();
// nd.channelTransferTo();
nd.copyFile();
}
/**
* NIO读
*/
private void fileChannelRead() {
try {
RandomAccessFile aFile = new RandomAccessFile("testNIO.in", "rw");
FileChannel inChannel = aFile.getChannel();
ByteBuffer buf = ByteBuffer.allocate(48);
int byteRead = inChannel.read(buf);
while (byteRead != -1) {
System.out.println("Read " + byteRead);
buf.flip();
while (buf.hasRemaining()) {
System.out.print((char) buf.get());
}
buf.clear();
byteRead = inChannel.read(buf);
System.out.println("\n" + byteRead);
}
aFile.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 向FileChannel写入数据
*/
private void fileChannelWrite() {
String newData = "New String to write to File!";
RandomAccessFile toFile = null;
try {
toFile = new RandomAccessFile("testNIO.out", "rw");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
FileChannel toChannel = toFile.getChannel();
ByteBuffer buf = ByteBuffer.allocate(50);
buf.clear();
buf.put(newData.getBytes());
buf.flip();
while (buf.hasRemaining()) {
try {
toChannel.write(buf);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
toFile.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* FileChannel的transferFrom()方法可以将数据从源通道传输到FileChannel中
*/
private void channelTransferFrom() {
try {
RandomAccessFile fromFile = new RandomAccessFile("testNIO1.in",
"rw");
FileChannel inChannel = fromFile.getChannel();
RandomAccessFile toFile = new RandomAccessFile("testNIO1.out", "rw");
FileChannel outChannel = toFile.getChannel();
long position = 0;
long count = inChannel.size();
outChannel.transferFrom(inChannel, position, count);
fromFile.close();
toFile.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* transferTo()方法将数据从FileChannel传输到其他的channel中
*/
private void channelTransferTo() {
try {
RandomAccessFile fromFile = new RandomAccessFile("testNIO2.in",
"rw");
FileChannel inChannel = fromFile.getChannel();
RandomAccessFile toFile = new RandomAccessFile("testNIO2.out", "rw");
FileChannel outChannel = toFile.getChannel();
long position = 0;
long count = inChannel.size();
inChannel.transferTo(position, count, outChannel);
fromFile.close();
toFile.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 复制文件
*/
private void copyFile() {
try {
ByteBuffer buf = ByteBuffer.allocate(50);
RandomAccessFile fromFile = new RandomAccessFile("testNIO3.in",
"rw");
FileChannel inChannel = fromFile.getChannel();
RandomAccessFile toFile = new RandomAccessFile("testNIO3.out", "rw");
FileChannel outChannel = toFile.getChannel();
int byteRead = inChannel.read(buf);
while (byteRead != -1) {
buf.flip();
outChannel.write(buf);
buf.clear();
byteRead = inChannel.read(buf);
}
fromFile.close();
toFile.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
版权声明:本文为博主原创文章,未经博主允许不得转载。