托了好久,终于开始JAVA File 的分析,废话不说,还是先上代码
public class File
implements Serializable, Comparable
{
static private FileSystem fs = FileSystem.getFileSystem();
abstract class FileSystem {
/**
* Return the FileSystem object representing this platform's local
* filesystem.
*/
public static native FileSystem getFileSystem();
#include "jni.h"
#include "jni_util.h"
#include "java_io_FileSystem.h"
JNIEXPORT jobject JNICALL
Java_java_io_FileSystem_getFileSystem(JNIEnv *env, jclass ignored)
{
return JNU_NewObjectByName(env, "java/io/UnixFileSystem", "()V");
}
看到getFileSystem了吧
JNIEXPORT jobject JNICALL
JNU_NewObjectByName(JNIEnv *env, const char *class_name,
const char *constructor_sig, ...)
{
jobject obj = NULL;
jclass cls = 0;
jmethodID cls_initMID;
va_list args;
if ((*env)->EnsureLocalCapacity(env, 2) < 0)
goto done;
cls = (*env)->FindClass(env, class_name);
if (cls == 0) {
goto done;
}
cls_initMID = (*env)->GetMethodID(env, cls,
"", constructor_sig);
if (cls_initMID == NULL) {
goto done;
}
va_start(args, constructor_sig);
obj = (*env)->NewObjectV(env, cls, cls_initMID, args);
va_end(args);
done:
(*env)->DeleteLocalRef(env, cls);
return obj;
}
代码就不跟了,有兴趣的同学自己跟吧,这个处理过程就是要通过传入的参数(类的路径和类名)生成类的实例。unix系统中 返回UnixFileSystem.java 类的实例,上面代码传入了“java/io/UnixFileSystem”,也就是生成UnixFileSystem的实现,也正是我们要研究的。
在这里多说一句,大家可以根据上面的代码,就能跟出一个java类的在虚拟机中实例创建过程
cls = (*env)->FindClass(env, class_name);
以上代码,在方法区,也就是永久区,找到了该要实例的类(注意是类,不是对象,对象是在堆区,实例的类指的是该类是否被虚拟机的类加载器加载)
cls_initMID = (*env)->GetMethodID(env, cls,
"", constructor_sig);
以上代码,获取方法区标识
va_start(args, constructor_sig);
obj = (*env)->NewObjectV(env, cls, cls_initMID, args);
va_end(args);
以上代码,实例化类,创建类的实例---对象
这里仅仅是一个流程,并没有进去方法内,其实现过程是相当复杂的。还是不多讲了。
接着说这个被实例化的类,我还是可耻的上代码啦
package java.io;
import java.security.AccessController;
import sun.security.action.GetPropertyAction;
class UnixFileSystem extends FileSystem {
private final char slash;
private final char colon;
private final String javaHome;
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"));
}
public char getSeparator() {
return slash;
}
public char getPathSeparator() {
return colon;
}
/* A normal Unix pathname contains no duplicate slashes and does not end
with a slash. It may be the empty string. */
/* Normalize the given pathname, whose length is len, starting at the given
offset; everything before this offset is already normal. */
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());
if (off > 0) sb.append(pathname.substring(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();
}
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;
return (pathname.charAt(0) == '/') ? 1 : 0;
}
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 getDefaultParent() {
return "/";
}
public String fromURIPath(String path) {
String p = path;
if (p.endsWith("/") && (p.length() > 1)) {
// "/foo/" --> "/foo", but "/" --> "/"
p = p.substring(0, p.length() - 1);
}
return p;
}
/* -- Path operations -- */
public boolean isAbsolute(File f) {
return (f.getPrefixLength() != 0);
}
public String resolve(File f) {
if (isAbsolute(f)) return f.getPath();
return resolve(System.getProperty("user.dir"), f.getPath());
}
private ExpiringCache cache = new ExpiringCache();
private ExpiringCache javaHomePrefixCache = new ExpiringCache();
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) {
// Note that this can cause symlinks that should
// be resolved to a destination directory to be
// resolved to the directory they're contained in
dir = parentOrNull(path);
if (dir != null) {
resDir = javaHomePrefixCache.get(dir);
if (resDir != null) {
// Hit only in prefix cache; full path is canonical
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);
// Note that we don't allow a resolved symlink
// to elsewhere in java.home to pollute the
// prefix cache (java.home prefix cache could
// just as easily be a set at this point)
if (resDir != null && resDir.equals(dir)) {
File f = new File(res);
if (f.exists() && !f.isDirectory()) {
javaHomePrefixCache.put(dir, resDir);
}
}
}
}
}
assert canonicalize0(path).equals(res) || path.startsWith(javaHome);
return res;
}
}
private native String canonicalize0(String path) throws IOException;
static String parentOrNull(String path) {
if (path == null) return null;
char sep = File.separatorChar;
int last = path.length() - 1;
int idx = last;
int adjacentDots = 0;
int nonDotCount = 0;
while (idx > 0) {
char c = path.charAt(idx);
if (c == '.') {
if (++adjacentDots >= 2) {
// Punt on pathnames containing . and ..
return null;
}
} else if (c == sep) {
if (adjacentDots == 1 && nonDotCount == 0) {
// Punt on pathnames containing . and ..
return null;
}
if (idx == 0 ||
idx >= last - 1 ||
path.charAt(idx - 1) == sep) {
// Punt on pathnames containing adjacent slashes
// toward the end
return null;
}
return path.substring(0, idx);
} else {
++nonDotCount;
adjacentDots = 0;
}
--idx;
}
return null;
}
/* -- Attribute accessors -- */
public native int getBooleanAttributes0(File f);
public int getBooleanAttributes(File f) {
int rv = getBooleanAttributes0(f);
String name = f.getName();
boolean hidden = (name.length() > 0) && (name.charAt(0) == '.');
return rv | (hidden ? BA_HIDDEN : 0);
}
public native boolean checkAccess(File f, int access);
public native long getLastModifiedTime(File f);
public native long getLength(File f);
public native boolean setPermission(File f, int access, boolean enable, boolean owneronly);
/* -- File operations -- */
public native boolean createFileExclusively(String path, boolean restrictive)
throws IOException;
public boolean delete(File f) {
cache.clear();
javaHomePrefixCache.clear();
return delete0(f);
}
private native boolean delete0(File f);
public native String[] list(File f);
public native boolean createDirectory(File f);
public boolean rename(File f1, File f2) {
cache.clear();
javaHomePrefixCache.clear();
return rename0(f1, f2);
}
private native boolean rename0(File f1, File f2);
public native boolean setLastModifiedTime(File f, long time);
public native boolean setReadOnly(File f);
/* -- Filesystem interface -- */
public File[] listRoots() {
try {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead("/");
}
return new File[] { new File("/") };
} catch (SecurityException x) {
return new File[0];
}
}
/* -- Disk usage -- */
public native long getSpace(File f, int t);
/* -- Basic infrastructure -- */
public int compare(File f1, File f2) {
return f1.getPath().compareTo(f2.getPath());
}
public int hashCode(File f) {
return f.getPath().hashCode() ^ 1234321;
}
private static native void initIDs();
static {
initIDs();
}
}
File类的所有功能都是在这个类基础上实现的
这个类其核心功能又都是native,还是无耻的上代码,上UnixFileSystem_md.c文件
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "jni.h"
#include "jni_util.h"
#include "jlong.h"
#include "jvm.h"
#include "io_util.h"
#include "java_io_FileSystem.h"
#include "java_io_UnixFileSystem.h"
/* -- Field IDs -- */
static struct {
jfieldID path;
} ids;
JNIEXPORT void JNICALL
Java_java_io_UnixFileSystem_initIDs(JNIEnv *env, jclass cls)
{
jclass fileClass = (*env)->FindClass(env, "java/io/File");
if (!fileClass) return;
ids.path = (*env)->GetFieldID(env, fileClass,
"path", "Ljava/lang/String;");
}
/* -- Large-file support -- */
#if defined(__solaris__) && !defined(_LFS_LARGEFILE) || !_LFS_LARGEFILE
typedef longlong_t off64_t; /* offsets within files */
typedef u_longlong_t ino64_t; /* expanded inode type */
typedef longlong_t blkcnt64_t; /* count of file blocks */
struct stat64 {
dev_t st_dev;
long st_pad1[3];
ino64_t st_ino;
mode_t st_mode;
nlink_t st_nlink;
uid_t st_uid;
gid_t st_gid;
dev_t st_rdev;
long st_pad2[2];
off64_t st_size;
timestruc_t st_atim;
timestruc_t st_mtim;
timestruc_t st_ctim;
long st_blksize;
blkcnt64_t st_blocks;
char st_fstype[_ST_FSTYPSZ];
long st_pad4[8];
};
#endif /* !_LFS_LARGEFILE */
typedef int (*STAT64)(const char *, struct stat64 *);
#if defined(__linux__) && defined(_LARGEFILE64_SOURCE)
static STAT64 stat64_ptr = &stat64;
#else
static STAT64 stat64_ptr = NULL;
#endif
#ifndef __linux__
#ifdef __GNUC__
static void init64IO(void) __attribute__((constructor));
#else
#pragma init(init64IO)
#endif
#endif
static void init64IO(void) {
void *handle = dlopen(0, RTLD_LAZY);
stat64_ptr = (STAT64) dlsym(handle, "_stat64");
dlclose(handle);
}
/* -- Path operations -- */
extern int canonicalize(char *path, const char *out, int len);
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(JVM_NativePath((char *)path),
canonicalPath, JVM_MAXPATHLEN) < 0) {
JNU_ThrowIOExceptionWithLastError(env, "Bad pathname");
} else {
rv = JNU_NewStringPlatform(env, canonicalPath);
}
} END_PLATFORM_STRING(env, path);
return rv;
}
/* -- Attribute accessors -- */
static jboolean
statMode(const char *path, int *mode)
{
if (stat64_ptr) {
struct stat64 sb;
if (((*stat64_ptr)(path, &sb)) == 0) {
*mode = sb.st_mode;
return JNI_TRUE;
}
} else {
struct stat sb;
if (stat(path, &sb) == 0) {
*mode = sb.st_mode;
return JNI_TRUE;
}
}
return JNI_FALSE;
}
JNIEXPORT jint JNICALL
Java_java_io_UnixFileSystem_getBooleanAttributes0(JNIEnv *env, jobject this,
jobject file)
{
jint rv = 0;
WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
int mode;
if (statMode(path, &mode)) {
int fmt = mode & S_IFMT;
rv = (jint) (java_io_FileSystem_BA_EXISTS
| ((fmt == S_IFREG) ? java_io_FileSystem_BA_REGULAR : 0)
| ((fmt == S_IFDIR) ? java_io_FileSystem_BA_DIRECTORY : 0));
}
} END_PLATFORM_STRING(env, path);
return rv;
}
JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_checkAccess(JNIEnv *env, jobject this,
jobject file, jint a)
{
jboolean rv = JNI_FALSE;
int mode;
switch (a) {
case java_io_FileSystem_ACCESS_READ:
mode = R_OK;
break;
case java_io_FileSystem_ACCESS_WRITE:
mode = W_OK;
break;
case java_io_FileSystem_ACCESS_EXECUTE:
mode = X_OK;
break;
default: assert(0);
}
WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
if (access(path, mode) == 0) {
rv = JNI_TRUE;
}
} END_PLATFORM_STRING(env, path);
return rv;
}
JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_setPermission(JNIEnv *env, jobject this,
jobject file,
jint access,
jboolean enable,
jboolean owneronly)
{
jboolean rv = JNI_FALSE;
WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
int amode, mode;
switch (access) {
case java_io_FileSystem_ACCESS_READ:
if (owneronly)
amode = S_IRUSR;
else
amode = S_IRUSR | S_IRGRP | S_IROTH;
break;
case java_io_FileSystem_ACCESS_WRITE:
if (owneronly)
amode = S_IWUSR;
else
amode = S_IWUSR | S_IWGRP | S_IWOTH;
break;
case java_io_FileSystem_ACCESS_EXECUTE:
if (owneronly)
amode = S_IXUSR;
else
amode = S_IXUSR | S_IXGRP | S_IXOTH;
break;
default:
assert(0);
}
if (statMode(path, &mode)) {
if (enable)
mode |= amode;
else
mode &= ~amode;
if (chmod(path, mode) >= 0) {
rv = JNI_TRUE;
}
}
} END_PLATFORM_STRING(env, path);
return rv;
}
JNIEXPORT jlong JNICALL
Java_java_io_UnixFileSystem_getLastModifiedTime(JNIEnv *env, jobject this,
jobject file)
{
jlong rv = 0;
WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
if (stat64_ptr) {
struct stat64 sb;
if (((*stat64_ptr)(path, &sb)) == 0) {
rv = 1000 * (jlong)sb.st_mtime;
}
} else {
struct stat sb;
if (stat(path, &sb) == 0) {
rv = 1000 * (jlong)sb.st_mtime;
}
}
} END_PLATFORM_STRING(env, path);
return rv;
}
JNIEXPORT jlong JNICALL
Java_java_io_UnixFileSystem_getLength(JNIEnv *env, jobject this,
jobject file)
{
jlong rv = 0;
WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
if (stat64_ptr) {
struct stat64 sb;
if (((*stat64_ptr)(path, &sb)) == 0) {
rv = sb.st_size;
}
} else {
struct stat sb;
if (stat(path, &sb) == 0) {
rv = sb.st_size;
}
}
} END_PLATFORM_STRING(env, path);
return rv;
}
/* -- File operations -- */
JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_createFileExclusively(JNIEnv *env, jclass cls,
jstring pathname,
jboolean restrictive)
{
jboolean rv = JNI_FALSE;
WITH_PLATFORM_STRING(env, pathname, path) {
int fd;
if (!strcmp (path, "/")) {
fd = JVM_EEXIST; /* The root directory always exists */
} else {
jint mode = (restrictive == JNI_TRUE) ? 0600 : 0666;
fd = JVM_Open(path, JVM_O_RDWR | JVM_O_CREAT | JVM_O_EXCL, mode);
}
if (fd < 0) {
if (fd != JVM_EEXIST) {
JNU_ThrowIOExceptionWithLastError(env, path);
}
} else {
JVM_Close(fd);
rv = JNI_TRUE;
}
} END_PLATFORM_STRING(env, path);
return rv;
}
JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_delete0(JNIEnv *env, jobject this,
jobject file)
{
jboolean rv = JNI_FALSE;
WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
if (remove(path) == 0) {
rv = JNI_TRUE;
}
} END_PLATFORM_STRING(env, path);
return rv;
}
JNIEXPORT jobjectArray JNICALL
Java_java_io_UnixFileSystem_list(JNIEnv *env, jobject this,
jobject file)
{
DIR *dir = NULL;
struct dirent64 *ptr;
struct dirent64 *result;
int len, maxlen;
jobjectArray rv, old;
WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
dir = opendir(path);
} END_PLATFORM_STRING(env, path);
if (dir == NULL) return NULL;
ptr = malloc(sizeof(struct dirent64) + (PATH_MAX + 1));
if (ptr == NULL) {
JNU_ThrowOutOfMemoryError(env, "heap allocation failed");
closedir(dir);
return NULL;
}
/* Allocate an initial String array */
len = 0;
maxlen = 16;
rv = (*env)->NewObjectArray(env, maxlen, JNU_ClassString(env), NULL);
if (rv == NULL) goto error;
/* Scan the directory */
while ((readdir64_r(dir, ptr, &result) == 0) && (result != NULL)) {
jstring name;
if (!strcmp(ptr->d_name, ".") || !strcmp(ptr->d_name, ".."))
continue;
if (len == maxlen) {
old = rv;
rv = (*env)->NewObjectArray(env, maxlen <<= 1,
JNU_ClassString(env), NULL);
if (rv == NULL) goto error;
if (JNU_CopyObjectArray(env, rv, old, len) < 0) goto error;
(*env)->DeleteLocalRef(env, old);
}
name = JNU_NewStringPlatform(env, ptr->d_name);
if (name == NULL) goto error;
(*env)->SetObjectArrayElement(env, rv, len++, name);
(*env)->DeleteLocalRef(env, name);
}
closedir(dir);
free(ptr);
/* Copy the final results into an appropriately-sized array */
old = rv;
rv = (*env)->NewObjectArray(env, len, JNU_ClassString(env), NULL);
if (rv == NULL) {
return NULL;
}
if (JNU_CopyObjectArray(env, rv, old, len) < 0) {
return NULL;
}
return rv;
error:
closedir(dir);
free(ptr);
return NULL;
}
JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_createDirectory(JNIEnv *env, jobject this,
jobject file)
{
jboolean rv = JNI_FALSE;
WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
if (mkdir(path, 0777) == 0) {
rv = JNI_TRUE;
}
} END_PLATFORM_STRING(env, path);
return rv;
}
JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_rename0(JNIEnv *env, jobject this,
jobject from, jobject to)
{
jboolean rv = JNI_FALSE;
WITH_FIELD_PLATFORM_STRING(env, from, ids.path, fromPath) {
WITH_FIELD_PLATFORM_STRING(env, to, ids.path, toPath) {
if (rename(fromPath, toPath) == 0) {
rv = JNI_TRUE;
}
} END_PLATFORM_STRING(env, toPath);
} END_PLATFORM_STRING(env, fromPath);
return rv;
}
/* Bug in solaris /usr/include/sys/time.h? */
#ifdef __solaris__
extern int utimes(const char *, const struct timeval *);
#elif defined(__linux___)
extern int utimes(const char *, struct timeval *);
#endif
JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_setLastModifiedTime(JNIEnv *env, jobject this,
jobject file, jlong time)
{
jboolean rv = JNI_FALSE;
WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
struct timeval tv[2];
#ifdef __solaris__
timestruc_t ts;
if (stat64_ptr) {
struct stat64 sb;
if (((*stat64_ptr)(path, &sb)) == 0)
ts = sb.st_atim;
else
goto error;
} else {
struct stat sb;
if (stat(path, &sb) == 0)
ts = sb.st_atim;
else
goto error;
}
#endif
/* Preserve access time */
#ifdef __linux__
struct stat sb;
if (stat(path, &sb) == 0) {
tv[0].tv_sec = sb.st_atime;
tv[0].tv_usec = 0;
}
#else
tv[0].tv_sec = ts.tv_sec;
tv[0].tv_usec = ts.tv_nsec / 1000;
#endif
/* Change last-modified time */
tv[1].tv_sec = time / 1000;
tv[1].tv_usec = (time % 1000) * 1000;
if (utimes(path, tv) >= 0)
rv = JNI_TRUE;
error: ;
} END_PLATFORM_STRING(env, path);
return rv;
}
JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_setReadOnly(JNIEnv *env, jobject this,
jobject file)
{
jboolean rv = JNI_FALSE;
WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
int mode;
if (statMode(path, &mode)) {
if (chmod(path, mode & ~(S_IWUSR | S_IWGRP | S_IWOTH)) >= 0) {
rv = JNI_TRUE;
}
}
} END_PLATFORM_STRING(env, path);
return rv;
}
JNIEXPORT jlong JNICALL
Java_java_io_UnixFileSystem_getSpace(JNIEnv *env, jobject this,
jobject file, jint t)
{
jlong rv = 0L;
WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
struct statvfs fsstat;
memset(&fsstat, 0, sizeof(struct statvfs));
if (statvfs(path, &fsstat) == 0) {
switch(t) {
case java_io_FileSystem_SPACE_TOTAL:
rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
long_to_jlong(fsstat.f_blocks));
break;
case java_io_FileSystem_SPACE_FREE:
rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
long_to_jlong(fsstat.f_bfree));
break;
case java_io_FileSystem_SPACE_USABLE:
rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
long_to_jlong(fsstat.f_bavail));
break;
default:
assert(0);
}
}
} END_PLATFORM_STRING(env, path);
return rv;
}
虽然代码很长,但是大家不要被这么长的代码吓到,无论它逻辑怎么写,最终核心都是调用前几篇中的方法,也就是通常操作系统提供的系统调用
不外乎就是 open ,read,opendir,stat ,write,chown ,chmod等,其操作也不外乎就是前几篇介绍的文件属性。
在JDK1.7之前FIle 一直都很弱,它没有全部实现unix下文件操作,比如查看某个文件是否是可执行文件等,在JDK7中逐渐完善。
目前JAVA所能实现的功能,仅仅是C语言能实现的一部分,java一些类过度封装,反而灵活性被大大限制,java的道路依然很漫长。
下一篇,开始字符编码问题。