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的读写操作又分为两种:字符流和字节流. 实际上字节流在操作时本身不会用到缓冲区(内存),是文件本身直接操作的,而字符流在操作时使用了缓冲区,通过缓冲区再操作文件. 什么是流? ...
随机推荐
- docker容器中安装vi
容器中输入vi提示 root@e36f8029c9f2:/# vi bash: vi: command not found 解决办法: 1.通过命令获取最新的软件包 apt-get update ap ...
- docker-compsoe & .netcore & nginx
1.引言 紧接上篇.NET Core容器化@Docker,这一节我们先来介绍如何使用Nginx来完成.NET Core应用的反向代理,然后再介绍多容器应用的部署问题. 2. Why Need Ngin ...
- 构建针对 iOS 和 Android 的原生扩展
第一部分:音量控制入门 http://www.adobe.com/cn/devnet/air/articles/building-ane-ios-android-pt1.html 第二部分: 开发 A ...
- 【bzoj4888】: [Tjoi2017]异或和 BIT-乱搞
[bzoj4888]: [Tjoi2017]异或和 题目大意:给定一个序列,求这个序列所有的连续和的异或值.(n<=1e5 ai<=1e6) 想了各种奇怪的方法就是不会做啊啊啊.. Orz ...
- ubuntu 12.0.4 下python3.x web环境搭建
ubuntu 12.0.4 安装python3.x 1. $ sudo add-apt-repository ppa:fkrull/deadsnakes$ sudo apt-get update$ s ...
- ThinkPHP 3.2.x 集成极光推送指北
3.2版本已经过了维护生命周期,官方已经不再维护,请及时更新至5.0版本 -- ThinkPHP 官方仓库 以上,如果有条件,请关闭这个页面,然后升级至 ThinkPHP 5,如果由于各种各样的原因无 ...
- Docker部署常见问题
一.删除容器和镜像 在删除镜像之前要先用 docker rm 删掉依赖于这个镜像的所有容器(哪怕是已经停止的容器),否则无法删除该镜像. 停止容器 # docker stop $(docker ps ...
- editplus 编辑 php双击选中变量问题
windows下,在很多地方双击鼠标左键可以选中一个连续的英文字符串. 在editplus 编辑器里可以双击选中一个变量,方便了编程,但是使用phptools(php.stx)增强语法插件后,在一个变 ...
- HTML基础信息笔记
HTML 是什么 HTML 指的是超文本标记语言 (Hyper Text Markup Language) HTML 标签(tag) HTML 标签是由尖括号包围的关键词,比如 <html> ...
- mysql 设置默认时间为now()
TIMESTAMP的变体1,TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP在创建新记录和修改现有记录的时候都对这个数据列 ...