java nio 基础之Buffer 用法分析

阿里 阅读:296 2021-03-31 21:15:32 评论:0

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); 
	} 
 
} 



声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

发表评论
搜索
KIKK导航

KIKK导航

排行榜
关注我们

一个IT知识分享的公众号