前言
之前曾經看過這樣一篇文章 [討論] java啓動目錄(user.dir)的問題 , 裏面出現了一個奇怪的問題
更新了 user.dir 之後, 創建 File 對象, 獲取路徑(getAbsolutePath) 可以看到獲取到的是 更新之後的路徑結果
但是當讀取改 File 上面的一些 屬性的時候, 發現 與預期不符(exists), 這裏獲取到的結果是 更新之前的路徑結果
呵呵 之前也整理了一些東西, 這裏就把這些東西 直接搬過來就好了
以下代碼, 截圖 基於 jdk8
測試用例
首先依然是測試用例
package com.hx.test03;
import java.io.File;
/**
* UpdateUserDir
*
* @author Jerry.X.He <[email protected]>
* @version 1.0
* @date 2019-12-29 10:42
*/
public class Test04UpdateUserDir {
// Test04UpdateUserDir
public static void main(String[] args) throws Exception {
System.out.println(System.getProperty("user.dir"));
File pomFile = new File("pom.xml");
System.out.println(pomFile.exists() + " - " + pomFile.getAbsolutePath());
String newUserDir = "/Users/jerry/IdeaProjects/HelloWorld/src/main/java/com/hx/test03";
System.setProperty("user.dir", newUserDir);
File thisJavaFile = new File("Test04UpdateUserDir.java");
System.out.println(thisJavaFile.exists() + " - " + thisJavaFile.getAbsolutePath());
// 實際的工作空間是固定的, 但是顯示層面上存在問題
File currentFolder = new File(".");
System.out.println(currentFolder.isDirectory());
for(File file : currentFolder.listFiles()) {
System.out.println(file.getAbsolutePath());
}
}
}
測試用例結果如下
我們發現結果 似乎是和我們預期的結果不一樣, 但是 和上面 "前言" 裏面分析的結果卻是一致的
getAbsolutePath 是以 user.dir 更新之後的環境的結果, exists, listFiles 是以更新之前的環境取文件作爲結果
.. 那麼 這麼神奇 是爲什麼呢?
R大在文章中 也有回覆, 但是 關於 R大的 "然則File.exists()在Windows上的實現在JDK的C代碼裏,這塊代碼會有current working directory做緩存,於是後面怎麼改"user.dir"都不影響它了。", 我表示沒有驗證出來 可能是 查看的 版本不一致吧, 又或者 水平不夠, stat 傳入的就是 一個相對路徑嘛(這個測試用例), 沒有找到 cwd 的相關處理(os的代碼?)
getAbsolutePath() 的分析
File. getAbsolutePath
public String getAbsolutePath() {
return fs.resolve(this);
}
UnixFileSystem.resolve
public String resolve(File f) {
if (isAbsolute(f)) return f.getPath();
return resolve(System.getProperty("user.dir"), f.getPath());
}
這個問題 比較簡單, 大致就是這樣, 是獲取的 user.dir 拼接上相對路徑的
exists() 的分析
呵呵 這個就稍微複雜一點了, 可能需要 c 相關的 api 的瞭解
File.exists()
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);
}
UnixFileSystem. getBooleanAttributes0
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);
}
UnixFileSystem_md.c 裏面的 Java_java_io_UnixFileSystem_getBooleanAttributes0
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;
}
依賴的 io_util.c 相關宏定義如下
#define WITH_PLATFORM_STRING(env, strexp, var)
\
if (1) {
\
const char *var;
\
jstring _##var##str = (strexp);
\
if (_##var##str == NULL) {
\
JNU_ThrowNullPointerException((env), NULL);
\
goto _##var##end;
\
}
\
var = JNU_GetStringPlatformChars((env), _##var##str, NULL);
\
if (var == NULL) goto _##var##end;
#define WITH_FIELD_PLATFORM_STRING(env, object, id, var)
\
WITH_PLATFORM_STRING(env,
\
((object == NULL)
\
? NULL
\
: (*(env))->GetObjectField((env), (object), (id))),
\
var)
#define END_PLATFORM_STRING(env, var)
\
JNU_ReleaseStringPlatformChars(env, _##var##str, var);
\
_##var##end: ;
\
} else ((void)NULL)
Java_java_io_UnixFileSystem_getBooleanAttributes0 宏定義替換之後的結果如下
JNIEXPORT jint JNICALL
Java_java_io_UnixFileSystem_getBooleanAttributes0(JNIEnv *env, jobject this,
jobject file)
{
jint rv = 0;
if(1) {
const char *path;
jstring _pathstr = ( (object == NULL) ? NULL : (*(env))->GetObjectField((env), (object), (ids.path)) );
if (_pathstr == NULL) {
JNU_ThrowNullPointerException((env), NULL);
goto _pathend;
}
path = JNU_GetStringPlatformChars((env), _pathstr, NULL);
if (path == NULL) goto _pathend;
{
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));
}
}
JNU_ReleaseStringPlatformChars(env, _pathstr, path);
_pathend: ;
} else ((void)NULL)
return rv;
}
ids.path 相關定義, 初始化如下
/* -- 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;");
}
UnixFileSystem_md.c 裏面的 statMode
#define stat64 stat
#define statvfs64 statvfs
static jboolean
statMode(const char *path, int *mode)
{
struct stat64 sb;
if (stat64(path, &sb) == 0) {
*mode = sb.st_mode;
return JNI_TRUE;
}
return JNI_FALSE;
}
整理一下 其實就是獲取到 給定的文件的 path(構造函數析之後的絕對路徑或者相對路徑)
然後使用 stat 函數來處理給定的路徑, 獲取文件的狀態信息(java層面的 user.dir 已經發生了變化, 但是c層面的這個 cwd 是沒有變化的), 因此就造成了 測試結果的奇怪現象
stat, getcwd, chdir 的使用
呵呵 相關的 api 的一些基礎的使用, 我之前整理了一下
stat 的使用
//
// Created by Jerry.X.He on 2019-12-29.
//
#include <iostream>
#include <sys/stat.h>
#include <unistd.h>
using namespace std;
int main() {
struct stat buf;
// stat("/etc/hosts", &buf);
stat("out.txt", &buf);
printf("file size = %d \n", (int) buf.st_size);
return 0;
}
測試結果如下
getcwd 的使用
//
// Created by Jerry.X.He on 2019-12-29.
//
#include <stdio.h>
#include <unistd.h>
int main() {
char buf[80];
getcwd(buf,sizeof(buf));
printf("current working directory: %s\n", buf);
return 0;
}
測試結果如下
chdir 的使用
//
// Created by Jerry.X.He on 2019-12-29.
//
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
int main() {
// update cwd
chdir("/Users/jerry/ClionProjects/HelloWorld");
// print cwd
char buf[80];
getcwd(buf,sizeof(buf));
printf("current working directory: %s\n", buf);
// use cwd
struct stat stats;
stat("Test06UpdatePwd.cpp", &stats);
printf("file size = %d \n", (int) stats.st_size);
return 0;
}
完
參考
[討論] java啓動目錄(user.dir)的問題