骏马金龙 (新博客:www.junmajinlong.com)

网名骏马金龙,钟情于IT世界里的各种原理和实现机制,强迫症重症患者。爱研究、爱翻译、爱分享。特借此一亩三分田记录自己成长点滴!!!

java IO(一):File类

1.File类简介

File类位于java.io包中。它面向文件层次级别操作、查看文件,而字节流、字符流操作数据时显然比之更底层。

学习File类包括以下几个重点:文件路径、文件分隔符、创建文件(目录)、删除文件(目录)、查看文件内容(输出目录内文件)、判断文件(是文件/目录?存在否?可读写执行?)、获取文件信息(文件名、路径名、大小等),最后还有如何筛选所需文件

2.创建、删除文件/目录

File类有四个构造方法:File(String pathname)、File(String parent,String child)、File(File parent,String child)、File(URI uri),暂不考虑最后一个。

File file1 = new File("D:\\myjava\\a.txt");      //File(String pathname)
File file2 = new File("D:\\myjava","a\\b.txt");  //File(String parent,String child)
File file3 = new File(file2,"abc");            //File(File parent,String child)

new一个File对象后,表示创建了一个File实例,不代表在文件系统中创建了实际的文件。要创建实际的文件、目录,应该使用File类中提供的方法:

  • boolean createNewFile() throws IOException:创建一个新的空文件,仅当文件不存在时才创建并返回true。
  • static createTempFile():创建临时文件。
  • boolean mkdir():创建空目录,仅当父路径存在且待创建目录不存在时才创建并返回true。注:不会递归创建父目录。
  • boolean mkdirs():和mkdir()不同的是,mkdirs()会递归创建所有所需要的父路径。

例如,在d:\myjava(已存在)中创建d:\myjava\a.txt,d:\myjava\a\abc\c.txt,以下提供了一种方法。

File file0 = new File("xyz.txt");   //相对路径,相对于system属性中的user.dir路径
File file1 = new File("D:\\myjava\\a.txt");
File file2 = new File("D:\\myjava","a\\abc");
File file3 = new File(file2,"c.txt");

try {
    System.out.println(file1.createNewFile());   //try ... catch createNewFile()
    System.out.println(file2.mkdirs());
    System.out.println(file3.createNewFile());
} catch (IOException e) {
    e.printStackTrace();
  }

注意上面的双反斜线\\,因为Windows系统的文件分隔符为\,而这是转义符号,因此需要对\自身进行转义。Unix操作系统中的文件分隔符号为/,但Windows也同样能够正确使用/作为分隔符。

执行代码,由于第一次创建文件,所以都返回true,但第二次执行都将返回false,因为待创建文件都已存在。如下:

λ java TestCreate
true
true
true


λ java TestCreate
false
false
false

File类只提供了一个delete()方法用来删除文件和目录,且删除目录时只能删除空目录。注:此为永久删除,不进入回收站。

System.out.println(file1.delete()); //true
System.out.println(file2.delete()); //false
File file4 = new File("d:/myjava/a/abc/c.txt");
System.out.println(file4.delete()); //true

文件名和路径相关问题

每一个File对象都有父路径、文件名,两者结合就是文件的绝对路径。此外,路径还分为相对路径(抽象路径)、绝对路径。

以unix的路径命名方式为例,绝对路径/etc/ssh/ssh_config中/etc/ssh为dirname,即父路径,ssh_config为basename。Windows中也差不多,如绝对路径c:\windows\system32\drivers\etc\hosts中的dirname为c:\windows\system32\drivers\etc,basename部分为hosts。

以下是File类提供的与文件名、父路径、路径相关的方法。

  • getName():获取文件名。获取的内容是file对象中给定的抽象路径名,即路径的basename部分。
  • getAbsoultePath():获取绝对路径名,即dirname/basename。
  • getParent():获取父路径,即dirname。
  • getPath():获取文件路径的字符串,其实调用的是toString()。可能返回相对路径、也可能绝对路径。见下文解释。
  • File getAbsoulteFile():获取绝对路径的文件对象,注意返回File。
  • File getParentFile():获取父路径的文件对象,注意返回File。

在new File时,可以给定相对路径(相对于user.dir),也可以给定绝对路径。例如,下面4个new File中,除了第一个是相对路径,其余三个都是绝对路径。

File file0 = new File("xyz.txt");     //相对路径,相对于system属性中的user.dir路径
File file1 = new File("D:\\myjava\\a.txt");
File file2 = new File("D:\\myjava","a\\abc");
File file3 = new File(file2,"c.txt");

当给定是相对路径时,则getParent()和getParentFile()无法正确获取到父路径,返回NULL,且getPath()返回的将是相对路径的文件名,即new File(PATH)给定什么PATH,就返回什么PATH。但无论如何,getAbsoultePath()总是能获取到绝对路径。

File file0 = new File("1.txt");
File file1 = new File("D:/myjava/a.txt");
File file2 = new File("D:/myjava","a/abc");
File file3 = new File(file2,"c.txt");
System.out.println("----------file0-----------------");
System.out.println(file0.getAbsoluteFile());   //D:\myjava\io\1.txt
System.out.println(file0.getAbsolutePath());   //D:\myjava\io\1.txt
System.out.println(file0.getName());           //1.txt
System.out.println(file0.getParent());         //null
System.out.println(file0.getParentFile());     //null
System.out.println(file0.getPath());           //1.txt
System.out.println("----------file1-----------------");
System.out.println(file1.getAbsoluteFile());   //D:\myjava\a.txt
System.out.println(file1.getAbsolutePath());   //D:\myjava\a.txt
System.out.println(file1.getName());           //a.txt
System.out.println(file1.getParent());         //D:\myjava
System.out.println(file1.getParentFile());     //D:\myjava
System.out.println(file1.getPath());           //D:\myjava\a.txt
System.out.println("----------file2-----------------");
System.out.println(file2.getAbsoluteFile());   //D:\myjava\a\abc
System.out.println(file2.getAbsolutePath());   //D:\myjava\a\abc
System.out.println(file2.getName());           //abc
System.out.println(file2.getParent());         //D:\myjava\a
System.out.println(file2.getParentFile());     //D:\myjava\a
System.out.println(file2.getPath());           //D:\myjava\a\abc
System.out.println("----------file3-----------------");
System.out.println(file3.getAbsoluteFile());   //D:\myjava\a\abc\c.txt
System.out.println(file3.getAbsolutePath());   //D:\myjava\a\abc\c.txt
System.out.println(file3.getName());           //c.txt
System.out.println(file3.getParent());         //D:\myjava\a\abc
System.out.println(file3.getParentFile());     //D:\myjava\a\abc
System.out.println(file3.getPath());           //D:\myjava\a\abc\c.txt

3. 文件判断和文件属性相关

new File对象时,无论这个对象是文件还是目录,new的方式是一样的,也就是说,从new File无法识别出这个对象是文件还是目录,也无法确认这个对象对应的文件是否存在。此外,这个文件是否可读、可写、可执行等也都可以进行判断。其实这些都算是文件的属性信息,除了这些信息外,文件名、文件大小、最近一次修改时间等也都是文件属性。

  • exists():File对象在文件系统中是否存在。
  • isFile():File对象在文件系统中是否存在且是否为普通文件。
  • isDirectory():File对象在文件系统中是否存在且是否为目录。
  • canRead():File对象是否可读。
  • canWrite():File对象是否可写。
  • canExecute():File对象是否可执行。

还有两个获取常见属性的方法:

  • length():文件大小,单位字节。可用来判断是否为空文件、空目录。
  • lastModified():最近一次修改时间,即mtime。

还可以设置读、写、执行、最近修改时间等属性。

  • setLastModified(long time):time用从1970-01-01开始的毫秒数表示。
  • setExecutable(true/false):对所有用户是否可执行,参数为true或false
  • setExecutable(true/false,true/false):第二个参数表示owneronly,为true时表示只有所有者可执行,否则表示所有用户可执行。
  • setWritable(true/false):和setExecute()一样,也支持owneronly参数。
  • setReadable(true/false):和setExecute()一样,也支持owneronly参数。
  • setReadOnly():设置只读,设置成功返回true。

4. 列出目录内文件

File类中没有提供查看文件内容的方法,仅只提供了查看目录内容的方法,也就是列出目录内的文件列表。这些方法都只能列出当前目录内容,不会递归到子目录中去遍历。

  • String[] list():返回一个字符串数组,元素为此目录中文件的basename。
  • String[] list(FilenameFilter filter):返回一个字符串数组,元素为满足过滤条件的basename。
  • File[] listFiles():返回一个抽象路径名数组,元素为此目录中文件的basename。
  • File[] listFiles(FileFilter filter):返回抽象路径名数组,元素为满足过滤条件的basename。
  • File[] listFiles(FilenameFilter filter):返回抽象路径名数组,元素为满足过滤条件的baesname。

因为文件列表返回的是多个值,甚至是多个File对象,因此这些方法返回值都是数组形式。之所以是数组而不是集合,是因为数组中的元素数量是不可更改的,而列出目录中文件列表本就是列出当前时刻目录内文件的动作,如果有增、删元素的操作,岂非列出前是一个列表,实际列出的又是另一个列表?

首先看不带过滤器的list()和listFiles(),关于文件筛选的方法见后文。假设D:\a目录内的结构如下:

d:\a
|--a.sql
|--back.log
|--b
|  |--e
|  |  |--1.txt
|  |  |--2.txt
|  |  `--3.txt
|  `--f
|     |--4.txt
|     |--5.txt
|     `--6.txt
|--c
|  |--e
|  |  |--ace1.txt
|  |  |--ace2.txt
|  |  `--ace3.txt
|  `--f
|     |--4.txt
|     |--5.txt
|     `--6.txt
`--d
   |--a.java
   |--abc (1).txt
   |--abc (2).txt
   |--abc (3).txt
   |--b.java
   `--c.java

列出D:\a内的文件列表。

File file1 = new File("d:/a");
String[] list = file1.list();
for (String f : list) {
    System.out.println(f);
}

得到的结果为

a.sql
b
back.log
c
d

实际的过程是将目录内的文件名存放到了字符串数组中,再遍历数组。而且,得到的结果仅仅只是basename部分的文件名,不是绝对路径。实际上,因为存储在String[]中,完全没办法得到其绝对路径。因此,更好的方式是使用listFiles()方法,因为它返回的是File数组,其内每一个元素都是File对象,可以使用getAbsolutePath()来获取各元素的绝对路径。

File file1 = new File("d:/a");
File[] filelist = file1.listFiles();
for (File file : filelist) {
    System.out.println(file.getAbsolutePath());
}

得到的结果为:

d:\a\a.sql
d:\a\b
d:\a\back.log
d:\a\c
d:\a\d

关于列出目录,有如下几个示例,见:java显示目录文件列表和删除目录
示例1:列出整个目录中的文件(递归)
示例2:列出整个目录中的文件(队列)
示例3:树形结构显示整个目录中的文件(递归)
示例4:删除整个目录

5.筛选文件

File类的listFiles()支持文件筛选功能。例如File[] listFiles(FileFilter filter),其中FileFilter为过滤器接口,需要实现该接口来筛选文件。

常用的筛选方法是根据文件名,其实也支持其他筛选条件,如是否可读、大小是否大于5M等。

import java.io.*;

public class Filter1 {
    public static void main(String[] args) {
        File dir = new File("d:/myjava");
        getJavaFiles(dir);
    }

    public static void getJavaFiles(File dir) {
        File[] files = dir.listFiles(new NameFilter());
        for (File file:files) {
            if(file.isDirectory()) {
                getJavaFiles(file);
            } else {
                System.out.println(file.getAbsolutePath());
            }
        }
    }
}


//实现FileFilter接口
class NameFilter implements FileFilter {
    public boolean accept(File pathname) {
        if(pathname.isDirectory()) {    //为了支持递归筛选,判断目录
            return true;
        } else {
            String name = pathname.getName();
            return name.endsWith(".class")?true:false;

            //return pathname.canRead()?true:false;        //根据是否可读筛选
            //return pathname.length()>5242880?true:false;  //根据大小筛选great than 5MB?(5*1024*1024)
        }
    }
}

注:若您觉得这篇文章还不错请点击右下角推荐,您的支持能激发作者更大的写作热情,非常感谢!

posted @ 2017-12-29 11:41  骏马金龙  阅读(1266)  评论(0编辑  收藏  举报