Java NIO.2总结

目录

 

NIO.2概述

文件系统

文件路径

文件及目录

文件元数据

文件、目录校验

创建、删除、复制、移动

文件、目录读写

新旧File API比较

参考文献


NIO.2概述

  NIO.2在JDK1.7中发布,针对原有的文件IO操作进行了优化及封装,并支持Asynchronous IO。从Java IO详细总结(源码解析)Java NIO详细总结(源码解析)这两篇文章中可以看到在针对文件进行操作时不管是IO还是NIO都需要搭配File,通过File来跟OS关联。而在NIO.2中围绕着文件系统提出了三大概念FileSystem,Path,Symbolic Links也是本文的核心内容,下面将逐一介绍。需要注意的是本文跟Java IO详细总结(源码解析)Java NIO详细总结(源码解析)一样,只讨论文件IO至于网络IO会在其他文章中展开。

文件系统

  在开始介绍NIO.2之前先来了解下什么是文件系统(File System)。文件系统在某种形式的媒体(通常是一个或多个硬盘)上存储和组织文件,以便于检索。目前使用的大多数文件系统都将文件存储在树(或层次结构)结构中。树的顶部是一个(或多个)根节点。在根节点下,有文件和目录(Microsoft Windows中的文件夹)。每个目录都可以包含文件和子目录,而这些文件和子目录又可以包含文件和子目录,潜在的深度几乎是无限的。一般常见的就是Windows以及UNIX系统,下面是一个常见的文件系统结构图:

                                                      

  对于Windows系统来说有分区的概念,文件系统的根节点可以是任意一个分区,而在UNIX系统中文件系统都是从/开始。拿test来说在Windos中的文件路径就是C:\data\test,对于UNIX就是/data/test。NIO.2中通过类Path来表示文件系统中的路径,其中路径又分为绝对路径以及相对路径:

  • 绝对路径:绝对路径一定带着根路径,例如/data/test就是一条完整的文件路径通过它可以找到对应的文件;
  • 相对路径:相对路径不是完整的文件路径例如data/test,它需要跟根目录/配合成一条完整的路径才能够找到对应文件。

  需要注意的是在Java中你可以随意构建一个Path实例,但是该对象对应的文件不一定存在于文件系统中,需要通过Files类来校验是否存在。

  在文件系统中值得一提的还有符号链接(Symbolic Link),符号链接指的是在文件系统中文件可以指向其他文件,在这个过程中源文件只是起到一个指向性的作用(指向目标文件)即路径C:\data\test与C:\etc\test访问的都是同一个文件test2,如下图所示:

                                                        

文件路径

  通过类Paths提供的静态方法get可以拿到对应的Path实例对象(Path为接口,get方法拿到的是其实现类后上转成Path),下面来看看Path一些常见的用法:

        //   Path path = Paths.get("D:\\", "data\\test.txt"); 相对路径
        Path path = Paths.get("D:\\data\\test.txt");//  绝对路径
        System.out.println(Files.exists(path));
        System.out.format("toString: %s%n", path.toString());
        System.out.format("getFileName: %s%n", path.getFileName());
        System.out.format("getName(0): %s%n", path.getName(0));
        System.out.format("getNameCount: %d%n", path.getNameCount());
        System.out.format("subpath(0,2): %s%n", path.subpath(0,2));
        System.out.format("getParent: %s%n", path.getParent());
        System.out.format("getRoot: %s%n", path.getRoot());

        // 删除路径中多余的东西,例如.或者..
        Path p1 = Paths.get("D:\\data\\.\\test.txt");
        System.out.println("normalize . : " + p1.normalize());
        p1 = Paths.get("D:\\data\\test\\.\\test.txt");
        System.out.println("normalize test\\. : " + p1.normalize());
        p1 = Paths.get("D:\\data\\test\\..\\test.txt");
        System.out.println("normalize test\\.. : " + p1.normalize());

        // 比较两个路径
        System.out.println("compare path : " + path.equals(p1));

        /* 输出
        exists : false
        toString: D:\data\test.txt
        getFileName: test.txt
        getName(0): data
        getNameCount: 2
        subpath(0,2): data\test.txt
        getParent: D:\data
        getRoot: D:\
        normalize . : D:\data\test.txt
        normalize test\. : D:\data\test\test.txt
        normalize test\.. : D:\data\test.txt
        compare path : false */

文件及目录

  NIO.2中通过Files类来操作文件以及目录,不需要实例化该对象其内部的所有方法都是静态的。值得一提的是Files的所有操作都是建立在Path之上的。

文件元数据

  文件的元数据指是文件的信息,包括但不局限于修改时间、创建时间、访问权限、文件大小。Files支持检索单个元数据,也可以批量获取多个文件的元数据(多个元数据放到一个对象中),前者查了api文档就可以了解,下文将介绍下后者。

  由于不同的OS有不同的文件系统,为此NIO.2针对这些不同分为成了6种视图:

  1. BasicFileAttributeView:提供所有文件系统实现所需支持的基本属性的视图;
  2. DosFileAttributeView:扩展基本属性视图,在支持DOS属性的文件系统上支持标准的四位;
  3. PosixFileAttributeView:扩展基本属性视图,在支持POSIX系列标准(如Unix)的文件系统上支持这些属性。这些属性包括文件所有者、组所有者和九个相关的访问权限;
  4. FileOwnerAttributeView:由支持文件所有者概念的任何文件系统实现支持;
  5. AclFileAttributeView:支持读取或更新文件的访问控制列表(acl)。支持NFSv4 ACL模型。也可以支持任何具有到NFSv4模型的定义良好的映射的ACL模型,如Windows ACL模型;
  6. UserDefinedFileAttributeView:支持用户定义的元数据。此视图可以映射到系统支持的任何扩展机制。例如,在Solaris操作系统中,可以使用此视图存储文件的mime类型。

  在jdk中有两种方式可以批量获取文件的属性,一种是通过getFileAttributeView获取上述的6种视图;另一种是通readAttributes获取属性的关联对象。两种方法获取到的属性关联对象都是一样的,区别在于,前者支持修改文件的元数据。下面是一个简单的示例:

        Path file = Paths.get("D:\\test.txt");
        BasicFileAttributes attr;
        BasicFileAttributeView bf = Files.getFileAttributeView(file, BasicFileAttributeView.class);
        try {
            attr = bf.readAttributes();
            System.out.println("viewName : " + bf.name());
            System.out.println("creationTime: " + attr.creationTime());
            System.out.println("lastAccessTime: " + attr.lastAccessTime());
            System.out.println("lastModifiedTime: " + attr.lastModifiedTime());
            System.out.println("isDirectory: " + attr.isDirectory());
            System.out.println("isOther: " + attr.isOther());
            System.out.println("isRegularFile: " + attr.isRegularFile());
            System.out.println("isSymbolicLink: " + attr.isSymbolicLink());
            System.out.println("size: " + attr.size());
            long currentTime = System.currentTimeMillis();
            FileTime ft = FileTime.fromMillis(currentTime);
            // 修改文件的最后修改时间
            bf.setTimes(ft, null, null);
        } catch (IOException e) {
            e.printStackTrace();
        }
/*      // 另一种方式批量获取文件的元数据
        try {
            attr = Files.readAttributes(file, BasicFileAttributes.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
*/

文件、目录校验

  下面将介绍通过Files来校验文件或者目录是否存在、是否有相应的操作权限、比较两个Path指向的文件(注意跟Path的equals区分开,由于符号链接的存在,即使Path不同指向的文件可以相同)是否相同。

        Path p1 = Paths.get("D:\\test.txt");
        boolean exist = Files.exists(p1);
        System.out.println(exist);
        boolean notExists = Files.notExists(p1);
        System.out.println(notExists);

  不管是exists还是noExists方法都可以指定LinkOption参数,通过该参数可以处理符号链接。此外,判断一个文件或者目录是否存在需要考虑下面三种情况:

  1. 如果exists返回true,则文件存在;
  2. 如果noExists返回true,则文件不存在;
  3. 如果exists以及noExists都返回false,文件可能不存在,也可能是无法访问。

  通过api可以看到Files可以查看一个文件的多种操作权限,比较简单不再赘述。

创建、删除、复制、移动

   Files中提供了创建文件以及目录的方法,其中根据不同的文件系统支持不同的特性:

        Path path = Paths.get("D:\\test");
        Path file = Paths.get("D:\\test\\test2.txt");
        try {
            // 如果是POSIX系列标准(如Unix)的文件系统,可以指定文件的访问权限
/*            Set<PosixFilePermission> perms =
                    PosixFilePermissions.fromString("rwx------");
            FileAttribute<Set<PosixFilePermission>> attr =
                    PosixFilePermissions.asFileAttribute(perms);*/
            // 本例运行在Windows下
            Files.createDirectory(path);
            Files.createFile(file);
        } catch (IOException e) {
            e.printStackTrace();
        }

  由于例子比较简单不再赘述,下面介绍下复制、移动中的可选参数。

  jdk中定义了枚举类StandardCopyOption来提供三种复制、移动过程中的可选参数:

文件、目录读写

  在NIO.2Files类中可以选择使用字节流,字符流或者是通道的方式来读写文件。

  跟StandardCopyOption一样jdk提供了枚举类StandardOpenOption来指定文件的操作方式:

  字节流、字符流、通道等操作文件IO用法基本一致,不在赘述。

新旧File API比较

  下面是原有java.io.File API与java.nio.File API中的对应关系:

参考文献

https://docs.oracle.com/javase/tutorial/essential/io/fileio.html


版权声明:本文为u010890358原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。