1. 概述

今天在项目中看到下面两行代码,看注释说是获取当前工作路径,之前也没有用过这种用法,比较好奇还能这样用,所以研究了一下源码。

//获取当前工作路径File file = new File("");String currentWorkDirectory = file.getAbsolutePath();

2. new File(“”)解析

首先,new File()是创建一个虚拟的文件(File)对象,通过这个对象可以调用很多方法来获取文件和目录的相关信息。以下列出一些常用方法:

2.1 File的常用方法

方法签名作用
boolean delete()删除文件或目录
void deleteOnExit()在jvm退出时删除文件或目录
boolean createNewFile()当以这个文件名命名的文件不存在时,创建一个新的空文件
boolean exists()判断文件或目录是否存在
String getAbsolutePath()获取File对象的绝对路径
String getName()获取文件或目录名称
File getParentFile()获取父File对象
String getPath()获取File对象创建时传入的pathname参数
boolean isDirectory()判断是否是目录
File[] listFiles()列出当前目录下的所有文件
boolean mkdir()创建单个目录
boolean mkdirs()创建多级目录

2.2 new File(“”)做了什么

我们先来看看源码是怎么描述的:

其中注释的意思是:将指定的文件路径转换为一个绝对路径,然后创建一个新的File实例。如果给定的文件路径参数为空值,则返回空的绝对路径名。

官方的注释比较难理解,通俗的将就是,当我传入一个相对路径就会转换为绝对路径,当我传入一个””参数时,就只有一个绝对路径。那么这个绝对路径代表什么含义呢?

2.3 深入源码看File绝对路径(abstract pathname)的含义2.3.1 File.getAbsolutePath()方法

其实我们可以看到new File(String pathname)这个构造方法只设置了相对路径和解析相对路径的长度。并没有有关绝对路径的操作。那么究竟是在哪里设置的绝对路径呢?这个就涉及到getAbsolutePath()方法了。

下面我们看一下getAbsolutePath()方法做了什么:

从注释可以看出,如果在new File()的时候传入的pathname已经是绝对路径的话,那么这个方法的返回就和getPath()一样,如果传入的是空字符串的话,那么就返回当前目录的路径,通过系统属性user.dir获取。

2.3.2 WinNTFileSystem.resolve()方法

那么我们继续往下看:

点进去resolve()方法可以看到返回值实际是从getUserPath()这个方法获取的。

2.3.3 WinNTFileSystem.getUserPath()方法

getUserPath()方法调用了System.getProperty(“user.dir”)方法来获取user.dir这个key的配置。再往里面看user.dir的值其实是从props这个Properties对象中获取的。

2.3.4 System.setProperties(props)方法

那props又是从哪里拿到这个值的呢?我们接着往下看:
在System类里面对着props这个属性用ctrl+鼠标左键查找props在哪几个地方使用了。可以看到有一个setProperties(Properties props)方法。再对着setProperties这个方法使用ctrl+鼠标左键查找。

2.3.5 Process.main()方法

在com.sun.org.apache.xalan.internal.xslt.Process的main方法里面我们可以看到,执行了System.setProperties(props),而System.setProperties(props)调用了本地方法private static native Properties initProperties(Properties props)对props进行初始化。

2.3.6 System.initProperties(Properties props)本地方法

到这里的话,其实java源代码已经结束了,总结来说就是jvm在启动的时候调用了System类的private static native Properties initProperties(Properties props)本地方法对props属性进行初始化,设置了user.dir的值。

这时可能还有些好奇宝宝不甘心,为什么调用了这个本地方法就可以获取user.dir的值,那么我们就来研究一下openjdk的c语言源码吧。

2.4 深入openjdk源码

我们从github上搜索到openjdk的源码仓库,随便找一个java8的tag来研究initProperties()这个本地方法到底做了什么。
github的openjdk代码仓库

  1. 首先我们找到jdk/src/share/native/java/lang目录下面的System.c文件。在里面搜索一下initProperties,可以看到有一个Java_java_lang_System_initProperties方法。

  2. 接着我们在这个方法里面搜索user_dir,可以看到值是从一个sprops指针中获取的,而sprops从上一张图可以看出是从GetJavaProperties(env)这个方法获取的值。

  3. 那我们ctrl+shift+F全局搜索一下GetJavaProperties这个方法。可以看到jdk/src/windows/native/java/lang/java_props_md.c文件中有这个方法,从文件路径可以看出这个文件是windows系统下执行的。

  4. 接着在这个方法里面搜索user_dir,可以看到user_dir实际是从GetCurrentDirectoryW(sizeof(buf)/sizeof(WCHAR), buf)方法里面获取值的。GetCurrentDirectoryW这个方法就是windows的系统函数,用来获取当前工作目录。

到这里jdk源码解析就结束了,可以看到在windows系统下是调用了它的系统函数获取当前工作目录。

备注:_wcsdup(buf)方法是c语言的库函数,作用是分配一块新的空间,然后从buf数组里面获取值赋值给新创建的空间。

3. 总结

  1. 绝对路径是什么:当前工作目录,就是当前项目的根路径
  2. 使用new File(“”)创建File对象时,绝对路径其实是getAbsolutePath()方法获取的,new File(“”)只是设置了path=””
  3. System.props这个属性其实包含了很多值,如:java.version、user.home等等,可以看源码中的注释
  4. 通过System.getProperty(key)可以获取系统的一些配置信息