NIO是JDK1.4加入的新包,目的是是可以让程序员可以实现高速I/O而无需编写自定义的本机代码。
NIO将最耗时的I/O操作(即填充和提取缓冲区)转移回操作系统,因而可以极大地提高速度。
流与块的比较
原来的I/O库(在java.io.*中)与NIO的区别: 数据打包和传输的方式不同,原来的I/O以流的方式处理数据,而NIO以块的方式处理数据。
面向流的I/O系统一次一个字节地处理数据。每一个操作都在一步中产生或者消费一个字节数据。。因此面向流的I/O通常比较慢。
面向块的I/O系统以块的形式处理数据。每一个操作都在一步中产生或者消费一个数据块。按块处理数据比按流(字节)处理数据要快得多。但是面向块的I/O缺少一些面向流的I/O所具有的优雅性和简单性。
缓冲区
在NIO库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区的,在写入数据时,它是写入到缓冲区的。任何时候访问NIO中的数据,都将它放到缓冲区。
缓冲区实质是一个数组。通常它是一个字节数组,但是也可以使用其他类型的数组。但是一个缓冲区不仅仅是一个数组。缓冲区提供了对数据的结构化访问,还可以跟踪系统的读/写进程。
缓冲区类
最常用的缓冲区类型是ByteBuffer。一个ByteBuffer可以在其底层字节数组上进行get/set操作(即字节的获取和设置)。ByteBuffer不是NIO中唯一的缓冲区类型。事实上,对于每一种基本java数据类型(除了布尔值)都有一种缓冲区类型
缓冲区状态变量
指定缓冲区在任意时刻的三个状态值:
position(位置):缓冲区的位置
limit(容量):缓冲区的容量大小
capacity(上限):缓冲区的实际上限(设定好了不变的)
代码示例:
package com.booy;
import java.nio.ByteBuffer;
public class NIODemo {
public static void main(String[] args) {
//创建一个字节缓冲区,申请8字节的内存空间
ByteBuffer bf = ByteBuffer.allocate(8);
//向缓冲区写入数据
bf.put((byte) 10);
bf.put((byte) 20);
bf.put((byte) 30);
bf.put((byte) 40);
System.out.println("缓冲区当前位置:"+bf.position());
System.out.println("缓冲区容量:"+bf.limit());//有效字节+空容量
System.out.println("缓冲区实际上限:"+bf.capacity());
System.out.println("---------------");
//缓冲区反转,不改变缓冲区数据,修改了位置和缓冲区容量
bf.flip();
System.out.println(bf.position());
System.out.println(bf.limit());
System.out.println(bf.capacity());
//当前位置和限制之间是否有元素
if(bf.hasRemaining()) {
//当前位置和限制之间元素个数
for(int i=0; i<bf.remaining();i++) {
byte b =bf.get(i);
System.out.println(b);
}
}
}
}
通道:Channel
是一个对象,可以通过它读取和写入数据,就像是原来I/O中的流。
所有数据都是通过Buffer对象来处理,永远不会将字节直接写入到通道中,反而是将数据写入到缓冲区(或从缓冲区读取数据)中,由缓冲区操作。
比较IO操作的性能:
1、内存映射最快
2、NIO读写文件
3、使用了缓存的IO流
4、无缓存的IO流
package com.booy;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
public class ChannelCopyFileDemo {
public static void main(String[] args) {
try {
copyFile();
} catch (Exception e) {
e.printStackTrace();
}
}
private static void copyFileMap() throws Exception{
//使用RandomAccessFile工具类加内存映射实现文件复制功能
//构建输入流
RandomAccessFile rIn= new RandomAccessFile("D:\\test\\1.jpg","r");
RandomAccessFile rOut= new RandomAccessFile("D:\\1.jpg","rw");
//构建通道
FileChannel chIn = rIn.getChannel();
FileChannel chOut = rOut.getChannel();
long size= chIn.size();//输入流的字节大小
//内存映射
MappedByteBuffer inBuf =chIn.map(MapMode.READ_ONLY, 0, size);
MappedByteBuffer outBuf =chIn.map(MapMode.READ_WRITE, 0, size);
for(int i=0;i<size;i++) {
outBuf.put(inBuf.get());
}
//关闭通道时会写入数据块
chIn.close();
chOut.close();
rIn.close();
rOut.close();
System.out.println("copy success!");
}
//NIO读写文件方式复制文件
private static void copyFile() throws IOException {
FileChannel chIn = new FileInputStream("D:\\test.zip").getChannel();//构建通道
FileChannel chOut = new FileOutputStream("D:\\test\\test.zip").getChannel();
ByteBuffer bytes = ByteBuffer.allocate(1024);//字节缓冲区
while(chIn.read(bytes)!=-1) {
bytes.flip();
chOut.write(bytes);
bytes.clear();//清空缓冲区
}
chIn.close();
chOut.close();
System.out.println("文件复制成功");
}
}
Path接口与Files工具类
JDK1.7引入了新的IO操作类,java.nio.file包下,java NIO Path接口和Files类
Path接口
1、Path表示一个目录名序列,其后可以跟着一个文件名,路径中第一个部件是根部件时就是绝对路径,如/或c:\,而允许访问的根部件取决于文件系统;否则就是相对路径。
2、静态的Path.get方法接受一个或多个的字符串,字符串之间自动使用默认文件系统的路径分隔符连接起来(Unix是/,Windows是\),这就解决了跨平台的问题,接着解析连接起来的结果,如果不是合法路径就抛出InvalidPathException异常,否则就返回一个Path对象。
代码示例:
package com.booy;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
public class NIOFilesDemo {
public static void main(String[] args) {
File file = new File("D:\\test\\1.jpg");
//path
Path p1 = Paths.get("D:\\test","1.jpg");
System.out.println(p1);
Path p2 = file.toPath();
System.out.println(p2);
Path p3 = FileSystems.getDefault().getPath("D:\\test","1.jpg");
System.out.println(p3);
//files工具类使用
Path p4 = Paths.get("D:\\test","test.txt");
String s = "小乔流水人家";
try {
//写文件操作,StandardOpenOption.APPEND表示追加
Files.write(p4, s.getBytes(), StandardOpenOption.APPEND);
} catch (IOException e) {
e.printStackTrace();
}
try {
//读文件操作
byte[] bytes = Files.readAllBytes(p4);
System.out.println(new String(bytes));
} catch (IOException e) {
e.printStackTrace();
}
// try {
// //文件的复制,StandardCopyOption.REPLACE_EXISTING表示替换
// Files.copy(p3, Paths.get("D:\\1.jpg"), StandardCopyOption.REPLACE_EXISTING);
// } catch (IOException e) {
// e.printStackTrace();
// }
// try {
// //文件的移动
// Files.move(p3, Paths.get("D:\\1.jpg"), StandardCopyOption.REPLACE_EXISTING);
// } catch (IOException e) {
// e.printStackTrace();
// }
// try {
//文件的删除
// Files.delete(p3);
// } catch (IOException e) {
// e.printStackTrace();
// }
// try {
// //创建目录
// Files.createDirectory(Paths.get("D:\\test\\booy\\1"));
// } catch (IOException e) {
// e.printStackTrace();
// }
// try {
// //创建多级目录
// Files.createDirectories(Paths.get("D:\\test\\booy\\2\\3"));
// } catch (IOException e) {
// e.printStackTrace();
// }
try {
//创建文件
Files.createFile(Paths.get("D:\\test\\booy\\1.txt"));
} catch (IOException e) {
e.printStackTrace();
}
}
}