一:概述
java中对文件的读写原始的实现是使用的BIO模式即读写流,java1.4后引入NIO,读取文件使用的是NIO的同步阻塞模式进行文件的读取,使用通道(channel)去读取一个文件资源,使用缓冲区去存放读写的数据,只不过 Channel本身不能直接访问数据,Channel 只能与Buffer 进行交互。
二:具体实现
(1)通道(channel)
通道是文件读取的核心,通道类似于文件读取的流,每一个通道来确定一个文件的资源并且通 道是双向的,即可以读数据也可以写数据,通道的获取方式有多种方式,每种方式获取的通道具有自己不同的特性。
java给我们提供的关于文件的通道主要实现类有FileChannel类。通道的获取主要有主要以下几种方式:
**方式一:**通过文件的输入和输出流来获取通道,但该种方式获取的通道只能读或者取,如果读的流获取的通道只能去读数据,写数据的话会报权限不足的异常,代码如下:
FileInputStream input = new FileInputStream(new File("D:\\from.txt"));
FileChannel readChannel = input.getChannel();
FileOutputStream out = new FileOutputStream(new File("D:\\to.txt"));
FileChannel writeChannel = out.getChannel();
**方式二:**通过RandomAccessFile来获取通道, 通过这种方式获取的通道既可以读又可以写,而且构造函数中还可以进行模式的匹配进行读写模式的切换,代码如下:
RandomAccessFile fromR = new RandomAccessFile(new File("D:\\from.txt"), "rw");
FileChannel fromChannel = from.getChannel();
其他方式:获取通道的其他方式是使用 Files 类的静态方法 newByteChannel() 获取字节通道。或者通过通道的静态方法 open() 打开并返回指定通道,或者通过Channels工具类来获取通道,代码如下:
FileInputStream in1 = new FileInputStream(from);
Channel channel = Channels.newChannel(in1);
(2) 缓冲区(Buffer)
缓冲区是用于存放通道读取的数据和通道将要读入的数据。
缓冲区有几个重要的概念如下:
容量(capacity) :表示 Buffer 最大数据容量,缓冲区容量不能为负,并且创建后不能更改。
限制 (limit) :第一个不应该读取或写入的数据的索引,即位于 limit 后的数据不可读写。缓冲区 的限制 不能为负,并且不能大于其容量。
位置 (position):下一个要读取或写入的数据的索引。缓冲区的位置不能为负,并且不能大于其限 制。
标记 (mark) 与重置 (reset) :标记是一个索引,通过 Buffer 中的 mark() 方法指定 Buffer 中一个特定的 position,之后可以通过调用 reset() 方法恢复到这个 position.
java中关于缓冲区有以下几个主要的实现类,并且这些类中都有各自的静态方法来获取缓冲区。
主要实现类:
ByteBuffer ,(常用的,分直接缓冲区和非直接缓冲区,前者用allocate()方法获取后者用 allocateDirect()方法来获取)
CharBuffer
DoubleBuffer
FloatBuffer
IntBuffer
LongBuffer
ShortBuffer
主要的方法:
Buffer 所有子类提供了两个用于数据操作的方法:get()与 put() 方法
get() :读取单个字节
get(byte[] dst):批量读取多个字节到 dst 中
get(int index):读取指定索引位置的字节(不会移动 position)
放到 入数据到 Buffer 中
put(byte b):将给定单个字节写入缓冲区的当前位置
put(byte[] src):将 src 中的字节写入缓冲区的当前位置
put(int index, byte b):将指定字节写入缓冲区的索引位置(不会移动position)
三:文件读写实操
(1)读
使用通道对文件数据进行读取,代码如下:
public static void main(String[] args) throws IOException {
File from = new File("D:\\from.txt");
File to = new File("D:\\to.txt");
FileInputStream in = new FileInputStream(from);
FileOutputStream out = new FileOutputStream(to);
nioRead(in);
//nioWrite(out);
//nioReadFensan(in);
//nioWriteJuce(out);
//RandomAccessFile fromR = new RandomAccessFile(from, "rw");
//RandomAccessFile toR = new RandomAccessFile(to, "rw");
//transFrom(fromR,toR);
//transTo(fromR,toR);
}
/**
* 读
*
* @param input
* @throws IOException
*/
public static void nioRead(FileInputStream input) throws IOException {
FileChannel readChannel = input.getChannel();
// 直接字节缓冲区
// ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
// 非直接字节缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
int capacity = byteBuffer.capacity();
System.out.println("字节缓冲区的容量:" + capacity);
Boolean direct = byteBuffer.isDirect();
System.out.println("是否直接缓冲区:" + direct);
int position = byteBuffer.position();
System.out.println("缓存区中指针的位置:" + position);
int limit = byteBuffer.limit();
System.out.println("缓存区的操作上限:" + limit);
byteBuffer.position(10);
int nextposition = byteBuffer.position();
System.out.println("缓存区中指定指针的位置:" + nextposition);
// 开始读取,a为返回的字节数,如果缓冲区中没有容量则返回0,如果要读的文件中没有数据返回为-1
int msg = readChannel.read(byteBuffer);
System.out.println("读取的字节数:" + msg);
System.out.println("读取的数据:" + new String(byteBuffer.array()).trim());
}
控制台输出:
字节缓冲区的容量:1024
是否直接缓冲区:false
缓存区中指针的位置:0
缓存区的操作上限:1024
缓存区中指定指针的位置:10
读取的字节数:11
读取的数据:fromfilemsg
(2)写
向文件中写入数据,代码如下:
public static void main(String[] args) throws IOException {
File from = new File("D:\\from.txt");
File to = new File("D:\\to.txt");
FileInputStream in = new FileInputStream(from);
FileOutputStream out = new FileOutputStream(to);
//nioRead(in);
nioWrite(out);
//nioReadFensan(in);
//nioWriteJuce(out);
//RandomAccessFile fromR = new RandomAccessFile(from, "rw");
//RandomAccessFile toR = new RandomAccessFile(to, "rw");
//transFrom(fromR,toR);
//transTo(fromR,toR);
}
/**
* 写
*
* @param out
* @throws IOException
*/
public static void nioWrite(FileOutputStream out) throws IOException {
FileChannel writeChannel = out.getChannel();
// 直接字节缓冲区
// ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
// 非直接字节缓冲区
String str = new String("123");
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
byteBuffer.put(str.getBytes());
// 向缓冲区中放入数据时position会改变,因此用flip方法将缓冲区位置值0
byteBuffer.flip();
int msg = writeChannel.write(byteBuffer);
System.out.println("写入的字节数" + msg);
}
控制台输出:
写入的字节数3
(3)分散读取
分散读取是将从通道中读入的数据放入一个缓冲区数组中,代码如下:
public static void main(String[] args) throws IOException {
File from = new File("D:\\from.txt");
File to = new File("D:\\to.txt");
FileInputStream in = new FileInputStream(from);
FileOutputStream out = new FileOutputStream(to);
//nioRead(in);
//nioWrite(out);
nioReadFensan(in);
//nioWriteJuce(out);
//RandomAccessFile fromR = new RandomAccessFile(from, "rw");
//RandomAccessFile toR = new RandomAccessFile(to, "rw");
//transFrom(fromR,toR);
//transTo(fromR,toR);
}
/**
* 分散和聚簇,将数据读到多个缓冲区中,从多个缓冲区中写数据
*
* @param input
* @throws IOException
*/
public static void nioReadFensan(FileInputStream input) throws IOException {
FileChannel readChannel = input.getChannel();
// 直接字节缓冲区
// ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
// 非直接字节缓冲区
ByteBuffer byteBuffer1 = ByteBuffer.allocate(7);
ByteBuffer byteBuffer2 = ByteBuffer.allocate(7);
ByteBuffer[] arr = { byteBuffer1, byteBuffer2 };
// 开始读取,a为返回的字节数,如果缓冲区中没有容量则返回0,
Long msg = readChannel.read(arr);
System.out.println("缓冲区1中读取到的数据:" +new String( byteBuffer1.array()));
System.out.println("缓冲区2中读取到的数据:" +new String( byteBuffer2.array()).trim());
System.out.println("读取的字节数a:" + msg);
}
控制台输出:
缓冲区1中读取到的数据:fromfil
缓冲区2中读取到的数据:emsg
读取的字节数a:11
(4)聚集写入
聚集写入是将缓冲区数组中的数据写到文件中,代码如下:
public static void main(String[] args) throws IOException {
File from = new File("D:\\from.txt");
File to = new File("D:\\to.txt");
FileInputStream in = new FileInputStream(from);
FileOutputStream out = new FileOutputStream(to);
//nioRead(in);
//nioWrite(out);
//nioReadFensan(in);
nioWriteJuce(out);
//RandomAccessFile fromR = new RandomAccessFile(from, "rw");
//RandomAccessFile toR = new RandomAccessFile(to, "rw");
//transFrom(fromR,toR);
//transTo(fromR,toR);
}
/**
*
* 聚集写入
* @param out
* @throws IOException
*/
public static void nioWriteJuce(FileOutputStream out) throws IOException {
FileChannel writeChannel = out.getChannel();
// 直接字节缓冲区
// ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
// 非直接字节缓冲区
String str = new String("123");
String str2 = new String("456");
ByteBuffer byteBuffer1 = ByteBuffer.allocate(1024);
ByteBuffer byteBuffer2 = ByteBuffer.allocate(1024);
byteBuffer1.put(str.getBytes());
byteBuffer2.put(str2.getBytes());
byteBuffer1.flip();
byteBuffer2.flip();
ByteBuffer[] arr = { byteBuffer1, byteBuffer2 };
// 向缓冲区中放入数据时position会改变,因此用flip方法将缓冲区位置值0
Long msg = writeChannel.write(arr);
System.out.println("写入的字节数" + msg);
}
控制台输出:
写入的字节数6
(5)通道传输
通道传输包括两种,一种是当前通道从其他通道中获取数据,一种是当前通道向另一通道传输数据。通道传输是将文件中的数据从一个文件直接传到另一个通道。代码如下:
public static void main(String[] args) throws IOException {
File from = new File("D:\\from.txt");
File to = new File("D:\\to.txt");
FileInputStream in = new FileInputStream(from);
FileOutputStream out = new FileOutputStream(to);
//nioRead(in);
//nioWrite(out);
//nioReadFensan(in);
//nioWriteJuce(out);
RandomAccessFile fromR = new RandomAccessFile(from, "rw");
RandomAccessFile toR = new RandomAccessFile(to, "rw");
transFrom(fromR,toR);
transTo(fromR,toR);
}
/**
* 通道传输,回直接将from文件中的数据传输到to的文件中去
* @param from
* @param to
* @throws IOException
*/
public static void transFrom(RandomAccessFile from,RandomAccessFile to) throws IOException{
FileChannel fromChannel = from.getChannel();
FileChannel toChannel = to.getChannel();
Long a = toChannel.transferFrom(fromChannel, 0, fromChannel.size());
System.out.println("传入的字节数" + a);
}
/**
* 通道传输,回直接将from文件中的数据传输到to的文件中去
* @param from
* @param to
* @throws IOException
*/
public static void transTo(RandomAccessFile from,RandomAccessFile to) throws IOException{
FileChannel fromChannel = from.getChannel();
FileChannel toChannel = to.getChannel();
Long a = fromChannel.transferTo(0, fromChannel.size(), toChannel);
System.out.println("传入的字节数" + a);
}
控制台输出:
传入的字节数11
传入的字节数11