新IO(NIO)
一.概述
ava NIO ( New IO )是从 Java 1.4 版本开始引入的一个新的 IO API ,可以替代标准的 Java IO API 。NIO 与原来的 IO 有同样的作用和目的,但是使用的方式完全不同, NIO 支持面向缓冲区的、基于通道的 IO 操作。 NIO 将以更加高效的方式进行文件的读写操作.
两种流的对比
标准IO:
面向流,面向字节的流动,是单向的, 是阻塞的,无选择器 读 :输入流 写: 输出流
新IO:
面向缓冲区,基于通道操作,是非阻塞的,有选择器
二.通道(Channel)
1.概述
java.nio.channels 包定义的。 Channel 表示 IO 源与目标打开的连接。
Channel 类似于传统的“流”。只不过 Channel本身不能直接访问数据, Channel 只能与Buffer 进行交互。
简单的来说就是连接目标地点与源地点,但它不能存储数据
(1)获取通道方式1
FileChannel inChannel = in.getChannel();
FileChannel outChannel = out.getChannel();
(2)获取通道的方式2
通过FileChannel中的静态方法 open()可以打开一个通道
FileChannel inChannel = FileChannel.open(Paths.get("a.mp3"), StandardOpenOption.READ);
//StandardOpenOption.CREATE_NEW 文件不存在就创建,存在就报错
//StandardOpenOption.CREATE 文件不存在,就创建,存在 就覆盖
FileChannel outChanle = FileChannel.open(Paths.get("b.mp3"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
获取通道的第三种方式
newByteChannel
2.通道中的文件传输
站在输入通道的角度复制文件
inChannel.transferTo(0,inChannel.size(),outChanle);
站在输出通道的角度复制文件
outChanle.transferFrom(inChannel,0,inChannel.size());
分配多个缓冲区
ByteBuffer byteBuffer1 = ByteBuffer.allocate(100);
ByteBuffer byteBuffer2 = ByteBuffer.allocate(1024 * 2);
ByteBuffer[] byteBuffers={byteBuffer1,byteBuffer2};//定义一个数组
inChannel.read(byteBuffers);//把通道中的数据,放到多个buffer中
聚集写入
FileChannel outChannel = out.getChannel();
outChannel.write(byteBuffers);
代码演示1(复制文件)
//通道中的文件传输
FileChannel inChannel = FileChannel.open(Paths.get("歌曲串烧.mp3"), StandardOpenOption.READ);
FileChannel outChanle = FileChannel.open(Paths.get("歌曲串烧2.mp3"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
//站在输入通道的角度
//inChannel.transferTo(0,inChannel.size(),outChanle);
//站在输出通道的角度
outChanle.transferFrom(inChannel,0,inChannel.size());
代码演示2(分散与聚集)
public static void main(String[] args) throws IOException {
RandomAccessFile in = new RandomAccessFile("E:\\demo.txt", "rw");
RandomAccessFile out = new RandomAccessFile("E:\\democopy.txt", "rw");
//获取读取通道
FileChannel inChannel = in.getChannel();
//创建多个缓冲区
ByteBuffer buffer1 = ByteBuffer.allocate(100);
ByteBuffer buffer2 = ByteBuffer.allocate(1024);
//分散读取到多个缓冲区中
ByteBuffer[] byteBuffers=new ByteBuffer[]{buffer1,buffer2};//把多个缓冲区放到一个大的数组中
long read = inChannel.read(byteBuffers);//把这个大的缓冲区传进去
//当然我们可以看看,每个缓冲区中读入的数据
//byteBuffers[0].flip(); //切换到读取模式 看一下第一个缓冲区,读入的100个字节
//byte[] array = byteBuffers[0].array();//把ByteBuffer转换成字节数组
//String s = new String(array, 0, byteBuffers[0].limit());
//System.out.println(s);
//把每个缓冲区,切换到读取模式
for (ByteBuffer buffer : byteBuffers) {
buffer.flip();
}
//聚集写入
FileChannel outChannel = out.getChannel();
outChannel.write(byteBuffers);
//释放资源
inChannel.close();
outChannel.close();
}
三.缓冲区(buffer)
1.概述
一个用于特定基本数据类型的容器。由 java.nio 包定义的,所有缓冲区都是 Buffer 抽象类的子类。
Java NIO 中的 Buffer 主要用于与 NIO 通道进行交互,数据是从通道读入缓冲区,从缓冲区写入通道中的。
简单的来说就是进行数据的处理,底层是数组,作用是用来存储数据
2.buffer常用的子类
ByteBuffer
CharBuffer
ShortBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBuffer
上述 Buffer 类 他们都采用相似的方法进行管理数据,只是各自
管理的数据类型不同而已。其中Bytebuffer最常用。
3.buffer的重要属性(以ByteBuffer为例)
capacity:缓冲区的容量,一旦指定容量后不能更改
ByteBuffer byteBuffer = ByteBuffer.allocate(10);//指定一个容量为10的缓冲区
limit:界限,从limit往后的数据不能读写
int limit = byteBuffer.limit();//返回当前界限值
posotion:位置,文件指针,从posotion开始可以读数据
int position = byteBuffer.position();//返回当前指针位置
put():往容器中放数据
bytebuffer.put(str.getBytes());
flip():读取缓冲区的数据,切换成读取模式
bytebuffer.flip();
get():读取数据
bytebuffer.get()
//读取俩个字节
byte[] bytes = new byte[byteBuffer.limit()];
byteBuffer.get(bytes,0,2);
rewind():可重复读取
bytebuffer.rewind()
clear():清空缓冲区,并不是把缓冲区的字节数据清除掉,而是把指针设置到最初状态
bytebuffer.clear();
mark():可以标记当前的position位置
bytebuffer.mark();
reset():回到上一次标记position的位置
bytebuffer.reset();
hasRemaining():查看还有多少可读数据
if(byteBuffer.hasRemaining()){
System.out.println(byteBuffer.remaining()); //remaining()还有多少可读取数据
}
针对基本类型(布尔类型除外)都有相应的缓冲区
4.缓冲区分类
(1)非直接缓冲区allocatr(1024)
将缓冲区建立在jvm的内存中
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
(2)直接缓冲区allocatrDirect(1024):
将缓冲区建立在物理内存中,效率高,比较耗费资源
ByteBuffer byteBuffer1 = ByteBuffer.allocateDirect(1024);
5.复制文件
(1)采用NIO非直接缓冲区复制本地文件
public static void main(String[] args) throws IOException {
//创建文件输入输入流
FileInputStream in = new FileInputStream("a1.mp3");
FileOutputStream out = new FileOutputStream("a2.mp3");
//文件输入输入流的getChannel()方法获取通道
FileChannel inChannel = in.getChannel();
FileChannel outChannel = out.getChannel();
//获取非直接缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//将通道中的数据放入到缓冲区中
while (inChannel.read(byteBuffer) != -1) {
//切换读取数据的模式
byteBuffer.flip();
//将缓冲区中的数据写入通道中
outChannel.write(byteBuffer);
//清空缓冲区
byteBuffer.clear();
}
//释放资源
in.close();
out.close();
inChannel.close();
outChannel.close();
}
(2)采用直接缓冲区复制文件
public static void main(String[] args) throws IOException {
//通过文件通道的静态方法,打开读写通道
//参1:通过Paths获取源文件的路径
//参2:操作模式 StandardOpenOption.READ 读取模式
//打开读取文件的通道
FileChannel in = FileChannel.open(Paths.get("a1.mp3"), StandardOpenOption.READ);
//打开写入的通道 模式要读还要写 StandardOpenOption.CREATE 意思是文件不存在就创建,如果存在就覆盖
//StandardOpenOption.CREATE_NEW 意思是文件不存在就创建,如果存在就报错
FileChannel out = FileChannel.open(Paths.get("a2.mp3"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
//操作内存映射文件(也就是这个缓冲区在物理内存中)
MappedByteBuffer inByteBuffer = in.map(FileChannel.MapMode.READ_ONLY, 0, in.size());
MappedByteBuffer outByteBuffer = out.map(FileChannel.MapMode.READ_WRITE, 0, in.size());
//直接对缓冲区进行读写操作
byte[] bytes = new byte[inByteBuffer.limit()];
inByteBuffer.get(bytes);
outByteBuffer.put(bytes);
//释放资源
in.close();
out.close();
}
files类复制文件
方式一:
Files.copy(new FileInputStream("demo.txt"), Paths.get("demo55.txt"), StandardCopyOption.REPLACE_EXISTING);
方式二:
Files.copy(Paths.get("demo55.txt"),new FileOutputStream("demo66.txt"));
方式三:
Files.copy(Paths.get("demo.txt"), Paths.get("demo77.txt"), StandardCopyOption.REPLACE_EXISTING);
注意:StandardCopyOption.REPLACE_EXISTING 是可选参数,复制文件,如果文件存在就替换,如果不写这个参数,就是文件存在,就报错,不会覆盖