讨论JDK的File.equal()

我们一般比较两个文件中的对象是相同的文件,通常使用java.io.File.equal()。这里,equal()是不是文件内容的比较结果为。象是否指向同一个文件。

File的equal()方法。实际上调用了当前文件系统FileSystem的compareTo()。

    public boolean equals(Object obj) {
        if ((obj != null) && (obj instanceof File)) {
            return compareTo((File)obj) == 0;
        }
        return false;
    }
    static private FileSystem fs = FileSystem.getFileSystem();
    public int compareTo(File pathname) {
        return fs.compare(this, pathname);
    }

我们发现,java.io.FileSystem中没有对Unix/Linux的实现,仅仅有Win32FileSystem,所以都是默认调用的这个实现类。 它对文件的比較,事实上就是对文件名称和绝对路径的比較。

假设两个File对象有同样的getPath(),就觉得他们是同一个文件。并且能看出来,Windows是不区分大写和小写的。

如以下的java.io.Win32FileSystem.compare()。

    public int compare(File f1, File f2) {
        return f1.getPath().compareToIgnoreCase(f2.getPath());
    }

这样通过比較绝对路径来检验两个对象是否指向同一个文件的方法,能适用大部分的情况,但也要小心。比方说,在Linux以下,文件名称对大写和小写是敏感的,就不能ignore了。并且通过硬链接建立的文件,实质还是指向同一个文件的,可是在File.equal()中却为false。

所以在JDK1.7后引入了工具类java.nio.file.Files,能够通过isSameFile()来推断两个文件对象是否指向同一个文件。

    public boolean isSameFile(Path path, Path path2) throws IOException {
        return provider(path).isSameFile(path, path2);
    }
    private static FileSystemProvider provider(Path path) {
        return path.getFileSystem().provider();
    }

他是获取当前系统的provider,再调用其isSameFile()来校验的。以下的FileSystem的实现层次结构:

        java.nio.file.spi.FileSystemProvider

            sun.nio.fs.AbstractFileSystemProvider

                sun.nio.fs.UnixFileSystemProvider

                    sun.nio.fs.LinuxFileSystemProvider

                sun.nio.fs.WindowsFileSystemProvider

我们先看看UnixFileSystemProvider.isSameFile() 是怎么实现的:

    public boolean isSameFile(Path obj1, Path obj2) throws IOException {
        UnixPath file1 = UnixPath.toUnixPath(obj1);
        if (file1.equals(obj2))
            return true;

        file1.checkRead();file2.checkRead();
        UnixFileAttributes attrs1 = UnixFileAttributes.get(file1, true);
        UnixFileAttributes attrs2 = UnixFileAttributes.get(file2, true);
        return attrs1.isSameFile(attrs2);
    }

他先调用了UnixPath.equal(),然后检查两个文件的可读性,最后再调用了UnixFileAttributes.isSameFile()。

非常显然,他会先检查两个文件的绝对路径是否同样(大写和小写敏感),假设同样的话,就觉得两者是同一个文件。假设不同,再检查两个文件的iNode号。

这是Unix文件系统的特点,文件是通过iNode来标识的,仅仅要iNode号同样,就说明指向同一个文件。

所以能用在推断两个硬链接是否指向同一个文件。

------------------------UnixPath------------------------

    public boolean equals(Object ob) {
        if ((ob != null) && (ob instanceof UnixPath))
            return compareTo((Path)ob) == 0;    // compare two path
        return false;
    }
    public int compareTo(Path other) {
        int len1 = path.length;
        int len2 = ((UnixPath) other).path.length;
        int n = Math.min(len1, len2);
        byte v1[] = path;
        byte v2[] = ((UnixPath) other).path;
        int k = 0;
        while (k < n) {
            int c1 = v1[k] & 0xff;
            int c2 = v2[k] & 0xff;
            if (c1 != c2)
                return c1 - c2;
        }
        return len1 - len2;
    }

------------------------UnixFileAttributes------------------------

    boolean isSameFile(UnixFileAttributes attrs) {
        return ((st_ino == attrs.st_ino) && (st_dev == attrs.st_dev));
    }

而对于Windows系统。也是大同小异,来看看WindowsFileSystemProvider.isSameFile(),WindowsPath.equal()和 WindowsFileAttributes.isSameFile()。

都是先推断文件绝对路径(忽略大写和小写),假设相等就觉得是同一个文件;假设不等就再进行底层推断。Windows底层文件的推断是检查磁盘号是否相等来完毕的。

------------------------ WindowsFileSystemProvider------------------------

    public boolean isSameFile(Path obj1, Path obj2) throws IOException {
        WindowsPath file1 = WindowsPath.toWindowsPath(obj1);
        if (file1.equals(obj2))
            return true;

        file1.checkRead();file2.checkRead();
        WindowsFileAttributes attrs1 =WindowsFileAttributes.readAttributes(h1);
        WindowsFileAttributes attrs2 =WindowsFileAttributes.readAttributes(h2);
        return WindowsFileAttributes.isSameFile(attrs1, attrs2);
    }

------------------------ WindowsPath ------------------------  

    public boolean equals(Object obj) {
        if ((obj != null) && (obj instanceof WindowsPath))
            return compareTo((Path)obj) == 0;
        return false;
    }
    public int compareTo(Path obj) {
        if (obj == null)
            throw new NullPointerException();
        String s1 = path;
        String s2 = ((WindowsPath)obj).path;
        int n1 = s1.length();
        int n2 = s2.length();
        int min = Math.min(n1, n2);
        for (int i = 0; i < min; i++) {
            char c1 = s1.charAt(i);
            char c2 = s2.charAt(i);
             if (c1 != c2) {
                 c1 = Character.toUpperCase(c1);
                 c2 = Character.toUpperCase(c2);
                 if (c1 != c2)
                     return c1 - c2;
             }
        }
        return n1 - n2;
    }

------------------------ WindowsFileAttributes------------------------

    static boolean isSameFile(WindowsFileAttributes attrs1, WindowsFileAttributes attrs2) {
        // volume serial number and file index must be the same
        return (attrs1.volSerialNumber == attrs2.volSerialNumber) &&
            (attrs1.fileIndexHigh == attrs2.fileIndexHigh) &&
            (attrs1.fileIndexLow == attrs2.fileIndexLow);
    }

    

这样一比較就清晰了。假设仅仅是对照文件的绝对路径是否相等(不是内容)。能够放心使用File.equal()。而假设要比較在OS中是否指向同一个文件。能够使用Files.isSameFile()。它考虑到了不同文件系统的差异。同一时候。我们通过观察这两种系统校验规则的不同实现,也能窥视到不同OS文件系统的差异。假设你有兴趣,能够进一步深入研究哦!

最后,付上一个OpenJava的源代码地址,你能够在里面找到JDK引用的非常多sun.xxx.xxx的源代码。比如上面提到的一系列sun.nio.fs.xxx。http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7-b147/sun/awt/shell/ShellFolder.java#ShellFolder.compareTo%28java.io.File%29

时间: 2024-10-11 19:10:39

讨论JDK的File.equal()的相关文章

浅谈JDK的File.equal()

我们一般比较两个文件对象是否同一个文件,一般会使用java.io.File.equal().这里所说的equal()并不是比较文件内容是否一样,而是看两个文件对象是否指向同一个文件. File的equal()方法,实际上调用了当前文件系统FileSystem的compareTo(). public boolean equals(Object obj) { if ((obj != null) && (obj instanceof File)) { return compareTo((File

用saltstack批量安装jdk和tomcat

jdk:            //install.sls jdk-install: file.managed: - name: /usr/local/src/jdk-8u65-linux-x64.tar.gz - source: salt://jdk/files/jdk-8u65-linux-x64.tar.gz - user: root - group: root - mode: 755 cmd.run: - name: cd /usr/local/src && tar zxf jdk

请慎用java的File#renameTo(File)方法(转)

以前我一直以为File#renameTo(File)方法与OS下面的 move/mv 命令是相同的,可以达到改名.移动文件的目的.不过后来经常发现问题:File#renameTo(File)方法会返回失败(false),文件没有移动,又查不出原因,再后来干脆弃用该方法,自己实现一个copy方法,问题倒是再也没有出现过. 昨天老板同学又遇到这个问题,File#renameTo(File)方法在windows下面工作的好好的,在linux下偶尔又失灵了.回到家我扫了一遍JDK中File#rename

salt部署jdk

使用普通用户安装jdk1.7,salt-minion端是ubuntu14.04系统64bit 普通用户如下: [email protected]:/srv/salt/base# cat init/user.sls  user:   user.present:     - name: www     - shell: /bin/bash #    - password: $1$ABpNeJ/S$YiTlhxqqjaSA/aQN5mYAE. 注:设置密码使用openssl passwd -1 123

请慎用java的File#renameTo(File)方法

转载地址:http://xiaoych.iteye.com/blog/149328 以前我一直以为File#renameTo(File)方法与OS下面的 move/mv 命令是相同的,可以达到改名.移动文件的目的.不过后来经常发现问题:File#renameTo(File)方法会返回失败(false),文件没有移动,又查不出原因,再后来干脆弃用该方法,自己实现一个copy方法,问题倒是再也没有出现过. 昨天老板同学又遇到这个问题,File#renameTo(File)方法在windows下面工作

从静态代码扫描引擎PMD源码学习-多线程任务模型和File过滤设计

不知不觉在工作中研究PMD并定制规则已经4个月左右了.其实PMD有许多值得我学习的源码,不过出于时间并不曾动笔.今天简单记录总结一下PMD的多线程和File过滤设计的源码. 1 public class MultiThreadProcessor extends AbstractPMDProcessor { 2 3 private ExecutorService executor; 4 private CompletionService<Report> completionService; 5

centos7编译JDK

参考: <深入理解Java虚拟机:JVM高级特性与最佳实践(第3版) - 周志明> 1.要编译的JDK源码 方法一:可以通过 一款叫 Mercurial (OpenJDK 的代码版本管理工具,类似于Git)代码版本管理工具从Respository中获取源码 安装后,执行 hg clone https://hg.openjd.java.net/jdk/jdk12 由于访问速度过慢,不推荐使用方法 一 方法二:直接在OpenJDK的官网上下载打包好的JDK12源码包 访问地址:http://hg.

菜鸟学SSH(十四)——Spring容器AOP的实现原理——动态代理

之前写了一篇关于IOC的博客--<Spring容器IOC解析及简单实现>,今天再来聊聊AOP.大家都知道Spring的两大特性是IOC和AOP. IOC负责将对象动态的注入到容器,从而达到一种需要谁就注入谁,什么时候需要就什么时候注入的效果,可谓是招之则来,挥之则去.想想都觉得爽,如果现实生活中也有这本事那就爽歪歪了,至于有多爽,各位自己脑补吧:而AOP呢,它实现的就是容器的另一大好处了,就是可以让容器中的对象都享有容器中的公共服务.那么容器是怎么做到的呢?它怎么就能让在它里面的对象自动拥有它

AOP的实现原理——动态代理

IOC负责将对象动态的 注入到容器,从而达到一种需要谁就注入谁,什么时候需要就什么时候注入的效果,可谓是招之则来,挥之则去.想想都觉得爽,如果现实生活中也有这本事那就爽 歪歪了,至于有多爽,各位自己脑补吧:而AOP呢,它实现的就是容器的另一大好处了,就是可以让容器中的对象都享有容器中的公共服务.那么容器是怎么做到 的呢?它怎么就能让在它里面的对象自动拥有它提供的公共性服务呢?答案就是我们今天要讨论的内容——动态代理. 动 态代理其实并不是什么新鲜的东西,学过设计模式的人都应该知道代理模式,代理模