Java入门系列-23-NIO(使用缓冲区和通道对文件操作)
NIO 是什么
java.nio全称java non-blocking(非阻塞) IO(实际上是 new io),是指jdk1.4 及以上版本里提供的新api(New IO) ,为所有的原始类型(boolean类型除外)提供缓存支持的数据容器,使用它可以提供非阻塞式的高伸缩性网络。
NIO与IO的区别
IO | NIO |
---|---|
面向流(Stream Oriented) | 面向缓冲区(Buffer Oriented) |
阻塞IO(Blocking IO) | 非阻塞(Non Blocking IO) |
无 | 选择器(Selectors) |
NIO系统的核心是:通道(Channel)和缓冲区(Buffer)
缓冲区(Buffer)
位于 java.nio 包,所有缓冲区都是 Buffer 抽象类的子类,使用数组对数据进行缓冲。
除了 boolean 类型,Buffer 对每种基本数据类型都有针对的实现类:
- ByteBuffer
- CharBuffer
- ShortBuffer
- IntBuffer
- LongBuffer
- FloatBuffer
- DoubleBuffer
创建缓冲区通过 xxxBuffer.allocate(int capacity)方法
ByteBuffer buf1 = ByteBuffer.allocate(512);
LongBuffer buf2 = LongBuffer.allocate(1024);
……
缓冲区的属性
容量(capacity):表示缓冲区存储数据的最大容量,不能为负数,创建后不可修改。
限制:第一个不可以读取或写入的数据的索引,即位于 limit 后的数据不能读写。不能为负数,不能大于容量。
位置(position):下一个要读取或写入的数据的索引,位置不能为负数,不能大于 limit
标记(mark):标记是一个索引,通过 Buffer 中的 mark() 方法指 Buffer 中一个特定的 position,之后可以通过 reset() 方法回到这个 postion。
Buffer 的常用方法
方法名称 | 说明 |
---|---|
Buffer clear() | 清空缓冲区并返回对缓冲区的引用 |
Buffer flip() | 将缓冲区的 limit 设置为当前位置,并将当前位置重置为0 |
int capacity() | 返回 Buffer 的容量大小 |
boolean hasRemaining() | 判断缓冲区是否还有元素 |
int limit() | 返回 限制的位置 |
Buffer limit(int n) | 将设置缓冲区界限为 n,并返回一个具有新 limit 的缓冲区对象 |
Buffer mark() | 对缓冲区设置标记 |
int position() | 返回缓冲区的当前位置 position |
Buffer position(int n) | 将设置缓冲区的当前位置为 n,并返回修改后的 Buffer 对象 |
int remaining() | 返回 position 和 limit 之间的元素个数 |
Buffer reset() | 将位置 position 转到以前设置的 mark 所在的位置 |
Buffer rewind() | 将位置设置为 0,取消设置的 mark |
Buffer 所有子类提供了两个操作的数据的方法:get() 方法和 put() 方法
缓冲区存取数据操作
package testnio;
import java.nio.ByteBuffer;
public class TestBuffer1 {
public static void main(String[] args) {
testuse();
}
public static void testuse() {
//1.分配一个指定大小的缓冲区
ByteBuffer buf=ByteBuffer.allocate(1024);
System.out.println("---------------allocate()----------------");
System.out.println(buf.position());
System.out.println(buf.limit());
System.out.println(buf.capacity());
//2.利用 put() 存入数据到缓冲区中
String str="hello";
//将字符串转为 byte 数组存入缓冲区
buf.put(str.getBytes());
System.out.println("---------------put()----------------");
System.out.println(buf.position());
System.out.println(buf.limit());
System.out.println(buf.capacity());
//3.切换读取数据模式
buf.flip();
System.out.println("---------------flip()----------------");
System.out.println(buf.position());
System.out.println(buf.limit());
System.out.println(buf.capacity());
//4.利用get() 读取缓冲区中的数据
byte[] data=new byte[buf.limit()];
System.out.println("---------------get()----------------");
buf.get(data);
System.out.println(new String(data,0,data.length));
System.out.println(buf.position());
System.out.println(buf.limit());
System.out.println(buf.capacity());
//5.rewind() 重复读
buf.rewind();
System.out.println("---------------rewind()----------------");
System.out.println(buf.position());
System.out.println(buf.limit());
System.out.println(buf.capacity());
//6.clear() 清空缓冲区,但缓冲区中的数据依然存在
buf.clear();
System.out.println("---------------clear()----------------");
System.out.println(buf.position());
System.out.println(buf.limit());
System.out.println(buf.capacity());
System.out.println((char)buf.get());
}
}
使用 mark()方法标记
package testnio;
import java.nio.ByteBuffer;
public class TestBuffer2 {
public static void main(String[] args) {
testmark();
}
public static void testmark() {
String str="jikedaquan.com";
//创建缓冲区
ByteBuffer buf=ByteBuffer.allocate(1024);
//存入数据
buf.put(str.getBytes());
//切换模式
buf.flip();
//临时数组用于接收缓冲区获取的数据,长度与缓冲区 limit 一值
byte[] data=new byte[buf.limit()];
//获取缓冲区的数据从0开始获取4个,存入 data 数组中
buf.get(data, 0, 4);
//将数组转为字符串打印
System.out.println(new String(data,0,4));
//打印 position
System.out.println(buf.position());
//标记
buf.mark();
System.out.println("---------------再次获取----------------");
//从索引4开始,获取6个字节(余下数据)
buf.get(data, 4, 6);
System.out.println(new String(data,4,6));
System.out.println(buf.position());
//恢复到标记位置
buf.reset();
System.out.println("---------------reset()----------------");
System.out.println(buf.position());
//判断缓冲区是是有还有剩余数据
if (buf.hasRemaining()) {
//获取缓冲区中可以操作的数量
System.out.println("可操作数量:"+buf.remaining());
}
}
}
mark <= position <= limit <= capacity
虽然使用了缓冲区提高了一定的IO速度,但这样的效率仍然不是最高的。非直接缓冲区在与物理磁盘操作中需要经过内核地址空间copy操作,直接缓冲区不经过copy操作,直接操作物理内存映射文件,
使用直接缓冲区将大大提高效率。
直接缓冲区进行分配和取消分配所需成本工厂高于非直接缓冲区,一般情况下,最好仅在直接缓冲区能在程序性能方面带来明显好处时分配它们。
直接缓冲区可以通过调用此类的 allocateDirect()工厂方法创建
创建直接缓冲区
package testnio;
import java.nio.ByteBuffer;
public class TestBuffer3 {
public static void main(String[] args) {
testAllocateDirect();
}
public static void testAllocateDirect() {
//创建直接缓冲区
ByteBuffer buf=ByteBuffer.allocateDirect(1024);
//是否是直接缓冲区
System.out.println(buf.isDirect());
}
}
通道(Channel)
缓冲区仅是运载数据的容器,需要对数据读写还需要有一条通道,这两者是密不可分的。
Channel 接口的主要实现类:
- FileChannel:用于读取、写入、映射和操作文件的通道
- DatagramChannel:通过 UDP 读写网络中的数据通道
- ScoketChannel:通过 TCP 读写网络中的数据
- ServerScoketChannel:可以监听新进来的 TCP 链接,对每一个新进来的连接都会创建一个 SocketChannel
如何获取通道?
1、通过支持通道的对象调用 getChannel() 方法
支持通道的类:
- FileInputStream
- FileOutputStream
- RandomAccessFile
- DatagramScoket
- Socket
- ServerScoket
使用通道和缓冲区实现文件读和写
package testnio;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class TestChannel {
public static void main(String[] args) {
FileInputStream fis=null;
FileOutputStream fos=null;
FileChannel inChannel=null;
FileChannel outChannel=null;
try {
//创建输入流
fis=new FileInputStream("F:/1.jpg");
//创建输出流
fos=new FileOutputStream("F:/2.jpg");
//获取通道
inChannel=fis.getChannel();
outChannel=fos.getChannel();
//分配指定大小的缓冲区
ByteBuffer buf=ByteBuffer.allocate(1024);
//将通道中的数据存入缓存区
while(inChannel.read(buf)!=-1) {
//切换读取数据的模式
buf.flip();
//将读入的缓冲区存入写数据的管道
outChannel.write(buf);
//清空缓存区(清空才能再次读入)
buf.clear();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if (fis!=null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos!=null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(inChannel!=null) {
try {
inChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (outChannel!=null) {
try {
outChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
2、通过通道类的静态方法 open()
package testnio;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
public class TestOpenAndMapped {
public static void main(String[] args) {
FileChannel inChannel=null;
FileChannel outChannel=null;
try {
//通过open创建通道
inChannel = FileChannel.open(Paths.get("F:/a.jpg"), StandardOpenOption.READ);
outChannel = FileChannel.open(Paths.get("F:/b.jpg"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
//内存映射文件 直接缓冲区
MappedByteBuffer inMappedBuf=inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());
MappedByteBuffer outMappedBuf=outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());
//直接对缓冲区进行数据的读写操作
byte[] data=new byte[inMappedBuf.limit()];
inMappedBuf.get(data);//读
outMappedBuf.put(data);//写
} catch (IOException e) {
e.printStackTrace();
}finally {
if (inChannel!=null) {
try {
inChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (outChannel!=null) {
try {
outChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
JDK 1.7 新增的方法 open(),参数 path 通常代表一个依赖系统的文件路径,通过Paths.get()获取。
参数 StandardOpenOption 是一个枚举类型,常用值如下:
- READ :打开读访问
- WRITE:打开写访问
- APPEND:向后追加
- CREATE:创建新文件,存在则覆盖
- CREATE_NEW:创建新文件,存在则报错
MappedByteBuffer:直接字节缓冲区,其内容是文件的内存映射区域。
通道数据传输
将数据从源通道传输到其他 Channel 中,transferTo() 和 transferFrom()
package testnio;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
public class TestChannelTransfer {
public static void main(String[] args) {
FileChannel inChannel=null;
FileChannel outChannel=null;
try {
//通过open创建管道
inChannel = FileChannel.open(Paths.get("F:/a.jpg"), StandardOpenOption.READ);
outChannel = FileChannel.open(Paths.get("F:/b.jpg"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
//将inChannel中所有数据发送到outChannel
//inChannel.transferTo(0, inChannel.size(), outChannel);
outChannel.transferFrom(inChannel, 0, inChannel.size());
} catch (IOException e) {
e.printStackTrace();
}finally {
if (inChannel!=null) {
try {
inChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (outChannel!=null) {
try {
outChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
Channel 负责传输,Buffer 负责存储
Java入门系列-23-NIO(使用缓冲区和通道对文件操作)的更多相关文章
- Java入门系列Java NIO
jdk1.4中新加入的NIO,引入了通道与缓冲区的IO方式,它可以调用Native方法直接分配堆外内存,这个堆外内存就是本机内存,不会影响到堆内存的大小.
- java io系列23之 BufferedReader(字符缓冲输入流)
转载请注明出处:http://www.cnblogs.com/skywang12345/p/io_23.html 更多内容请参考:java io系列01之 "目录" Buffere ...
- Java入门系列-25-NIO(实现非阻塞网络通信)
还记得之前介绍NIO时对比传统IO的一大特点吗?就是NIO是非阻塞式的,这篇文章带大家来看一下非阻塞的网络操作. 补充:以数组的形式使用缓冲区 package testnio; import java ...
- Java入门系列-26-JDBC
认识 JDBC JDBC (Java DataBase Connectivity) 是 Java 数据库连接技术的简称,用于连接常用数据库. Sun 公司提供了 JDBC API ,供程序员调用接口和 ...
- Java入门系列-22-IO流
File类的使用 Java程序如何访问文件?通过 java.io.File 类 使用File类需要先创建文件对象 File file=new File(String pathname);,创建时在构造 ...
- Java入门系列-19-泛型集合
集合 如何存储每天的新闻信息?每天的新闻总数是不固定的,太少浪费空间,太多空间不足. 如果并不知道程序运行时会需要多少对象,或者需要更复杂方式存储对象,可以使用Java集合框架. Java 集合框架提 ...
- NIO的缓冲区、通道、选择器关系理解
Buffer的数据存取 一个用于特定基本数据类行的容器.有java.nio包定义的,所有缓冲区都是抽象类Buffer的子类. Java NIO中的Buffer主要用于与NIO通道进行交互,数 ...
- Python学习入门基础教程(learning Python)--5.6 Python读文件操作高级
前文5.2节和5.4节分别就Python下读文件操作做了基础性讲述和提升性介绍,但是仍有些问题,比如在5.4节里涉及到一个多次读文件的问题,实际上我们还没有完全阐述完毕,下面这个图片的问题在哪呢? 问 ...
- Java入门系列(十)Java IO
概述 总体而言,java的读写操作又分为两种:字符流和字节流. 实际上字节流在操作时本身不会用到缓冲区(内存),是文件本身直接操作的,而字符流在操作时使用了缓冲区,通过缓冲区再操作文件. 什么是流? ...
随机推荐
- 单例模式(Singleton)小记
概念 引用维基百科对单例的说明: 单例模式,也叫单子模式,是一种常用的软件设计模式.在应用这个模式时,单例对象的类必须保证只有一个实例存在. 继续引用维基百科的实现思路: 实现单例模式的思路是:一个类 ...
- WinForm中的重绘 - 按钮等控件的背景渐变色重绘
注:brush通过起止坐标来控制重绘范围及方向.比如从上到下渐变时,brush第二个Point参数是左下角坐标. private void PaintGradientBackground(Button ...
- Data Base oracle常见错误及解决方案
Data Base oracle常见错误及解决方案 一.TNS协议适配器错误: 原因: 此问题的原因都是由于监听没有配置好. 解决: 1.打开oracle工具Net Manager,删除服务及监听,重 ...
- ng 发生 Error: ELOOP: too many symbolic links encountered...
ng g component components/home 发生如下提示: 由于使用 cnpm install 安装 node_modules 导致这样. 解决办法: 删除 node_modules ...
- 爬虫开发9.scrapy框架之递归解析和post请求
今日概要 递归爬取解析多页页面数据 scrapy核心组件工作流程 scrapy的post请求发送 今日详情 1.递归爬取解析多页页面数据 - 需求:将糗事百科所有页码的作者和段子内容数据进行爬取切持久 ...
- Weekly Contest 121
984. String Without AAA or BBB Given two integers A and B, return any string S such that: S has leng ...
- 并发编程---线程 ;python中各种锁
一,概念 在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程 线程顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程 --车间负责把资源整合到 ...
- xml约束技术之dtd
DTD(文档类型定义)的作用是定义 XML 文档的合法构建模块.这篇文章作简单介绍下DTD的用法.想学习完整的请点击下面w3c的教程. 1.DTD官方教程 ##2.xml约束技术: DTD约束:语法相 ...
- Using Request Headers for Metadata Address
问题描述 我将一个在本地调试正常的service部署到服务器后遇到了添加服务引用失败的问题.在把配置文件中基址使用的localhost替换成服务器的ip地址后问题得到了解决.但我感觉这并不是一个因为粗 ...
- Selenium三种等待元素的方式及代码,需要特别注意implicitlyWait的用法
一.显式等待 1.显式等待: 就是明确的要等到某个元素的出现或者是某个元素的可点击等条件,等不到,就一直等,除非在规定的时间之内都没找到,那么就跳出Exception. 2.代码: new WebDr ...