面向流的 I/O 系统被视为单个的字节的移动,通过一个称为 Stream 的对象一次移动一个字节。流 I/O 用于与外部世界接触。它也在内部使用,用于将对象转换为字节,然后再转换回对象。
以下是用字节读出文件的例子:
publicstaticvoid readFileByBytes(String fileName)
{
File file=new File(fileName);
FileInputStream in = null;
try {
in = new FileInputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
try {
while ((in.read())!=-1) {}
if(in!=null)
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
用字符读出文件只需将上面程序的
FileInputStream in = null;
in = new FileInputStream(file);
改为:
Reader reader=null;
reader=new InputStreamReader(new FileInputStream(file));
由此可见,用字符读出文件是对用字节读出文件的一种包装。
对用字符读文件加缓冲区提高速度可写为:
reader=new BufferedReader(new InputStreamReader(new FileInputStream(file)));
NIO 与原来的 I/O 有同样的作用和目的,两者之间最重要的区别是数据打包和传输的方式。NIO 以块的方式处理数据,块 I/O 的效率可以比流 I/O 高许多。但是面向块的 I/O 缺少一些面向流的 I/O 所具有的优雅性和简单性。NIO采用Channel和Buffer结合的方式实现高速I/O。
NIO 的创建目的是为了让 Java 程序员可以实现高速 I/O 而无需编写自定义的本机代码。NIO 将最耗时的 I/O 操作(即填充和提取缓冲区)转移回操作系统,因而可以极大地提高速度。
NIO读取文件涉及三个步骤:(1) 从FileInputStream获取 Channel,(2) 创建 Buffer,(3) 将数据从 Channel读到 Buffer 中。
例如:
publicstaticvoid readFileByChannel(String fileName)
{
FileInputStream fin = null;
try {
fin = new FileInputStream(fileName);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
FileChannel fc=fin.getChannel();
ByteBuffer buffer=ByteBuffer.allocate(1024*10);
try {
while (true)
{
buffer.clear(); //clear()清除此缓冲区
int r = fc.read( buffer );
buffer.flip();//flip()反转此缓冲区
while (buffer.hasRemaining()){
buffer.get();
}
if (r==-1) {
break;
}
}
} catch (IOException e)
e.printStackTrace();
}
}
为了加快I/O速度,还可以采用一下两种方式:
1) 1)直接缓冲区;
2) 2)内存映射文件I/O;
实现直接缓冲区需将上面代码中的
ByteBuffer buffer=ByteBuffer.allocate(1024*10);
改为:
ByteBuffer buffer=ByteBuffer.allocateDirect(1024*10);
用以上五种方式读取22.8MB的txt文件,对运行时间进行比较,结果如下:
结果一:
ReadFileByBytes: 34138ms
ReadFileByChars: 5296ms
ReadFileByBufferedReader: 3156ms
ReadFileByChannel: 312ms
ReadFileByChannelBufferDirect: 313ms
结果二:
ReadFileByBytes: 32935ms
ReadFileByChars: 5202ms
ReadFileByBufferedReader: 3125ms
ReadFileByChannel: 359ms
ReadFileByChannelBufferDirect: 297ms
对比发现,速度由慢到快依次是:
Bytes,Chars,BufferedReader,Channel,ChannelBufferDirect
此外,后两种方式的实现速度变化较大是因为:
1)用直接字节缓冲区,虚拟机都会尽量避免将缓冲区的内容复制到中间缓冲区中(或从中间缓冲区中复制内容)。
2 2)缓冲区进行分配和取消分配所需成本通常高于非直接缓冲区。
3)本文件太小,体现不出直接字节缓冲区的优势。
对于内存映射文件I/O尽管创建内存映射文件相当简单,但是向它写入可能是危险的。仅只是改变数组的单个元素这样的简单操作,就可能会直接修改磁盘上的文件。修改数据与将数据保存到磁盘是没有分开的。