目录
getAbsoluteFile()与getCanonicalFile()
File的本质
对java来说,File表示的是文件的路径,路径指向的文件可能是存在的,也可以是不存在的。可以是相对路径,也可以是绝对路径。File的本质,我们可以看成是一串字符串。指向了一个文件。File类有一个FileSystem成员变量,在进行大部分操作时,他都会用fileSystem对象解释这个路径。
/* File类源码节选 */
public class File
implements Serializable, Comparable<File>
{
private static final FileSystem fs = DefaultFileSystem.getFileSystem();
.....
}
fileSystem对象会怎样解释路径呢?主要分3种情况:
File对象的3种类型:
File主要有3种类型:在调用各种file方法前,fileSystem对象会对file保存的路径进行解释,这时候就会体现出区别。
普通类型:
"ok.txt"这个文件名,表示文件是在当前上下文路径的“ok.txt”,fileSystem会认为file指向的文件是处于当前上下文路径下的。因此对fileSystem来说,new File("ok.txt")就是指向 “上下文路径/ok.txt”的文件。
斜杠开头的类型:
“/ok.txt”表示在最外层根目录(我的是D盘)下的ok.txt。fileSystem会认为file指向的文件是处于跟目录下的。因此对fileSystem来说,new File("/ok.txt")就是指向 “C:/ok.txt”的文件,在解释真实位置时,不会给file路径加上上下文路径的全部,而只是加上上下文路径所在的盘符。
盘符开头的类型:
盘符开头的路径,是一个绝对路径,因此“D:/ok.txt”则是一个绝对路径,fileSystem会认为file就是绝对路径,在执行操作之前,不会再解释。他跟斜杠开头的区别是:斜杠开头会在解释时给file路径加上盘符,而盘符开头的类型则不会加。
ps: 如果想表示上下文路径本身,应该用new File(".")
/**
* 3种不同类型的file对象在调用getAbsoluteFile()时的不同结果
*/
public class FileTest {
public static void main(String[] args) {
//第一种类型
File type_1 = new File("ok.txt");
System.out.println("type_1.getAbsoluteFile() = " + type_1.getAbsoluteFile());
// D:\IdeaProjects\Blog\ok.txt 获得的是上下文路径+当前路径
//第二种类型
File type_2 = new File("/ok.txt");
System.out.println("type_2.getAbsoluteFile() = " + type_2.getAbsoluteFile());
//D:\ok.txt 盘符+当前路径
//第三种类型
File type_3 = new File("D:/ok.txt");
System.out.println("type_3.getAbsoluteFile() = " + type_3.getAbsoluteFile());
//D:\ok.txt 文件本身
}
}
IDEA的当前上下文路径是工程文件夹,而找类则会在"工程文件夹/out/production/模块文件夹/"下开始找。
在window系统中,路径分隔符可以是“\\”或者"/",而在Linux中只能是"/",建议我们都用"/",可以通用。
getAbsoluteFile()与getCanonicalFile()
在介绍getAbsoluteFIle()和getCannonicalFile()之前,先说一个符号:".."
".."是指上级文件夹,new File("../123")表示跟当前上下文路径同级的“123”文件夹。
file.getAbsoluteFile()/file.getCanonicalFile()两个的效果差不多,但CannonicalFile的文件名会更规范
- getAbsoluteFile()只是把上下文所处的位置和相对路径强行插到一起,所以有可能会出现"D:\a\.\c.txt"或"D:\a\..\c.txt"这样的路径,
- 而getCanonicalFile()则是会处理"."和".."
- 两者都返回的file对象,只是外观上不同,不会改变fileSystem解释的结果,也就不会改变file路径所指向的最终文件。这是因为fileSystem解释路径时,会自动解释"."和".."符号。
public class AbsoluteTest {
public static void main(String[] args) throws IOException {
File file_1 = new File("./abc.txt");
File file_2 = new File("../abc.txt");
System.out.println("file_1.getAbsoluteFile() = " + file_1.getAbsoluteFile());
System.out.println("file_2.getAbsoluteFile() = " + file_2.getAbsoluteFile());
System.out.println("file_1.getCanonicalFile() = " + file_1.getCanonicalFile());
System.out.println("file_2.getCanonicalFile() = " + file_2.getCanonicalFile());
}
}
运行结果:
file_1.getAbsoluteFile() = D:\IdeaProjects\Blog\.\abc.txt
file_2.getAbsoluteFile() = D:\IdeaProjects\Blog\..\abc.txt
file_1.getCanonicalFile() = D:\IdeaProjects\Blog\abc.txt
file_2.getCanonicalFile() = D:\IdeaProjects\abc.txt
从上面代码可以看出,getCannonicalFile()会给出更加规范的文件名。
getCannonicalFile()解释".."的方法就是把路径往前推一格。
getAbsoluteFile()则是直接拼接。
File类的核心:file.listFiles()方法
file.listFiles()可以获取当前路径的所有子文件夹,以file[]的形式返回:
public class ListFileTest {
public static void main(String[] args) {
File currentFile = new File("."); //定义当前上下文路径这个文件夹
File[] files = currentFile.listFiles();
for (File file : files) {
System.out.println(file); //遍历当前上下文路径的所有文件夹
}
}
}
运行结果:
.\.git
.\.idea
.\API_test
.\out
在以下情况下,listFiles()会返回null
1、file所指向的文件不是文件夹
2、file所指向的文件不存在
3、JVM没有权限访问file所指向的文件
如果是文件夹但没有文件,则返回空数组。
他们都可以接受一个filter参数,来决定每个文件夹是存是留。
public class ListFileTest_2 {
public static void main(String[] args) {
File notExit = new File("notExit"); //定义不存在的文件夹
File[] filesNotExit = notExit.listFiles();
System.out.println("filesNotExit = " + filesNotExit); //filesNotExit = null
File empty = new File("empty"); //定义空的文件夹
File[] filesInEmpty = empty.listFiles();
System.out.println("filesInEmpty.length = " + filesInEmpty.length); //不为null,但filesInEmpty.length = 0
File file = new File(".idea");
File[] xmls = file.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
System.out.println("dir = " + dir); //dir = .idea
System.out.println("name = " + name); //name = encodings.xml
//过滤器自动帮我们把dir(文件夹)和文件名分开了
return name.endsWith("xml");
}
});
for (File xml : xmls) {
System.out.println("xml = " + xml);
}
//xml = .idea\encodings.xml
//xml = .idea\misc.xml
//xml = .idea\modules.xml
//xml = .idea\vcs.xml
//xml = .idea\workspace.xml
}
}
相对路径.listFile()得到的是相对路径的file列表,绝对路径.listFile()得到的是绝对路径的file列表。
如上所述,File在对文件执行操作的时候,会自动解释"."和".."符号,因此getAbsoluteFile()和getCanonicalFile()的区别,就是打印时候的区别。
public class AbsoluteTest {
public static void main(String[] args) throws IOException {
File file_1 = new File("./abc");
File file_2 = new File("b/../abc");
/*System.out.println("file_1.getAbsoluteFile() = " + file_1.getAbsoluteFile());
System.out.println("file_2.getAbsoluteFile() = " + file_2.getAbsoluteFile());
System.out.println("file_1.getCanonicalFile() = " + file_1.getCanonicalFile());
System.out.println("file_2.getCanonicalFile() = " + file_2.getCanonicalFile());*/
File file_1_AbsoluteFile = file_1.getAbsoluteFile();
File file_2_AbsoluteFile = file_2.getAbsoluteFile();
File file_1CanonicalFile = file_1.getCanonicalFile();
File file_2CanonicalFile = file_2.getCanonicalFile();
System.out.println("file_1CanonicalFile = " + file_1CanonicalFile);
for (File file : file_1CanonicalFile.listFiles()) {
System.out.println(file);
}
System.out.println("------------");
System.out.println("file_2CanonicalFile = " + file_2CanonicalFile);
for (File file : file_2CanonicalFile.listFiles()) {
System.out.println(file);
}
System.out.println("------------");
System.out.println("file_1_AbsoluteFile = " + file_1_AbsoluteFile);
for (File file : file_1_AbsoluteFile.listFiles()) {
System.out.println(file);
}
System.out.println("------------");
System.out.println("file_2_AbsoluteFile = " + file_2_AbsoluteFile);
for (File file : file_2_AbsoluteFile.listFiles()) {
System.out.println(file);
}
}
}
运行结果:
file_1CanonicalFile = D:\IdeaProjects\Blog\abc
D:\IdeaProjects\Blog\abc\hello.txt
------------
file_2CanonicalFile = D:\IdeaProjects\Blog\abc
D:\IdeaProjects\Blog\abc\hello.txt
------------
file_1_AbsoluteFile = D:\IdeaProjects\Blog\.\abc
D:\IdeaProjects\Blog\.\abc\hello.txt
------------
file_2_AbsoluteFile = D:\IdeaProjects\Blog\b\..\abc
D:\IdeaProjects\Blog\b\..\abc\hello.txt
除了listFiles()方法,还有list()方法,返回的是String[],包含的信息比File[]少,视实际情况使用。
File的常用方法:
如文章开头所述,在执行如下大部分操作前,fileSystem对象都会根据路径解释路径,找到file所指向的文件,然后才执行下面的操作:
file.isDirectory() 文件是否为文件夹
file.isFile() 文件是否为文件
file.exists() 文件是否存在
file.createNewFile():跟formatter(),OutputStream()一样,如果作为参数的文件不存在,JVM都会帮你创建的,但是他不会帮你创建dir的!如果dir不存在,会直接报错!
f.getparent(): 获得父母路径,
这个方法没有调用fileSystem来解释,只是简单地截取分隔符后面的字符串,因此若是new File("abc")生成的file调用这个方法,会获得null,先用getAbsolute()再用getParent()就OK!
file.mkDirs():可以把路径指向的文件当成dir创建,所以要小心hello.txt也被当成文件夹创建了。file.getParentFile().mkDirs()可以马上创建文件所需的dir(如果dir存在了则会不创建)
createNewFile() 和 mkDirs() 一样,如果文件存在,则返回false,成功则返回true
file.delete(); 删除指定文件,如果文件是文件夹且里面有内容,则会返回false且不能删除
file.length() ; 获取文件的字节数,路径目标是文件夹,或路径目标文件不存在会返回0
file1.rename(file2) 把file1文件重命名为file2。
file.getName(),返回最后一次被分隔符分割的文件名。
file.getPath() 返回当前路径,可能是相对路径,也可能是绝对路径,看File构造的时候传的参数是否盘符开头。
file.getAbsolutePath(): 获取文件的绝对路径,如上文所述,他不会处理“."或者".."这个符号的。
file.getCanonicalPath(): 获取文件的规范路径,也就是处理了"."和".."之后的路径。
file.lastModified() 最后编辑时间,若文件真实存在则返回long,否则返回0。
file.isFile() / f.isDirectory() 判断是文件还是文件夹。
file.setReadable() / f.canRead(); 顾名思义
file.setWritable() / f.canWrite(); 顾名思义
File.createTempFile() 在临时文件目录生成一个空文件,这个目录跟系统有关。
综合案例:
最后通过一个综合案例练习我们学到的知识:只需要输入文件名和过滤逻辑,就能获得文件名下的所有符合要求的文件集合:
/**
* 获得指定文件夹下所有的java文件(不考虑子文件夹的)并输出到控制台
*/
public class TestDemo {
public static void main(String[] args) {
Set<File> walk = walk("..",".*\\.java");
for (File file : Objects.requireNonNull(walk)) {
System.out.println(file);
}
}
public static Set<File> walk(File file, Predicate<File> predicate) {
if (!file.exists()) return null; //如果路径指向的文件不存在则返回null
HashSet<File> fileHashSet = new HashSet<>();
if (file.isFile()) {
if (predicate.test(file)) fileHashSet.add(file);
} else {
File[] files = file.listFiles(f -> f.isDirectory() || predicate.test(f));
if(files == null) return fileHashSet; //如果访问权限不够,也可能得到空文件数组
for (File fileListed : Objects.requireNonNull(files)) {
fileHashSet.addAll(Objects.requireNonNull(walk(fileListed, predicate)));
}
}
return fileHashSet;
}
public static Set<File> walk(File file) {
return walk(file, ".*");
}
public static Set<File> walk(File file, String pattern) {
return walk(file, f -> Pattern.matches(pattern, f.getName()));
}
public static Set<File> walk(String pathName) {
return walk(new File(pathName));
}
public static Set<File> walk(String pathName, String pattern) {
return walk(new File(pathName), pattern);
}
}
本章所有源码已经上传github:https://github.com/huheman/Blog,
本人水平有限,如您发下有错漏请在评论不吝指教。