File类源码
文件和目录路径名的抽象表示。用户界面和操作系统使用系统相关的路径名字符串来命名文件和目录。这个类展示了层次路径名的一个抽象的、系统独立的视图。抽象路径名有两个组成部分:一个可选的系统相关的前缀字符串,例如磁盘驱动器说明符“/”,用于UNIX根目录,或“\\”,用于
Microsoft Windows UNC
路径名,以及一个由零个或多个字符串名称组成的序列。抽象路径名中的第一个名称可以是目录名,或者,在
Microsoft Windows UNC
路径名的情况下,是主机名。抽象路径名中的每个后续名称表示一个目录;最后一个名称可以表示目录或文件。空的抽象路径名没有前缀和空的名称序列。路径名字符串与抽象路径名之间的转换本质上依赖于系统。当将抽象路径名转换为路径名字符串时,每个名称与下一个名称之间使用默认分隔符的单个副本分隔。默认的名称分隔符由系统属性文件定义。separator,并且在该类的公共静态字段{@link #separator}和{@link #separatorChar}中可用。当路径名字符串转换为抽象路径名时,其中的名称可以用默认的名称分隔符或底层系统支持的任何其他名称分隔符分隔。
路径名,无论是抽象的还是字符串形式,可以是绝对的,也可以是相对的。绝对路径名是完整的,因为为了定位它所表示的文件,不需要任何其他信息。相反,相对路径名必须根据从其他路径名获取的信息进行解释。默认情况下,java中的类。IO包总是针对当前用户目录解析相对路径名。此目录由系统属性user.dir命名,通常是调用Java虚拟机的目录。
抽象路径名的父路径可以通过调用这个类的{@link #getParent}方法来获得,它由路径名的前缀和路径名序列中的每个名称(最后一个除外)组成。每个目录的绝对路径名是任何File对象的祖先,该对象的绝对抽象路径名以目录的绝对路径名开始。例如,由抽象路径名“/usr”表示的目录是由路径名“/usr/local/bin”表示的目录的祖先。
前缀概念用于处理UNIX平台上的根目录,以及Microsoft Windows平台上的驱动器说明符、根目录和UNC路径名,如下所示:
- 对于UNIX平台,绝对路径名的前缀总是“/”。相对路径名没有前缀。表示根目录的抽象路径名有前缀“/”和一个空的名称序列。
- 对于Microsoft Windows平台,包含驱动器说明符的路径名前缀由驱动器号后跟“:”组成,如果路径名是绝对的,则可能后跟“\”。UNC路径名的前缀为“\\”;主机名和共享名是名称序列中的前两个名称。不指定驱动器的相对路径名没有前缀。
这个类的实例可以表示也可以不表示实际的文件系统对象,比如文件或目录。如果它表示这样的对象,那么该对象驻留在一个分区中。分区是文件系统特定于操作系统的存储部分。单个存储设备(例如物理磁盘驱动器、闪存、CD-ROM)可能包含多个分区。如果有,该对象将驻留在分区上,该分区由该路径名的绝对形式的某个祖先命名为。
文件系统可以对实际文件系统对象的某些操作实现限制,例如读、写和执行。这些限制统称为访问权限。文件系统在一个对象上可以有多组访问权限。例如,一个集合可以应用于对象的所有者,另一个集合可以应用于所有其他用户。对象的访问权限可能会导致该类中的某些方法失败。
File类的实例是不可变的;也就是说,一旦创建,由File对象表示的抽象路径名将永远不会改变。
与{@code java.nio.file}互操作性包
一个
java.nio.file
包定义了Java虚拟机访问文件、文件属性和文件系统的接口和类。这个API可以用来克服java.io.file
d类的许多限制。toPath
方法可以用来获取一个Path
,这个Path
是使用一个由File
对象表示的抽象路径来定位文件。生成的Path
可以与java.nio.file
一起使用。类提供对附加文件操作、文件属性和I/O异常的更有效和广泛的访问,以在文件操作失败时帮助诊断错误。
public class File implements Serializable, Comparable<File>
{
//FileSystem对象表示平台的本地文件系统。
private static final FileSystem fs = DefaultFileSystem.getFileSystem();
//这个抽象路径名是规范化的路径名字符串。规范化的路径名字符串使用默认的名称分隔符,并且不包含任何重复或冗余分隔符。
private final String path;
//用于标识文件路径状态的Enum类型。
private static enum PathStatus {
INVALID, CHECKED };
//指示文件路径是否无效的标志。
private transient PathStatus status = null;
//检查文件的路径是否无效。目前对文件路径的检查非常有限,只包括Nul字符检查。返回true意味着路径绝对是无效的/垃圾。
//但是返回false不能保证路径是有效的。
//@return true if the file path is invalid.
final boolean isInvalid() {
if (status == null) {
status = (this.path.indexOf('\u0000') < 0) ? PathStatus.CHECKED
: PathStatus.INVALID;
}
return status == PathStatus.INVALID;
}
//这个抽象路径名前缀的长度,如果没有前缀,则为零。
private final transient int prefixLength;
//返回这个抽象路径名前缀的长度。供文件系统类使用。
int getPrefixLength() {
return prefixLength;
}
//系统依赖的默认名称分隔符。这个字段被初始化为包含系统属性file.separator值的第一个字符。
//在UNIX系统上,这个字段的值是'/';在Microsoft Windows系统上,它是'\\'。
public static final char separatorChar = fs.getSeparator();
//系统相关的默认名称分隔符,为方便起见表示为字符串。这个字符串包含一个字符,即separatorChar
public static final String separator = "" + separatorChar;
//系统相关的路径分隔符。这个字段被初始化为包含系统属性path.separator值的第一个字符。
//此字符用于分隔作为路径列表给定的文件序列中的文件名。在UNIX系统上,这个字符是':';在Microsoft Windows系统上,它是';'。
public static final char pathSeparatorChar = fs.getPathSeparator();
//系统相关的路径分隔符,为方便起见表示为字符串。该字符串只包含一个字符,即pathSeparatorChar。
public static final String pathSeparator = "" + pathSeparatorChar;
/* -- 构造器 -- */
//已经规范化的路径名字符串的内部构造函数。
private File(String pathname, int prefixLength) {
this.path = pathname;
this.prefixLength = prefixLength;
}
//已经规范化的路径名字符串的内部构造函数。参数顺序用于消除此方法与公共(File, String)构造函数之间的歧义。
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;
}
//通过将给定的路径名字符串转换为抽象路径名,创建一个新的File实例。如果给定的字符串是空字符串,那么结果就是空的抽象路径名。
public File(String pathname) {
if (pathname == null) {
throw new NullPointerException();
}
this.path = fs.normalize(pathname);
this.prefixLength = fs.prefixLength(this.path);
}
//注意:两个参数的File构造函数不会将一个空的父抽象路径名解释为当前用户目录。空的父进程会导致子进程根据FileSystem定义的与系统相关的目录进行解析。
//getDefaultParent方法。在Unix上这个默认值是"/",而在Microsoft Windows上它是"\\"。这是与该类的原始行为兼容所必需的。
//用父路径名字符串和子路径名字符串创建一个新的File实例。
//如果parent为空,则创建新的File实例,就像通过调用给定子路径名字符串上的单参数File构造函数一样。
//否则,父pathname字符串将表示目录,子pathname字符串将表示目录或文件。
//如果子路径名字符串是绝对的,那么它将以系统相关的方式转换为相对路径名。
//如果parent是空字符串,则通过将child转换为抽象路径名并根据系统依赖的默认目录解析结果来创建新的File实例。
//否则,每个路径名字符串将转换为一个抽象路径名,子抽象路径名将根据父抽象路径名进行解析。
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);
}
//从父抽象路径名和子路径名字符串创建一个新的File实例。
//如果parent为空,则创建新的File实例,就像通过调用给定子路径名字符串上的单参数File构造函数一样。
//否则父抽象路径名将表示一个目录,子路径名字符串将表示一个目录或一个文件。
//如果子路径名字符串是绝对的,那么它将以系统相关的方式转换为相对路径名。
//如果parent是空的抽象路径名,那么通过将child转换为抽象路径名并根据系统依赖的默认目录解析结果来创建新的File实例。
//否则,每个路径名字符串将转换为一个抽象路径名,子抽象路径名将根据父抽象路径名进行解析。
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);
}
//通过将给定的File: URI转换为抽象路径名来创建一个新的File实例。
//文件的确切形式:URI是系统依赖的,因此这个构造函数执行的转换也是系统依赖的。
//对于给定的抽象路径名f,它保证new File(f.toURI()).equals(f.getAbsoluteFile()),只要原始的抽象路径名、URI和新的抽象路径名都是在同一个Java虚拟机(可能是不同的调用)中创建的。但是,当在一个操作系统上的虚拟机上创建的file: URI被转换为另一个操作系统上的虚拟机中的抽象路径名时,这种关系通常不成立。
//@see java.net.URI @since 1.4
public File(URI uri) {
// 检查我们的许多先决条件
if (!uri.isAbsolute())
throw new IllegalArgumentException("URI is not absolute");
if (uri.isOpaque())
throw new IllegalArgumentException("URI is not hierarchical");
String scheme = uri.getScheme();
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");
// 好的,现在初始化
p = fs.fromURIPath(p);
if (File.separatorChar != '/')
p = p.replace('/', File.separatorChar);
this.path = fs.normalize(p);
this.prefixLength = fs.prefixLength(this.path);
}
/* -- 路径成分访问器 -- */
//返回由这个抽象路径名表示的文件或目录的名称。这只是路径名的名称序列中的姓氏。如果路径名的名称序列为空,则返回空字符串。
//@return 由这个抽象路径名表示的文件或目录名,或者如果这个路径名的名称序列为空,则为空字符串
public String getName() {
int index = path.lastIndexOf(separatorChar);
if (index < prefixLength) return path.substring(prefixLength);
return path.substring(index + 1);