前言
磁驱动分割符中,在unix中使用/
表示,在window中使用\\\
查看其源码,实现Serializable, Comparable的接口
public class File
implements Serializable, Comparable<File>
{
对于Serializable的具体功能可看我这篇文章:java之序列化与反序列化的详细解析(全)
对于Comparable的接口可看我这篇文章稍微提及的知识点:javaSE从入门到精通的二十万字总结(二)
1. 属性
其具体属性定义如下:
// 文件系统对象
// 后面很多方法也用到了文件系统类的方法,比如系统创建文件的时间等,这里简称fs,后面多处都用到了
private static final FileSystem fs = DefaultFileSystem.getFileSystem();
// 路径名
private final String path;
// 枚举类型
// file类对象的地址是否合法通过枚举类判定
private static enum PathStatus { INVALID, CHECKED };
// 文件路径是否有效
private transient PathStatus status = null;
// 检查路径是否有效 但只涉及nul字符,true绝对无效,false不一定有效
final boolean isInvalid() {
if (status == null) {
status = (this.path.indexOf('\u0000') < 0) ? PathStatus.CHECKED
: PathStatus.INVALID;
}
return status == PathStatus.INVALID;
}
// 路径名前缀长度
private final transient int prefixLength;
int getPrefixLength() {
return prefixLength;
}
// 依赖系统分隔符
public static final char separatorChar = fs.getSeparator();
// 将其转换为字符串表示
public static final String separator = "" + separatorChar;
2. 构造函数
函数 | 描述 |
---|---|
public File(String pathname) | 创建路径名实例 |
public File(String parent, String child) | 父+子路径 创建实例 |
public File(URI uri) | 根据URI 路径创建一个新的 File 实例 |
源码如下:(部分已加注释, 可配合下方的具体例子理解)
private File(String pathname, int prefixLength) {
this.path = pathname;
this.prefixLength = prefixLength;
}
private File(String child, File parent) {
assert parent.path != null;
assert (!parent.path.equals(""));
this.path = fs.resolve(parent.path, child);
this.prefixLength = parent.prefixLength;
}
// 路径名
public File(String pathname) {
if (pathname == null) {
throw new NullPointerException();
}
this.path = fs.normalize(pathname);
this.prefixLength = fs.prefixLength(this.path);
}
/* 从父路径名字符串和子路径名字符串创建新的文件实例。
如果parent为null,则将通过调用给定子路径名字符串上的单参数文件构造函数来创建新的文件实例。
否则,父路径名字符串表示目录,子路径名字符串表示目录或文件。如果子路径名字符串是绝对的,则会以系统相关的方式将其转换为相对路径名。如果parent是空字符串,则通过将child转换为抽象路径名并根据系统相关的默认目录解析结果来创建新的文件实例。否则,每个路径名字符串将转换为抽象路径名,子抽象路径名将根据父路径名进行解析
*/
public File(String parent, String child) {
if (child == null) {
throw new NullPointerException();
}
if (parent != null) {
if (parent.equals("")) {
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);
}
// 根据URI 路径创建一个新的 File 实例
public File(URI uri) {
// Check our many preconditions
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");
// Okay, now initialize
p = fs.fromURIPath(p);
if (File.separatorChar != '/')
p = p.replace('/', File.separatorChar);
this.path = fs.normalize(p);
this.prefixLength = fs.prefixLength(this.path);
}
具体实现代码如下:
public class file {
public static void main(String [] args){
// 这样子会出错
// File file = new File();
File file = new File("");
System.out.println(file); // 输出为空
File file1 = new File("manongyanjiuseng");
System.out.println(file1); // 输出manongyanjiuseng
/*----------------父路径 + 子路径构造函数------------*/
// manong为父路径名字 ,yanjiuseng 为子路径名字
// 父路径不为空的时候,既通过父+子路径的拼接
File file2 = new File("manong","yanjiuseng");
System.out.println(file2); // 输出manongyanjiuseng
// 如果父路径为空的时候,既通过输出子路径创建实例即可
String parent = null;
File file3 = new File(parent, "manongyanjisueng");
System.out.println(file3);// 输出manongyanjiuseng
// 如果子路径为空,后面跟着null,空指针就会抛出异常
String child = null;
File file4 = new File("manongyanjisueng", child);
System.out.println(file4);// 抛出异常
// 异常 Exception in thread "main" java.lang.NullPointerException
}
}
结果输出如下:
最后一种构造函数比较特殊 单独拎出来
根据URI 路径创建一个新的 File 实例
public class file {
public static void main(String [] args){
File file5;
try {
file5 = new File(new URI("file:///C:/Users/13399/Desktop"));
System.out.println(file5.getName());// 输出 Desktop
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
截图如下:
3. 方法
函数 | 描述 |
---|---|
getName() | 获取文件名 |
getParent() | 获取上级目录 |
getParentFile() | 默认 以上级目录为父路径创建实例 |
getPath() | 输出文件实际路径 |
isAbsolute() | 是否以window系统的磁盘文件 |
getAbsolutePath() | 获取文件的绝对路径 |
exists() | 实例的file类对象是否存在 |
isFile() | 是否为文件 |
isDirectory() | 是否为目录 |
createNewFile() | 创建文件 |
mkdir() | 创建(前提是父目录存在)单目录true,文件false |
mkdirs() | 可创建多级目录,即使单目录也可,区分一点就是,父目录不存在也可创建 |
delete() | 删除文件或者文件夹(文件夹必须为空才可删除) |
使用方法,按照惯例,先看一波源码(已加注释)
3.1 常用方法
常用方法源码具体如下(已加上注释):
// 返回文件或者目录的名称
public String getName() {
// public static final char separatorChar = fs.getSeparator();
// 此处的separatorChar 为系统分割符号,也就是获取最后一个分割符的名字
int index = path.lastIndexOf(separatorChar);
// 判断下,防止异常
if (index < prefixLength) return path.substring(prefixLength);
return path.substring(index + 1);
}
// 获取上级目录
// 代码注释跟上面一样,唯一的区别在于获取上级目录,所以通过substring截取即可
public String getParent() {
int index = path.lastIndexOf(separatorChar);
if (index < prefixLength) {
if ((prefixLength > 0) && (path.length() > prefixLength))
// 截取上级目录的关键部分
return path.substring(0, prefixLength);
return null;
}
return path.substring(0, index);
}
// 获取上级目录之后在实例对象
public File getParentFile() {
// 通过this指针获取上级目录
String p = this.getParent();
if (p == null) return null;
if (getClass() != File.class) {
p = fs.normalize(p);
}
return new File(p, this.prefixLength);
}
// 返回路径,抽象路径的字符串形式
public String getPath() {
return path;
}
// 测试是否是绝对路径
/* 在 UNIX 系统上,如果路径名的前缀为“/”,则该路径名是绝对的。
在 Microsoft Windows 系统上,如果路径名的前缀是后跟“”的驱动器说明符,
或者其前缀是“”,则路径名是绝对的 */
public boolean isAbsolute() {
return fs.isAbsolute(this);
}
// 获取绝对路径
public String getAbsolutePath() {
return fs.resolve(this);
}
// 获取绝对路径,相当于new File(this.getAbsolutePath)
// 注意与上面的区别,一个是file类一个是String类
public File getAbsoluteFile() {
String absPath = getAbsolutePath();
if (getClass() != File.class) {
// 给定的路径字符串转换为正常形式,如果字符串已经是正常形式 无需转换
absPath = fs.normalize(absPath);
}
return new File(absPath, fs.prefixLength(absPath));
}
对于上面的normalize格式化函数
有如下三个源代码函数
public String normalize(String path)
private String normalize(String path, int len, int off)
private int normalizePrefix(String path, int len, StringBuilder sb)
内部函数代码用到的变量
// 区分系统盘符
slash = props.getProperty("file.separator").charAt(0);
altSlash = (this.slash == '\\') ? '/' : '\\';
核心代码如下 浅显易懂就post图出来
==是否存在、被隐藏、读取修改等源码==
源代码如下:
// FileSystem类 属性
/* Constants for simple boolean attributes */
@Native public static final int BA_EXISTS = 0x01;
@Native public static final int BA_REGULAR = 0x02;
@Native public static final int BA_DIRECTORY = 0x04;
@Native public static final int BA_HIDDEN = 0x08;
// 判断文件或者路径是否存在
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);
}
// 判断此抽象路径名表示的是否为目录
public boolean isDirectory() {
// 代码同exists()函数
return ((fs.getBooleanAttributes(this) & FileSystem.BA_DIRECTORY)
!= 0);
}
// 判断此抽象路径名表示的是否为文件
public boolean isFile() {
// 代码同exists()函数
return ((fs.getBooleanAttributes(this) & FileSystem.BA_REGULAR) != 0);
}
// 路径名命名的文件是否为隐藏文件,取决于操作系统
public boolean isHidden() {
// 代码同exists()函数
return ((fs.getBooleanAttributes(this) & FileSystem.BA_HIDDEN) != 0);
}
文件是否被读取修改等权限源码:
关于下面这个注解可看我之前的文章:Springboot中@SuppressWarnings注解详细解析
主要为了屏蔽一些错误警告,但不是代码错误警告
// FileSystem类 属性
@Native public static final int ACCESS_READ = 0x04;
@Native public static final int ACCESS_WRITE = 0x02;
@Native public static final int ACCESS_EXECUTE = 0x01;
// 文件或者目录可否被读取
public boolean canRead() {
@SuppressWarnings("removal")
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead(path);
}
if (isInvalid()) {
return false;
}
return fs.checkAccess(this, FileSystem.ACCESS_READ);
}
// 文件或者目录可否被修改
public boolean canWrite() {
@SuppressWarnings("removal")
// 代码同上
if (security != null) {
security.checkWrite(path);
}
// ...
return fs.checkAccess(this, FileSystem.ACCESS_WRITE);
}
// 文件或者目录可否被执行
public boolean canExecute() {
@SuppressWarnings("removal")
// 代码同上
if (security != null) {
security.checkExec(path);
}
// ...
return fs.checkAccess(this, FileSystem.ACCESS_EXECUTE);
}
以及最后的修改时间lastModified,长度length
public long lastModified() {
@SuppressWarnings("removal")
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead(path);
}
if (isInvalid()) {
return 0L;
}
// 获取的系统文件时间
// 等同于 调用的最终函数 public native long getLastModifiedTime(File f);
return fs.getLastModifiedTime(this);
}
// 路径名表示的文件的长度。如果此路径名表示目录,则返回值未指定
public long length() {
@SuppressWarnings("removal")
// 省略代码,同lastModified函数
// 获取系统文件长度
// 等同于 调用的最终函数 public native long getLength(File f);
return fs.getLength(this);
}
具体例子如下:
import java.io.IOException;
import java.io.File;
public class file {
public static void main(String [] args){
File file = new File("C:/Users/13399/Desktop");
// 输出文件名
System.out.println(file.getName()); // Desktop
// 输出上级目录
System.out.println(file.getParent()); // C:\Users\13399
File file1 = new File("C:/Users/13399","Desktop");
// 未指定父路径,默认以上级目录为父路径创建实例
System.out.println(file.getParentFile()); // C:\Users\13399
// 指定父路径,则以父路径创建实例
System.out.println(file1.getParentFile()); // C:\Users\13399
// 输出文件实际路径
System.out.println(file.getPath()); // C:\Users\13399\Desktop
// 是否以window系统的磁盘文件
System.out.println(file.isAbsolute()); // true
// 获取文件的绝对路径
System.out.println(file.getAbsolutePath()); // C:\Users\13399\Desktop
// 实例的file类对象是否存在
System.out.println(file.exists()); // true
// 是否为文件
System.out.println(file.isFile()); // false
// 是否为目录
System.out.println(file.isDirectory()); // true
// 创建新文件,只有文件不存在的时候才可以创建
try {
System.out.println(file.createNewFile()); // false 已存在无需创建
} catch (IOException e) {
throw new RuntimeException(e);
}
// 删除,为空的时候才可以删除
System.out.println(file.delete()); // false
}
}
==创建、删除操作源码==
// 创建文件
public boolean createNewFile() throws IOException {
SecurityManager security = System.getSecurityManager();
if (security != null) security.checkWrite(path);
if (isInvalid()) {
throw new IOException("Invalid file path");
}
// 获取系统文件长度
// 等同于 调用的最终函数 public native boolean createFileExclusively(String path) throws IOException;
return fs.createFileExclusively(path);
}
/*
删除此抽象路径名所表示的文件或目录。
如果此路径名表示一个目录,则该目录必须为空才能删除
*/
public boolean delete() {
@SuppressWarnings("removal")
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkDelete(path);
}
if (isInvalid()) {
return false;
}
return fs.delete(this);
}
/*
请求在虚拟机终止时删除此抽象路径名所表示的文件或目录。
文件(或目录)的删除顺序与注册顺序相反。
调用此方法删除已注册删除的文件或目录不起作用。只有在虚拟机正常终止时才会尝试删除
*/
public void deleteOnExit() {
@SuppressWarnings("removal")
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkDelete(path);
}
if (isInvalid()) {
return;
}
DeleteOnExitHook.add(path);
}
// 创建以此抽象路径名命名的目录
public boolean mkdir() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkWrite(path);
}
if (isInvalid()) {
return false;
}
// 此为Filesystem类下的抽象方法
return fs.createDirectory(this);
}
// 由该路径名命名的目录
public boolean mkdirs() {
if (exists()) {
return false;
}
if (mkdir()) {
return true;
}
File canonFile = null;
try {
// 返回此抽象路径名的规范形式
canonFile = getCanonicalFile();
} catch (IOException e) {
return false;
}
// 返回此抽象路径名的父目录的抽象路径名,如果此路径名未命名父目录,则返回null
File parent = canonFile.getParentFile();
return (parent != null && (parent.mkdirs() || parent.exists()) &&
canonFile.mkdir());
}
对于创建文件、单目录、多目录、删除操作都是由File类新建对象来决定的
File file = new File("???")
mkdir的例子具体如下:
// 原路径为C:/Users/13399/Desktop
File file3 = new File("C:/Users/13399/Desktop/11");
System.out.println(file3.mkdir()); // 创建11目录,成功创建为true
// 原路径为C:/Users/13399/Desktop
File file4 = new File("C:/Users/13399/Desktop/11.txt");
System.out.println(file4.mkdir()); // 创建的11.txt为文件,则为false,不成功
// 无法创建不存在的多级目录 或者 不存在的父目录下创建
mkdirs的例子具体如下:
// 原路径为C:/Users/13399/Desktop
File file3 = new File("C:/Users/13399/Desktop/13");
System.out.println(file3.mkdirs()); // 创建13目录,成功创建为true
// 原路径为C:/Users/13399/Desktop
File file4 = new File("C:/Users/13399/Desktop/13.txt");
System.out.println(file4.mkdirs()); // 创建的13.txt为文件,也可创建,为true
// 创建不存在的多级目录 也可创建成功
==遍历列表源码==
// 返回一个数组表示文件
public File[] listFiles() {
String[] ss = normalizedList();
if (ss == null) return null;
int n = ss.length;
File[] fs = new File[n];
for (int i = 0; i < n; i++) {
fs[i] = new File(ss[i], this);
}
return fs;
}
// 这个与上面的不同,多了一个filter 主要是过滤
public File[] listFiles(FilenameFilter filter) {
String ss[] = normalizedList();
if (ss == null) return null;
ArrayList<File> files = new ArrayList<>();
for (String s : ss)
// 区别在于这个核心代码中,filter为null 或者对应过滤相应文件,this表示当前路径
// accept 主要是 测试是否应将指定的文件包含在文件列表中
// 核心函数 boolean accept(File dir, String name); 重构这个函数即可
if ((filter == null) || filter.accept(this, s))
files.add(new File(s, this));
return files.toArray(new File[files.size()]);
}
// 注意与上方函数模块的不同
public File[] listFiles(FileFilter filter) {
String ss[] = normalizedList();
if (ss == null) return null;
ArrayList<File> files = new ArrayList<>();
for (String s : ss) {
File f = new File(s, this);
//测试指定的抽象路径名是否应包含在路径名列表中
// 核心函数 boolean accept(File pathname);重构这个函数即可
if ((filter == null) || filter.accept(f))
files.add(f);
}
return files.toArray(new File[files.size()]);
}
具体示例代码如下:
为了对比不同,此处引入list返回的是String类型
File file = new File("C:/Users/13399/Desktop/13");
System.out.println(Arrays.toString(file.list())); // 输出目录中各个文件名
System.out.println(Arrays.toString(file.listFiles())); // 输出目录中各个文件的绝对路径
// 查看源码可得知 重构函数即可
// 区分list 返回值 String[] list = file.list(
File[] list = file.listFile((File dir, String name) ->name.endsWith(".xml") // 筛选过滤出.xml文件,之后输出这个文件的绝对路径
);
System.out.println(Arrays.toString(list));
// 区别在于重构函数不同
File[] list1 = file.listFiles((File dir, String name) -> name.endsWith(".xml")); // 筛选过滤出.xml文件,之后输出这个文件的绝对路径
System.out.println(Arrays.toString(list1));
3.2 磁盘与临时文件
==磁盘文件源码==
/* -- Disk usage -- */
@Native public static final int SPACE_TOTAL = 0;
@Native public static final int SPACE_FREE = 1;
@Native public static final int SPACE_USABLE = 2;
// 返回由此抽象路径名命名的分区的大小
// 分区的大小(以字节为单位),如果此抽象路径名未命名分区,则为0L
public long getTotalSpace() {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("getFileSystemAttributes"));
sm.checkRead(path);
}
if (isInvalid()) {
return 0L;
}
return fs.getSpace(this, FileSystem.SPACE_TOTAL);
}
// 此抽象路径名命名的分区中未分配的字节数 注意区别,就一行代码不同
public long getFreeSpace() {
// 代码同getTotalSpace()
return fs.getSpace(this, FileSystem.SPACE_FREE);
}
// 返回此抽象路径名命名的分区上此虚拟机可用的字节数 注意区别,就一行代码不同
public long getUsableSpace() {
// 代码同getTotalSpace()
return fs.getSpace(this, FileSystem.SPACE_USABLE);
}
==临时文件源码==
private static class TempDirectory {
private TempDirectory() { }
// temporary directory location
private static final File tmpdir = new File(AccessController
.doPrivileged(new GetPropertyAction("java.io.tmpdir")));
static File location() {
return tmpdir;
}
// file name generation
private static final SecureRandom random = new SecureRandom();
static File generateFile(String prefix, String suffix, File dir)
throws IOException
{
long n = random.nextLong();
if (n == Long.MIN_VALUE) {
n = 0; // corner case
} else {
n = Math.abs(n);
}
// Use only the file name from the supplied prefix
prefix = (new File(prefix)).getName();
String name = prefix + Long.toString(n) + suffix;
File f = new File(dir, name);
if (!name.equals(f.getName()) || f.isInvalid()) {
if (System.getSecurityManager() != null)
throw new IOException("Unable to create temporary file");
else
throw new IOException("Unable to create temporary file, " + f);
}
return f;
}
}
/*
指定目录创建新的空文件,使用给定的前缀和后缀字符串生成器名称,成功即返回
*/
public static File createTempFile(String prefix, String suffix,
File directory)
throws IOException
{
// 前缀长度小于3 会抛出异常,必须至少是3的长度以上
if (prefix.length() < 3) {
throw new IllegalArgumentException("Prefix string \"" + prefix +
"\" too short: length must be at least 3");
}
// 如果后缀为null,则默认给予赋值
if (suffix == null)
suffix = ".tmp";
// 判断目录是否为空,为空则创建一个临时的文件目录
// 具体文件目录有系统决定,unix默认值为 /tmp 或 /var/tmp,windiow下为C:WINNTTEMP
File tmpdir = (directory != null) ? directory
: TempDirectory.location();
@SuppressWarnings("removal")
// 次函数与安全管理器有关
SecurityManager sm = System.getSecurityManager();
File f;
do {
// 该方法调用在上方
f = TempDirectory.generateFile(prefix, suffix, tmpdir);
if (sm != null) {
try {
sm.checkWrite(f.getPath());
} catch (SecurityException se) {
// don't reveal temporary directory location
if (directory == null)
throw new SecurityException("Unable to create temporary file");
throw se;
}
}
} while (fs.hasBooleanAttributes(f, FileSystem.BA_EXISTS));
if (!fs.createFileExclusively(f.getPath()))
throw new IOException("Unable to create temporary file");
return f;
}
/*
默认临时文件目录中创建一个空文件, 并使用给定的前缀和后缀生成其名称
调用此方法等效于调用 createTempFile(前缀、后缀、空值)
通过该方法创建的文件可能对此方法创建的文件具有更严格的访问权限
因此可能更适合安全敏感的应用程序
*/
public static File createTempFile(String prefix, String suffix)
throws IOException
{
return createTempFile(prefix, suffix, null);
}
3.3 其他
基本的方法函数源码:
// 比较两个抽象路径名,通过字典顺序的方法
// 具体比较还是根据操作系统,毕竟unix字母大小写很重要
public int compareTo(File pathname) {
return fs.compare(this, pathname);
}
// 测试路径名与给定的对象是否相等,是否相等取决于操作系统
public boolean equals(Object obj) {
// instanceof 判断是否是这个实例对象
if (obj instanceof File file) {
return compareTo(file) == 0;
}
return false;
}
// 哈希码
public int hashCode() {
return fs.hashCode(this);
}
// 返回路径名,只不过通过getPath返回字符串
public String toString() {
return getPath();
}
以上代码都是基本的代码,没什么特殊之处
源码部分中还跟其他的类集成源码:
// 保存文件名以及分隔符,不同系统如果不同的时候可以重建,并且替换
@java.io.Serial
private synchronized void writeObject(java.io.ObjectOutputStream s)
throws IOException
{
s.defaultWriteObject();
s.writeChar(separatorChar); // Add the separator character
}
// 读取文件名以及原始分割符,如果与系统分割符不同,则用旧的替换当前的
@java.io.Serial
private synchronized void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException
{
ObjectInputStream.GetField fields = s.readFields();
String pathField = (String)fields.get("path", null);
char sep = s.readChar(); // read the previous separator char
if (sep != separatorChar)
pathField = pathField.replace(sep, separatorChar);
String path = fs.normalize(pathField);
UNSAFE.putReference(this, PATH_OFFSET, path);
UNSAFE.putIntVolatile(this, PREFIX_LENGTH_OFFSET, fs.prefixLength(path));
}
// 集成java.nio.file
private transient volatile Path filePath;
// 返回相同的path,如果为空路径名,则可用于访问当前用户目录的 Path
public Path toPath() {
Path result = filePath;
if (result == null) {
synchronized (this) {
result = filePath;
if (result == null) {
result = FileSystems.getDefault().getPath(path);
filePath = result;
}
}
}
return result;
}
对于上面toPath()这个方法中用到的synchronized关键字
可看我这篇文章:java并发之synchronized详细分析(全)
抽象出来,类似设计模式中的单例:JAVA设计模式之单例模式详细分析(全)
public String getAbsolutePath(String filename){
File file = new File(filename);
return file.getAbsolutePath().trim().replaceAll("\\\\", "/");
}