JDK8源码阅读之File(二)

来源:互联网 发布:淘宝外卖粮票是什么 编辑:程序博客网 时间:2024/05/23 22:11

文件操作

1.自动创建由该抽象路径名命名的新的空文件,当且仅当这个名字的文件不存在。检查文件的存在和文件的创建,如果文件不存在是否是一个可能影响该文件的所有其他的文件系统活动的单原子操作。

注意:这个方法不应该用于文件锁定,因为生成的协议不能稳定工作。应该使用java.nio.channels.FileLock工具代替。

当命名的文件不存在,并且成功创建的时候返回true;当命名的文件已经存在返回false。

如果出现I/O错误,会抛出IOException;当存在安全管理并且java.lang.SecurityManager的checkWrite方法拒绝对这个文件进行写操作就会抛出SecurityException。

 public boolean createNewFile() throws IOException {     SecurityManager security = System.getSecurityManager();     if (security != null) security.checkWrite(path);     if (isInvalid()) {         throw new IOException("Invalid file path");     }     return fs.createFileExclusively(path);}

2.删除由抽象路径名表示的文件或目录。如果这个路径名表示一个目录,这个目录必须是空的。

注意:当一个文件不能被删除时,java.nio.file.Files定义的delete方法会抛出IOException。对错误报告和诊断一个文件为什么不能被删除是十分有帮助的。

当且仅当文件或目录被成功删除时,返回true,否则返回false。

如果存在安全管理并且java.lang.SecurityManager类的checkDelete方法拒绝对这个文件进行删除操作,会抛出SecurityException。

 public boolean delete() {     SecurityManager security = System.getSecurityManager();     if (security != null) {         security.checkDelete(path);     }     if (isInvalid()) {         return false;     }     return fs.delete(this); }

3.要求当虚拟机终止时删除由该抽象路径名表示的文件或目录。文件(或目录)以相反的顺序被删除,并被注册。调用这个方法去删除已经被标记为删除的文件或目录不起作用。正如Java语言规范定义的那样,删除仅用于尝试虚拟机的正常终止。

一旦请求删除,是不能取消请求的。因此应该小心使用这个方法。

注意:这个方法不应该用于文件锁定,因为产生的协议不能稳定工作,应该使用java.nio.channels.FileLock工具代替。

如果存在安全管理和它的java.lang.SecurityManager的checkDelete方法拒绝对这个文件进行删除操作就会抛出SecurityException。

 public void deleteOnExit() {     SecurityManager security = System.getSecurityManager();     if (security != null) {         security.checkDelete(path);     }     if (isInvalid()) {         return;     }     DeleteOnExitHook.add(path);}

4.返回一个字符串数组,命名该抽象路径名表示的目录下的文件和目录。

如果该抽象路径名不表示一个目录,那么这个方法会返回null。否则会返回字符串数组,用于目录中的每个文件或目录。结果中不包括目录本身和目录的父目录的名称。每个字符串是一个文件名称而不是一个完整的路径。

不能保证结果数组中的名字字符串会以特定的顺序出现,也不能特别保证以字母顺序出现。

注意类java.nio.file.Files定义的newDirectoryStream方法用来打开一个目录和迭代目录中文件的名称。当使用大目录时可能会占用比较少的资源,并且在使用远程目录时会更具有响应性。

如果存在安全管理和SecurityManager类的checkRead方法拒绝对这个目录的读访问会抛出SecurityException。

public String[] list() {    SecurityManager security = System.getSecurityManager();    if (security != null) {        security.checkRead(path);    }    if (isInvalid()) {        return null;    }    return fs.list(this);}

5.返回一个字符串数组,命名此抽象路径名表示的目录下的文件和目录,满足指定的过滤器。除了返回的数组的字符串必须满足过滤器,这个方法的行为与list()是一致的。如果给定的过滤器是空的,接受所有的名字。否则,当且仅当在抽象路径名上调用过滤器的FilenameFilter.accept()方法时返回的值和它表示的目录中的文件或目录的名称,满足指定的过滤器。

public String[] list(FilenameFilter filter) {    String names[] = list();    if ((names == null) || (filter == null)) {        return names;    }    List<String> v = new ArrayList<>();    for (int i = 0 ; i < names.length ; i++) {        if (filter.accept(this, names[i])) {            v.add(names[i]);        }    }    return v.toArray(new String[v.size()]);}

6.返回由此抽象路径名表示的目录中的文件的抽象路径名数组。
如果该抽象路径名不表示一个目录,这个方法会返回null。否则会返回File对象的数组,在目录中的每个文件或目录。路径名表示目录本身和目录的父目录不会包含在结果中。每个生成的抽象路径名是该抽象路径名使用File(File,String)构造函数构造的。因此,如果这个路径名是绝对的,每一个生成的路径名也是绝对的;如果这个路径名是相对的,那么每个生成的路径名将是相对于同一个目录。

不保证在结果数组中的名字字符串会以特定的顺序出现;特别地也不能保证以字母的顺序出现。

注意java.nio.file.Files类定义了java.nio.file.Files的newDirectoryStream(Path)方法去打开目录和迭代目录中的文件名字。在操作非常大的目录的时候,可能会占用较少的资源。

如果该抽象路径名表示的的目录是空的,返回的数组是空的。如果该抽象路径名不表示一个目录,或是出现I/O错误,会返回null。

如果存在安全管理和它的SecurityManager类的checkRead(String)方法拒绝对这个目录的写操作,会抛出SecurityException。

public File[] listFiles() {    String[] ss = list();    if (ss == null) return null;    int n = ss.length;    File[] fs = new File[n];    for (int i = 0; i < n; i++) {        fs[i] = new File(ss[i], this);    }    return fs;}

7.返回该抽象路径名表示的目录中的文件或目录表示匹配指定的过滤器的抽象路径名的数组。除了返回的数组中的路径名必须匹配过滤器,这个方法的表现和listFiles()方法一样。如果给定的过滤器是null,会返回全部的路径名。否则,当且仅当在该抽象路径名上调用过滤器的FilenameFilter类的accept(File,String)方法,才会产生值和它表示的目录中的文件或目录。

 public File[] listFiles(FilenameFilter filter) {    String ss[] = list();    if (ss == null) return null;    ArrayList<File> files = new ArrayList<>();    for (String s : ss)        if ((filter == null) || filter.accept(this, s))            files.add(new File(s, this));    return files.toArray(new File[files.size()]);}

8.返回该抽象路径名表示的目录中的文件或目录的匹配指定过滤器的抽象路径名的数组。除了返回的数组中的路径名必须匹配过滤器,这个方法的表现与listFiles()方法是一致的。如果给定的过滤器是null,会返回全部的路径名。否则,当且仅当在路径名上调用过滤器的FileFilter类的accept(File,String)方法时,才会产生值。

public File[] listFiles(FileFilter filter) {    String ss[] = list();    if (ss == null) return null;    ArrayList<File> files = new ArrayList<>();    for (String s : ss) {        File f = new File(s, this);        if ((filter == null) || filter.accept(f))            files.add(f);    }    return files.toArray(new File[files.size()]);}

9.创建该抽象路径名命名的目录。当且仅当创建了目录,返回true;否则返回false。如果存在安全管理和它的SecurityManager类的checkWrite(String)方法不允许这个命名的目录被创建,会抛出SecurityException。

public boolean mkdir() {    SecurityManager security = System.getSecurityManager();    if (security != null) {        security.checkWrite(path);    }    if (isInvalid()) {        return false;    }    return fs.createDirectory(this);}

10.创建此抽象路径名命名的目录,包括任何有必要但不存在的父目录。注意如果这个操作失败,但是可能成功创建了一些必要的父目录。当且仅当创建了目录和所有必要的父目录时返回true,否则返回false。如果存在安全管理和它的SecurityManager类的checkRead(String)方法不允许验证存在的命名的目录和所有必需的父目录;或者如果java.lang.SecurityManager的checkWrite(String)方法不允许创建命名的目录和所有的必需的父目录。

 public boolean mkdirs() {    if (exists()) {        return false;    }    if (mkdir()) {        return true;    }    File canonFile = null;    try {        canonFile = getCanonicalFile();    } catch (IOException e) {        return false;    }    File parent = canonFile.getParentFile();    return (parent != null && (parent.mkdirs() || parent.exists()) && canonFile.mkdir());}

11.重新命名该抽象路径名表示的文件。
这个方法很多方面的行为本质上是平台独立的:重命名的操作也可能不能从一个文件系统移动文件到另一个,这可能不是原子的,并且如果具有目的抽象路径名的文件已经存在也许不会成功。应该检查返回的值以确保重命名操作是成功的。

注意java.nio.file.Files类定义move()方法以平台无关的方式移动或是重命名一个文件。当且仅当重命名成功,返回true,否则,返回false。如果存在安全管理和它的java.lang.SecurityManager的checkWrite(String)方法拒绝对新的或是旧的路径名进行写操作,抛出SecurityException。如果参数是null,抛出NullPointException。

 public boolean renameTo(File dest) {    SecurityManager security = System.getSecurityManager();    if (security != null) {        security.checkWrite(path);        security.checkWrite(dest.path);    }    if (dest == null) {        throw new NullPointerException();    }    if (this.isInvalid() || dest.isInvalid()) {        return false;    }    return fs.rename(this, dest);}

12.给该抽象路径名命名的文件或目录设置最后更新时间。
所有平台支持文件更新时间到最新的秒数,但有些支持更精确。参数将被截断以适应支持的精度。如果操作成功和没有对文件进行干预,那么下次调用lastModified方法会返回(可能的截断的)时间参数传递给这个方法。

参数是最新的更新时间,以毫秒为单位,自上个世纪(1970年1月1日,格林尼治标准时间00:00:00)开始。当前仅当操作成功,返回true,否则返回false。如果参数是负的,返回IllegalArgumentException。如果存在安全管理和它的java.lang.SecurityManager类的checkWrite(String)方法拒绝命名的文件的读操作。

public boolean setLastModified(long time) {    if (time < 0) throw new IllegalArgumentException("Negative time");    SecurityManager security = System.getSecurityManager();    if (security != null) {        security.checkWrite(path);    }    if (isInvalid()) {        return false;    }    return fs.setLastModifiedTime(this, time);}

13.标记由此抽象路径名命名的文件或目录,以便只允许读取操作。在调用这个方法之后,文件或是目录不会改变直到它删除或是标记可以被写访问。在一些平台,它可能以特殊的权限开启Java虚拟机,以便允许它修改被标记为只读的文件。无论是否删除只读文件或是目录,取决于底层系统。

public boolean setReadOnly() {    SecurityManager security = System.getSecurityManager();    if (security != null) {        security.checkWrite(path);    }    if (isInvalid()) {        return false;    }    return fs.setReadOnly(this);}

14.设置此抽象路径名的所有者或每个人的写权限。在一些平台,它可能以特殊的权限开启Java虚拟机,以便允许它修改不允许修改操作的文件。java.nio.file.Files类定义方法对文件属性进行操作包括文件权限。当需要更精细的文件权限操作时,可以使用这个选项。

参数writable如果为true,那么允许写操作。如果为false,不允许写操作。
参数ownerOnly如果为true,拥有者具有写权限;否则,所有人都具有。如果底层文件系统不能区分拥有者和其他人的的写权限,那么所有人都拥有权限,而不管这个值是什么。

当且仅当操作成功返回true。当用户不具备改变该抽象路径访问权限的权限的时候,操作失败。

public boolean setWritable(boolean writable, boolean ownerOnly) {   SecurityManager security = System.getSecurityManager();    if (security != null) {        security.checkWrite(path);    }    if (isInvalid()) {        return false;    }    return fs.setPermission(this, FileSystem.ACCESS_WRITE, writable, ownerOnly);}

15.一个为该抽象路径名设置拥有者的读权限的更简便的方法。在一些平台,它可能会以特殊的权限开启Java虚拟机,以便允许它修改文件来限制写操作。file.setWritable(args)的调用等价于file.setWritable(args,true)。

public boolean setWritable(boolean writable) {    return setWritable(writable, true);}

16.为该抽象路径名设置拥有者或每个人的读权限。在一些平台,它可能以特别的权限开启Java虚拟机,以便读取被标记为不可读的文件。java.nio.file.Files类定义了操作文件属性包括文件权限的方法。当要求更精确的文件权限,可以使用这个选项。

参数readable如果为true,允许设置读操作的访问权限;如果为false,表示不允许读操作。
参数ownerOnly如果为true,为拥有者设置读访问权限;否则,为每个人设置读访问权限。如果底层文件系统不能从其他人中区分拥有者的读权限,权限会被应用在每个人上,而不管这个值是什么。

当且仅当操作成功返回true,如果使用者没有权限改变该抽象路径名的访问权限,操作失败。如果readable的值是false和底层文件系统没有实现读权限,操作会失败。

 public boolean setReadable(boolean readable, boolean ownerOnly) {   SecurityManager security = System.getSecurityManager();    if (security != null) {        security.checkWrite(path);    }    if (isInvalid()) {        return false;    }    return fs.setPermission(this, FileSystem.ACCESS_READ, readable, ownerOnly);}

17.这个方法等价于setReadable(arg,true)

public boolean setReadable(boolean readable) {    return setReadable(readable, true);}

18.为该抽象路径名设置拥有者或是每个人的执行权限。

public boolean setExecutable(boolean executable, boolean ownerOnly) {    SecurityManager security = System.getSecurityManager();    if (security != null) {        security.checkWrite(path);    }    if (isInvalid()) {        return false;    }    return fs.setPermission(this, FileSystem.ACCESS_EXECUTE, executable, ownerOnly);}

19.这个方法等价于setExecutable(arg,true)。

public boolean setExecutable(boolean executable) {    return setExecutable(executable, true);}

20.测试一个应用是否可以执行该抽象路径名表示的文件。在一些平台,它可能以特别的权限开启Java虚拟机,以便允许执行被标记为不可执行的文件。所以这个方法可能会返回true,尽管文件没有执行的权限。

当且仅当抽象路径名存在并且应用允许执行文件的情况下返回true。如果存在安全管理和它的java.lang.SecurityManager类的checkExec(String)方法拒绝文件的执行权限,会抛出SecurityException。

public boolean canExecute() {    SecurityManager security = System.getSecurityManager();    if (security != null) {        security.checkExec(path);    }    if (isInvalid()) {        return false;    }    return fs.checkAccess(this, FileSystem.ACCESS_EXECUTE);}

文件系统接口

1.列出可用的文件系统根目录
一个特别的Java平台可能支持0或更多分层组织的文件系统。每一个文件系统都有一个根目录,可以访问文件系统中所有其他的文件。Windows平台,例如,每一个活动驱动器有一个根目录;UNIX平台有单个根目录,即“/”。可用的文件系统根目录的集合受各种不同的系统级别操作的影响,例如插入或弹出可移动的媒介和断开或卸下物理或虚拟磁盘驱动器。

这个方法返回File对象的数组表示可用的文件系统根目录。保证在本地机器上物理存在的任何文件的规范路径名将以这个方法返回的一个根开始。

驻留在一些其他机器上并可以通过远程文件系统协议访问的文件的规范路径名可能会也可能不会以此方法返回的一个根开始。如果远程文件的路径名与本地文件的路径名在语法上无法区分,那么它将会以这个方法返回的根之一开始。因此,例如,表示Windows平台的映射网络驱动的根目录的File对象会由这个方法返回,而包括UNC路径名的File对象不会由这个方法返回。

不像这个类的大多数方法,这个方法不会抛出security exception。如果存在安全管理和它的SecurityManager类的checkRead(String)方法拒绝对特别的根目录的写操作,那么目录不会出现在结果中。

这个方法返回表示可用的文件系统的根的File对象数组,或者如果无法确定根,返回null。如果没有文件系统根,会返回空。

public static File[] listRoots() {  return fs.listRoots();}

磁盘使用情况

2.通过这个抽象路径名返回分区的大小。
如果这个抽象路径名没有命名分区,那么这个分区或是0L的大小以字节为单位。如果安全管理已经建立和它拒绝RuntimePermission(“getFileSystemAttributes”)或者它的SecurityManager.checkRead(String)方法拒绝这个抽象路径名命名的文件的读操作。

public long getTotalSpace() {    SecurityManager sm = System.getSecurityManager();    if (sm != null) {        sm.checkPermission(new RuntimePermission("getFileSystemAttributes"));        sm.checkRead(path);    }    if (isInvalid()) {        return 0L;    }    return fs.getSpace(this, FileSystem.SPACE_TOTAL);}

3.通过此抽象路径名返回分区中未被分配的数量。
返回的未分配的字节的数量是一个提示,但不是保证,它可能使用大多数或任意这些字节。在这个方法调用之后,未分配的字节的数量更可能是准确的。任何外部I/O操作包括在虚拟机外部的系统上的操作都可能导致不准确。这个方法不保证对文件系统的写操作会成功。

 public long getFreeSpace() {    SecurityManager sm = System.getSecurityManager();    if (sm != null) {        sm.checkPermission(new RuntimePermission("getFileSystemAttributes"));        sm.checkRead(path);    }    if (isInvalid()) {        return 0L;    }    return fs.getSpace(this, FileSystem.SPACE_FREE);}

4.该抽象路径名上返回虚拟机上的分区的可用字节的数量。如果可能,这个方法检查写入权限和其他操作系统限制,因此通常比getFreeSpace()方法对实际可以写入多少新数据提供更精确的估计。

返回的可用字节的数量是个提示,不是保证,大多数或任何这些字节可能被使用。这次调用之后,未分配的字节数量更可能是准确的。有可能会因为任何外部I/O操作包括虚拟机之外的系统上的操作导致不准确。这个方法不保证对这个文件系统的写操作会成功。

这个方法最后返回分区可用字节的数量或者如果抽象路径名没有命名一个分区的时候,返回0L。在没有此信息的系统上,这个方法等价于调用getFreeSpace()方法。

如果已经建立系统管理员和它拒绝RuntimePermission(“getFileSystemAttributes”)或者它的SecurityManager.checkRead(String)方法拒绝该抽象路径名命名的文件的读操作,会抛出SecurityException。

public long getUsableSpace() {    SecurityManager sm = System.getSecurityManager();    if (sm != null) {        sm.checkPermission(new RuntimePermission("getFileSystemAttributes"));        sm.checkRead(path);    }    if (isInvalid()) {        return 0L;    }    return fs.getSpace(this, FileSystem.SPACE_USABLE);}

临时文件

1.临时目录类

private static class TempDirectory {    private TempDirectory() { }    //临时目录位置    private static final File tmpdir = new File(    AccessController.doPrivileged(new GetPropertyAction("java.io.tmpdir")));    static File location() {        return tmpdir;    }    //文件名称生成     private static final SecureRandom random = new SecureRandom();    static File generateFile(String prefix, String suffix, File dir)        throws IOException    {        long n = random.nextLong();        if (n == Long.MIN_VALUE) {            n = 0;      // corner case        } else {            n = Math.abs(n);        }        //只使用提供的前缀中的文件名称        prefix = (new File(prefix)).getName();        String name = prefix + Long.toString(n) + suffix;        File f = new File(dir, name);        if (!name.equals(f.getName()) || f.isInvalid()) {            if (System.getSecurityManager() != null)                throw new IOException("Unable to create temporary file");            else                throw new IOException("Unable to create temporary file, " + f);        }        return f;    }}

2.在指定的目录中创建新的空文件,使用给定的前缀和后缀字符串生成它的名字。如果方法成功返回将保证以下:
(1)返回的抽象路径名表示的文件在这个方法调用之前不存在。
(2)这个方法及其任何变体都不会在虚拟机的当前调用再次返回相同的抽象路径名。
这个方法提供临时文件设施的一部分。如果要让这个方法创建的文件自动删除,使用deleteOnExit方法。

前缀参数至少三个字符长。建议前缀是简短,有意义的字符串,类似”hjb”或者”mail”。后缀字符串必须为null,为null的时候会使用”.tmp”替代。为了创建新文件,前缀和后缀会首先调整以适应底层平台的限制。如果前缀太长会被截断,但是前三个字符总是会被保留。如果后缀太长也会被截断,但是如果后缀是是以句点符号”.”开始,那么句点符号”.”和接下来的前三个字符会被保留。一旦做出这些调整,新文件的名字的生成会通过连接前缀,五个或更多内部生成的字符和后缀来组成。

如果目录参数是null,那么会使用系统相关的默认的临时文件目录。默认的临时文件目录是由系统属性java.io.tmpdir指定的。在UNIX系统,这个属性的默认值是典型的”/tmp”或者”/var/tmp”。在Microsoft Windows系统,是典型的”C:\WINNT\TEMP”。当java虚拟机被调用,一个不同的值会被传递给这个系统属性,但是编程更改这个属性不保证对这个方法使用的临时目录产生影响。

public static File createTempFile(String prefix, String suffix,File directory)throws IOException{    if (prefix.length() < 3)        throw new IllegalArgumentException("Prefix string too short");    if (suffix == null)        suffix = ".tmp";    File tmpdir = (directory != null) ? directory : TempDirectory.location();    SecurityManager sm = System.getSecurityManager();    File f;    do {        f = TempDirectory.generateFile(prefix, suffix, tmpdir);        if (sm != null) {            try {                sm.checkWrite(f.getPath());            } catch (SecurityException se) {                // don't reveal temporary directory location                if (directory == null)                    throw new SecurityException("Unable to create temporary file");                throw se;            }        }    } while ((fs.getBooleanAttributes(f) & FileSystem.BA_EXISTS) != 0);    if (!fs.createFileExclusively(f.getPath()))        throw new IOException("Unable to create temporary file");    return f;}

3.这个方法等价于createTempFile(String,String,null)。java.nio.file.Files的createTempFile(String,String,java.nio.file.attribute.FileAttribute[])提供了另外一种方法在临时文件目录创建空文件。由那个方法创建的文件对这个方法创建的文件有更严格的访问权限,更适合安全敏感应用程序。

public static File createTempFile(String prefix, String suffix) throws IOException{    return createTempFile(prefix, suffix, null);}

基础设施

1.按照字典顺序比较两个抽象路径。这个方法定义的顺序取决于底层系统。在UNIX系统,字母顺序对比较路径名很重要;在Microsoft Windows系统不是。

public int compareTo(File pathname) {    return fs.compare(this, pathname);}

2.测试这个抽象路径名是否等同于给定的对象。当且仅当参数不是null,并且和当前抽象路径名表示的是同一个文件或目录。两个抽象路径名是否相同取决于底层系统。

public boolean equals(Object obj) {   if ((obj != null) && (obj instanceof File)) {        return compareTo((File)obj) == 0;    }    return false;}

3.计算抽象路径名的哈希值。因为抽象路径名的相等显然是系统相关的,所以计算它们的哈希值也是。在UNIX系统,抽象路径名的哈希值等于独占的或者它的路径名字符串的哈希码和十进制值1234321。在Microsoft Windows系统,哈希值等于独占的或者它的转成小写的路径名字符串的哈希值和十进制值1234321。小写路径名字符串不考虑语言环境。

public int hashCode() {    return fs.hashCode(this);}

4.返回抽象路径名的路径名字符串

public String toString() {    return getPath();}

5.WriteObject被调用来保存文件名。分隔符也会被保存以防路径会被重构到不同主机类型。

private synchronized void writeObject(java.io.ObjectOutputStream s) throws IOException{    s.defaultWriteObject();    s.writeChar(separatorChar); // Add the separator character}

6.readObject用来存储文件名。读取原始分隔符。如果和系统中的分隔符不同,本地分隔符取代旧的分隔符。

private synchronized void readObject(java.io.ObjectInputStream s) throws IOException,    ClassNotFoundException{    ObjectInputStream.GetField fields = s.readFields();    String pathField = (String)fields.get("path", null);    char sep = s.readChar(); // read the previous separator char    if (sep != separatorChar)        pathField = pathField.replace(sep, separatorChar);    String path = fs.normalize(pathField);    UNSAFE.putObject(this, PATH_OFFSET, path);    UNSAFE.putIntVolatile(this, PREFIX_LENGTH_OFFSET, fs.prefixLength(path));}private static final long PATH_OFFSET;private static final long PREFIX_LENGTH_OFFSET;private static final sun.misc.Unsafe UNSAFE;static {    try {        sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe();        PATH_OFFSET = unsafe.objectFieldOffset(File.class.getDeclaredField("path"));        PREFIX_LENGTH_OFFSET = unsafe.objectFieldOffset(File.class.getDeclaredField("prefixLength"));        UNSAFE = unsafe;    } catch (ReflectiveOperationException e) {        throw new Error(e);    }}//使用serialVersionUID增加互操作性private static final long serialVersionUID = 301077366599181567L

集成java.nio.file

1.从该抽象路径返回一个java.nio.file.Path对象。结果Path与java.nio.file.FileSystem.getDefault()得到的默认操作系统有关。

private volatile transient Path filePath;public Path toPath() {    Path result = filePath;    if (result == null) {        synchronized (this) {            result = filePath;            if (result == null) {                result = FileSystems.getDefault().getPath(path);                filePath = result;            }        }    }    return result;}
原创粉丝点击