7.1 File类的基本操作(一)

系列 -
本节摘要
这一节我们将学习Java中的文件操作基础知识。通过动手实验,你将掌握如何使用File类进行文件和目录的创建、删除、遍历等基本操作。这些知识是后续学习文件读写的重要基础。

在Java中,File类不仅可以表示文件,也可以表示目录(文件夹)。让我们从简单的文件创庺开始:

重要概念
  • File对象只是一个路径的抽象表示,创建File对象并不会真正创建文件
  • 需要调用createNewFile()方法才能真正创建文件

java

import java.io.File;  
  
public class Example01 {  
    public static void main(String[] args) {  
        /**  
         * Step 1 创建普通文件  
         */  
        // 创建File对象,指定文件路径
        File file1 = new File("out/chapter7/file1.txt");  
        if (file1.exists()) {  
            // 如果文件存在就删除  
            System.out.println("文件存在");  
            file1.delete();  
            System.out.println("文件已删除");  
        } else {  
            // 如果文件不存在就创建  
            System.out.println("文件不存在");  
  
            try {  
                file1.createNewFile();  
                System.out.println("文件已创建");  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
        }  
  
        System.out.println("\n");  
    }  
}
可能的问题
运行上面的代码时,如果out/chapter7/这个目录不存在,程序会抛出异常。因为创建文件之前,必须先创建父目录

下面的代码展示了如何正确处理这个问题:

创建目录的方法
  • mkdirs() - 创建多级目录,如a/b/c一次性创建所有不存在的目录
  • mkdir() - 只能创建单级目录,父目录必须先存在
  • 建议使用mkdirs(),更安全可靠
基本概念
在操作系统中,目录(文件夹)也是一种特殊的文件,只不过它的内容是其他文件和子目录的名单。

Java提供了多种遍历目录的方式,让我们从简单的开始:

方法一:简单遍历

这种方法只能获取文件名,不能区分文件和目录:

java

import java.io.File;  
  
public class Example03 {  
    public static void main(String[] args) {  
        File file = new File("src");  
        if (file.isDirectory()) {  
            System.out.println("是目录");  
            String[] names = file.list();  
            for (String name : names) {  
                System.out.println(name);  
            }  
        }  
    }  
}

方法二:带过滤器的遍历

有时候我们只关心特定类型的文件,可以使用过滤器:

java

import java.io.File;  
import java.io.FilenameFilter;  
  
public class Example04 {  
    public static void main(String[] args) {  
        // 创建 File 对象  
        File file = new File("src");  
        // 创建过滤器对象  
        FilenameFilter filter = new FilenameFilter() {  
            // 这个方法决定哪些文件会被包含在结果中
            @Override  
            public boolean accept(File currFile, String name) {  
                // 只要.java文件
                if (name.endsWith(".java")) {  
                    return true; // 返回true表示接受这个文件
                } else {  
                    return false; // 返回false表示过滤掉这个文件
                }  
            }  
        };  
  
        if (file.exists() && file.isDirectory()) {  
            String[] names = file.list(filter);  
            for (String name : names) {  
                System.out.println(name);  
            }  
        }  
    }  
}

前面的例子都只能遍历当前目录下的文件,但如果目录下还有子目录,就需要用到递归遍历

list() vs listFiles()
  • list() - 返回字符串数组,只有文件名
  • listFiles() - 返回File对象数组,可以进一步操作每个文件

java

import java.io.File;  
  
public class Example05 {  
    public static void main(String[] args) {  
        File file = new File("src");  
        showFiles(file);  
    }  
  
    /**  
     * 递归显示指定目录下的所有文件(包括子目录中的文件)
     * @param dir 要遍历的目录  
     */  
    public static void showFiles(File dir) {  
        // 获取当前目录下所有文件和子目录的数组  
        File[] fileArray = dir.listFiles();  
        
        if (fileArray == null) return; // 防止空指针异常
        
        for (File file : fileArray) {  
            if (file.isDirectory()) {  
                // 如果是目录,就递归调用自己来遍历子目录
                showFiles(file);  
            }  
            // 打印文件的绝对路径  
            System.out.println(file.getAbsolutePath());  
        }  
    }  
}

File类的delete()方法可以删除文件或目录,但有一些限制。让我们先看一个简单的例子:

准备测试数据
在电脑的D盘中创建一个名为hello的文件夹,并在里面放一个文本文件。然后试试用下面的代码删除。

java

import java.io.File;  
  
public class Example06 {  
    public static void main(String[] args) {  
        File file = new File("d:/hello");  
        if (file.exists()) {  
            if (file.delete()) {  
                System.out.println("删除成功!");  
            } else {  
                System.out.println("删除失败!");  
            }   
        }  
    }  
}

为什么删除失败?
delete()方法有一个重要限制:只能删除空目录或文件。如果目录下还有其他文件或子目录,删除就会失败。

要删除非空目录,必须先删除里面的所有内容,再删除目录本身。这需要用到递归:

java

import java.io.File;  
  
public class Example07 {  
    public static void main(String[] args) {  
        File dir = new File("d:/hello");  
        deleteFiles(dir);  
    }  
  
    /**  
     * 递归删除整个目录(包括其中的所有文件和子目录)
     * @param dir 要删除的目录  
     */  
    public static void deleteFiles(File dir) {  
        if (dir.exists()) {  
            File[] files = dir.listFiles();  
            
            if (files != null) { // 防止空指针异常
                // 先删除目录下的所有内容
                for (File file : files) {  
                    if (file.isDirectory()) {  
                        // 如果是子目录,递归删除
                        deleteFiles(file);  
                    } else {  
                        // 如枟是文件,直接删除
                        file.delete();  
                    }  
                }  
            }
            
            // 最后删除空目录本身
            dir.delete();  
        }  
    }  
}
删除操作风险
注意:Java的delete()方法是永久删除,不会放入回收站!文件一旦删除就无法恢复,所以在使用时要格外小心

在前面的过滤器代码中,我们用到了匿名内部类。这是Java中一个很实用的特性:

java

FilenameFilter filter = new FilenameFilter() {
    @Override
    public boolean accept(File currFile, String name) {
        // 方法实现
    }
};
匿名内部类的原理

这种写法相当于同时做了三件事:

  1. 定义了一个实现FilenameFilter接口的类(没有名字,所以叫匿名类)
  2. 创建了这个类的对象
  3. 把对象赋值给filter变量

等价的常规写法:

java

// 先定义一个实现类
class MyFilter implements FilenameFilter {
    @Override
    public boolean accept(File currFile, String name) {
        if (currFile.isFile() && name.endsWith(".java")) {
            return true;
        } else {
            return false;
        }
    }
}

// 然后创建对象
FilenameFilter filter = new MyFilter();

编写一个程序,实现以下功能:

  1. 搜索指定目录下的所有以.txt结尾的文件(只搜索当前目录,不包括子目录)
  2. 显示文件名和文件大小(以KB为单位)
  3. 统计找到的文件总数

参考代码框架:

java

import java.io.File;
import java.io.FilenameFilter;

public class FileSearcher {
    public static void main(String[] args) {
        // 请将此处替换为你的目录路径
        File directory = new File("D:\\test");

		// 检查目录是否存在,若不存在需要给出提示,并退出程序
        
        // 待实现:创建文件过滤器,只接受.txt文件
        FilenameFilter filter = null;  // 在此处修改,实现文件过滤器
        
        // 待实现:遍历目录,打印文件信息
        
        // 待实现:统计文件总数
    }
}
  1. 使用匿名内部类实现FilenameFilter
  2. 文件大小要求显示为KB为单位
  3. 要处理目录不存在的情况

text

文件名:test1.txt,大小:1.5 KB
文件名:test2.txt,大小:2.3 KB
文件名:note.txt,大小:0.5 KB
共找到 3 个txt文件

编写一个程序,实现以下功能:

  1. 递归搜索指定目录下的所有以.java结尾的文件(包括子目录中的)
  2. 显示文件名和文件大小(以KB为单位)
  3. 删除找到的文件
  4. 统计删除的文件总数
  1. 使用匿名内部类实现FilenameFilter
  2. 文件大小要求显示为KB为单位
  3. 要处理目录不存在的情况

text

文件名:test1.java,大小:1.5 KB
文件名:test2.java,大小:2.3 KB
文件名:note.java,大小:0.5 KB
已删除 test1.java
已删除 test2.java
已删除 note.java
共删除 3 个java文件

相关内容