目录
1、separator / pathSeparatorChar
3、isAbsolute / getAbsolutePath / getCanonicalPath
4、exists / isDirectory / isFile / isHidden / lastModified / length
5、createNewFile / createTempFile / delete / deleteOnExit
7、list / listFiles / listRoots
File表示文件系统中的一个文件,提供了文件操作的相关API,其底层实现都是通过FileSystem实现的。FileSystem表示底层操作系统的一个文件系统,windows下的实现是WinNTFileSystem,类Unix系统下的实现是UnixFileSystem,我们重点关注后者的实现细节。
1、separator / pathSeparatorChar
这两个是File类的public static属性,分别表示路径分隔符和多个路径字符串的分隔符,其定义如下:
//路径分隔符,对应系统属性file.separator
public static final String separator = "" + separatorChar;
public static final char separatorChar = fs.getSeparator();
//多个路径字符串的分隔符,对应系统属性path.separator
public static final String pathSeparator = "" + pathSeparatorChar;
public static final char pathSeparatorChar = fs.getPathSeparator();
//fs表示文件系统的实现
private static final FileSystem fs = DefaultFileSystem.getFileSystem();
//slash和colon都是UnixFileSystem的私有属性,在构造方法中完成初始化
public char getSeparator() {
return slash;
}
public char getPathSeparator() {
return colon;
}
public UnixFileSystem() {
//读取对应的系统属性
slash = AccessController.doPrivileged(
new GetPropertyAction("file.separator")).charAt(0);
colon = AccessController.doPrivileged(
new GetPropertyAction("path.separator")).charAt(0);
javaHome = AccessController.doPrivileged(
new GetPropertyAction("java.home"));
}
window下DefaultFileSystem的实现如下:
类Unix下DefaultFileSystem的实现如下,其源码在jdk\src\solaris\classes\java\io目录下:
测试用例如下:
import java.io.File;
public class FileTest {
public static void main(String[] args) {
//windows下文件路径
// File file=new File("D:\\export\\data\\test.txt");
//Linux下的文件路径
File file=new File("/export/data/test.txt");
System.out.println(file.getName());
System.out.println("separator->"+File.separator);
System.out.println("pathSeparator->"+File.pathSeparator);
}
window下输出如下:
Linux下输出如下:
2、构造方法
public File(String pathname) {
if (pathname == null) {
throw new NullPointerException();
}
//将文件路径转换成正常格式
this.path = fs.normalize(pathname);
//获取路径前缀的长度,Linux下如果以/开始则prefixLength是1,否则是0,
//windows下需要计算包含前面盘符在内的前缀的长度
this.prefixLength = fs.prefixLength(this.path);
}
public File(String parent, String child) {
if (child == null) {
throw new NullPointerException();
}
if (parent != null) {
if (parent.equals("")) {
//父路径为空,则使用默认的父路径,Linux下是/
//resolve返回该父路径下某个子路径的真实路径
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);
}
public File(URI uri) {
//检查uri是否符合file类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);
}
//去掉路径中多余的/
public String normalize(String pathname) {
int n = pathname.length();
char prevChar = 0;
for (int i = 0; i < n; i++) {
char c = pathname.charAt(i);
if ((prevChar == '/') && (c == '/'))
//如果是连续两个//
return normalize(pathname, n, i - 1);
prevChar = c;
}
//如果最后一个字符是/
if (prevChar == '/') return normalize(pathname, n, n - 1);
return pathname;
}
//计算路径中前缀字符串的长度
public int prefixLength(String pathname) {
if (pathname.length() == 0) return 0;
//如果以/开头则返回1,否则返回0
return (pathname.charAt(0) == '/') ? 1 : 0;
}
//计算某个父路径下子路径的完整路径
public String resolve(String parent, String child) {
//child是空,则直接返回parent
if (child.equals("")) return parent;
if (child.charAt(0) == '/') {
//parent是/
if (parent.equals("/")) return child;
return parent + child;
}
if (parent.equals("/")) return parent + child;
return parent + '/' + child;
}
//去掉路径中多余的/
private String normalize(String pathname, int len, int off) {
if (len == 0) return pathname;
int n = len;
//倒着遍历,找到第一个不是/的字符
while ((n > 0) && (pathname.charAt(n - 1) == '/')) n--;
if (n == 0) return "/";
StringBuffer sb = new StringBuffer(pathname.length());
//截取0到off之间的字符串
if (off > 0) sb.append(pathname.substring(0, off));
char prevChar = 0;
//遍历off到n之间的字符
for (int i = off; i < n; i++) {
char c = pathname.charAt(i);
//如果是连续多个/则跳过后面的/
if ((prevChar == '/') && (c == '/')) continue;
sb.append(c);
prevChar = c;
}
return sb.toString();