java nio Buffer的本质是“数据通过通道写入缓存区中,从缓存中读取数据到通道中”。
总结Buffer 基本用法(本段参考至:Java NIO系列教程(三)Buffer)
1、写入数据到Buffer
2、调用filp(),实现读取模式和写入模式切换
3、读取Buffer 数据
4、调用clear()或者compact()方法,清理已经读取的相关信息。
对于以上步骤有一个详细版本讲解:
1、当向buffer写入数据时,buffer会记录下写了多少数据。一旦要读取数据,需要通过flip()方法将Buffer从写模式切换到读模式。在读模式下,可以读取之前写入到buffer的所有数据。
2、一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。有两种方式能清空缓冲区:调用clear()或compact()方法。clear()方法会清空整个缓冲区。compact()方法只会清除已经读过的数据。任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面。
简单的demo 实例:将‘demo.txt’文本中的内容写入缓存中,读取缓存中的内容,使用filp()切换(读写模式)方法,再次使用通道是否还可以读取缓存中的数据信息。
package com.nio.one;
import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class ChannelModelSelectDemo {
private String path;
private Boolean bool;
public ChannelModelSelectDemo(String path, Boolean bool) {
this.path = path;
this.bool = bool;
}
public Boolean getBool() {
return bool;
}
public void setBool(Boolean bool) {
this.bool = bool;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
ChannelModelSelectDemo demo = new ChannelModelSelectDemo("D:\\demo.txt", true);
FileInputStream in = new FileInputStream(new File(demo.path));
FileChannel inChannel = in.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(24);
// 通过通道将数据读取到缓存中
int num = inChannel.read(buffer);
while (num != -1) {
// buffer 读写模式进行切换(切换为读取模式)
buffer.flip();
// 读取已经写入缓存的数据
System.out.println("读取内容是:" + new String(buffer.array()));
// 清空已经读取到buffer 缓存中的数据(切换为写入模式)
buffer.clear();
// 通过通道将数据写入缓存中
num = inChannel.read(buffer);
}
inChannel.close();
in.close();
}
}
Buffer 的三大属性值:capacity、position和limit
java nio 中的Buffer,在我的眼中看:“Buffer 只是一块既可以读取数据也可以写入数据的缓存地址空间”。
下面关于Buffer 三大属性值的描述参考:Java NIO 系列教程(三) Buffer
position和limit的含义取决于Buffer处在读模式还是写模式。不管Buffer处在什么模式,capacity的含义总是一样的。
这里有一个关于capacity,position和limit在读写模式中的说明,详细的解释在插图后面。
capacity
作为一个内存块,Buffer有一个固定的大小值,也叫“capacity”.你只能往里写capacity个byte、long,char等类型。一旦Buffer满了,需要将其清空(通过读数据或者清除数据)才能继续写数据往里写数据。
position
当你写数据到Buffer中时,position表示当前的位置。初始的position值为0.当一个byte、long等数据写到Buffer后, position会向前移动到下一个可插入数据的Buffer单元。position最大可为capacity – 1.
当读取数据时,也是从某个特定位置读。当将Buffer从写模式切换到读模式,position会被重置为0. 当从Buffer的position处读取数据时,position向前移动到下一个可读的位置。
limit
在写模式下,Buffer的limit表示你最多能往Buffer里写多少数据。 写模式下,limit等于Buffer的capacity。
当切换Buffer到读模式时, limit表示你最多能读到多少数据。因此,当切换Buffer到读模式时,limit会被设置成写模式下的position值。换句话说,你能读到之前写入的所有数据(limit被设置成已写数据的数量,这个值在写模式下就是position)
demo 实例:输出创建数据缓存三大属性,输出数据缓存写入模式式三大属性值,输出数据缓存读取模式三大属性值。
package com.nio.one;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class FileCopyUtil {
private String to;
private String from;
public FileCopyUtil(String to, String from) {
this.to = to;
this.from = from;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public String getFrom() {
return from;
}
public boolean copy() throws Exception {
boolean target = false;
FileInputStream in = new FileInputStream(new File(this.to));
FileOutputStream out = new FileOutputStream(new File(this.from));
//获取文件读取通道
FileChannel inChannel = in.getChannel();
//获取文件写入通道
FileChannel outChannel = out.getChannel();
//定义缓存区大小
ByteBuffer buffer = ByteBuffer.allocate(1024);
System.out.println("创建缓存数据capacity size is:"+buffer.capacity());
System.out.println("创建缓存数据position size is:"+buffer.position());
System.out.println("创建缓存数据limit size is:"+buffer.limit());
while (true) {
// 清空缓存空间
buffer.clear();
//读取数据到缓存区
int num = inChannel.read(buffer);
System.out.println("缓存数据读取模式capacity size is:"+buffer.capacity());
System.out.println("缓存数据读取模式position size is:"+buffer.position());
System.out.println("缓存数据读取模式limit size is:"+buffer.limit());
if (num == -1) {
break;
}
//将buffer 指针指向头部
buffer.flip();
//将缓存数据写入通道
outChannel.write(buffer);
System.out.println("缓存数据写入模式capacity size is:"+buffer.capacity());
System.out.println("缓存数据写入模式position size is:"+buffer.position());
System.out.println("缓存数据写入模式limit size is:"+buffer.limit());
}
target = true;
return target;
}
public void setFrom(String from) {
this.from = from;
}
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
FileCopyUtil util = new FileCopyUtil("D:\\demo.txt", "D:\\one.txt");
boolean target = util.copy();
System.out.println("file copy is successful:"+target);
}
}
输出结果:
创建缓存数据capacity size is:1024
创建缓存数据position size is:0
创建缓存数据limit size is:1024
缓存数据读取模式capacity size is:1024
缓存数据读取模式position size is:166
缓存数据读取模式limit size is:1024
缓存数据写入模式capacity size is:1024
缓存数据写入模式position size is:166
缓存数据写入模式limit size is:166
缓存数据读取模式capacity size is:1024
缓存数据读取模式position size is:0
缓存数据读取模式limit size is:1024
file copy is successful:true
Buffer对象分配:每个继承自Buffer类的子类都有一个allocate方法。
示例代码一:分配一个48字节大小的ByteBuffer.
ByteBuffer buffer = ByteBuffer.allocate(48);
示例代码二:分配一个1024字符大小的CharBuffer.
CharBuffer buffer = CharBuffer.allocate(1024);
Buffer 读取数据方式:
第一种:通过channel.read(buffer)方法。
示例代码: filerChannel.read(buffer) //这句代码的意思是:文件通道将读取到的数据存入buffer缓存数据中。
第二种:通过buffer.put()方法。
示例代码:buffer.put("传递参数"); //这句代码的意思是:向buffer缓存数据中put(添加)相关数据;
Buffer 写入数据方式:
第一种:通过channel.write(buffer)方法。
示例代码:fileChannel.write(buffer) //这句话的意思是:buffer缓存中的数据,通过文件管道写入指定位置。
第二种:通过buffer.get()方法
示例代码:buffer.get(); //这句代码的意思是:通过get方法获取buffer中的缓存数据。
Buffer 的scatter 和 gather
Java NIO开始支持scatter/gather,scatter/gather用于描述从Channel(译者注:Channel在中文经常翻译为通道)中读取或者写入到Channel的操作。
分散(scatter)从Channel中读取是指在读操作时将读取的数据写入多个buffer中。因此,Channel将从Channel中读取的数据“分散(scatter)”到多个Buffer中。
聚集(gather)写入Channel是指在写操作时将多个buffer的数据写入同一个Channel,因此,Channel 将多个Buffer中的数据“聚集(gather)”后发送到Channel。
scatter / gather经常用于需要将传输的数据分开处理的场合,例如传输一个由消息头和消息体组成的消息,你可能会将消息体和消息头分散到不同的buffer中,这样你可以方便的处理消息头和消息体。
Scattering Reads
Scattering Reads是指数据从一个channel读取到多个buffer中。如下图描述:
Gattering Writes
Gathering Writes是指数据从多个buffer写入到同一个channel。如下图描述:
示例代码:通过sactter和gather,完善并且修改文件CopyUtil .java 代码。
package com.nio.one;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class FileScatterGather {
private String to;
private String from;
public FileScatterGather(String to, String from) {
this.to = to;
this.from = from;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public String getFrom() {
return from;
}
public boolean copy() throws Exception {
boolean target = false;
FileInputStream in = new FileInputStream(new File(this.to));
FileOutputStream out = new FileOutputStream(new File(this.from));
//获取文件读取通道
FileChannel inChannel = in.getChannel();
//获取文件写入通道
FileChannel outChannel = out.getChannel();
//定义缓存区大小--one
ByteBuffer bufferone = ByteBuffer.allocate(48);
//定义缓存区大小--two
ByteBuffer buffertwo = ByteBuffer.allocate(48);
//定义缓存数组大小
ByteBuffer[] bufferArray = { bufferone, buffertwo };
while (true) {
// 清空缓存空间
bufferone.clear();
buffertwo.clear();
//读取数据到缓存区
long num = inChannel.read(bufferArray);
if (num == -1) {
break;
}
//将buffer 指针指向头部
bufferone.flip();
buffertwo.flip();
//将缓存数据写入通道
outChannel.write(bufferArray);
}
target = true;
return target;
}
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
FileScatterGather util = new FileScatterGather("D:\\demo.txt", "D:\\one.txt");
boolean target = util.copy();
System.out.println("file copy is successful:"+target);
}
}
今天在回顾java.nio 基础知识时参考:
Java NIO 基础教程,发现自己漏看了一个《通道数据之间的传递》。
在这里重新补充一下:通道传递:简单的来说就是两个通过之间的数据传递,实现的方法主要是channel.transferFrom()和channel.transferTo.
通道类存在这么简单的方法,我们的目标持续调整,我修改我之间FileCopy.java (文件拷贝)类,通过通道之间的数据传递实现文件拷贝功能。
实例代码:
package com.nio.one;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class TwoFileChannelTrans {
private String to;
private String from;
public TwoFileChannelTrans(String to, String from) {
this.to = to;
this.from = from;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public String getFrom() {
return from;
}
public boolean copy() throws Exception {
boolean target = false;
FileInputStream in = new FileInputStream(new File(this.to));
FileOutputStream out = new FileOutputStream(new File(this.from));
//获取文件读取通道
FileChannel inChannel = in.getChannel();
//获取文件写入通道
FileChannel outChannel = out.getChannel();
long position = 0;
long count = inChannel.size();
//通道之间写入
//inChannel.transferTo(position, count, outChannel);
//通道之间读取
outChannel.transferFrom(inChannel, position, count);
target = true;
return target;
}
public void setFrom(String from) {
this.from = from;
}
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
TwoFileChannelTrans util = new TwoFileChannelTrans("D:\\demo.txt", "D:\\one.txt");
boolean target = util.copy();
System.out.println("file copy is successful:"+target);
}
}