java进阶笔记之Path

背景

Path 是jdk1.7 推出的专用于访问文件的一种路径抽象.

  • 位于包 java.nio.file .

  • public interface Path extends Comparable<Path>, Iterable<Path>, Watchable

  • 一种可用于在文件系统中定位文件的对象。它通常表示一个系统相关的文件路径。

  • path 可以表示一个根、一个根和一系列名称,或者仅仅是一个或多个名称元素。

  • 使用空路径访问文件相当于访问文件系统的默认目录

  • 这个接口扩展了 Watchable接口,这样位于路径的目录可以是register registered,带有WatchService 和被监视目录中的条目。

  • 警告: 此接口仅供开发自定义文件系统实现的人员实现。方法可以在将来的版本中添加到这个接口。

  • Path和Files类一起用于操作文件、目录和其他类型的文件。

  • 并发性: 该接口的实现是不可变的,并且对多个并发线程的使用是安全的。

简析

ps: 文件的 watchService 是阻塞式的, 所以不存在空自旋带来的性能损耗.

demo示例

import org.junit.Test;
 
import java.io.File;
import java.io.IOException;
import java.nio.file.*;
 
public class PathTest {
    @Test
    public void baseTest() throws IOException, InterruptedException {
        Path path = Paths.get("/tmp","test01","test02");
        println("toString",path.toString());
        println("toUri",path.toUri());
        println("getNameCount",path.getNameCount());
        println("getName",path.getName(1));
        println("getRoot",path.getRoot());
        println("getParent",path.getParent());
        println("toAbsolutePath",path.toAbsolutePath());
        println("...","...");
        path.iterator().forEachRemaining(it -> {
            println("iterator",it.toString());
        });
        WatchService watchService = FileSystems.getDefault().newWatchService();
        Files.createDirectories(path);
        new Thread(() -> {
            try {
                Thread.sleep(2000);
                File file = path.resolve("file01").toFile();
                file.createNewFile();
                Thread.sleep(2000);
                file.delete();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
 
        //路径不存在会保存
        path.register(watchService, new WatchEvent.Kind<?>[]{StandardWatchEventKinds.ENTRY_CREATE,StandardWatchEventKinds.ENTRY_DELETE});
        boolean flag = true;
        while (flag) {
            WatchKey key = watchService.take();
            for (WatchEvent<?> event : key.pollEvents()) {
                //获取path下变动的的文件名
                String fileName = event.context().toString();
                println("fileWatcher,eventType: " + event.kind().name(),fileName);
                if(StandardWatchEventKinds.ENTRY_DELETE.equals(event.kind())){
                    flag = false;
                }
                key.reset();
            }
 
            System.out.println("...WatchKey ... cycle once..");
        }
        Thread.sleep(2000);
        println("end...","");
    }
    private void println(String title,Object object){
        System.out.println(title + ": " + object);
    }
}

输出:

toString: \tmp\test01\test02
toUri: file:///D:/tmp/test01/test02/
getNameCount: 3
getName: test01
getRoot: \
getParent: \tmp\test01
toAbsolutePath: D:\tmp\test01\test02
...: ...
iterator: tmp
iterator: test01
iterator: test02
fileWatcher,eventType: ENTRY_CREATE: file01
...WatchKey ... cycle once..
fileWatcher,eventType: ENTRY_DELETE: file01
...WatchKey ... cycle once..
end...:

源码简析

/**
* @since 1.7
* @see Paths
*/
public interface Path
    extends Comparable<Path>, Iterable<Path>, Watchable
{
    /**
     * @return  返回创建当前对象的文件系统
     */
    FileSystem getFileSystem();
 
    /**
     * 判断路径是否是绝对路径
     */
    boolean isAbsolute();
 
    /**
     * 返回rootPath , 或者 null ( 如果没有)
     */
    Path getRoot();
 
    /**
     * 返回Path的文件/文件夹 名称 .文件名是Path中路径最远的元素. 或者 null 如果Path中
     * 没有任何元素
     */
    Path getFileName();
 
    /**
     * 如果此Path有一个或者多个元素,但是没有Parent , 则等同与调用表达式:
     * subpath(0, getNameCount()-1);
     */
    Path getParent();
 
    /**
     * 即Path中层次的深度, 默认为 只有根元素是 返回 0
     * 如: /a/b/c = 2
     */
    int getNameCount();
 
    /**
     * 返回第index + 1层的路径值.
     */
    Path getName(int index);
 
    /**
     * 截取path , 默认根元素是 index=0
     */
    Path subpath(int beginIndex, int endIndex);
 
    /**
     * Tests if this path starts with the given path.
     * 文件系统不同,则返回false
     */
    boolean startsWith(Path other);
 
    /**
     * Tests if this path starts with a  Path constructed by converting
     * 注意: /foo/ab  start whith /foo  not /fo
     */
    boolean startsWith(String other);
 
    /**
     * Tests if this path ends with the given path.
     *
     * 如果给定路径有N个元素,没有根元素; 而这条路径有N个或更多的元素,那么如果
     * 每条路径的最后N个元素,从离根最远的元素开始,相等,那么这条路径结束于给定的路径。
     * ps: 即当前路径是相对的, 且从叶子路径逆序作比较均和给定路径相等 , 则返回true.
     * 如果 FileSystem 不同 , 返回false
     */
    boolean endsWith(Path other);
 
    /**
     * Tests if this path ends with a code Path
     * 例如,在UNIX上,路径"foo/bar"以"foo/bar"和"bar"结尾。
     * 它不以“r”或“/bar”结尾。注意,后面的分隔符没有考虑在内,所以在路
     * 径“foo/bar”上调用这个方法并使用字符串“bar/”返回true。
     */
    boolean endsWith(String other);
 
    /**
     * 返回删除冗余名称元素的此路径。
     * 此方法的精确定义依赖于实现,但通常它派生于此路径,此路径不包含冗余的name元素。
     * 在许多文件系统中,“ .”和“..” 是用于指示当前目录和父目录的特殊名称。在这样的
     * 文件系统中,所有出现的“. ”都被认为是多余的。如果一个".. "前面有一个
     * 非".. "然后这两个名称都被认为是多余的(识别这些名称的过程会重复,直到不再适用)。
     * 此方法不访问文件系统;该路径可能无法找到已存在的文件。消除“. ."和路径的前一个
     * 名称可能导致路径中所定位的文件与原始路径不同。如果前面的名称是符号链接,就会
     * 出现这种情况。
     * @see #getParent
     * @see #toRealPath
     */
    Path normalize();
 
    // -- resolution and relativization --
 
    /**
     * Resolve the given path against this path.
     * 如果other是绝对路径,则直接返回; 否则和当前路径组装为绝对路径.
     */
    Path resolve(Path other);
 
    Path resolve(String other);
 
    /**
     * 根据相同的父目录组织地址;
     * 如果没有相同的路径则返回  empty path.
     * eg: this = dir1/dir2/foo ; other = bar , re = dir1/dir2/bar
     *
     * @see #resolve(Path)
     */
    Path resolveSibling(Path other);
 
    Path resolveSibling(String other);
 
    /**
     * Constructs a relative path between this path and a given path.
     * 在此路径和给定路径之间构造相对路径。
     * 例如,在UNIX上,如果这个路径是“/a/b”,而给定的路径是“/a/b/c/d”,那么得到
     * 的相对路径就是“c/d” ; 如果此路径和给定路径没有根组件,则可以构造相对路径。
     * 如果只有一个路径具有根组件,则不能构造相对路径。如果两个路径都有一个根组件,
     * 那么如果可以构造一个相对路径,那么它是依赖于实现的。如果此路径和给定路径相
     * 等,则返回一个空路径。
     * 对于任意两条归一化路径p和q,其中q没有根分量,
     * p.relativize (p.resolve (q)) .equals (q)
     * 当支持符号链接时,结果路径(根据此路径解析)是否生成可用于定位相同文件的路径取
     * 决于实现。例如,如果这个路径是“/a/b”,给定的路径是“/a/x”,那么得到的相对路
     * 径可能是“../x”。如果"b"是一个符号链接,那么实现取决于"a/b/.. ""将与"/a/x"
     * 定位相同的文件。
     */
    Path relativize(Path other);
 
    /**
     * Returns a URI to represent this path.
     * 返回一个URI , 能代表此path, 一般用绝对路径.
     * Path可以理解为是一个特定于文件系统的复合URI
     */
    URI toUri();
 
    /**
     * 通过文件系统的实现将之转为
     */
    Path toAbsolutePath();
 
    /**
     * 返回现有文件的真实路径。绝对路径.
     * 这个方法的精确定义依赖于实现,但通常它派生于这个路径,一个绝对路径,它将
     * isSameFile 文件定位为这个路径,但是名称元素表示目录和文件的实际名称。
     * 例如,在文件系统上的文件名比较不区分大小写的情况下,name元素以其实际大小
     * 写表示名称。此外,生成的路径删除了冗余的name元素。
     * 如果这个路径是相对的,那么首先获得它的绝对路径,toAbsolutePath方法一样。
     *  options 数组可以用来表示符号链接的处理方式。默认情况下,符号链接被解析
     * 到最终目标。如果选项LinkOption#NOFOLLOW_LINKS存在,那么这个方法不会
     * 解析符号链接。
     * 当不解析符号链接且前面的名称是符号链接时,只有在保证结果路径将定位到与此
     * 路径相同的文件时,名称才会被删除。
     *
     */
    Path toRealPath(LinkOption... options) throws IOException;
 
    /**
     * 如果此Path是通过File得到的,又通过此path#toFile创建了File
     * 则这两个File可能并不equals.
     */
    File toFile();
 
    // -- watchable --
 
    /**
     * 用一个监视服务注册此路径中找到的文件。
     * 在此版本中,此路径定位一个已存在的目录。该目录向监视服务注册,以便可以
     * 监视目录中的条目。参数是要注册的事件,可以包含以下事件:
     *  standardwatcheventtypes 下的
     *    #ENTRY_CREATE ENTRY_CREATE  已创建或移动到目录中的条目
     *  #ENTRY_DELETE ENTRY_DELETE} 条目被删除或移出目录
     *  #ENTRY_MODIFY ENTRY_MODIFY} 目录中的条目被修改
     * 这些事件的WatchEvent#context是该路径所定位的目录和定位被创建、删除
     * 或修改的目录条目的路径之间的相对路径。
     * 事件集可能包括enum standardwatcheventtypes未定义的其他实现特定事件。
     * 参数指定限定目录注册方式的修饰符。此版本没有定义任何标准修饰符。它可能包含
     * 实现特定的修饰符。
     * 如果一个文件是通过符号链接在手表服务中注册的,那么在注册后如果watch 继续
     * 依赖符号链接的存在,那么它是特定于实现的。
     *
     * @param   watcher
     *          the watch service to which this object is to be registered
     * @param   events
     *          the events for which this object should be registered
     * @param   modifiers
     *          修改对象注册方式的修饰符(如果有的话)
     *
     * @return  a key representing the registration of this object with the
     *          given watch service
     */
    @Override
    WatchKey register(WatchService watcher,
                      WatchEvent.Kind<?>[] events,
                      WatchEvent.Modifier... modifiers)
        throws IOException;
 
    @Override
    WatchKey register(WatchService watcher,
                      WatchEvent.Kind<?>... events)
        throws IOException;
 
    // -- Iterable --
 
    /**
     * 在此路径的name元素上返回一个迭代器。
     * 迭代器返回的第一个元素表示目录层次结构中最接近根的name元素,第二个元素是下一
     * 个最接近根的元素,依此类推。最后返回的元素是此路径所表示的文件或目录的名称。如
     * 果存在 getRoot组件,则迭代器不会返回该组件。
     * ps: 相当于根据Path 和分隔符截取字符串 , 没有使用文件系统
     * @return  an iterator over the name elements of this path.
     */
    @Override
    Iterator<Path> iterator();
 
    // -- compareTo/equals/hashCode --
 
    /**
     * 按照字母顺序比较两个路径
     */
    @Override
    int compareTo(Path other);
 
    /**
     * Tests this path for equality with the given object.
     * 如果 FileSystem 不同, 则返回false ; 否则比较
     */
    boolean equals(Object other);
 
    int hashCode();
 
    /**
     * 返回能代表这个Path的String
     */
    String toString();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值