File类的源码学习


File类是Java中IO部分的一个类,用于表示文件或者目录的。关于File类有很多的常规操作这里就不介绍了,来看一下不常规的东西。


File英文意思是文件,但是它也可以用来表示目录,文件的概念还是比较广的。



public class File implements Serializable, Comparable<File>


File类实现的Comparable接口


File类实现了Serializable和Comparable接口,可是为什么要实现Comparable接口呢?



/**
 * 比较两个抽象路径的字典序.  比较的结果依赖于底层的文件系统.
 * 在UNIX上路径是大小写敏感的, 在Microsoft Windows系统上则不是.
 *
 * @param   pathname  The abstract pathname to be compared to this abstract
 *                    pathname
 *
 * @return  Zero if the argument is equal to this abstract pathname, a
 *          value less than zero if this abstract pathname is
 *          lexicographically less than the argument, or a value greater
 *          than zero if this abstract pathname is lexicographically
 *          greater than the argument
 *
 * @since   1.2
 */
 public int compareTo(File pathname) {
     return fs.compare(this, pathname);
 }



这就要看一下接口中的方法是干啥的。然后这就要和文件系统有关系。

其中有个fs.compare(),这个是文件系统对象的比较方法


File类中有一个关于文件系统的引用

abstract class FileSystem; // 一个抽象类表示文件系统


然后一个类是DefaultFileSystem,这个类中有一个静态方法返回本地默认的文件系统。


class DefaultFileSystem {

    /**
     * Return the FileSystem object for Windows platform.
     */
    public static FileSystem getFileSystem() {
        return new WinNTFileSystem();
    }
}


然后这个WinNTFileSystem继承的是抽象的FileSystem:

class WinNTFileSystem extends FileSystem;


WinNTFileSystem的compare方法为:

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


final和static配合使用

final表示的对象是不可变的,但是有时候我们需要根据情况来初始化final属性。这里在File类中用的方法就是static代码段。



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);
    }
}


前面三个final属性没有在定义的时候进行初始化,而是在static代码中进行的。这个可以借鉴一下哦。

文件过滤器


在列举一个目录的文件的时候使用了文件过滤器:

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()]);
}


先是由list方法列举这个文件下所有的文件(包括目录),然后将文件过滤器用于文件的过滤。

这也算一个小的设计模式吧。

文件删除的钩子

这个也可以学习一下,在虚拟机退出的时候如果干一点事的。

在File类中的:

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


package java.io;

import java.util.*;
import java.io.File;

/**
 * 虚拟机关闭时删除文件的钩子
 * 使用LinkedHashSet可以防止同名文件的二次插入, 同时可以快速移除
 */

class DeleteOnExitHook {
    private static LinkedHashSet<String> files = new LinkedHashSet<>();
    static {
        // DeleteOnExitHook必须是最后一个被激活的关闭虚拟机的钩子.
        sun.misc.SharedSecrets.getJavaLangAccess()
            .registerShutdownHook(2 /* Shutdown hook invocation order */,
                true /* register even if shutdown in progress */,
                new Runnable() {
                    public void run() {
                       runHooks();
                    }
                }
        );
    }

    private DeleteOnExitHook() {}

    // 这是一个将文件添加到删除列表的同步方法
    static synchronized void add(String file) {
    
        // 判断DeleteOnExitHook是否正在运行中
        if(files == null) {
            // DeleteOnExitHook正在运行中, 此时已经不能加入到删除文件列表了
            throw new IllegalStateException("Shutdown in progress");
        }

        files.add(file);
    }

    static void runHooks() {
        LinkedHashSet<String> theFiles;

        synchronized (DeleteOnExitHook.class) {
            theFiles = files;
            files = null;
        }

        ArrayList<String> toBeDeleted = new ArrayList<>(theFiles);

        // reverse the list to maintain previous jdk deletion order.
        // 最后进来的首先被删除
        Collections.reverse(toBeDeleted);
        for (String filename : toBeDeleted) {
            (new File(filename)).delete();
        }
    }
}


临时文件内部类

内部类还是挺有用的,在File类中就用到了内部类。

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);
        }

        // Use only the file name from the supplied prefix
        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;
    }
}




总结一下:

1. Comparable接口以及文件系统的封装

2. final和static的配合使用

3. 文件过滤器

4. 文件删除的钩子

5. 内部类的使用

原文地址:https://www.cnblogs.com/tuhooo/p/7858255.html