`

Java7中那些新特性 - 5 (文件系统篇)

阅读更多
  一个文件系统通常指的是一个或多个根目录,其下面包含一定的文件和子目录,并由此组成的目录结构。每一种文件系统都支持一种文件存储机制。这种机制有可能是一个设备,例如C盘或一个磁盘分区,或者是其它的某种组织文件系统空间的方式。Java7中的java.nio.file.FileStore就代表了一种文件存储机制。下面来举例说明如何获得FileStore以及其相关信息。

      
    public static final long kiloByte = 1024;

    public static void main(String[] args) {

        String format = "%-16s %-20s %-8s %-8s %12s %12s %12s\n";

        System.out.printf(format, "Name", "Filesystem", "Type", "Readonly", "Size(KB)", "Used(KB)", "Available(KB)");

        FileSystem fileSystem = FileSystems.getDefault();

        try {

            for (FileStore fileStore : fileSystem.getFileStores()) {

                long totalSpace = fileStore.getTotalSpace() / kiloByte;
                long usedSpace = (fileStore.getTotalSpace() - fileStore.getUnallocatedSpace()) / kiloByte;
                long usableSpace = fileStore.getUsableSpace() / kiloByte;

                String name = fileStore.name();
                String type = fileStore.type();
                boolean readOnly = fileStore.isReadOnly();

                NumberFormat numberFormat = NumberFormat.getInstance();

                System.out.printf(format, name, fileStore, type, readOnly, numberFormat.format(totalSpace),
                        numberFormat.format(usedSpace), numberFormat.format(usableSpace));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

  上面这个例子中,我们在控制台上打印出来了各个磁盘的名称,类型以及磁盘空间使用情况。与此同时FileSystem类提供了getRootDirectories方法来获得磁盘的各个跟目录(Linux中通常只有一个根目录/,但Windows中通常有多个根目录C: D: E:)。参照下面的例子来看看如何打印出磁盘的各个根目录。

    public static void main(String[] args) {

        FileSystem fileSystem = FileSystems.getDefault();
        FileSystemProvider provider = fileSystem.provider();

        System.out.println("Provider: " + provider.toString());
        System.out.println("Open: " + fileSystem.isOpen());
        System.out.println("Read Only: " + fileSystem.isReadOnly());

        Iterable<Path> rootDirectories = fileSystem.getRootDirectories();

        System.out.println();
        System.out.println("Root Directories");

        for (Path path : rootDirectories) {
            System.out.println(path);
        }
    }


  当我们和一个目录文件系统打交道的时候,很多情况下都要遍历整个文件夹以及其子文件夹。Java7中对此也提供了新的API - java.nio.file.SimpleFileVisitor。话不多说直接看例子:
    public static void main(String[] args) {

        try {

            Path path = Paths.get("D:/Home/sample");
            ListFiles listFiles = new ListFiles();
            Files.walkFileTree(path, listFiles);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }


class ListFiles extends SimpleFileVisitor<Path> {

    private final int indentionAmount = 3;
    private int indentionLevel;

    public ListFiles() {
        indentionLevel = 0;
    }

    private void indent() {
        for (int i = 0; i < indentionLevel; i++) {
            System.out.print(' ');
        }
    }

    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
        indent();
        System.out.println("Visiting file:" + file.getFileName());
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
        indentionLevel = indentionLevel - indentionAmount;
        indent();
        System.out.println("Finished with the directory: " + dir.getFileName());
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
        indent();
        System.out.println("About to traverse the directory: " + dir.getFileName());
        indentionLevel = indentionLevel + indentionAmount;
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
        System.out.println("A file traversal error ocurred");
        return super.visitFileFailed(file, exc);
    }


  Files的walkFileTree方法以我们给定的一个目录作为根目录,对每一级文件夹都做了遍历。SimpleFileVisitor接口的每一个方法通过其名称就能知道其具体用途。本人理解即是对访问目录前,访问文件以及访问目录后的回调函数。每个方法都返回一个FileVisitResult,上层API由这个返回值来决定是否继续访问后面的目录以及文件。

  我们之前的博客提到过,如果想删除一个文件夹,文件夹必须是空的,那有没有办法能做到删除一个包含了很多目录和文件的文件夹呢?此时,我们就可以通过SimpleFileVisitor来实现。

    public static void main(String[] args) {

        try {
            Files.walkFileTree(Paths.get("D:/Home/sample/subtest"), new DeleteDirectory());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }



class DeleteDirectory extends SimpleFileVisitor<Path> {

    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
        System.out.println("Deleting " + file.getFileName());
        Files.delete(file);
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult postVisitDirectory(Path dir, IOException exception) throws IOException {
        if (exception == null) {
            System.out.println("Deleting " + dir.getFileName());
            Files.delete(dir);
            return FileVisitResult.CONTINUE;
        } else {
            throw exception;
        }
    }

}


  同样的方法,我们也可以复制一个文件夹下所有的内容到一个新文件夹下。
    public static void main(String[] args) {
        try {
            Path source = Paths.get("D:/Home/sample");
            Path target = Paths.get("D:/Home/backup");
            Files.walkFileTree(source, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, new CopyDirectory(
                    source, target));
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }



class CopyDirectory extends SimpleFileVisitor<Path> {

    private Path source;
    private Path target;

    public CopyDirectory(Path source, Path target) {
        this.source = source;
        this.target = target;
    }

    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
        System.out.println("Copying " + source.relativize(file));
        Files.copy(file, target.resolve(source.relativize(file)));
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
        Path targetDirectory = target.resolve(source.relativize(dir));
        try {
            System.out.println("Copying " + source.relativize(dir));
            Files.copy(dir, targetDirectory);
        } catch (FileAlreadyExistsException e) {
            if (!Files.isDirectory(targetDirectory)) {
                throw e;
            }
        }
        return FileVisitResult.CONTINUE;
    }

}


  最后我们来看看如何监控一个目录下的变化,并对不同变化作出一些处理。

    public static void main(String[] args) {

        try {

            FileSystem fileSystem = FileSystems.getDefault();
            WatchService watchService = fileSystem.newWatchService();

            Path dir = Paths.get("D:/home/sample");

            WatchEvent.Kind<?>[] events = {
                StandardWatchEventKinds.ENTRY_CREATE, //
                StandardWatchEventKinds.ENTRY_MODIFY, //
                StandardWatchEventKinds.ENTRY_DELETE
            };

            dir.register(watchService, events);

            while (true) {
                System.out.println("Waiting for a watch event");
                WatchKey watchKey = watchService.take();
                System.out.println("Path being watched: " + watchKey.watchable());
                System.out.println();

                if (watchKey.isValid()) {
                    for (WatchEvent<?> event : watchKey.pollEvents()) {
                        System.out.println("Kinds: " + event.kind());
                        System.out.println("Contnet: " + event.context());
                        System.out.println("Count: " + event.count());
                        System.out.println();
                    }

                    boolean valid = watchKey.reset();

                    if (!valid) {
                        // The watchKey is not longer registered
                    }
                }

            }

        } catch (IOException | InterruptedException e) {
            // TODO: handle exception
        }
    }

  当调用WatchService的take方法时,这个方法会阻塞当前线程,直到一个文件变化的事件发生。此时,一个WatchKey对象会被返回。WatchKey对象包含了所发生事件的相关信息。它的watchable方法返回的是所被观察的对象信息(例子中的D:/home/sample文件夹)。pollEvents方法返回了一个所有要被执行的事件列表。最后我们调用了watchKey的reset方法来将这个key设置成初始状态,接受新的响应事件。

  Java7中新增对于文件系统操作的API不少,在此只是列出了一些常会用到的功能,有一些API本人也没有完全理解,有待进一步的研究和学习。
分享到:
评论

相关推荐

    java私塾跟我学系列-java篇

    6. **IO流**:Java的输入/输出流(IO流)系统用于读写文件、网络通信,包括字节流、字符流、对象流等。 7. **多线程**:Java支持多线程编程,会讲解Thread类的使用,以及synchronized关键字、volatile变量、线程池...

    java学习教材(java私塾跟我学系列--java篇)

    ### Java学习教材(Java私塾跟我学系列--Java篇)知识点总结 #### 一、Java的历史与发展 - **Java的起源**:Java起源于Sun公司的Green项目,最初目的是为家用消费电子产品开发分布式代码系统。该项目起初尝试使用...

    JDK7新特性(完整篇)

    综上所述,JDK7的新特性涵盖了语言层面、库增强、并行处理和文件系统交互等多个方面,这些改进不仅提高了开发者的生产力,还为Java应用程序提供了更好的性能和扩展性。了解和掌握这些特性对于Java开发者来说至关重要...

    eclipse-java-2021-09-R-linux-gtk-x86_64.tar.gz

    本篇将详细解析"Eclipse IDE for Java Developers",即2021年9月发布的版本,具体文件为“eclipse-java-2021-09-R-linux-gtk-x86_64.tar.gz”,针对Linux x86_64架构的系统。 首先,Eclipse Java版是专为Java开发者...

    jdk-7u79-windows系统64位安装包

    本篇文章将详细讲解如何在Windows 64位系统上安装"jdk-7u79"版本,以及这个版本的一些关键特性和使用场景。 首先,我们需要了解JDK 7u79的特性。这是Oracle公司发布的一个更新版本,它包含了对Java 7平台的一系列...

    java1.7 特性实现

    在本篇文章中,我们将深入探讨Java 7的一些核心特性,并通过实际代码示例来理解它们的工作原理。 1. **自动资源管理 (try-with-resources)** Java 7引入了一个新的语法结构——`try-with-resources`,用于更有效地...

    Java语言程序设计-基础篇、进阶篇(原书第8版).pdf

    《Java语言程序设计-基础篇、进阶篇(原书第8版)》是一本全面且深入学习Java编程的权威教材。这本书分为基础篇和进阶篇,旨在为读者提供从入门到精通的完整Java学习路径。对于Java编程的初学者和有一定经验的开发者来...

    java-tutorial-2008-03-14

    5. **输入/输出(I/O)系统**:Java提供了丰富的I/O流API,用于处理文件读写、网络通信等。我们将会学习InputStream、OutputStream、Reader、Writer以及相关的流类。 6. **多线程**:Java支持多线程编程,允许程序...

    java技能百练--游戏篇

    首先,Java语言在游戏开发中的优势在于其跨平台兼容性,Java的"一次编写,到处运行"特性使得游戏可以在不同的操作系统上运行,这极大地扩大了潜在玩家群体。此外,Java拥有丰富的库和框架,如LWJGL(Lightweight ...

    深入解读Java12&13;新特性.zip

    本篇将重点解析Java 12和13中的关键新特性,帮助开发者更好地理解和运用这些改进。 **Java 12新特性** 1. **Switch Expressions (JEP 325)**: Java 12引入了开关表达式,这是对传统switch语句的扩展,使其更符合...

    JAVA程序员必读--基础篇chm

    "JAVA程序员必读--基础篇chm"这个压缩包文件显然是为那些想要深入理解Java编程基础知识的开发者准备的。CHM(Compiled Help Manual)是一种常见的Windows帮助文档格式,通常包含一系列章节和索引,便于读者按需查阅...

    java高级特性 - io流.pdf

    Java IO流是Java编程中处理输入输出流的高级特性,其目的是为了提供一个统一的方式来处理不同类型的数据,如文本文件、二进制文件等。Java的IO流是设计来实现数据传输的,主要使用java.io包下的各种类和接口。本篇...

    java访问共享文件

    这篇博客文章可能详细解释了如何在Java环境中实现对远程文件系统的访问,以便读取、写入或管理共享文件。 在Java中,可以使用开源库如jcifs(Java CIFS Client)来处理SMB/CIFS协议。jcifs库提供了SmbFile类,它...

    Java-核心篇.zip

    Java多线程编程是Java的一个关键特性,它允许程序同时执行多个任务,从而提高系统的效率和响应性。在"Java多线程编程实战指南-核心篇@www.java1234.com.pdf"这本书中,你可以期待以下知识点的详细讲解: 1. **线程...

    [Java]华为机试题 2011-9-17

    在Java开发中,常用的工具有IDE(如Eclipse、IntelliJ IDEA)、版本控制系统(Git)、构建工具(Maven、Gradle)、单元测试框架(JUnit、TestNG)等。熟练掌握这些工具能提高开发效率,确保代码质量。 对于“SWTest...

    Java语言程序设计-基础篇-进阶篇源码

    5. **IO/NIO**:Java的I/O系统升级到NIO(非阻塞I/O),Channel、Buffer和Selector的使用。 6. **网络编程**:Socket编程,实现客户端和服务端的通信。 7. **枚举与注解**:枚举类型的使用,注解的定义和应用,以及...

    Java语言程序设计-基础篇-原书第8版

    《Java语言程序设计-基础篇-原书第8版》是一部深入浅出、全面系统地讲解Java编程基础知识的优秀教材,对于初学者和有一定基础的程序员来说,都是一个极佳的学习资源。本书由Cay S. Horstmann与Gary Cornell共同编写...

    Java语言程序设计-进阶篇(原书第10版 中英文)

    5. 泛型:泛型是Java 5引入的新特性,用于增强类型安全性,减少强制类型转换。书中会详细讲述泛型的定义、使用以及通配符的概念。 6. 注解:注解在现代Java开发中广泛应用,如编译时校验、运行时元数据等。本书将...

    JAVA程序员必读--基础篇

    Java是世界上最流行的编程语言之一,尤其对于初学者来说,它具有清晰的语法和广泛的应用领域。...通过系统学习并实践书中所讲的内容,Java初学者可以成长为熟练的开发者,能够在各种项目中运用Java解决实际问题。

Global site tag (gtag.js) - Google Analytics