咱们知道不一样的操做系统有各自的文件系统,这些文件系统又存在不少差别,而Java 由于是跨平台的,因此它必需要统一处理这些不一样平台文件系统之间的差别,才能往上提供统一的入口。java
JDK 里面抽象出了一个 FileSystem 来表示文件系统,不一样的操做系统经过继承该类实现各自的文件系统,好比 Windows NT/2000 操做系统则为 WinNTFileSystem,而 unix-like 操做系统为 UnixFileSystem。缓存
须要注意的一点是,WinNTFileSystem类 和 UnixFileSystem类并非在同一个 JDK 里面,也就是说它们是分开的,你只能在 Windows 版本的 JDK 中找到 WinNTFileSystem,而在 unix-like 版本的 JDK 中找到 UnixFileSystem,一样地,其余操做系统也有本身的文件系统实现类。安全
这里分红两个系列分析 JDK 对两种(Windows 和 unix-like )操做系统的文件系统的实现类,前面已经讲了 Windows操做系统,对应为 WinNTFileSystem 类。这里接着讲 unix-like 操做系统,对应为 UnixFileSystem 类。篇幅所限,分为上中下篇,此为上篇。bash
--java.lang.Object
--java.io.FileSystem
--java.io.UnixFileSystem
复制代码
class UnixFileSystem extends FileSystem
复制代码
private final char slash;
private final char colon;
private final String javaHome;
private ExpiringCache cache = new ExpiringCache();
private ExpiringCache javaHomePrefixCache = new ExpiringCache();
复制代码
构造方法很简答,直接从 System 中获取到 Properties ,而后再分别根据 file.separator 、 path.separator 和 java.home 获取对应的属性值并赋给 UnixFileSystem 对象的属性。并发
public UnixFileSystem() {
Properties props = GetPropertyAction.privilegedGetProperties();
slash = props.getProperty("file.separator").charAt(0);
colon = props.getProperty("path.separator").charAt(0);
javaHome = props.getProperty("java.home");
}
复制代码
其中的 GetPropertyAction.privilegedGetProperties()
其实就是 System.getProperties()
,这里只是将安全管理器相关的处理抽离出来而已。app
public static Properties privilegedGetProperties() {
if (System.getSecurityManager() == null) {
return System.getProperties();
} else {
return AccessController.doPrivileged(
new PrivilegedAction<Properties>() {
public Properties run() {
return System.getProperties();
}
}
);
}
}
复制代码
该方法主要是对路径进行标准化, unix-like 的路径标准化可比 Windows 简单,不像 Windows 状况复杂且还要调用本地方法处理。机器学习
有两个 normalize 方法,第一个 normalize 方法主要是负责检查路径是否标准,若是不是标准的则要传入第二个 normalize 方法进行标准化处理。而判断路径是否标准的逻辑主要有两个,分布式
/
。/
结尾。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;
}
复制代码
进入到路径标准处理后的逻辑以下,函数
/
,主要做用是去掉尾部多余的斜杠,若是所有都是/
(好比///////
)则直接返回/
。/
则直接跳过,这个其实就是只保留一个/
。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 "/";
StringBuilder sb = new StringBuilder(pathname.length());
if (off > 0) sb.append(pathname, 0, off);
char prevChar = 0;
for (int i = off; i < n; i++) {
char c = pathname.charAt(i);
if ((prevChar == '/') && (c == '/')) continue;
sb.append(c);
prevChar = c;
}
return sb.toString();
}
复制代码
该方法用于返回路径前缀长度,对于传进来的标准路径,以/
开始则返回1,不然返回0。学习
public int prefixLength(String pathname) {
if (pathname.length() == 0) return 0;
return (pathname.charAt(0) == '/') ? 1 : 0;
}
复制代码
有两个 resolve 方法,第一个方法用于合并父路径和子路径获得一个新的路径,逻辑为,
/
开头的状况下,若是父路径为/
则直接返回子路径,不然则返回父路径+子路径。/
则返回父路径+子路径。/
+子路径。public String resolve(String parent, String child) {
if (child.equals("")) return parent;
if (child.charAt(0) == '/') {
if (parent.equals("/")) return child;
return parent + child;
}
if (parent.equals("/")) return parent + child;
return parent + '/' + child;
}
public String resolve(File f) {
if (isAbsolute(f)) return f.getPath();
return resolve(System.getProperty("user.dir"), f.getPath());
}
复制代码
第二个 resolve 方法用于兼容处理 File 对象,逻辑是,
user.dir
属性值做为父路径,而后 File 对象对应的路径做为子路径,再调用第一个 resolve 方法合并父路径和子路径。该方法获取默认父路径,直接返回/
。
public String getDefaultParent() {
return "/";
}
复制代码
该方法主要是格式化路径。主要逻辑是完成相似如下的转换处理:
/root/
--> /root
。/
--> /
,这是经过长度来限制的,即当长度超过1时才会去掉尾部的 /
。public String fromURIPath(String path) {
String p = path;
if (p.endsWith("/") && (p.length() > 1)) {
p = p.substring(0, p.length() - 1);
}
return p;
}
复制代码
该方法判断 File 对象是否为绝对路径,直接根据 File 类的 getPrefixLength 方法获取前缀长度是否为0做为判断条件,该方法最终就是调用该类的 prefixLength 方法,有前缀就说明是绝对路径。
public boolean isAbsolute(File f) {
return (f.getPrefixLength() != 0);
}
复制代码
该方法用来标准化某路径,标准路径不只是一个绝对路径并且仍是惟一的路径,并且标准的定义是依赖于操做系统的。比较典型的就是处理包含"."或".."的路径,还有符号连接等。下面看 unix-like 操做系统如何标准化路径:
public String canonicalize(String path) throws IOException {
if (!useCanonCaches) {
return canonicalize0(path);
} else {
String res = cache.get(path);
if (res == null) {
String dir = null;
String resDir = null;
if (useCanonPrefixCache) {
dir = parentOrNull(path);
if (dir != null) {
resDir = javaHomePrefixCache.get(dir);
if (resDir != null) {
String filename = path.substring(1 + dir.length());
res = resDir + slash + filename;
cache.put(dir + slash + filename, res);
}
}
}
if (res == null) {
res = canonicalize0(path);
cache.put(path, res);
if (useCanonPrefixCache &&
dir != null && dir.startsWith(javaHome)) {
resDir = parentOrNull(res);
if (resDir != null && resDir.equals(dir)) {
File f = new File(res);
if (f.exists() && !f.isDirectory()) {
javaHomePrefixCache.put(dir, resDir);
}
}
}
}
}
return res;
}
}
private native String canonicalize0(String path) throws IOException;
复制代码
本地方法 canonicalize0 以下,处理逻辑经过 canonicalize 函数实现,因为函数较长,这里再也不贴出来,主要的处理逻辑:
/./
/../
符号的表示、多余的/
符号。但有时对于一些特殊的非正常写法可能致使没法经过 realpath 函数处理掉,好比...
或....
,因此接着还得再判断是否须要进一步处理,须要则继续处理,不然直接返回路径。...
或....
状况处理掉并返回标准路径。JNIEXPORT jstring JNICALL
Java_java_io_UnixFileSystem_canonicalize0(JNIEnv *env, jobject this,
jstring pathname)
{
jstring rv = NULL;
WITH_PLATFORM_STRING(env, pathname, path) {
char canonicalPath[JVM_MAXPATHLEN];
if (canonicalize((char *)path,
canonicalPath, JVM_MAXPATHLEN) < 0) {
JNU_ThrowIOExceptionWithLastError(env, "Bad pathname");
} else {
#ifdef MACOSX
rv = newStringPlatform(env, canonicalPath);
#else
rv = JNU_NewStringPlatform(env, canonicalPath);
#endif
}
} END_PLATFORM_STRING(env, path);
return rv;
}
复制代码
如下是***广告***和***相关阅读***
=============广告时间===============
公众号的菜单已分为“分布式”、“机器学习”、“深度学习”、“NLP”、“Java深度”、“Java并发核心”、“JDK源码”、“Tomcat内核”等,可能有一款适合你的胃口。
鄙人的新书《Tomcat内核设计剖析》已经在京东销售了,有须要的朋友能够购买。感谢各位朋友。
=========================
相关阅读:
JDK不一样操做系统的FileSystem(Windows)上篇
JDK不一样操做系统的FileSystem(Windows)中篇
JDK不一样操做系统的FileSystem(Windows)下篇
欢迎关注: