java NIO (Java编程思想)

本文介绍了Java NIO的基本概念,包括通道和缓冲区的工作原理。通过示例代码展示了如何使用FileChannel进行文件操作,并比较了传统I/O与NIO在效率上的差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    jdK1.4的java.nio.*包中引入了JavaI/O类库,其目的在于提高速度。实际上,旧的I/O包已经使用nio重新实现过,以便充分利用这种速度提高,因此,即使我们不显示的用nio编写代码,也能从中受益。

    速度的提高来自于所使用的结构更加接近操作系统执行I/O的方式:通道和缓冲器。我们可以把它想象成一个煤矿,通道是一个包含煤层(数据)的矿藏,而缓冲器则是派送到矿藏的卡车。卡车载满煤炭而归,我们再从卡车上获得煤炭。也就是说,我们并没有直接和通道打交道,我们只是和缓冲器交互,并把缓冲器派送到通道。通道要么从缓冲器获得数据,要么向缓冲器发送数据。

    唯一直接与通道交互的缓冲器是ByteBuffer---也就是说,可以存储未加工字节的缓冲器。当我们查询JDK文档中的java.nio.ByteBuffer时,会发现它是相当基础的类:通过告知分配多少存储空间来创建一个ByteBuffer对象,并且还有一个方法选择集,用于以原始的字节形式或基本数据类型输出和读取数据。但是,没有办法输出或读取对象,即使是字符串对象也不行。这种处理虽然很低级,但却正好,因为这是大多数操作系统中更有效的映射方式。

    旧的IO类库有三个类被修改,用以产生FileChannel(用于读取、写入、映射和操作文件的通道)。这三个被修改的类是FileInputStreamFileOutputStream以为用于既读又写的RandomAccessFile。注意这些是字节操纵流,与低层的nio性质一致。ReaderWriter这种字符模式类不能用于产生通道;但是java.nio.channels.Channels类提供类实用方法,用以在通道中产生ReaderWriter

    下面的简单实例演示了上面三种类型的流,用以产生可以写的、可读可写的及可读的通道。

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 {
 private static final int BSIZE = 1024;
 public static void main(String[] args) throws IOException{
  //write a file
  FileChannel fc = new FileOutputStream("data.txt").getChannel();
  fc.write(ByteBuffer.wrap("hello world".getBytes()));
  fc.close();
  //加到文件末尾
  fc = new RandomAccessFile("data.txt", "rw").getChannel();
  fc.position(fc.size()); //移到文件末尾用position方法
  fc.write(ByteBuffer.wrap(" hello world again".getBytes()));
  fc.close();
  //读取文件
  fc = new FileInputStream("data.txt").getChannel();
  ByteBuffer buffer = ByteBuffer.allocate(BSIZE);
  fc.read(buffer); //通道从缓冲器中读取内容
  buffer.flip(); //读了之后要flip一下
  while(buffer.hasRemaining())
   System.out.print((char)buffer.get());
 }
}

    对于这里展示的任何流类,getChannel()将会产生一个FileChannel。通道是一种相当基础的东西,可以向它传送关于读写的ByteBuffer。

    将字节存放于ByteBuffer的方法之一是:使用“put”方法直接填充,填入一个或者多个字节,或基本数据类型的值。也可以使用wrap()方法将已经存在的字节数组“包装”到ByteBuffer中。

    对于只读访问,我们必须显式的使用静态allocate()方法来分配ByteBuffer。nio的目标就是快读移动大量数据,因此ByteBuffer的大小就显得尤为重要。甚至达到更高的速度也有可能,方法就是allocateDirect而不是allocate,以产生一个与操作系统更高耦合性的直接缓冲器。但是,这种分配开支会更大,并且具体实现也随着操作系统的不同而不同。

    一旦调用read()来告知FileChannelByteBuffer存储字节,就必须调用缓冲器上的flip(),让它做好让别人读取字节的准备。如果我们打算使用缓冲器执行进一步的read()操作,我们必须得调用clear()来为每个read()做好准备。

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class ChannelCopy {
 private static final int BSIZE = 1024;
 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(),
   out = new FileOutputStream(args[1]).getChannel();
  ByteBuffer buffer = ByteBuffer.allocate(BSIZE);
  while(in.read(buffer) != -1){
   buffer.flip(); //prepare for writing
   out.write(buffer);
   buffer.clear(); //prepare for reading
  }
 }
}

    每次read()操作之后,就会将数据输入到缓冲器中,flip()则是准备缓冲器以便她的信息可以由write()提取。write()操作之后,信息扔存在缓冲器中,接着clear()操作则对所有的内部指针重新安排,以便缓冲器在另一个read()操作期间能够做好接受数据的准备。

    然而,上面那个程序并不是处理此类操作的理想方式。特殊方法transferTo()和transferFrom()则允许我们将一个通道和另一个通道直接相连。

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;

public class TransferTo {
 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(),
   out = new FileOutputStream(args[1]).getChannel();
  in.transferTo(0, in.size(), out);
  //或者
  out.transferFrom(in, 0, in.size());
 }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值