Java IO之File对象常用操作
File类:用于文件或文件夹或网址相关联的操作。可以关联或不关联文件(即关联不存在的文件)。构造函数有:
- public File(String pathname) : 通过指定文件或文件夹路径的字符串进行构造
- public File(String parent, String child) :通过父路径字符串与子路径的字符串进行构造
- public File(File parent, String child) :通过父对象及子路径的字符串进行构造
- public File(URI uri) :通过传入网址URI网址对象进行构造
一般采用的构造方式为: File file = new File(pathname),其它3个由于特定需求较少或者复杂,除非不得已,通常情况下不用。
路径pathname可以是相对路径,也可以是绝对路径。
- 相对路径:指pathname相对于应用程序执行路径而言(对于Eclipse或者其它IDE编辑工具时执行时,一般是都是相对于项目根路径而言)。在windows中如果以\开头则代表相对该盘符开始。
- 绝对路径:指相对于操作系统的路径(window系统以盘符+\开头,Linux系统以/开头)
一般采用相对路径来进行指定路径字符串(因为最终项目需要打包,采用绝对路径的话打包后移植到不同系统部署的话很可能找不到)。
而进行测试或者练习性代码编写的情况下,为了快捷也可以采用绝对路径(另外:在JavaEE的Web环境中通常可以获取绝对路径进行处理)。如果采用相对路径则需要将相关资源放置到项目目录相关的文件夹下。
不管是绝对路径还是相对路径,其File分隔符在window与Linux中是有区别的。
其中window的为:\ 如:abc\1.txt。其分隔符在Java代码中构造字符串时需要转义,书写成: "abc\\1.txt"
Linux的为 :/ 如: abc/2.txt。其分隔符在Java代码中构造字符串时不需要转义,直接书写成:"abc/1.txt"
为了兼容File分隔符,可以采用File.sperator代表File分隔符字符串或者File.speratorChar代表File分隔符字符。如:
"abc"+File.sperator+"1.txt" 或 "abc"+File.speratorChar+"1.txt" (推荐采用前者,原因:少书写,少转化)
另外还能够通过环境变量 System.getProperties().getProperty("file.separator") 获取分隔符并指定给某个常量来进行拼接操作。
但是采用兼容性的File分隔符方式或系统变量指派常量来拼接字符串是一件费力麻烦事,幸而的是,File分隔符在File构造时,windows下既可以采用 \ 也可以采用 / 。因此,除非要表现自己写了很多代码,要不然还是直接采用 / 来作为分隔。
代码:
1 import java.io.File; 2 3 public class File001 { 4 public static void main(String[] args) { 5 File file = new File("c:/abc/1.txt"); 6 System.out.println(file); 7 } 8 }
输出:
c:\abc\1.txt
说明:
- 虽然Window情况下在构造时可以采用 / 或 \ ,但在内存中仍然是采用Window风格的路径分隔符 \ 。
- new File(pathname)传递pathname时,要求pathname不可为null,否则出错。另外,传递空字符串""虽然可以,但并没有什么意义。
- 另外,如果是Windows,并且是快捷方式文件,则指定路径是必须以.lnk结尾才能被关联到。
一般情况下自己输入的字符路径是正正规规的,但在外部未明的传递时,可能是比较混乱的情况,如何知道查看查看呢?
File对象可以获取其各种信息,其中获取路径的方法如下:
getPath():获取文件或文件夹路径。File对象的toString()方法直接调用getPath()。
getAbsolutePath():获取文件或文件夹绝对路径(不会处理.以及..等特殊级符,原样保留)
getCanonicalPath():验证后获取规范化的路径(自动消除.以及..等特殊级符。如果.或..特殊级符连接出错,则抛出异常)。
此方法抛出IOException异常。此方法对于处理有级符或者从外部构造而来的路径时可用。一般自己指定的路径或系统分配的路径直接采用getPath()或getAbsolutePath()处理则可。
代码:
1 import java.io.File; 2 import java.io.IOException; 3 4 public class File001 { 5 public static void main(String[] args) { 6 File file = new File("../..///./a/bc/../abc//////"); 7 System.out.println(file); 8 System.out.println(file.getPath()); 9 System.out.println(file.getAbsolutePath()); 10 try { 11 String filepath = file.getCanonicalPath(); 12 System.out.println(filepath); 13 14 File file2 = new File(filepath+"/1.txt"); 15 System.out.println(file2.getCanonicalPath()); 16 } catch (IOException e) { 17 e.printStackTrace(); 18 } 19 } 20 21 }
输出:
..\..\.\a\bc\..\abc
..\..\.\a\bc\..\abc
E:\java2019\javacode\LearnJavaIO\..\..\.\a\bc\..\abc
E:\java2019\a\abc
G:\java2019\a\abc\1.txt
说明:
- 一般情况下结尾的分隔符都会在结果时被消除掉,所以在以结果进行拼接其它路径的时候要注意。
- 只有在 File file = new File("E:") 等处理根盘符的特殊情况下,getAbsolutePath()与getCanonicalPath()才会在结果加上分隔符。
- 对于非结尾的多个相连的分隔符,构造时会自动进行处理成一个。
- 综合以上3条,强烈建议在对以结果进行拼接其它路径时都加上分隔符。
- 通常情况下自主传入路径的处理中,getAbsolutePath()是最常用的。
另外要判断是相对路径还是绝对路径,可以使用以下方法:
isAbsolute():判断是否绝对路径。
代码:
1 import java.io.File; 2 3 public class File001 { 4 public static void main(String[] args) { 5 File file = new File("G:/java2019/1.txt"); 6 System.out.println(file.isAbsolute()); 7 } 8 }
输出:
true
除了可以获取File对象的路径信息,还可以获取其它信息,比如:
String getParent():获取其所在路径上级目录。
String getName():获取其名称(文件或文件夹的名称)。
File getParentFile():获取其所在路径上级目录相关联的File对象。
String[] list():获取其下一级目录列表的字符串形式(如果文件对象则返回null)。
String[] list(FilenameFilter filter):获取其下一级目录列表的字符串形式,但通过文件名称过滤器过滤。
File[] listFiles():获取其下一级目录列表对象(如果是文件对象则返回null)。较list() 更常用,可以更进一步获取各对象的详细信息。
File[] listFiles(FilenameFilter filter):获取其下一级目录列表对象并通过文件名过滤器进行过滤。
File[] listFiles(FileFilter filter):获取其下一级目录列表对象并通文件过滤器进行过滤。
long lastModified():文件(夹)最近被修改的时间。
代码:
1 import java.io.File; 2 import java.io.FileFilter; 3 import java.io.FilenameFilter; 4 import java.text.SimpleDateFormat; 5 import java.util.Date; 6 7 public class File001 { 8 public static void main(String[] args) { 9 File file = new File("G:/java2019/1.txt"); 10 System.out.println("文件全路径:"+file.getAbsolutePath()); 11 System.out.println("文件名称:"+file.getName()); 12 System.out.println("上级目录:"+file.getParent()); 13 System.out.println("修改时间:"+new SimpleDateFormat("yyyy年MM月dd日 hh时mm分ss秒").format(new Date(file.lastModified()))) ; 14 File file2 = file.getParentFile(); 15 File[] brothers = file2.listFiles(); 16 System.out.println(file.getName()+"同级目录下还有以下内容:"); 17 for(File f : brothers){ 18 if(file.compareTo(f)!=0){ 19 System.out.println("\t"+f); 20 } 21 } 22 23 System.out.println(file.getName()+"同级目录下文件后缀为zip的有:"); 24 File[] brothers2 = file2.listFiles(new FilenameFilter() { 25 @Override 26 public boolean accept(File dir, String name) { 27 return name.toLowerCase().endsWith(".zip"); 28 } 29 }); 30 for(File f2 : brothers2){ 31 if(file.compareTo(f2)!=0){ 32 System.out.println("\t"+f2); 33 } 34 } 35 36 37 System.out.println(file.getName()+"同级目录下文件大小大于9M的有:"); 38 File[] brothers3 = file2.listFiles(new FileFilter() { 39 @Override 40 public boolean accept(File pathname) { 41 return pathname.length()>1024*1024*9; 42 } 43 }); 44 for(File f3 : brothers3){ 45 if(file.compareTo(f3)!=0){ 46 System.out.println("\t"+f3); 47 } 48 } 49 } 50 }
输出:
文件全路径:G:\java2019\1.txt
文件名称:1.txt
上级目录:G:\java2019
修改时间:2019年07月01日 01时41分30秒
1.txt同级目录下还有以下内容:
G:\java2019\11.txt.lnk
G:\java2019\apache-tomcat-7.0.52
G:\java2019\apache-tomcat-7.0.52.zip
G:\java2019\apache-tomcat-8.5.11
G:\java2019\apache-tomcat-8.5.11.zip
1.txt同级目录下文件后缀为zip的有:
G:\java2019\apache-tomcat-7.0.52.zip
G:\java2019\apache-tomcat-8.5.11.zip
1.txt同级目录下文件大小大于9M的有:
G:\java2019\apache-tomcat-8.5.11.zip
File对象比较是否同一文件或文件夹相关联的对象,采用:
int compareTo(File pathname):比较确定是否同一文件关联对象,如果是则返回0.可以用来排除当前路径。代码见上边示例。
File判断文件或文件夹是否存在,方法是:
exists():判断文件或文件夹是否存在(在Window中,如果是链接文件,必须最后以.lnk结果才能被查找到)。代码见下边示例。
File根据传入的路径可以判断是文件还是文件夹,方法是:
isFile():判断是否是文件。如果文件不存在,返回false。
isDirectory():判断是否是文件夹。如果文件夹不存在,则返回false。
说明:isFile()与isDrectory()不可能同时为true。但可能同时为false(此时文件或文件夹不存在。或者存在Window情况下的快捷方式文件路径指定时没有以.lnk结果)。所以在未知文件(夹)是否存在的情况下建议先进行文件(夹)存在性的判断。
代码:
1 import java.io.File; 2 3 public class File001 { 4 public static void main(String[] args) { 5 File file = new File("G:/java2019/1.txt"); 6 boolean exsits = file.exists(); 7 if(exsits) { 8 System.out.println("文件存在"); 9 System.out.println(file.isFile()); 10 System.out.println(file.isDirectory()); 11 }else{ 12 System.out.println("文件不存在"); 13 } 14 15 } 16 }
输出:
文件存在
true
false
File对象可以判断文件(夹)的属性,方法有:
isHidden():是否隐藏文件
canExecute():是否可执行
canRead():是否可读
canWrite():是否可写
代码:
1 import java.io.File; 2 3 public class File001 { 4 public static void main(String[] args) { 5 File file = new File("G:/java2019/1.txt"); 6 System.out.println(file.isHidden()); 7 System.out.println(file.canExecute()); 8 System.out.println(file.canRead()); 9 System.out.println(file.canWrite()); 10 } 11 }
输出:
false
true
true
true
除了判断,还可以进行设置:
setExecutable(boolean executable):设置为可执行的状态
setWriteable(boolean writeable):设置可写状态
setReadable(boolean readable):设置可读状态
boolean setLastModified(long time):设置最后修改时间。
File对象获取其空间大小相关的方法为(单位:字节 B):
getTotalSpace():获取文件(夹)所在磁盘的总大小。
getFreeSpace():获取文件(夹)所在磁盘的空闲大小(大于或等于可用大小,因为只有分配了的空间才可用)。
getUsableSpace():获取文件(夹)所在磁盘的可用大小。
length():获取文件大小。如果是文件夹,则返回4096。如果是文件夹(通常通过递归获取其下所有文件大小之和)。
代码:
1 import java.io.File; 2 3 public class File001 { 4 public static void main(String[] args) { 5 File file = new File("G:/java2019/1.txt"); 6 System.out.println("文件大小:"+file.length()+ " B"); 7 System.out.println("当前磁盘总大小:"+file.getTotalSpace()/(1024*1024*1024)+" GB"); 8 System.out.println("当前磁盘空闲大小:"+file.getFreeSpace()/(1024*1024*1024)+" GB"); 9 System.out.println("当前磁盘可用大小:"+file.getUsableSpace()/(1024*1024*1024)+" GB"); 10 } 11 12 }
输出:
文件大小:3 B
当前磁盘总大小:615 GB
当前磁盘空闲大小:17 GB
当前磁盘可用大小:17 GB
用递归获取文件夹总大小代码如下:
代码:
1 import java.io.File; 2 3 public class File001 { 4 public static void main(String[] args) { 5 File file = new File("G:/java2019/"); 6 System.out.println("文件夹总大小为:"+getDirSize(file)/(1024*1024)+"MB"); 7 } 8 9 private static long getDirSize(File dir){ 10 long dirSize = 0; 11 File[] files = dir.listFiles(); 12 for(File f : files){ 13 if(f.isDirectory()){ 14 dirSize += getDirSize(f); 15 }else{ 16 dirSize += f.length(); 17 } 18 } 19 return dirSize; 20 } 21 }
输出:
文件夹总大小为:2246MB
或者:
1 import java.io.File; 2 import java.io.FileFilter; 3 4 public class File001 { 5 public static void main(String[] args) { 6 File file = new File("G:/java2019/"); 7 System.out.println("文件夹总大小为:" + getDirSize(file) / (1024 * 1024) + "MB"); 8 } 9 10 11 private static long getDirSize(File dir) { 12 final ThreadLocal<Long> t = new ThreadLocal<>(); 13 dir.listFiles(new FileFilter() { 14 { 15 if(t.get()==null){t.set(0L);} 16 } 17 public boolean accept(File f) { 18 t.set(t.get()+ (f.isDirectory() ? getDirSize(f) : f.length())); 19 return true; 20 } 21 }); 22 23 return t.get(); 24 } 25 }
File类还有一个用于列出所有磁盘的静态方法:
static File[] listRoots():列出操作系统中所有根磁盘相关联的File对象
代码:
1 import java.io.File; 2 3 public class File001 { 4 public static void main(String[] args) { 5 File[] files = File.listRoots(); 6 for(File file : files){ 7 System.out.println(file.getPath()+" 大小为:"+file.getTotalSpace()/(1024*1024*1024)+" GB"); 8 } 9 } 10 11 }
输出:
C:\ 大小为:223 GB
D:\ 大小为:200 GB
E:\ 大小为:616 GB
F:\ 大小为:616 GB
G:\ 大小为:615 GB
File对象可以创建与之关联的新文件,方法:
boolean createNewFile():创建相关联的文件,如果成功则返回true。如果在创建时如果其目录路径不存在,则会出现:java.io.IOException: 系统找不到指定的路径。
所以如果不确保上级路径是否存在的情况下,建议先进行判断。另外如果文件在创建前已经存在或者权限不足,则返回false,建议在创建之前也判断一下(虽然并不能确切保证)。
代码:
1 import java.io.File; 2 import java.io.IOException; 3 4 public class File001 { 5 public static void main(String[] args) throws IOException { 6 File file = new File("G:/java2019/a"); 7 System.out.println(file.getCanonicalPath()); 8 boolean flag = file.createNewFile(); 9 if(flag){ 10 System.out.println("创建成功!"); 11 }else{ 12 System.out.println("创建失败,文件已经存在或者权限不足!"); 13 } 14 } 15 16 }
输出:
G:\java2019\a
创建失败,文件已经存在或者权限不足!
注意:虽然 G:/java2019/a 形式上很像文件夹,但确实是文件(只不过是没有后缀而已),而createNewFile()也确切只能创建文件,不能用它创建文件夹,要创建文件夹可以使用File对象下边的方法。
File对象创建文件夹,可以使用以下方法:
boolean mkdir():创建路径相关联的文件夹,成功返回true。如果上级目录不存在或者权限不足,则返回false。如果要创建的文件夹已经存在,也返回false(如果要区分哪种情况造成的,可以先判断文件夹存在与否)。该方法并不会产生异常。
boolean mkdirs():创建路径相关联的文件夹,成功返回true。该方法会逐级创建不存在的文件夹。如果权限不足时,才返回false。如果要创建的文件夹已经存在,也返回false(如果要区分哪种情况造成的,可以先判断文件夹存在与否)。该方法并不会产生异常。
建议优先使用mkdirs(),它除了能达到mkdir()的意图,也同时不必关心上级目录的存在情况。
代码:
1 import java.io.File; 2 3 public class File001 { 4 public static void main(String[] args) { 5 File file = new File("G:/java2019/dir123"); 6 File fileDir = file.getParentFile(); 7 if (!fileDir.exists()) { 8 System.out.println("找不到指定目录,无法创建该目录下的文件夹!"); 9 } else { 10 if (file.exists()) { 11 System.out.println("文件夹已经存在,不需要进行创建!"); 12 } else { 13 boolean flag = file.mkdir(); 14 if (flag) { 15 System.out.println("创建成功!"); 16 } else { 17 System.out.println("创建失败,权限不足!"); 18 } 19 } 20 } 21 22 File file2 = new File("G:/java2019/dirABC/aaa/c/d/c"); 23 boolean flag2 = file2.mkdirs(); 24 if (flag2) { 25 System.out.println("创建成功!"); 26 } else { 27 System.out.println("创建失败,文件夹已经存在或者权限不足!"); 28 } 29 } 30 31 }
输出:
创建成功!
创建成功!
再次执行时输出:
文件夹已经存在,不需要进行创建!
创建失败,文件夹已经存在或者权限不足!
File对象要删除文件或者文件夹,可以用以下方法:
boolean delete():该方法删除相关联的文件或文件夹,成功则返回true。如果文件不存在或者权限不足(可以先用exsits()方法区分),则返回false。该方法不会产生异常。
void deleteOnExit():在虚拟机退出(即程序执行结束)时进行文件删除。一般用于清除缓存文件或临时文件等清理操作。
1 import java.io.File; 2 3 public class File001 { 4 public static void main(String[] args) { 5 File file = new File("G:/java2019/dirA"); 6 boolean flag = file.delete(); 7 if(flag){ 8 System.out.println("文件删除成功!"); 9 }else{ 10 System.out.println("文件删除失败!文件不存在或权限不足!"); 11 } 12 } 13 }
输出:
文件删除成功!
File对象进行文件或文件夹的重命名(或剪贴操作),可以用以下方法:
boolean renameTo(File dest):进行文件或文件夹重命名,操作成功返回true。如果重命名后的目标文件(夹)已经存在的时候,则操作失败,返回false。注意:renameTo不能进行跨盘操作(必须在同一分区),否则返回false并操作不成功。操作时其内容必须不被锁定(即已经close或者没有文件流操作),否则操作也不成功。当renameTo时,如果源与目录相同目录,则进行的是重命名操作,不同目录(同分区)进行的是剪贴操作。对于跨分区的操作renameTo()无法达成,必须采用复制+删除的方式,利用流的读取写入操作进行(这里暂时不进行)。如果目标文件(夹)的上级目录不存在,则也操作失败。权限不足也操作失败。
代码:
1 import java.io.File; 2 3 public class File001 { 4 public static void main(String[] args) { 5 File src1 = new File("G:/java2019/1.txt"); 6 File src2 = new File("G:/java2019/dirABC"); 7 File dest1 = new File("G:/java2019/2.txt"); 8 File dest2 = new File("G:/java2019/dirABC22"); 9 boolean flag1 = src1.renameTo(dest1); 10 boolean flag2 = src2.renameTo(dest2); 11 if(flag1){ 12 System.out.println("文件rename操作成功!"); 13 }else{ 14 System.out.println("文件rename操作失败!"); 15 } 16 if(flag2){ 17 System.out.println("文件夹rename操作成功!"); 18 }else{ 19 System.out.println("文件夹rename操作失败!"); 20 } 21 } 22 }
输出:
文件rename操作成功!
文件夹rename操作成功!
再次执行输出:
文件rename操作失败!
文件夹rename操作失败!
对于同分区要将某个文件夹里边的内容剪贴到另一个文件夹的情况,可以用下边代码操作(用到了递归):
1 import java.io.File; 2 import java.io.IOException; 3 4 public class File001 { 5 public static void main(String[] args) throws IOException { 6 File src = new File("E:/studentMsg"); 7 File dest = new File("E:/cccccccccc"); 8 9 long total = cx(src,dest); 10 System.out.println("剪贴成功!总大小:"+(float)total/(1024*1024) + " MB"); 11 } 12 13 14 private static long cx(File from, File to) throws IOException{ 15 long total = 0; 16 if(!to.exists()){ 17 to.mkdir(); 18 } 19 File[] files = from.listFiles(); 20 for(File file : files){ 21 if(file.isDirectory()){ 22 cx(file,new File(to.getAbsolutePath()+"/"+file.getName())); 23 }else{ 24 total += file.length(); 25 file.renameTo(new File(to.getAbsolutePath()+"/"+file.getName())); 26 } 27 } 28 29 //删除空文件夹 30 for(File file : files){ 31 file.delete(); 32 } 33 34 return total; 35 } 36 }
输出:
剪贴成功!总大小:9.250087 MB
File类静态方法,用于创建临时文件:
static File createTempFile(String prefix, String suffix):在系统默认的临时目录(比如:C:\Users\ADMINISTRATOR\AppData\Local\Temp\)创建相应前缀与后缀的临时文件。前缀要求不为null,并且不少于3个字符,否则出现错误。后缀如果为null则默认为".tmp"。该方法抛出IOException
static File createTempFile(String prefix, String suffix, File directory):在自定义的目录(由directory指定)创建相应前缀与后缀的临时文件。前缀要求不为null,并且不少于3个字符,否则出现错误。后缀如果为null则默认为".tmp"。该方法抛出IOException
代码:
1 import java.io.File; 2 import java.io.IOException; 3 4 public class File001 { 5 public static void main(String[] args) throws IOException { 6 File file = new File("G:/java2019/"); 7 File file1 = File.createTempFile("tmp_", null); 8 File file2 = File.createTempFile("jsp-", ".j"); 9 File file3 = File.createTempFile("java-", null, file); 10 file1.deleteOnExit(); 11 file2.deleteOnExit(); 12 file3.deleteOnExit(); 13 System.out.println(file1); 14 System.out.println(file2); 15 System.out.println(file3); 16 17 String rndStr = file1.toString().replace(file1.getParent()+File.separator,"").replace("tmp_", "").replace(".tmp", ""); 18 System.out.println("file1-随机数字串:"+rndStr+"(长度:"+rndStr.length()+")"); 19 } 20 }
输出:
C:\Users\ADMINI~1\AppData\Local\Temp\tmp_4849272979148170396.tmp
C:\Users\ADMINI~1\AppData\Local\Temp\jsp-819744399204818986.j
G:\java2019\java-2876660561527385317.tmp
随机数字串:4849272979148170396(长度:19)
File类当中的静态属性,用于获取兼容各操作系统的路径相关分隔符号:
public static final String separator:兼容的路径分隔符。在windows下为 \ 。在Linux下为 /
public static final String pathSeparator:兼容的路径集分隔符。在windows下为 ;。在Linux下为 :
代码:
1 import java.io.File; 2 3 public class File001 { 4 public static void main(String[] args) { 5 System.out.println("File路径分隔符:"+File.separator); 6 System.out.println("Path路径集分隔符:"+File.pathSeparator); 7 } 8 }
输出:
File路径分隔符:\
Path路径集分隔符:;
File对象其它的方法,用于转化成java.nio.Path及java.net.URI对象:
Path toPath():将file对象转化为Path对象,以方便新IO操作。
URI toURI():将file对象转化为URI对象,以方便网络操作。
URL toURL() throws MalformedURLException:将file对象转化为URL对象,以方便网络操作。该方法已经过期,不作详解。
代码:
1 import java.io.File; 2 import java.net.MalformedURLException; 3 import java.net.URI; 4 import java.net.URL; 5 import java.nio.file.Path; 6 7 public class File001 { 8 @SuppressWarnings("deprecation") 9 public static void main(String[] args) { 10 File file = new File("G:/java2019/"); 11 Path path = file.toPath(); 12 URI uri = file.toURI(); 13 14 URL url = null; 15 try { 16 url = file.toURL(); 17 } catch (MalformedURLException e) { 18 e.printStackTrace(); 19 } 20 System.out.println(path); 21 System.out.println(uri); 22 System.out.println(url); 23 } 24 }
输出:
G:\java2019
file:/G:/java2019/
file:/G:/java2019/
说明:以上方法中,toURL()已经过期不使用。另外两个:toPath()有关新IO的内容,toURI()有关网络相关内容。这里不作详解(在相关章节介绍)。
总结:
- File对象可以用于操作文件或文件夹。以其关联性,对文件或文件夹进行创建,删除,复制,重命名(剪贴),获取或设置权限(读、写、执行)。可以获取相关联文件的大小,可以判断存在与否。可以获取磁盘相关信息。可以进行路径比较,获取上级路径等。
- File类静态属性可以获取兼容操作系统的分隔符。
- File对象不对文件具体内容进行读取或修改,所以不需要进行close(关闭)。