目录:
Java NIO 学习笔记(一)----概述,Channel/Buffer
Java NIO 学习笔记(二)----汇集和分散,通道到通道
Java NIO 学习笔记(三)----Selector
Java NIO 学习笔记(四)----文件通道和网络通道
Java NIO 学习笔记(五)----路径、文件和管道 Path/Files/Pipe
Java NIO 学习笔记(六)----异步文件通道 AsynchronousFileChannel
Java NIO 学习笔记(七)----NIO/IO 的对比和总结html
Path 接口是 NIO2(AIO) 的一部分,是对 NIO 的更新,Path 接口已添加到 Java 7 中,彻底限定类名是 java.nio.file.Path 。java
Path 实例表示文件系统中的路径。 路径能够指向文件或目录,也能够是绝对的或相对的。在某些操做系统中,不要将文件系统路径与环境变量中的 path 路径相混淆。 java.nio.file.Path 接口与路径环境 path 变量无关。api
在许多方面,java.nio.file.Path 接口相似于 java.io.File 类,但存在一些细微差异。 但在许多状况下,可使用 Path 接口替换 File 类的使用。网络
可使用名为 Paths.get() 的 Paths 类(java.nio.file.Paths)中的静态方法建立 Path 实例,get()方法是 Path 实例的工厂方法,一个示例以下:oracle
public class PathExample { public static void main(String[] args) { // 使用绝对路径建立 Path absolutePath = Paths.get("D:\\test\\1.txt"); // 使用相对路径建立 Path relativePath = Paths.get("D:\\test", "1.txt"); System.out.println(absolutePath.equals(relativePath)); // ture } }
注意路径分隔符在 Windows 上是“\”,在 Linux 上是 “/”。异步
Paths 类只有2个方法:ide
方法 | 描述 |
---|---|
static Path get(String first, String... more) | 将路径字符串或在链接时造成路径字符串的字符串序列转换为路径。 |
static Path (URI uri) | 将给定URI转换为路径对象。 |
Path 接口部分方法:函数
方法 | 描述 |
---|---|
boolean endsWith(Path other) | 测试此路径是否以给定路径结束。 |
boolean equals(Object other) | 取决于文件系统的实现。通常不区分大小写,有时区分。 不访问文件系统。 |
Path normalize() | 返回一个路径,该路径消除了冗余的名称元素,好比'.', '..' |
Path toAbsolutePath() | 返回表示该路径的绝对路径的路径对象。 |
File toFile() | 返回表示此路径的 File 对象。 |
String toString() | 返回的路径字符串使用默认名称分隔符分隔路径中的名称。 |
NIO 文件类(java.nio.file.Files)为操做文件系统中的文件提供了几种方法,File 类与 java.nio.file.Path 类一块儿工做,须要了解 Path 类,而后才能使用 Files 类。post
static boolean exists(Path path, LinkOption... options)
options 参数用于指示,在文件是符号连接的状况下,如何处理该符号连接,默认是处理符号连接的。其中 LinkOption 对象是一个枚举类,定义如何处理符号连接的选项。整个类只有一个 NOFOLLOW_LINKS;
常亮,表明不跟随符号连接。学习
Path output = Paths.get("D:\\test\\output"); Path newDir = Files.createDirectory(output); // Files.createDirectories(output); // 这个方法能够一并建立不存在的父目录 System.out.println(output == newDir); // true System.out.println(Files.exists(output)); // true
若是建立目录成功,则返回指向新建立的路径的 Path 实例,此实例和参数是同一个实例。
若是该目录已存在,则抛出 FileAlreadyExistsException 。 若是出现其余问题,可能会抛出IOException ,例如,若是所需的新目录的父目录不存在。
一共有 3 个复制方法:
static long copy(Path source, OutputStream out); static Path copy(Path source, Path target, CopyOption... options); static long copy(InputStream in, Path target, CopyOption... options)
其中 CopyOption 选项能够选择指定复制模式,通常是其子枚举类 StandardCopyOption 提供选项,有 3 种模式,第二个参数是可变形参,能够多个组合一块儿使用:
ATOMIC_MOVE
:原子复制,不会被线程调度机制打断的操做;一旦开始,就一直运行到结束;COPY_ATTRIBUTES
:同时复制属性,默认是不复制属性的;REPLACE_EXISTING
:重写模式,会覆盖已存在的目的文件;一个例子以下:
Path sourcePath = Paths.get("D:\\test\\source.txt"); // 源文件必须先存在 Path desPath = Paths.get("D:\\test\\des.txt"); // 目的文件能够不存在 Files.copy(sourcePath, desPath); // 默认状况,若是目的文件已存在则抛出异常 Files.copy(sourcePath, desPath, StandardCopyOption.REPLACE_EXISTING); // 覆盖模式
注意:复制文件夹的时候,只能复制空文件夹,若是文件夹非空,须要递归复制,不然只能获得一个空文件夹,而文件夹里面的文件不会被复制。
只有 1 个移动文件或文件夹的方法:
static Path move(Path source, Path target, CopyOption... options);
若是文件是符号连接,则移动符号连接自己,而不是符号连接指向的实际文件。
和移动文件同样,也存在第三个可选参数 CopyOption ,参考上述。若是移动文件失败,可能会抛出 IOException,例如,若是文件已存在于目标路径中,而且遗漏了覆盖选项,或者要移动的源文件不存在等。
和复制文件夹不同,若是文件夹里面有内容,复制只会复制空文件夹,而移动会把文件夹里面的全部东西一块儿移动过去,如下是一个移动文件夹的示例:
// 移动 s 目录到一个不存在的新目录 Path s = Paths.get("D:\\s"); Path d = Paths.get("D:\\test\\test"); Files.createDirectories(d.getParent()); Files.move(s, d);
和 Linux mv 命令同样,重命名文件与移动文件方式相同,移动文件还能够将文件移动到不一样的目录并能够同时更改其名称。 另外 java.io.File 类也可使用它的 renameTo() 方法来实现移动文件,但如今 java.nio.file.Files 类中也有文件移动功能。
static void delete(Path path); static boolean deleteIfExists(Path path); // 若是文件被此方法删除则返回 true
若是文件是目录,则该目录必须为空才能删除。
删除和复制文件夹的时候,若是文件夹为空,那么会删除失败或者只能复制空文件夹,此时可使用 walkFileTree() 方法进行遍历文件树,而后在 FileVisitor 对象的 visitFile() 方法中执行删除或复制文件操做。
Files 类有 2 个重载的 walkFileTree() 方法,以下:
static Path walkFileTree(Path start, FileVisitor<? super Path> visitor); static Path walkFileTree(Path start, Set<FileVisitOption> options, int maxDepth, FileVisitor<? super Path> visitor);
将 Path 实例和 FileVisitor 做为参数,walkfiletree() 方法能够递归遍历目录树。Path 实例指向要遍历的目录。在遍历期间调用 FileVisitor ,首先介绍 FileVisitor 接口:
public interface FileVisitor<T> { FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs) throws IOException; FileVisitResult visitFile(T file, BasicFileAttributes attrs) throws IOException; FileVisitResult visitFileFailed(T file, IOException exc) throws IOException; FileVisitResult postVisitDirectory(T dir, IOException exc) throws IOException; }
必须本身实现 FileVisitor 接口,并将其实现的实例传递给 walkFileTree() 方法。在目录遍历期间,将在不一样的时间调用 FileVisitor 实现的 4 个方法,表明对遍历到的文件或目录进行什么操做。若是不须要使用到全部方法,能够扩展 SimpleFileVisitor 类,该类包含 FileVisitor 接口中全部方法的默认实现。
Files.walkFileTree(inputPath, new FileVisitor<Path>() { // 访问文件夹以前调用此方法 @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { System.out.println("pre visit dir:" + dir); return FileVisitResult.CONTINUE; } // 访问的每一个文件都会调用此方法,只针对文件,不会对目录执行 @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { return FileVisitResult.CONTINUE; } // 访问文件失败会调用此方法,只针对文件,不会对目录执行 @Override public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { return FileVisitResult.CONTINUE; } // 访问文件夹以后会调用此方法 @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { return FileVisitResult.CONTINUE; } });
这四个方法都返回一个 FileVisitResult 枚举实例。FileVisitResult 枚举包含如下四个选项:
经过返回其中一个值,被调用的方法能够决定文件遍历时接下来应该作什么。
walkFileTree() 方法还能够用于搜索文件,下面这个例子扩展了 SimpleFileVisitor 来查找一个名为 input.txt 的文件:
Path rootPath = Paths.get("D:\\test"); String fileToFind = File.separator + "input.txt"; Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { String fileString = file.toAbsolutePath().toString(); System.out.println("pathString: " + fileString); if (fileString.endsWith(fileToFind)) { System.out.println("file found at path: " + fileString); return FileVisitResult.TERMINATE; } return FileVisitResult.CONTINUE; } });
同理,删除有内容的目录时,能够重写 visitFile() 方法,并在里面执行删除文件操做,重写 postVisitDirectory() 方法,并在里面执行删除目录操做便可。
Files 类包含许多其余有用的函数,例如用于建立符号连接,肯定文件大小,设置文件权限等的函数。有关java.nio.file.Files 类的详细信息,请查看 JavaDoc
Pipe 是两个线程之间的单向数据链接。管道有 source 通道和一个 sink 通道,将数据写入 sink 通道,就能够从 source 通道读取该数据。
如下是管道原理的说明:
先看一个完整的例子:
public class PipeExample { public static void main(String[] args) throws IOException { Pipe pipe = Pipe.open(); Pipe.SinkChannel sinkChannel = pipe.sink(); // sink 通道写入数据 String data = "some string"; ByteBuffer buffer = ByteBuffer.allocate(32); buffer.clear(); buffer.put(data.getBytes()); buffer.flip(); // 反转缓冲区,准备被读取 while (buffer.hasRemaining()) { sinkChannel.write(buffer); // 将 Buffer 的数据写入 sink 通道 } Pipe.SourceChannel sourceChannel = pipe.source(); // 源通道读取数据 ByteBuffer readBuffer = ByteBuffer.allocate(32); int bytesRead = sourceChannel.read(readBuffer); // 返回值表明读取了多少数据 System.out.println("Read: " + bytesRead); // Read: 11 System.out.println(new String(readBuffer.array())); // some string } }
如上代码,首先要建立管道,打开管道以后是使用同一个管道对象获取对应的 sink 通道和 source 通道的,这会自动地将两个通道链接起来,做为对比,在标准 IO 管道中是分别建立读管道和写管道,而后在构造器中或者使用pipe1.connect(pipe2)
方法来链接起来,以下:
PipedOutputStream output = new PipedOutputStream(); PipedInputStream input = new PipedInputStream(); input.connect(output); // 或者使用以下1行代码,能够代替上面2行代码来链接2个管道 //PipedInputStream input = new PipedInputStream(output);