1、为什么要使用NIO?
NIO是JDK1.4加入的新包,NIO的创建目的是为了让Jva程序员可以实现高速IO而无需编写自定义的本机代码。NIO将最耗时的/操作(即填充和提取缓冲区)转移回操作系统,因而可以极人地提高速度。
2、流与块的比较
原来的/o库(在java.io.*中)与NIo最重要的区别是数据打包和传输的方式,原来的/o以流的方式处理数据,而NIO以块的方式处理数据。面向流的/系统一次一个字节地处理数据。一个输入流产生一个字节的数据,一个输出流消费一个字节的数据。不利的一面是,面向流的IO通常相当慢。
一个面向块的/系统以块的形式处理数据。每一个操作都在一步中产生或者消费一个数据块。按块处理数据比按(流式的)字节处理数据要快得多。但是面向块的IO缺少一些面向流的IO所具有的优雅性和简单性。3、缓冲区
在NIO库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接语读到缓冲区中的。在写入数据时,它是写入到缓冲区中的。任何时候访问NIO中的数据,您都是将它放到缓冲区中。
缓冲区实质上是一个数组。通常它是一个字节数组,但是也可以使用其他种类的数组。但是一个缓冲区不仅仅是数组。缓冲区提供了对数据的结构化访问,而且还可以跟踪系统的读/写进程。
4、缓冲区类型
最常用的缓冲区类型是ByteBuffer。一个ByteBuffer可以在其底层字节数组上进行get/set操作(即字节的获取和设置)。ByteBuffer不是NIO中唯一的缓冲区类型。事实上,对于每一种基本的Java类型都有一种缓冲区类型:ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer等。
示例:
import java.nio.ByteBuffer;
public class io_new_io_demo {
public static void main(String[] args) {
//在C中,‘allocate’表示分配内存空间,同样的,在这表示分配缓存空间
ByteBuffer buf = ByteBuffer.allocate(8);//创建一个字节缓冲区,申请一个内存空间为8字节
//查看原始状态
System.out.println("position="+buf.position());//指向存储的位置,一开始为0,存储后,向后依次移动,0
System.out.println("limit="+buf.limit());//限制,8
System.out.println("capacity="+buf.capacity());//最大容量,8
System.out.println("--------------------------------");
//向缓冲区写入数据
buf.put((byte) 10);
buf.put((byte) 20);
buf.put((byte) 30);
buf.put((byte) 40);
//查看插入数据后的状态
System.out.println("position="+buf.position());//4
System.out.println("limit="+buf.limit());//8
System.out.println("capacity="+buf.capacity());//8
System.out.println("--------------------------------");
//缓冲区没有将数据放满,缓冲区反转(截取),就是截取带有真实数据的字节数组
buf.flip();
//查看反转后的状态
System.out.println("position="+buf.position());//0
System.out.println("limit="+buf.limit());//4
System.out.println("capacity="+buf.capacity());//8
//告知当前元素和限制之间是否有元素,即 position != limit ? true:false
if(buf.hasRemaining()){
//返回当前位置与限制之间的元素个数
for (int i=0;i<buf.remaining();i++){
//取出数据
byte b = buf.get(i);
System.out.println(b);
}
}
}
}
5、通道Channel
Channel是一个对象,可以通过它读取和写入数据,拿NIO与IO作比较,通道就像是流。正如前面提到的,所有数据都是通过Buffer对象来处理,您永远不会将字节直接写入通道中,相反,您是将数据写入包含一个或多个字节的缓冲区。同样,您不会从通道中读取字节,而是将数据从通道读入缓冲区,再从缓存区中获取这个字节。
示例:
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
public class io_13_nio_copeFileDemo2 {
public static void main(String[] args) {
// nioCopy();
randomAccessFileCopy();
}
/**
* IO操作的性能比较
* 1.最快:内存映射
* 2.其次:NIO读写
* 3.然后:使用了缓存的IO
* 4.最后:无缓存的IO流
*/
/**
* 通过内存映射实现文件的复制
*/
public static void randomAccessFileCopy(){
try {
//读
RandomAccessFile in = new RandomAccessFile("1.txt","r");
//写
RandomAccessFile out = new RandomAccessFile("2.txt","rw");
//文件通道
FileChannel fcIn = in.getChannel();
FileChannel fcOut = out.getChannel();
/**
* 映射
*/
long size = fcIn.size();//输入流的字节大小
//输入输出流(映射方式)的缓冲区 FileChannel.MapMode.READ_ONLY表示类型为只读 READ_WRITE 可读可写
MappedByteBuffer inBuf = fcIn.map(FileChannel.MapMode.READ_ONLY,0,size);
MappedByteBuffer outBuf = fcOut.map(FileChannel.MapMode.READ_WRITE,0,size);
for(int i=0;i<size;i++){
outBuf.put(inBuf.get(i));
}
//关闭---关闭通道时会写入数据块
fcIn.close();
fcOut.close();
in.close();
out.close();
System.out.println("Success!");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 通过文件通道实现文件的复制
*/
public static void nioCopy() {
try {
/**
* 通道
*/
//创建一个输入文件的通道
FileChannel fcIn = new FileInputStream("1.txt").getChannel();
//创建一个输出文件的通道
FileChannel fcOut = new FileOutputStream("2.txt").getChannel();
/**
* 缓冲区
*/
//创建一个缓冲区,申请1024个字节内存
ByteBuffer buf = ByteBuffer.allocate(1024);
while (fcIn.read(buf)!=-1){
//数据反转,一块数据
buf.flip();
//已经反转,无需写到 0-->len
fcOut.write(buf);
//清空
buf.clear();
}
//关闭流
fcIn.close();
fcOut.close();
System.out.println("Success!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
NIO可以提高读取数据的根本原因是利用了操作系统(如Linux)底层的IO多路复用机制
6、NIO的工具类
JDK1.7引入了新的I0操作类,java.nio.file包 下,Java NIO Path接口和Files类
①Path接口
1、Path表示的是一个目录名序列,其后还可以跟着一个文件名,路径中第一个部件是根部件时就是
绝对路径,例如/或C:\,而允许访问的根部件取诀于文件系统;
2、以根部件开始的路径是绝对路径,否则就是相对路径;
3、静态的Paths.get方法接受- 一个或多个字符串,字符串之间自动使用默认文件系统的路径分隔符连接
起来(Unix是 /, Windows是\),这就解决了跨平台的问题,接着解析连接起来的结果,如果不
是合法路径就抛出InvalidPathException异常,否则就返回-个Path对象
②Files工具类
static path write(Path path, byte[] bytes, OpenOption... options)---写入文件
static byte[] readAllBytes(Path path)---读取文件中的所有字节。
static path copy(Path source, Path target, CopyOption... options)---复制文件
static path move(Path source, Path target, CopyOption... options)---剪切文件
static void delete(Path path) ---如果path不存在文件将抛出异常,此时调用下面的比较好
static boolean deletelfExists(Path path)---删除文件
Files.createDirectory(path);---创建新目录,除了最后一个部件,其他必须是已存在的
Files.createDirectories(path);---创建路径中的中间目录,能创建不存在的中间部件
Files.createFile(path);---创建一个空文件,检查文件存在,如果已存在则抛出异常而检查文件存在是原子性的,因此在此过程中无法执行文件创建操作
Path newPath = Files.createTempFile(dir, prefix, suffix);
Path newPath = Files.createTempDirectory(dir, prefix);---添加前/后缀创建临时文件或临时目录
......
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
public class io_13_nio_pathFilesDemo {
public static void main(String[] args) {
File file = new File("d:\\1.txt");
//path
Path p1 = Paths.get("d:\\","1.txt");//调用的p3的哪个方法,和p3一样
Path p2 = file.toPath();
Path p3 = FileSystems.getDefault().getPath("d:\\","1.txt");
//files工具类
Path p4 = Paths.get("1.txt");
// StandardOpenOption.APPEND 表示追加
try {
//写入文件
Files.write(p4,"lsq666啊实打实".getBytes(StandardCharsets.UTF_8), StandardOpenOption.APPEND);
//读取文件
// byte[] bytes = Files.readAllBytes(p4);
// System.out.println(new String(bytes));
//复制文件 StandardCopyOption.REPLACE_EXISTING 表示如果存在就替换
// Files.copy(p4,Paths.get("3.txt"),StandardCopyOption.REPLACE_EXISTING);
//剪切文件
// Files.move(p4,Paths.get("3.txt"),StandardCopyOption.REPLACE_EXISTING);
//删除文件
// Files.delete(p4);
// Files.deleteIfExists(p4);
//创建新目录,除了最后一个部件,其他必须是已存在的
// Files.createDirectory(Paths.get("c:\\test\\lsq.jpg"));
//创建路径中的中间目录,能创建不存在的中间部件
// Files.createDirectories(Paths.get("c:\\test\\lsq.jpg"));
//创建一个空文件,检查文件存在,如果已存在则抛出异常,因为检查文件存在是原子性的,因此在此过程中无法执行文件创建操作
// Files.createFile(Paths.get("c:\\test\\lsq.jpg"));
//添加前/后缀创建临时文件或临时目录
// Path newPath = Files.createTempFile(dir, prefix, suffix);
// Path newPath = Files.createTempDirectory(dir, prefix);
} catch (IOException e) {
e.printStackTrace();
}
}
}