Java 中文官方教程 2022 版(九)(1)

简介: Java 中文官方教程 2022 版(九)

链接,符号或其他

原文:docs.oracle.com/javase/tutorial/essential/io/links.html

如前所述,java.nio.file包,特别是Path类是“链接感知”的。每个Path方法都会检测遇到符号链接时该做什么,或者提供一个选项,使您能够配置遇到符号链接时的行为。

到目前为止的讨论一直是关于符号或链接,但一些文件系统也支持硬链接。硬链接比符号链接更受限制,具体如下:

  • 链接的目标必须存在。
  • 通常不允许在目录上创建硬链接。
  • 硬链接不允许跨分区或卷。因此,它们不能存在于不同文件系统之间。
  • 一个硬链接看起来和行为都像一个普通文件,所以它们可能很难找到。
  • 从所有方面来看,硬链接与原始文件是相同的实体。它们具有相同的文件权限、时间戳等。所有属性都是相同的。

由于这些限制,硬链接不像符号链接那样经常使用,但Path方法与硬链接无缝配合。

有几种方法专门处理链接,并在以下部分中介绍:

  • 创建符号链接
  • 创建硬链接
  • 检测符号链接
  • 查找链接的目标

创建符号链接

如果你的文件系统支持,你可以使用createSymbolicLink(Path, Path, FileAttribute)方法创建一个符号链接。第二个Path参数表示目标文件或目录,可能存在也可能不存在。以下代码片段创建了一个带有默认权限的符号链接:

Path newLink = ...;
Path target = ...;
try {
    Files.createSymbolicLink(newLink, target);
} catch (IOException x) {
    System.err.println(x);
} catch (UnsupportedOperationException x) {
    // Some file systems do not support symbolic links.
    System.err.println(x);
}

FileAttributes vararg 使您能够指定在创建链接时原子设置的初始文件属性。但是,这个参数是为将来使用而设计的,目前尚未实现。

创建硬链接

你可以使用createLink(Path, Path)方法创建一个到现有文件的硬(或常规)链接。第二个Path参数定位现有文件,它必须存在,否则会抛出NoSuchFileException。以下代码片段展示了如何创建链接:

Path newLink = ...;
Path existingFile = ...;
try {
    Files.createLink(newLink, existingFile);
} catch (IOException x) {
    System.err.println(x);
} catch (UnsupportedOperationException x) {
    // Some file systems do not
    // support adding an existing
    // file to a directory.
    System.err.println(x);
}

检测符号链接

要确定Path实例是否是符号链接,可以使用isSymbolicLink(Path)方法。以下代码片段展示了如何:

Path file = ...;
boolean isSymbolicLink =
    Files.isSymbolicLink(file);

欲了解更多信息,请参阅管理元数据。

查找链接的目标

通过使用readSymbolicLink(Path)方法,您可以获取符号链接的目标,如下所示:

Path link = ...;
try {
    System.out.format("Target of link" +
        " '%s' is '%s'%n", link,
        Files.readSymbolicLink(link));
} catch (IOException x) {
    System.err.println(x);
}

如果Path不是一个符号链接,该方法会抛出NotLinkException

遍历文件树

原文:docs.oracle.com/javase/tutorial/essential/io/walk.html

您是否需要创建一个应用程序,递归访问文件树中的所有文件?也许您需要删除树中的每个.class文件,或者找到在过去一年中未被访问的每个文件。您可以通过FileVisitor接口实现这一点。

本节涵盖以下内容:

  • FileVisitor 接口
  • 启动过程
  • 创建 FileVisitor 时的注意事项
  • 控制流程
  • 示例

FileVisitor 接口

要遍历文件树,首先需要实现一个FileVisitorFileVisitor指定了在遍历过程的关键点上所需的行为:当访问文件时,在访问目录之前,在访问目录之后,或者当发生故障时。该接口有四个方法对应于这些情况:

  • preVisitDirectory – 在访问目录条目之前调用。
  • postVisitDirectory – 在访问目录中的所有条目之后调用。如果遇到任何错误,特定异常将传递给该方法。
  • visitFile – 在访问文件时调用。文件的BasicFileAttributes被传递给该方法,或者您可以使用 file attributes 包来读取特定的属性集。例如,您可以选择读取文件的DosFileAttributeView来确定文件是否设置了“hidden”位。
  • visitFileFailed – 当无法访问文件时调用。特定异常被传递给该方法。您可以选择是否抛出异常,将其打印到控制台或日志文件等。

如果您不需要实现所有四个FileVisitor方法,而是扩展SimpleFileVisitor类,而不是实现FileVisitor接口。这个类实现了FileVisitor接口,访问树中的所有文件,并在遇到错误时抛出IOError。您可以扩展这个类,并仅覆盖您需要的方法。

这是一个扩展SimpleFileVisitor以打印文件树中所有条目的示例。它打印条目,无论条目是常规文件、符号链接、目录还是其他类型的“未指定”文件。它还打印每个文件的字节大小。遇到的任何异常都会打印到控制台。

FileVisitor方法以粗体显示:

import static java.nio.file.FileVisitResult.*;
public static class PrintFiles
    extends SimpleFileVisitor<Path> {
    // Print information about
    // each type of file.
    @Override
    public FileVisitResult visitFile(Path file,
                                   BasicFileAttributes attr) {
        if (attr.isSymbolicLink()) {
            System.out.format("Symbolic link: %s ", file);
        } else if (attr.isRegularFile()) {
            System.out.format("Regular file: %s ", file);
        } else {
            System.out.format("Other: %s ", file);
        }
        System.out.println("(" + attr.size() + "bytes)");
        return CONTINUE;
    }
    // Print each directory visited.
    @Override
    public FileVisitResult postVisitDirectory(Path dir,
                                          IOException exc) {
        System.out.format("Directory: %s%n", dir);
        return CONTINUE;
    }
    // If there is some error accessing
    // the file, let the user know.
    // If you don't override this method
    // and an error occurs, an IOException 
    // is thrown.
    @Override
    public FileVisitResult visitFileFailed(Path file,
                                       IOException exc) {
        System.err.println(exc);
        return CONTINUE;
    }
}

启动过程

一旦您实现了您的FileVisitor,如何启动文件遍历?Files类中有两个walkFileTree方法。

第一个方法只需要一个起始点和您的FileVisitor的实例。您可以按以下方式调用PrintFiles文件访问者:

Path startingDir = ...;
PrintFiles pf = new PrintFiles();
Files.walkFileTree(startingDir, pf);

第二个walkFileTree方法还允许您额外指定访问级别的限制和一组FileVisitOption枚举。如果您希望确保此方法遍历整个文件树,您可以为最大深度参数指定Integer.MAX_VALUE

您可以指定FileVisitOption枚举FOLLOW_LINKS,表示应该跟随符号链接。

此代码片段显示了如何调用四参数方法:

import static java.nio.file.FileVisitResult.*;
Path startingDir = ...;
EnumSet<FileVisitOption> opts = EnumSet.of(FOLLOW_LINKS);
Finder finder = new Finder(pattern);
Files.walkFileTree(startingDir, opts, Integer.MAX_VALUE, finder);

创建FileVisitor时的注意事项

文件树以深度优先方式遍历,但不能假设子目录的访问顺序。

如果您的程序将更改文件系统,您需要仔细考虑如何实现您的FileVisitor

例如,如果您正在编写递归删除,您首先删除目录中的文件,然后再删除目录本身。在这种情况下,您在postVisitDirectory中删除目录。

如果您正在编写递归复制,您需要在preVisitDirectory中创建新目录,然后尝试将文件复制到其中(在visitFiles中)。如果您想要保留源目录的属性(类似于 UNIX 的cp -p命令),您需要在文件被复制后,在postVisitDirectory中执行此操作。Copy示例展示了如何做到这一点。

如果您正在编写文件搜索,您可以在visitFile方法中执行比较。此方法找到所有符合您条件的文件,但不会找到目录。如果您想要找到文件和目录,您还必须在preVisitDirectorypostVisitDirectory方法中执行比较。Find示例展示了如何做到这一点。

你需要决定是否要遵循符号链接。例如,如果你正在删除文件,跟随符号链接可能不明智。如果你正在复制文件树,你可能希望允许它。默认情况下,walkFileTree不会遵循符号链接。

对于文件,会调用visitFile方法。如果你指定了FOLLOW_LINKS选项,并且你的文件树有一个指向父目录的循环链接,循环目录将在visitFileFailed方法中报告,带有FileSystemLoopException。以下代码片段显示了如何捕获循环链接,并来自于Copy示例:

@Override
public FileVisitResult
    visitFileFailed(Path file,
        IOException exc) {
    if (exc instanceof FileSystemLoopException) {
        System.err.println("cycle detected: " + file);
    } else {
        System.err.format("Unable to copy:" + " %s: %s%n", file, exc);
    }
    return CONTINUE;
}

这种情况只会在程序遵循符号链接时发生。

控制流程

也许你想要遍历文件树查找特定目录,并且在找到后希望进程终止。也许你想要跳过特定目录。

FileVisitor方法返回一个FileVisitResult值。你可以通过在FileVisitor方法中返回的值来中止文件遍历过程或控制是否访问目录:

  • CONTINUE – 表示文件遍历应该继续。如果preVisitDirectory方法返回CONTINUE,则会访问该目录。
  • TERMINATE – 立即中止文件遍历。在返回此值后不会调用更多的文件遍历方法。
  • SKIP_SUBTREE – 当preVisitDirectory返回此值时,指定的目录及其子目录将被跳过。这个分支将从树中“剪掉”。
  • SKIP_SIBLINGS – 当preVisitDirectory返回此值时,指定的目录不会被访问,postVisitDirectory不会被调用,也不会访问更多未访问的兄弟节点。如果从postVisitDirectory方法返回,不会访问更多的兄弟节点。基本上,在指定的目录中不会发生更多的事情。

在这段代码片段中,任何名为SCCS的目录都会被跳过:

import static java.nio.file.FileVisitResult.*;
public FileVisitResult
     preVisitDirectory(Path dir,
         BasicFileAttributes attrs) {
    (if (dir.getFileName().toString().equals("SCCS")) {
         return SKIP_SUBTREE;
    }
    return CONTINUE;
}

在这段代码片段中,一旦找到特定文件,文件名就会被打印到标准输出,并且文件遍历会终止:

import static java.nio.file.FileVisitResult.*;
// The file we are looking for.
Path lookingFor = ...;
public FileVisitResult
    visitFile(Path file,
        BasicFileAttributes attr) {
    if (file.getFileName().equals(lookingFor)) {
        System.out.println("Located file: " + file);
        return TERMINATE;
    }
    return CONTINUE;
}

示例

以下示例演示了文件遍历机制:

  • Find – 递归查找符合特定通配符模式的文件和目录。此示例在查找文件中讨论。
  • Chmod – 递归更改文件树上的权限(仅适用于 POSIX 系统)。
  • Copy – 递归复制文件树。
  • WatchDir – 演示了监视目录中已创建、删除或修改的文件的机制。使用-r选项调用此程序会监视整个树的更改。有关文件通知服务的更多信息,请参见监视目录的更改。

查找文件

原文:docs.oracle.com/javase/tutorial/essential/io/find.html

如果你曾经使用过 shell 脚本,你很可能使用过模式匹配来定位文件。事实上,你可能已经广泛使用了它。如果你还没有使用过,模式匹配使用特殊字符创建模式,然后文件名可以与该模式进行比较。例如,在大多数 shell 脚本中,星号,*,匹配任意数量的字符。例如,以下命令列出当前目录中以.html结尾的所有文件:

% ls *.html

java.nio.file包为这一有用功能提供了编程支持。每个文件系统实现都提供了一个PathMatcher。你可以通过在FileSystem类中使用getPathMatcher(String)方法来检索文件系统的PathMatcher。以下代码片段获取默认文件系统的路径匹配器:

String pattern = ...;
PathMatcher matcher =
    FileSystems.getDefault().getPathMatcher("glob:" + pattern);

传递给getPathMatcher的字符串参数指定语法风格和要匹配的模式。本示例指定了glob语法。如果你不熟悉 glob 语法,请参阅什么是 Glob。

Glob 语法易于使用和灵活,但如果你喜欢,也可以使用正则表达式,或regex语法。有关正则表达式的更多信息,请参阅正则表达式课程。一些文件系统实现可能支持其他语法。

如果你想使用其他形式的基于字符串的模式匹配,你可以创建自己的PathMatcher类。本页中的示例使用 glob 语法。

一旦你创建了PathMatcher实例,你就可以准备好根据它匹配文件。PathMatcher接口有一个方法,matches,它接受一个Path参数并返回一个布尔值:它要么匹配模式,要么不匹配。以下代码片段查找以.java.class结尾的文件并将这些文件打印到标准输出:

PathMatcher matcher =
    FileSystems.getDefault().getPathMatcher("glob:*.{java,class}");
Path filename = ...;
if (matcher.matches(filename)) {
    System.out.println(filename);
}

递归模式匹配

搜索与特定模式匹配的文件与遍历文件树密切相关。有多少次你知道一个文件在某处在文件系统上,但在哪里?或者也许你需要找到文件树中具有特定文件扩展名的所有文件。

Find示例正是如此。Find类似于 UNIX 的find实用程序,但功能更简化。你可以扩展这个示例以包含其他功能。例如,find实用程序支持-prune标志来排除搜索中的整个子树。你可以通过在preVisitDirectory方法中返回SKIP_SUBTREE来实现该功能。要实现-L选项,即跟随符号链接,你可以使用四个参数的walkFileTree方法,并传入FOLLOW_LINKS枚举(但请确保在visitFile方法中测试循环链接)。

要运行 Find 应用程序,请使用以下格式:

% java Find <path> -name "<glob_pattern>"

模式被放置在引号内,以防止 shell 解释任何通配符。例如:

% java Find . -name "*.html"

这里是Find示例的源代码:

/**
 * Sample code that finds files that match the specified glob pattern.
 * For more information on what constitutes a glob pattern, see
 * https://docs.oracle.com/javase/tutorial/essential/io/fileOps.html#glob
 *
 * The file or directories that match the pattern are printed to
 * standard out.  The number of matches is also printed.
 *
 * When executing this application, you must put the glob pattern
 * in quotes, so the shell will not expand any wild cards:
 *              java Find . -name "*.java"
 */
import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.*;
import static java.nio.file.FileVisitResult.*;
import static java.nio.file.FileVisitOption.*;
import java.util.*;
public class Find {
    public static class Finder
        extends SimpleFileVisitor<Path> {
        private final PathMatcher matcher;
        private int numMatches = 0;
        Finder(String pattern) {
            matcher = FileSystems.getDefault()
                    .getPathMatcher("glob:" + pattern);
        }
        // Compares the glob pattern against
        // the file or directory name.
        void find(Path file) {
            Path name = file.getFileName();
            if (name != null && matcher.matches(name)) {
                numMatches++;
                System.out.println(file);
            }
        }
        // Prints the total number of
        // matches to standard out.
        void done() {
            System.out.println("Matched: "
                + numMatches);
        }
        // Invoke the pattern matching
        // method on each file.
        @Override
        public FileVisitResult visitFile(Path file,
                BasicFileAttributes attrs) {
            find(file);
            return CONTINUE;
        }
        // Invoke the pattern matching
        // method on each directory.
        @Override
        public FileVisitResult preVisitDirectory(Path dir,
                BasicFileAttributes attrs) {
            find(dir);
            return CONTINUE;
        }
        @Override
        public FileVisitResult visitFileFailed(Path file,
                IOException exc) {
            System.err.println(exc);
            return CONTINUE;
        }
    }
    static void usage() {
        System.err.println("java Find <path>" +
            " -name \"<glob_pattern>\"");
        System.exit(-1);
    }
    public static void main(String[] args)
        throws IOException {
        if (args.length < 3 || !args[1].equals("-name"))
            usage();
        Path startingDir = Paths.get(args[0]);
        String pattern = args[2];
        Finder finder = new Finder(pattern);
        Files.walkFileTree(startingDir, finder);
        finder.done();
    }
}

递归遍历文件树的内容在遍历文件树中有详细介绍。

监视目录更改

原文:docs.oracle.com/javase/tutorial/essential/io/notification.html

你是否曾经发现自己正在编辑一个文件,使用 IDE 或另一个编辑器,并且出现一个对话框通知您文件系统中的一个打开文件已更改并需要重新加载?或者,就像 NetBeans IDE 一样,应用程序悄悄地更新文件而不通知您。以下示例对话框显示了使用免费编辑器jEdit时的通知外观:

jEdit 对话框显示检测到修改的文件

要实现此功能,称为文件更改通知,程序必须能够检测到文件系统上相关目录发生的变化。一种方法是轮询文件系统以查找更改,但这种方法效率低下。它不适用于具有数百个打开文件或目录需要监视的应用程序。

java.nio.file包提供了一个文件更改通知 API,称为 Watch Service API。此 API 使您能够向观察服务注册目录(或目录)。在注册时,您告诉服务您感兴趣的事件类型:文件创建、文件删除或文件修改。当服务检测到感兴趣的事件时,它会转发给注册的进程。注册的进程有一个专用于监视其注册事件的线程(或线程池)。当事件发生时,根据需要进行处理。

本节涵盖以下内容:

  • 观察服务概述
  • 试一试
  • 创建 Watch Service 并注册事件
  • 处理事件
  • 获取文件名
  • 何时使用和不使用此 API

Java 中文官方教程 2022 版(九)(2)https://developer.aliyun.com/article/1486343

相关文章
|
2天前
|
Web App开发 JavaScript 前端开发
《手把手教你》系列技巧篇(三十九)-java+ selenium自动化测试-JavaScript的调用执行-上篇(详解教程)
【5月更文挑战第3天】本文介绍了如何在Web自动化测试中使用JavaScript执行器(JavascriptExecutor)来完成Selenium API无法处理的任务。首先,需要将WebDriver转换为JavascriptExecutor对象,然后通过executeScript方法执行JavaScript代码。示例用法包括设置JS代码字符串并调用executeScript。文章提供了两个实战场景:一是当时间插件限制输入时,用JS去除元素的readonly属性;二是处理需滚动才能显示的元素,利用JS滚动页面。还给出了一个滚动到底部的代码示例,并提供了详细步骤和解释。
31 10
|
2天前
|
Java 测试技术 Python
《手把手教你》系列技巧篇(三十六)-java+ selenium自动化测试-单选和多选按钮操作-番外篇(详解教程)
【4月更文挑战第28天】本文简要介绍了自动化测试的实战应用,通过一个在线问卷调查(&lt;https://www.sojump.com/m/2792226.aspx/&gt;)为例,展示了如何遍历并点击问卷中的选项。测试思路包括找到单选和多选按钮的共性以定位元素,然后使用for循环进行点击操作。代码设计方面,提供了Java+Selenium的示例代码,通过WebDriver实现自动答题。运行代码后,可以看到控制台输出和浏览器的相应动作。文章最后做了简单的小结,强调了本次实践是对之前单选多选操作的巩固。
25 0
|
17小时前
|
算法 Java Python
保姆级Java入门练习教程,附代码讲解,小白零基础入门必备
保姆级Java入门练习教程,附代码讲解,小白零基础入门必备
|
20小时前
|
Web App开发 JavaScript 测试技术
《手把手教你》系列技巧篇(四十五)-java+ selenium自动化测试-web页面定位toast-上篇(详解教程)
【5月更文挑战第9天】本文介绍了在Appium中处理App自动化测试中遇到的Toast元素定位的方法。Toast在Web UI测试中也常见,通常作为轻量级反馈短暂显示。文章提供了两种定位Toast元素的技巧.
10 0
|
2天前
|
Web App开发 缓存 前端开发
《手把手教你》系列技巧篇(四十四)-java+ selenium自动化测试-处理https 安全问题或者非信任站点-下篇(详解教程)
【5月更文挑战第8天】这篇文档介绍了如何在IE、Chrome和Firefox浏览器中处理不信任证书的问题。作者北京-宏哥分享了如何通过编程方式跳过浏览器的证书警告,直接访问不受信任的HTTPS网站。文章分为几个部分,首先简要介绍了问题背景,然后详细讲解了在Chrome浏览器中的两种方法,包括代码设计和运行效果,并给出了其他浏览器的相关信息和参考资料。最后,作者总结了处理此类问题的一些通用技巧。
16 2
|
2天前
|
Java Android开发
【Java开发指南 | 第十八篇】Eclipse安装教程
【Java开发指南 | 第十八篇】Eclipse安装教程
11 2
|
2天前
|
Web App开发 JavaScript 前端开发
《手把手教你》系列技巧篇(四十三)-java+ selenium自动化测试-处理https 安全问题或者非信任站点-上篇(详解教程)
【5月更文挑战第7天】本文介绍了如何在Java+Selenium自动化测试中处理浏览器对不信任证书的处理方法,特别是针对IE、Chrome和Firefox浏览器。在某些情况下,访问HTTPS网站时会遇到证书不可信的警告,但可以通过编程方式跳过这些警告。
13 1
|
2天前
|
前端开发 Java 测试技术
《手把手教你》系列技巧篇(四十二)-java+ selenium自动化测试 - 处理iframe -下篇(详解教程)
【5月更文挑战第6天】本文介绍了如何使用Selenium处理含有iframe的网页。作者首先解释了iframe是什么,即HTML中的一个框架,用于在一个页面中嵌入另一个页面。接着,通过一个实战例子展示了在QQ邮箱登录页面中,由于输入框存在于iframe内,导致直接定位元素失败。作者提供了三种方法来处理这种情况:1)通过id或name属性切换到iframe;2)使用webElement对象切换;3)通过索引切换。最后,给出了相应的Java代码示例,并提醒读者根据iframe的实际情况选择合适的方法进行切换和元素定位。
9 0
|
2天前
|
前端开发 测试技术 Python
《手把手教你》系列技巧篇(四十一)-java+ selenium自动化测试 - 处理iframe -上篇(详解教程)
【5月更文挑战第5天】本文介绍了HTML中的`iframe`标签,它用于在网页中嵌套其他网页。`iframe`常用于加载外部内容或网站的某个部分,以实现页面美观。文章还讲述了使用Selenium自动化测试时如何处理`iframe`,通过`switchTo().frame()`方法进入`iframe`,完成相应操作,然后使用`switchTo().defaultContent()`返回主窗口。此外,文章提供了一个包含`iframe`的HTML代码示例,并给出了一个简单的自动化测试代码实战,演示了如何在`iframe`中输入文本。
17 3
|
2天前
|
JavaScript 前端开发 Java
《手把手教你》系列技巧篇(四十)-java+ selenium自动化测试-JavaScript的调用执行-下篇(详解教程)
【5月更文挑战第4天】本文介绍了如何使用JavaScriptExecutor在自动化测试中实现元素高亮显示。通过创建并执行JS代码,可以改变元素的样式,例如设置背景色和边框,以突出显示被操作的元素。文中提供了一个Java示例,展示了如何在Selenium中使用此方法,并附有代码截图和运行效果展示。该技术有助于跟踪和理解测试过程中的元素交互。
10 0