Java Nio 十六、Java NIO Files

最后更新:2015-04-15

这个Java NIO的Files类(java.nio.file.Files)提供了数个方法中文件系统中操作文件。这个Java NIO的Files类教程将会覆盖这些方法的大部分通用方法的使用。这个类包含了许多的方法,所以也可以核对这个JavaDoc,如果你需要一个没有在这里描述的方法。这个Files类只是可能为了它仍然有一个方法。

这个java.nio.file.Files类同java.nio.file.Path实例一起工作,以至于你在同Files类工作之前需要理解这个Path类。

Files.exists()

这个方法检查给予的这个路径是否在这个文件中。

去创建一个在文件系统不存在的Path实例是可能的。例如,如果你计划创建一个新的目录,你将会首先创建相应的Path实例,然后创建这个目录。

因为Path实例可能指向或者可能没有指向存在这个文件系统的路径,你可以使用Files.exists()方法去决定是否他们是(一旦你需要检查那个)。

这里有一个Java的Files.exists()实例:

Path path = Paths.get("data/logging.properties");

boolean pathExists =
        Files.exists(path,
            new LinkOption[]{ LinkOption.NOFOLLOW_LINKS});

这个例子首先创建一个指向这个路径的一个Path实例,我们去检查他是否存在。第二,这个例子使用Path实例作为第一个参数,调用这个Files.exists()方法。

注意Files.exists的第二个参数。这个参数是一个数组选项,它影响着这个Files.exists是怎么决定文件路径是否存在。在上面的这个例子中,这个数组包括这个LinkOption.NOFOLLOW_LINKS,这个意味着这个Files.exists方法应该不会跟随这个文件系统的符号链接去决定路径是否存在。

Files.createDirectory()

这个方法创建一个来自于Path实例的一个新的目录。这里有一个例子:

Path path = Paths.get("data/subdir");

try {
    Path newDir = Files.createDirectory(path);
} catch(FileAlreadyExistsException e){
    // the directory already exists.
} catch (IOException e) {
    //something else went wrong
    e.printStackTrace();
}

第一行创建了一个将会去创建的一个目录的Path实例。在try-catch块内部的这个Files.createDirectory()方法使用这个path作为一个参数被调用。如果创建一个目录成功,一个执行这个新创建的路径的Path实例被返回。

如果这个目录已经存在了,一个java.nio.file.FileAlreadyEXistsException将会抛出。如果其他的出现错误的话,一个IOException可能会抛出,例如,如果所期望的上级目录,新目录不存在的话,一个IOException会抛出。这个上级目录是你想创建的那个新的目录。因此,它意味着这个新目录的上级目录。

Files.copy()

这个方法拷贝一个文件从一个路径到另外一个。这里有一个例子:

Path sourcePath      = Paths.get("data/logging.properties");
Path destinationPath = Paths.get("data/logging-copy.properties");

try {
    Files.copy(sourcePath, destinationPath);
} catch(FileAlreadyExistsException e) {
    //destination file already exists
} catch (IOException e) {
    //something else went wrong
    e.printStackTrace();
}

首先,这个例子创建了一个源和目的的Path实例。然后这个例子调用了Files.copy()方法,通过这两个Path实例作为参数。这个将会导致通过源路径的文件引用被拷贝到被目录路径的文件引用中。

如果这个目的文件已经存在,一个java.nio.file.FileAlreadyEXistsException将会抛出。如果其他的事情出现错误,一个IOException会抛出。例如,如果拷贝文件的这个目录不存在,将会抛出一个IOException。

覆盖已经存在的文件

强制的Files.copy()去覆盖已经存在的文件是可能的。这里有一个例子展示怎样通过Files.copy()方法去覆盖已经存在的文件:

Path sourcePath      = Paths.get("data/logging.properties");
Path destinationPath = Paths.get("data/logging-copy.properties");

try {
    Files.copy(sourcePath, destinationPath,
            StandardCopyOption.REPLACE_EXISTING);
} catch(FileAlreadyExistsException e) {
    //destination file already exists
} catch (IOException e) {
    //something else went wrong
    e.printStackTrace();
}

注意Files.copy()方法中的第三个参数。这个参数指明如果目的文件已经存在将会覆盖。

Files.move()

这个Java NIO类也包含了一个这样的函数,移动一个文件从一个路径到另外一个路径。移动一个文件就像重命名它一样,移动一个文件既可以移动到不同的目录,还可以同时改变他的名字。是的,这个java.io.File类的renameTo()方法也可以做这个事情,但是现在你也可以在java.nio.file.Files类中做文件移动的功能。

这里有一个例子:

Path sourcePath      = Paths.get("data/logging-copy.properties");
Path destinationPath = Paths.get("data/subdir/logging-moved.properties");

try {
    Files.move(sourcePath, destinationPath,
            StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
    //moving file failed.
    e.printStackTrace();
}

首先源路径和目的路径被创建。这个源路径指向了将要移动的文件,以及整个目的路径指向了这个文件将要移动的目的地。然后Files.move方法被调用了。这个就会导致这个文件将会被移动。

注意这个方法里面的第三个参数,这个参数告诉这个方法将会覆盖目的路径中任何存在的文件。这个参数实际上是可选择的。

如果移动文件失败可能会抛出IOException异常。例如,如果一个文件已经存在目的路径中,并且你遗漏了StandardCopyOption.REPLACE_EXISTING这个选项,或者将要移动的这个文件不存在等等。

Files.delete()

这个方法可以删除一个文件或者一个目录。这里有一个例子:

Path path = Paths.get("data/subdir/logging-moved.properties");

try {
    Files.delete(path);
} catch (IOException e) {
    //deleting file failed
    e.printStackTrace();
}

首先创建一个指向要删除的文件的Path实例。然后调用这个删除的方法。如果因为一些原因(例如文件或者目录不存在)删除失败,一个IOException将会抛出。

Files.walkFileTree()

这个方法包含了可以递归遍历一个目录树的功能。这个方法携带着一个Path实例和FileVisitor作为参数。这个Path实例指向了你想要遍历的目录。这个FileVisitor是中遍历期间被调用。

在我解释这个遍历是如何工作的之前,这里首先有一个FileVisitor接口:

public interface FileVisitor {

    public FileVisitResult preVisitDirectory(
        Path dir, BasicFileAttributes attrs) throws IOException;

    public FileVisitResult visitFile(
        Path file, BasicFileAttributes attrs) throws IOException;

    public FileVisitResult visitFileFailed(
        Path file, IOException exc) throws IOException;

    public FileVisitResult postVisitDirectory(
        Path dir, IOException exc) throws IOException {

}

你不得不自己去实现FileVisitor接口,以及传递你实现的一个实例到walkFileTree方法中。你的FileVisitor实现的每一个方法中目录遍历期间的不同时间将会被调用。如果你不想去实现这些所有的方法,你可以继承SimpleFileVisitor类,这个类里面包含了FileVisitor接口所有方法的默认实现。

这里有一个walkFileTree的例子:

Files.walkFileTree(path, 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 {
    System.out.println("visit file: " + file);
    return FileVisitResult.CONTINUE;
  }

  @Override
  public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
    System.out.println("visit file failed: " + file);
    return FileVisitResult.CONTINUE;
  }

  @Override
  public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
    System.out.println("post visit directory: " + dir);
    return FileVisitResult.CONTINUE;
  }
});

在FileVisitor实现的每一个方法中目录遍历期间的不同时间将会被调用:

这个preVisitDirectory() 方法将会中访问任何目录之前会被调用。这个 postVisitDirectory()方法是中访问目录之后被调用。

这个visitFile()方法是中每一个文件访问期间会被调用,它对于目录是不会被调用的--只是相对文件。这个visitFileFailed()方法一旦访问文件失败将会被调用。例如,如果你没有正确的权限,或者是其他事情出错了。

四个方法中的每一个都返回了一个FileVisitResult 枚举实例。这个枚举实例包含下面四个选项:

  • CONTINUE
  • TERMINATE
  • SKIP_SIBLINGS
  • SKIP_SUBTREE

通过返回的值去决定接下来这个方法应该怎么走。

CONTINUE意味着意味着这个文件将会正常的继续访问下去。

TERMINATE意味着这个文件的访问现在将会终止。

SKIP_SIBLINGS意味着这个文件的访问将会继续,但是不会访问这个文件或者目录的兄弟节点。

SKIP_SUBTREE意味着这个文件的访问将会继续,但是不会访问这个目录的条目。这个值只是如果从preVisitDirectory() 返回就会有这个功能。如果从其他任何方法返回,他将会作为一个CONTINUE被解释使用。

搜索文件

这里有一个walkFileTree方法,它继承了SimpleFileVisitor 去寻找README.txt的文件:

Path rootPath = Paths.get("data");
String fileToFind = File.separator + "README.txt";

try {
  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: " + file.toAbsolutePath());
        return FileVisitResult.TERMINATE;
      }
      return FileVisitResult.CONTINUE;
    }
  });
} catch(IOException e){
    e.printStackTrace();
}

递归的删除目录

walkFileTree方法也可以用来删除所有文件的一个目录以及它里面的子目录。如果它是空的,这个Files.delete()方法将只是删除一个目录。通过穿行所有的目录以及删除所有的文件(在visitFile()里面)在每一个目录中,并且删除目录本身之后(在postVisitDirectory()里面),你可以删除一个带有所有子目录和文件的一个目录。这里有一个递归删除目录的一个例子:

Path rootPath = Paths.get("data/to-delete");

try {
  Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
      System.out.println("delete file: " + file.toString());
      Files.delete(file);
      return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
      Files.delete(dir);
      System.out.println("delete dir: " + dir.toString());
      return FileVisitResult.CONTINUE;
    }
  });
} catch(IOException e){
  e.printStackTrace();
}

在Files类里面的额外的方法

这个java.nio.file.Files类包含了很多其他有用的功能,例如像创建符号链接的功能,决定文件大小的功能,设置文件权限的功能等等。你可以去查看这个类的Java文档去获取这些方法的更多信息。

翻译地址:http://tutorials.jenkov.com/java-nio/files.html

时间: 2024-11-06 03:51:23

Java Nio 十六、Java NIO Files的相关文章

Java笔记十六.java中的this和super用法

一.this引用句柄 this引用句柄在Java程序里的作用,可表现为它在函数内部就是这个函数所属的对象的引用变量,即当前对象.在成员方法中,对访问的同类中成员前加不加this引用,效果都是一样的,这就好像同一公司的职员彼此在提及和自己公司有关的事时,不必说出公司名一样,当然为了强调,可以加上"咱们公司....."这样的前缀,而在Java程序中,this就相当于"我们所属于的那个对象". 每个成员方法内部,都有一个this引用变量,指向调用这个方法的对象.下面我们开

“全栈2019”Java第十六章:下划线在数字中的意义

难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第十六章:下划线在数字中的意义 下一章 "全栈2019"Java第十七章:赋值运算符和算术运算符 学习小组 加入同步学习小组,共同交流与进步. 方式一:关注头条号Gorhaf,私信"Java学习小组". 方式二:关注公众号Gorhaf,回复"Java学习小组&q

“全栈2019”Java第九十六章:抽象局部内部类详解

难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第九十六章:抽象局部内部类详解 下一章 "全栈2019"Java第九十七章:在方法中访问局部内部类成员详解 学习小组 加入同步学习小组,共同交流与进步. 方式一:关注头条号Gorhaf,私信"Java学习小组". 方式二:关注公众号Gorhaf,回复"Java学

Java基础十六

1 Set接口概述 一个不包含重复元素的Collection. 2 HashSet类 2.1 HashSet类的概述 它不保证Set的迭代顺序,特别是它不保证该顺序恒久不变. 底层数据结构是哈希表. 添加功能依赖于两个方法: public int hashCode() public boolean equals(Object obj) 2.2 HashSet的应用 应用: package com.xuweiwei; import java.util.HashSet; import java.ut

系统学习 Java IO (十六)----这么多类,应该用哪个?

目录:系统学习 Java IO---- 目录,概览 ### Java IO目的和功能 Java IO 包含 InputStream,OutputStream,Reader 和 Writer 类的许多子类. 原因是,所有这些子类都在解决各种不同的目的. 所涉及的目的总结如下: 网络访问 内部缓冲区访问 线程间通信(管道) 缓冲 过滤 解析 阅读和写作文本(Reader/Writer) 读写原始数据(long,int等) 读和写对象 Java IO类概述表 在讨论了 Java IO 类所针对的源,目

Java从零开始学三十六(JAVA IO- 字符流)

一.字符流 BufferedReader:BufferedReader是从缓冲区之中读取内容,所有的输入的字节数据都将放在缓冲区之中 BufferedWriter:把一批数据写入到缓冲区,当缓冲区区的满时,再把缓冲区的内容写到字符输出流中 二.对文本文件的读写 2.1.字符输入流 2.2.字符输出流 2.3.综合使用 package com.pb.io.buffered; import java.io.BufferedReader; import java.io.BufferedWriter;

java并发系列(六)-----Java并发:volatile关键字解析

在 Java 并发编程中,要想使并发程序能够正确地执行,必须要保证三条原则,即:原子性.可见性和有序性.只要有一条原则没有被保证,就有可能会导致程序运行不正确.volatile关键字 被用来保证可见性,即保证共享变量的内存可见性以解决缓存一致性问题.一旦一个共享变量被 volatile关键字 修饰,那么就具备了两层语义:内存可见性和禁止进行指令重排序.在多线程环境下,volatile关键字 主要用于及时感知共享变量的修改,并使得其他线程可以立即得到变量的最新值,例如,用于 修饰状态标记量 和 D

java学习(十六):对象的自定义比较,Comparator和Comparable

通过两个例子实现对象的自定义排序 1.实现Comparator接口. 1 import java.util.ArrayList; 2 import java.util.Collections; 3 import java.util.Comparator; 4 import java.util.List; 5 6 public class StudentComparator implements Comparator<Object> 7 { 8 @Override 9 public int co

Java进阶 十六 使用new Date 和System currentTimeMillis 获取当前时间戳

java使用new Date()和System.currentTimeMillis()获取当前时间戳 在开发过程中,通常很多人都习惯使用new Date()来获取当前时间,使用起来也比较方便,同时还可以获取与当前时间有关的各方面信息,例如获取小时,分钟等等,而且还可以格式化输出,包含的信息是比较丰富的.但是有些时候或许你并不需要获取那么多信息,你只需要关心它返回的毫秒数就行了,例如getTime().为了获取这个时间戳,很多人也喜欢使用new Date().getTime()去获取,咋一看没什么