File是相当常用的一个类,也是IO包中的基础类之一
File类引用了如下包
import java.net.URI; import java.net.URL; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.util.List; import java.util.ArrayList; import java.security.AccessController; import java.security.SecureRandom; import java.nio.file.Path; import java.nio.file.FileSystems; import sun.security.action.GetPropertyAction;
继承了如下接口
implements Serializable, Comparable<File>
该类的类头注释如下(比较长,毕竟功能比较多)
/** * An abstract representation of file and directory pathnames. * * <p> User interfaces and operating systems use system-dependent <em>pathname * strings</em> to name files and directories. This class presents an * abstract, system-independent view of hierarchical pathnames. An * <em>abstract pathname</em> has two components: * * <ol> * <li> An optional system-dependent <em>prefix</em> string, * such as a disk-drive specifier, <code>"/"</code> for the UNIX root * directory, or <code>"\\\\"</code> for a Microsoft Windows UNC pathname, and * <li> A sequence of zero or more string <em>names</em>. * </ol> * * The first name in an abstract pathname may be a directory name or, in the * case of Microsoft Windows UNC pathnames, a hostname. Each subsequent name * in an abstract pathname denotes a directory; the last name may denote * either a directory or a file. The <em>empty</em> abstract pathname has no * prefix and an empty name sequence. * * <p> The conversion of a pathname string to or from an abstract pathname is * inherently system-dependent. When an abstract pathname is converted into a * pathname string, each name is separated from the next by a single copy of * the default <em>separator character</em>. The default name-separator * character is defined by the system property <code>file.separator</code>, and * is made available in the public static fields <code>{@link * #separator}</code> and <code>{@link #separatorChar}</code> of this class. * When a pathname string is converted into an abstract pathname, the names * within it may be separated by the default name-separator character or by any * other name-separator character that is supported by the underlying system. * * <p> A pathname, whether abstract or in string form, may be either * <em>absolute</em> or <em>relative</em>. An absolute pathname is complete in * that no other information is required in order to locate the file that it * denotes. A relative pathname, in contrast, must be interpreted in terms of * information taken from some other pathname. By default the classes in the * <code>java.io</code> package always resolve relative pathnames against the * current user directory. This directory is named by the system property * <code>user.dir</code>, and is typically the directory in which the Java * virtual machine was invoked. * * <p> The <em>parent</em> of an abstract pathname may be obtained by invoking * the {@link #getParent} method of this class and consists of the pathname's * prefix and each name in the pathname's name sequence except for the last. * Each directory's absolute pathname is an ancestor of any <tt>File</tt> * object with an absolute abstract pathname which begins with the directory's * absolute pathname. For example, the directory denoted by the abstract * pathname <tt>"/usr"</tt> is an ancestor of the directory denoted by the * pathname <tt>"/usr/local/bin"</tt>. * * <p> The prefix concept is used to handle root directories on UNIX platforms, * and drive specifiers, root directories and UNC pathnames on Microsoft Windows platforms, * as follows: * * <ul> * * <li> For UNIX platforms, the prefix of an absolute pathname is always * <code>"/"</code>. Relative pathnames have no prefix. The abstract pathname * denoting the root directory has the prefix <code>"/"</code> and an empty * name sequence. * * <li> For Microsoft Windows platforms, the prefix of a pathname that contains a drive * specifier consists of the drive letter followed by <code>":"</code> and * possibly followed by <code>"\\"</code> if the pathname is absolute. The * prefix of a UNC pathname is <code>"\\\\"</code>; the hostname and the share * name are the first two names in the name sequence. A relative pathname that * does not specify a drive has no prefix. * * </ul> * * <p> Instances of this class may or may not denote an actual file-system * object such as a file or a directory. If it does denote such an object * then that object resides in a <i>partition</i>. A partition is an * operating system-specific portion of storage for a file system. A single * storage device (e.g. a physical disk-drive, flash memory, CD-ROM) may * contain multiple partitions. The object, if any, will reside on the * partition <a name="partName">named</a> by some ancestor of the absolute * form of this pathname. * * <p> A file system may implement restrictions to certain operations on the * actual file-system object, such as reading, writing, and executing. These * restrictions are collectively known as <i>access permissions</i>. The file * system may have multiple sets of access permissions on a single object. * For example, one set may apply to the object's <i>owner</i>, and another * may apply to all other users. The access permissions on an object may * cause some methods in this class to fail. * * <p> Instances of the <code>File</code> class are immutable; that is, once * created, the abstract pathname represented by a <code>File</code> object * will never change. * * <h3>Interoperability with {@code java.nio.file} package</h3> * * <p> The <a href="../../java/nio/file/package-summary.html">{@code java.nio.file}</a> * package defines interfaces and classes for the Java virtual machine to access * files, file attributes, and file systems. This API may be used to overcome * many of the limitations of the {@code java.io.File} class. * The {@link #toPath toPath} method may be used to obtain a {@link * Path} that uses the abstract path represented by a {@code File} object to * locate a file. The resulting {@code Path} may be used with the {@link * java.nio.file.Files} class to provide more efficient and extensive access to * additional file operations, file attributes, and I/O exceptions to help * diagnose errors when an operation on a file fails. * * @author unascribed * @since JDK1.0 */
大意如下
该类是一个文件和目录的路径名的抽象表示形式
用户界面和操作系统使用基于操作系统的命名方法,使用路径字符串对目录和目录命名
该类表现了一个不基于操作系统的、抽象的的层级路径视图
抽象路径分为两个组成部分:
1.可选的基于系统的前缀字符串,比如:磁盘驱动器的标识符。‘/’表示UNIX的根目录,‘\\\’表示windows的UNC目录
2.一个含有零个或更多字符的字符串序列
抽象路径的第一个名称可能是路径名,对于UNC来说就是主机名
抽象路径第一个之后的名称,每一个都表示一个目录名称,最后的名称可以表示目录或者文件
空路径没有前缀和名称序列
实际路径的字符串与抽象路径的转换方法取决于操作系统(操作系统固有)
当一个抽象路径转化为实际路径字符串时,每一个名字之间用默认的分隔符相互分离
默认分隔符通过操作系统的属性file.separator定义,可以调用此类的公共静态字段separator和separatorChar来使用
当实际路径字符串转化为抽象路径时,其中的名字可以被默认的名称分隔符分隔或者系统支持的其他任意名称分隔符分隔
无论是抽象路径还是实际路径字符串,都只能是相对路径名或绝对路径名
绝对路径是完整的,不需要其他任何信息去定位其表示的文件
相对路径相比之下必须要依靠其他路径信息来解释该路径指向
默认情况下,在IO包中的该类总是使用当前用户目录来解释相对路径
该目录由系统属性user.dir指定,并且该目录通常由java虚拟机调用
抽象路径的父路径通常通过调用该类的getParent()方法获得(包括前缀和每一级的名称,直到本级)
每一个目录的绝对路径是许多含有从该路径开始的绝对抽象路径的File对象的原型
例如,表示为‘/usr’的抽象路径是表示为‘/usr/local/bin’的抽象路径的原型
前缀概念用于:操作UNIX平台的根目录;在windows下使用根目录、特殊标识符和UNC的路径名。如下所示:
1. 对于 UNIX 平台,绝对路径名的前缀始终是 "/"
。相对路径名没有前缀。表示根目录的绝对路径名的前缀为 "/"
且名称序列为空。
2.对于 Microsoft Windows 平台,包含盘符的路径名前缀由驱动器号和一个 ":"
组成。如果路径名是绝对路径名,还可能后跟 "\\"
。UNC 路径名的前缀是 "\\\\"
;主机名和共享名是名称序列中的前两个名称。没有指定驱动器的相对路径名没有前缀。
此类的实例可能表示(也可能不表示)实际文件系统对象,如文件或目录。如果它表示这种对象,那么该对象驻留在一个分区 中。分区是文件系统特定于操作系统的存储分区。一个存储设备(例如,物理磁盘驱动器、闪存、CD-ROM)可以包含多个分区。对象(如果有)将驻留在此路径名(绝对形式)某个祖先指定的分区上。
文件系统可以实现对实际文件系统对象上的某些操作(比如,读、写、执行)进行限制。这些限制统称为访问权限。文件系统可以对一个对象设置多个访问权限。例如,一个设置可能适用于对象的所有者,另一个设置则可能适用于所有其他用户。对象上的访问权限可能导致此类的某些方法执行失败。
File类的实例是不可变的;也就是说,一旦创建,File对象表示的抽象路径名将永不改变。
File类包含如下的成员变量:
文件系统
private static final FileSystem fs = DefaultFileSystem.getFileSystem();
文件路径
private final String path;
文件路径状态枚举(无效,已审核)
private static enum PathStatus { INVALID, CHECKED };
文件路径状态(transient修饰的变量不能被串行化)
private transient PathStatus status = null;
抽象路径前缀名长度
private final transient int prefixLength;
路径分隔符(路径中的分隔符,如‘/’,‘//’)
public static final char separatorChar = fs.getSeparator();
字符串格式的路径分隔符
public static final String separator = "" + separatorChar;
路径序列的分隔符(分离多个路径,如‘:’,‘;’)
public static final char pathSeparatorChar = fs.getPathSeparator();
字符串格式的路径序列分隔符
public static final String pathSeparator = "" + pathSeparatorChar;
静态代码块绑定的常量
private static final long PATH_OFFSET; private static final long PREFIX_LENGTH_OFFSET; private static final sun.misc.Unsafe UNSAFE;
串行序列号
private static final long serialVersionUID = 301077366599181567L;
路径(要求每次使用都要读值,不会被编译器优化,同时不会被序列化)
private volatile transient Path filePath;
File 类包含如下的方法:
判定文件路径状态是否为无效(仅在该类初始化时使用,仅在初始化时状态为null)
final boolean isInvalid() { if (status == null) { status = (this.path.indexOf('\u0000') < 0) ? PathStatus.CHECKED//不存在为空的字符 : PathStatus.INVALID;//存在为空的字符 } return status == PathStatus.INVALID; }
获取前缀长度
int getPrefixLength() { return prefixLength; }
构造函数(指定文件路径和前缀长度)
private File(String pathname, int prefixLength) { this.path = pathname; this.prefixLength = prefixLength; }
构造函数(相对路径,参照文件)
private File(String child, File parent) { assert parent.path != null;//判定路径存在 assert (!parent.path.equals(""));//判定路径不为空字符 this.path = fs.resolve(parent.path, child);//转化为绝对路径 this.prefixLength = parent.prefixLength; }
构造函数(输入的是抽象路径,比如/usr/bin)
public File(String pathname) { if (pathname == null) {//文件名为空,不是文件找不到 throw new NullPointerException(); } this.path = fs.normalize(pathname);//输入的路径转化为实际路径字符串 this.prefixLength = fs.prefixLength(this.path); }
构造函数(相对路径,父类绝对路径)
public File(String parent, String child) { if (child == null) { throw new NullPointerException(); } if (parent != null) { if (parent.equals("")) {//为空字符 this.path = fs.resolve(fs.getDefaultParent(),//系统根目录 fs.normalize(child)); } else { this.path = fs.resolve(fs.normalize(parent), fs.normalize(child)); } } else {//父路径为空视作绝对路径处理 this.path = fs.normalize(child); } this.prefixLength = fs.prefixLength(this.path); }
构造函数(参照文件和相对路径)
public File(File parent, String child) { if (child == null) { throw new NullPointerException(); } if (parent != null) { if (parent.path.equals("")) { this.path = fs.resolve(fs.getDefaultParent(), fs.normalize(child)); } else { this.path = fs.resolve(parent.path, fs.normalize(child)); } } else { this.path = fs.normalize(child); } this.prefixLength = fs.prefixLength(this.path); }
构造函数(uri,统一资源标识符)
public File(URI uri) { // Check our many preconditions if (!uri.isAbsolute()) throw new IllegalArgumentException("URI is not absolute");//是否为绝对uri if (uri.isOpaque()) throw new IllegalArgumentException("URI is not hierarchical");//是否不透明 String scheme = uri.getScheme();//uri方案组成 if ((scheme == null) || !scheme.equalsIgnoreCase("file"))//忽略大小写进行判等,并且判定方案是否为空 throw new IllegalArgumentException("URI scheme is not \"file\""); if (uri.getAuthority() != null)//原始授权组成部分不为空 throw new IllegalArgumentException("URI has an authority component"); if (uri.getFragment() != null)//已解码的片段组成部分不为空 throw new IllegalArgumentException("URI has a fragment component"); if (uri.getQuery() != null)//已解码的查询组成部分不为空 throw new IllegalArgumentException("URI has a query component"); String p = uri.getPath();//已解码的路径组成部分 if (p.equals("")) throw new IllegalArgumentException("URI path component is empty"); // Okay, now initialize p = fs.fromURIPath(p); if (File.separatorChar != '/') p = p.replace('/', File.separatorChar);//替换分隔符 this.path = fs.normalize(p); this.prefixLength = fs.prefixLength(this.path); }
返回前缀符后面的字符串值(即文件路径即名称)
public String getName() { int index = path.lastIndexOf(separatorChar);//最后分隔符的位置 if (index < prefixLength) return path.substring(prefixLength);//返回前缀之后的字符串 return path.substring(index + 1);//不存在前缀情况 }
返回前缀(文件父路径)
public String getParent() { int index = path.lastIndexOf(separatorChar); if (index < prefixLength) { if ((prefixLength > 0) && (path.length() > prefixLength))//存在前缀且前缀长度小于整个路径长度(前缀有效) return path.substring(0, prefixLength); return null; } return path.substring(0, index); }
获得父文件(返回File对象)
public File getParentFile() { String p = this.getParent(); if (p == null) return null; return new File(p, this.prefixLength); }
获得文件路径
public String getPath() { return path; }
根据系统判定该文件对象路径是否为绝对路径
public boolean isAbsolute() { return fs.isAbsolute(this); }
获得当前对象的绝对路径
public String getAbsolutePath() { return fs.resolve(this); }
获得当前对象的新对象(绝对路径构建)
public File getAbsoluteFile() { String absPath = getAbsolutePath(); return new File(absPath, fs.prefixLength(absPath)); }
返回当前对象的规范路径名字符串
public String getCanonicalPath() throws IOException { if (isInvalid()) {//有效性检查 throw new IOException("Invalid file path"); } return fs.canonicalize(fs.resolve(this)); }
返回当前对象的规范路径名称形式(返回对象)
public File getCanonicalFile() throws IOException { String canonPath = getCanonicalPath(); return new File(canonPath, fs.prefixLength(canonPath)); }
对路径进行规整化处理(私有,静态)
private static String slashify(String path, boolean isDirectory) { String p = path; if (File.separatorChar != '/')//保证分隔符为‘/’ p = p.replace(File.separatorChar, '/'); if (!p.startsWith("/"))//起始位置不存在分隔符 p = "/" + p; if (!p.endsWith("/") && isDirectory)//如果该路径为目录,则最后也要加入‘/’ p = p + "/"; return p; }
返回路径的URL格式(该方法已经过时,无法自动转义非法字符,一般情况下使用toURI,然后使用URI.toURl方法转义)
public URL toURL() throws MalformedURLException { if (isInvalid()) { throw new MalformedURLException("Invalid file path"); } return new URL("file", "", slashify(getAbsolutePath(), isDirectory())); }
返回路径的URI格式
public URI toURI() { try { File f = getAbsoluteFile(); String sp = slashify(f.getPath(), f.isDirectory());//路径标准化 if (sp.startsWith("//")) sp = "//" + sp;//如果有两斜杠就再加两斜杠(格式要求 return new URI("file", null, sp, null);//根据给定素材构造分层URI,其中host和fragement为空(file方案,sp路径) } catch (URISyntaxException x) {//语法异常 throw new Error(x); // Can't happen } }
检测文件或目录可读性
public boolean canRead() { SecurityManager security = System.getSecurityManager();//获得安全管理器 if (security != null) { security.checkRead(path);//判定线程是否能读取给定字符串所表示的文件,不行就抛出SecurityException } if (isInvalid()) { return false; } return fs.checkAccess(this, FileSystem.ACCESS_READ);//默认使用的是UNIX文件系统,该方法在UNIX文件系统中,作用是检查该抽象路径指定的文件是否能完成后一个参数的要求 }
检测文件或目录可写性
public boolean canWrite() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkWrite(path); } if (isInvalid()) { return false; } return fs.checkAccess(this, FileSystem.ACCESS_WRITE); }
检测文件或目录存在
public boolean exists() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkRead(path); } if (isInvalid()) { return false; } return ((fs.getBooleanAttributes(this) & FileSystem.BA_EXISTS) != 0);//判定文件是否存在,中间符号是与不是且,不过没看太懂为啥要和16进制的1与 }
检测抽象路径是否为目录
public boolean isDirectory() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkRead(path); } if (isInvalid()) { return false; } return ((fs.getBooleanAttributes(this) & FileSystem.BA_DIRECTORY)//这次是和16进制的4与,目测该方法返回值是根据不同状态返回不同值(没找到getBooleanAttributes0()的源码,不过由于返回值为int,应该是这样 != 0); }
检测抽象目录是否为文件
public boolean isFile() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkRead(path); } if (isInvalid()) { return false; } return ((fs.getBooleanAttributes(this) & FileSystem.BA_REGULAR) != 0);//和2与 }
检测指定的目录和文件是否为隐藏目录或文件
public boolean isHidden() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkRead(path); } if (isInvalid()) { return false; } return ((fs.getBooleanAttributes(this) & FileSystem.BA_HIDDEN) != 0);//和8与 }
返回文件的最后修改日期
public long lastModified() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkRead(path); } if (isInvalid()) { return 0L; } return fs.getLastModifiedTime(this); }
获得文件的大小
public long length() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkRead(path); } if (isInvalid()) { return 0L; } return fs.getLength(this); }
创建当前对象绑定的文件
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); }
删除当前对象绑定的文件
public boolean delete() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkDelete(path); } if (isInvalid()) { return false; } return fs.delete(this); }
在虚拟机退出时删除该文件
public void deleteOnExit() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkDelete(path); } if (isInvalid()) { return; } DeleteOnExitHook.add(path);//加入删除map }
返回字符串数组,表示抽象路径中的目录和文件(分离开各层)
public String[] list() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkRead(path); } if (isInvalid()) { return null; } return fs.list(this); }
返回字符串数组,这些字符串指定此抽象路径名表示的目录中满足指定过滤器的文件和目录。
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()]);//数组化 }返回文件数组(每一个单元都是给定路径中的一层)
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; }
返回满足过滤器的文件数组
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()]); }
返回满足过滤器的文件数组(作用同上,写法不同,这个是先生成对象再对对象检查,上一个是检查通过再生成对象,主要是由于过滤器的类型不同)
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()]); }
创建当前抽象路径所指定的目录
public boolean mkdir() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkWrite(path); } if (isInvalid()) { return false; } return fs.createDirectory(this); }
创建当前抽象路径所指定的目录,同时生成必须的父目录
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());//父路径不为空且父路径存在或上溯成功且规范化后创建成功 }
改名(同时修改路径名称)
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);//修改 }
设置最后修改时间
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); }
设置只读属性
public boolean setReadOnly() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkWrite(path); } if (isInvalid()) { return false; } return fs.setReadOnly(this); }
设置可写属性
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);//bool为真则仅允许所有者修改,为假则所有人均可修改 }
设置可写属性(默认)
public boolean setWritable(boolean writable) { return setWritable(writable, true); }
设置可读属性
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);//仅允许所有者读 }
设置可读属性(默认)
public boolean setReadable(boolean readable) { return setReadable(readable, true); }
修改文件类型为可执行文件
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);//执行文件权限 }
修改文件类型为可执行文件
public boolean setExecutable(boolean executable) { return setExecutable(executable, true); }
判断文件是否可执行
public boolean canExecute() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkExec(path);//检测路径的可执行性 } if (isInvalid()) { return false; } return fs.checkAccess(this, FileSystem.ACCESS_EXECUTE); }
返回文件系统根目录(可用的)
public static File[] listRoots() { return fs.listRoots(); }
返回该路径指定的分区空间大小(byte返回
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); }
返回该路径中未被使用的分区空间大小(byte返回
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); }
返回该路径中已被使用的分区大小(byte返回
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); }
创建临时文件
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)//没有文件类型标识符就认为是.tmp 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; }
创建无父路径的临时文件
public static File createTempFile(String prefix, String suffix) throws IOException { return createTempFile(prefix, suffix, null); }
路径比较
public int compareTo(File pathname) { return fs.compare(this, pathname); }
实例比较(实际还是调用路径比较)
public boolean equals(Object obj) { if ((obj != null) && (obj instanceof File)) {//对象不为空且对象为File实例 return compareTo((File)obj) == 0; } return false; }
返回路径的hash值
public int hashCode() { return fs.hashCode(this); }
返回路径的字符串
public String toString() { return getPath(); }
保存文件名(保存对象,分隔符也同时保存
private synchronized void writeObject(java.io.ObjectOutputStream s) throws IOException { s.defaultWriteObject(); s.writeChar(separatorChar); // Add the separator character }
读取保存文件(恢复文件
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)); }
抽象路径实际路径化
public Path toPath() { Path result = filePath; if (result == null) { synchronized (this) {//线程锁 result = filePath; if (result == null) { result = FileSystems.getDefault().getPath(path);//获取默认文件系统(“java.nio.file.spi.DefaultFileSystemProvider”),根据文件系统来获取实际路径 filePath = result; } } } return result; }
含有一个静态代码块
static { try { sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe();//unsafe之后可以“突破限制”,有兴趣可以自己去查阅资料,(虽然资料很少)这里不再赘述 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); } }
含有一个私有静态类:
临时目录
private static class TempDirectory { private TempDirectory() { }//私有构造函数 // temporary directory location private static final File tmpdir = new File(AccessController .doPrivileged(new GetPropertyAction("java.io.tmpdir")));//实际是调用System.getProperty查询这个预定参数,获得的是默认的临时文件路径(权限不检查 static File location() {//default权限仅允许包内访问, return tmpdir; } // file name generation private static final SecureRandom random = new SecureRandom();//生成强随机数 static File generateFile(String prefix, String suffix, File dir)//前缀、后缀、上层目录名 throws IOException { long n = random.nextLong();//获得一个随机long值 if (n == Long.MIN_VALUE) {//其实都是绝对值,只不过long的最小值取绝对值会溢出,所以取0,此时各个值的所取概率也恰好相等(long的最小值为-2的63次方,最大值为2的63次方-1)
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; } }
File类实质就是一个抽象路径,在类中封装了大量对该抽象路径的操作,也同时封装了对当前文件系统的大量操作(其实file感觉啥都没做,全是filesystem在做),功能十分的全面,推荐大家去自主学习并观察底层的使用与链接方式
该类也是目前做到的类中最难的,如有错误请不吝赐教。