跳过正文
Java--File与IO流
  1. TP` Blog/

Java--File与IO流

·8 分钟· loading ·
龙城飞将
作者
龙城飞将
最美的是月亮🌙……
目录
Java学习之旅 - 这篇文章属于一个选集。
§ 8: 本文

📁1. File 类
#

什么是 File 类
#

File 类是 Java 提供的用于操作文件和目录的类,位于 java.io 包中。它代表文件或目录的路径名抽象表示,但不代表文件的实际内容

重要提示File 类只能操作文件或目录本身(创建、删除、重命名等),不能读写文件内容。要读写文件内容需要使用 IO 流。

核心作用
#

功能类别 具体作用
文件/目录信息获取 获取名称、路径、大小、修改时间等
文件/目录操作 创建、删除、重命名文件或目录
判断功能 判断是否存在、是否为文件/目录、是否可读写
目录遍历 列出目录下的文件和子目录

3个常用构造方法
#

// 1. 根据路径字符串创建
File file1 = new File("D:\\test.txt");

// 2. 根据父路径和子路径创建
File file2 = new File("D:\\", "test.txt");

// 3. 根据父 File 对象和子路径创建
File parent = new File("D:\\");
File file3 = new File(parent, "test.txt");

常用方法一览
#

判断方法
#

方法 说明
boolean exists() 判断文件或目录是否存在
boolean isFile() 判断是否为文件
boolean isDirectory() 判断是否为目录
boolean canRead() 判断是否可读
boolean canWrite() 判断是否可写

获取方法
#

方法 说明
String getName() 获取文件或目录名称
String getPath() 获取路径字符串
String getAbsolutePath() 获取绝对路径
long length() 获取文件大小**(字节)**
long lastModified() 获取最后修改时间(时间戳)

创建和删除方法
#

方法 说明
boolean createNewFile() 创建新文件(文件不存在时)
boolean mkdir() 创建单级目录
boolean mkdirs() 创建多级目录
boolean delete() 删除文件或空目录

遍历方法
#

方法 说明
String[] list() 获取目录下所有文件和目录的名称数组(一级)
File[] listFiles() 获取目录下所有文件和目录的 File 对象数组(一级)

使用实例
#

import [java.io](http://java.io).File;
import [java.io](http://java.io).IOException;
import java.text.SimpleDateFormat;
import [java.util.Date](http://java.util.Date);

public class FileDemo {
    public static void main(String[] args) throws IOException {
        // 1. 创建 File 对象
        File file = new File("D:\\demo\\test.txt");
        
        // 2. 创建文件(需要先创建父目录)
        File parentDir = file.getParentFile();
        if (!parentDir.exists()) {
            parentDir.mkdirs(); // 创建多级目录
        }
        if (!file.exists()) {
            file.createNewFile(); // 创建文件
            System.out.println("文件创建成功");
        }
        
        // 3. 获取文件信息
        System.out.println("文件名:" + file.getName());
        System.out.println("绝对路径:" + file.getAbsolutePath());
        System.out.println("文件大小:" + file.length() + " 字节");
        
        // 格式化时间
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("最后修改时间:" + sdf.format(new Date(file.lastModified())));
        
        // 4. 判断
        System.out.println("是否为文件:" + file.isFile());
        System.out.println("是否可读:" + file.canRead());
        
        // 5. 遍历目录
        File dir = new File("D:\\demo");
        File[] files = dir.listFiles();
        if (files != null) {
            for (File f : files) {
                System.out.println((f.isDirectory() ? "[目录] " : "[文件] ") + f.getName());
            }
        }
    }
}

🌊2. IO 流体系
#

什么是 IO 流
#

IO 流(Input/Output Stream) 是 Java 用于处理输入输出操作的机制,可以实现数据在程序与外部设备(文件、网络、内存等)之间的传输。

形象理解:把数据想象成水,IO 流就是水管,负责将数据从源头(输入)流向目的地(输出)。

IO 流的分类
#

按流向分类
#

类型 说明 代表类
输入流(Input) 将数据从外部读入程序 InputStream、Reader
输出流(Output) 将数据从程序写出到外部 OutputStream、Writer

按数据类型分类
#

类型 说明 单位 代表类
字节流 处理二进制数据(图片、视频、音频等) 字节(byte) InputStream、OutputStream
字符流 处理文本数据(自动处理编码) 字符(char) Reader、Writer

IO 流体系结构
#


📖3. 字节流详解
#

FileInputStream(文件字节输入流)
#

核心方法
#

方法 说明 缺点
int read() 读取一个字节,返回字节值(0-255),到末尾返回 -1 每次读一个字节效率低
int read(byte[] b) 读取多个字节到数组,返回实际读取的字节数 还是无法解决读汉字乱码问题
void close() 关闭流,释放资源

使用实例
#

import [java.io](http://java.io).FileInputStream;
import [java.io](http://java.io).IOException;

public class FileInputStreamDemo {
    public static void main(String[] args) {
        // 方式一:单字节读取(效率低,不推荐)
        try (FileInputStream fis = new FileInputStream("D:\\test.txt")) {
            int data;
            while ((data = [fis.read](http://fis.read)()) != -1) {
                System.out.print((char) data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // 方式二:字节数组读取(推荐)
        try (FileInputStream fis = new FileInputStream("D:\\test.txt")) {
            byte[] buffer = new byte[1024]; // 缓冲区
            int len;
            while ((len = [fis.read](http://fis.read)(buffer)) != -1) {
                System.out.print(new String(buffer, 0, len));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

FileOutputStream(文件字节输出流)
#

核心方法
#

方法 说明
void write(int b) 写入一个字节
void write(byte[] b) 写入字节数组
void write(byte[] b, int off, int len) 写入字节数组的一部分
void close() 关闭流并刷新缓冲区

使用实例
#

import [java.io](http://java.io).FileOutputStream;
import [java.io](http://java.io).IOException;

public class FileOutputStreamDemo {
    public static void main(String[] args) {
        // 创建输出流(第二个参数 true 表示追加模式)
        try (FileOutputStream fos = new FileOutputStream("D:\\output.txt", true)) {
            // 方式一:写入单个字节
            fos.write(65); // 写入字符 'A'
            
            // 方式二:写入字节数组
            String text = "Hello, Java IO!";
            fos.write(text.getBytes());
            
            // 写入换行符
            fos.write("\r\n".getBytes());
            
            System.out.println("写入成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

文件复制实战
#

import [java.io](http://java.io).*;

public class FileCopyDemo {
    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        
        try (FileInputStream fis = new FileInputStream("D:\\[source.mp](http://source.mp)4");
             FileOutputStream fos = new FileOutputStream("D:\\[target.mp](http://target.mp)4")) {
            
            byte[] buffer = new byte[8192]; // 8KB 缓冲区
            int len;
            while ((len = [fis.read](http://fis.read)(buffer)) != -1) {
                fos.write(buffer, 0, len);
            }
            
            long endTime = System.currentTimeMillis();
            System.out.println("复制完成,耗时:" + (endTime - startTime) + " 毫秒");
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

📝4. 字符流详解
#

FileReader(文件字符输入流)
#

使用实例
#

import [java.io](http://java.io).FileReader;
import [java.io](http://java.io).IOException;

public class FileReaderDemo {
    public static void main(String[] args) {
        try (FileReader fr = new FileReader("D:\\test.txt")) {
            char[] buffer = new char[1024];
            int len;
            while ((len = [fr.read](http://fr.read)(buffer)) != -1) {
                System.out.print(new String(buffer, 0, len));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

FileWriter(文件字符输出流)
#

核心方法
#

方法 说明
void write(int c) 写入单个字符
void write(char[] cbuf) 写入字符数组
void write(String str) 写入字符串
void flush() 刷新缓冲区
void close() 关闭流(自动刷新)

使用实例
#

import [java.io](http://java.io).FileWriter;
import [java.io](http://java.io).IOException;

public class FileWriterDemo {
    public static void main(String[] args) {
        try (FileWriter fw = new FileWriter("D:\\output.txt")) {
            // 写入字符串
            fw.write("第一行文本\r\n");
            fw.write("第二行文本\r\n");
            
            // 写入字符数组
            char[] chars = {'J', 'a', 'v', 'a'};
            fw.write(chars);
            
            fw.flush(); // 刷新到磁盘
            System.out.println("写入成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

⚡5. 缓冲流(高效流)
#

为什么需要缓冲流
#

普通流每次读写都直接操作磁盘,效率较低。缓冲流内部维护一个缓冲区(默认 8KB)对之前的流进行改装,减少实际的磁盘访问次数,大幅提升性能

BufferedInputStream & BufferedOutputStream
#

使用实例
#

import [java.io](http://java.io).*;

public class BufferedStreamDemo {
    public static void main(String[] args) {
        // 使用缓冲流复制文件
        try (BufferedInputStream bis = new BufferedInputStream(
                 new FileInputStream("D:\\[source.zip](http://source.zip)"));
             BufferedOutputStream bos = new BufferedOutputStream(
                 new FileOutputStream("D:\\[target.zip](http://target.zip)"))) {
            
            byte[] buffer = new byte[1024];
            int len;
            while ((len = [bis.read](http://bis.read)(buffer)) != -1) {
                bos.write(buffer, 0, len);
            }
            
            System.out.println("复制完成");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

BufferedReader & BufferedWriter
#

特有方法
#

方法 说明
String readLine() 读取一行文本(不包含换行符)
void newLine() 写入系统相关的换行符

使用实例
#

import [java.io](http://java.io).*;

public class BufferedReaderWriterDemo {
    public static void main(String[] args) {
        // 按行读取文件
        try (BufferedReader br = new BufferedReader(
                 new FileReader("D:\\input.txt"));
             BufferedWriter bw = new BufferedWriter(
                 new FileWriter("D:\\output.txt"))) {
            
            String line;
            int lineNumber = 1;
            while ((line = br.readLine()) != null) {
                // 在每行前添加行号
                bw.write(lineNumber + ": " + line);
                bw.newLine(); // 写入换行符
                lineNumber++;
            }
            
            System.out.println("处理完成");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

🔄6. 转换流
#

作用
#

转换流是字节流与字符流之间的桥梁,可以指定字符编码。

类名 作用
InputStreamReader 将字节输入流转换为字符输入流(解码)
OutputStreamWriter 将字符输出流转换为字节输出流(编码)

使用场景
#

  • 处理不同编码的文本文件(UTF-8、GBK 等)
  • 从网络、控制台等字节流中读取字符数据

使用实例
#

import [java.io](http://java.io).*;

public class ConvertStreamDemo {
    public static void main(String[] args) {
        // 读取 GBK 编码的文件
        try (InputStreamReader isr = new InputStreamReader(
                 new FileInputStream("D:\\gbk.txt"), "GBK");
             BufferedReader br = new BufferedReader(isr)) {
            
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // 写入 UTF-8 编码的文件
        try (OutputStreamWriter osw = new OutputStreamWriter(
                 new FileOutputStream("D:\\utf8.txt"), "UTF-8");
             BufferedWriter bw = new BufferedWriter(osw)) {
            
            bw.write("这是 UTF-8 编码的文本");
            bw.newLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

📦7. 对象流(序列化)
#

什么是序列化
#

概念 说明
序列化 将对象转换为字节序列,保存到文件或网络传输
反序列化 将字节序列还原为对象

使用条件
#

对象的类必须实现 Serializable 接口(标记接口,无需实现方法)

import [java.io](http://java.io).Serializable;

public class Student implements Serializable {
    private static final long serialVersionUID = 1L; // 版本号
    
    private String name;
    private int age;
    private transient String password; // transient 修饰的字段不会被序列化
    
    // 构造方法、getter、setter 省略
}

使用实例
#

import [java.io](http://java.io).*;

public class ObjectStreamDemo {
    public static void main(String[] args) {
        // 序列化:将对象写入文件
        try (ObjectOutputStream oos = new ObjectOutputStream(
                 new FileOutputStream("D:\\student.dat"))) {
            
            Student student = new Student("张三", 20, "123456");
            oos.writeObject(student);
            System.out.println("序列化成功");
            
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // 反序列化:从文件读取对象
        try (ObjectInputStream ois = new ObjectInputStream(
                 new FileInputStream("D:\\student.dat"))) {
            
            Student student = (Student) ois.readObject();
            System.out.println("反序列化成功:" + student);
            
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

📋8. IO 流选择指南
#

常见组合方案
#

场景 推荐方案
读取文本文件 FileReader + BufferedReader
写入文本文件 FileWriter + BufferedWriter
复制二进制文件 FileInputStream + BufferedInputStream
  • FileOutputStream + BufferedOutputStream | | 读取指定编码文本 | FileInputStream + InputStreamReader + BufferedReader | | 对象持久化 | FileOutputStream + ObjectOutputStream |

💡9. 最佳实践
#

1. 使用 try-with-resources 自动关闭流
#

// 推荐写法:自动关闭资源
try (FileInputStream fis = new FileInputStream("test.txt")) {
    // 使用流
} catch (IOException e) {
    e.printStackTrace();
}

2. 优先使用缓冲流
#

// 普通流
FileReader fr = new FileReader("test.txt");

// 缓冲流(性能更好)
BufferedReader br = new BufferedReader(new FileReader("test.txt"));

3. 合理设置缓冲区大小
#

// 小文件:1KB - 4KB
byte[] buffer = new byte[1024];

// 大文件:8KB - 64KB
byte[] buffer = new byte[8192];

4. 处理异常要具体
#

try {
    // IO 操作
} catch (FileNotFoundException e) {
    System.out.println("文件不存在");
} catch (IOException e) {
    System.out.println("读写错误");
}

10. IO框架
#


commons-io框架包

  1. FileUtils类
  2. IOUtils类

🧰 Apache Commons IO 简介
#

Apache Commons IO 是 Apache 提供的 IO 工具库,封装了文件与流的常见操作,减少样板代码、提升可读性与安全性。常用组件包括 FileUtils、IOUtils、FilenameUtils、FileFilterUtils、EndianUtils 等。

依赖引入
#

  • Maven
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.15.1</version>
</dependency>
  • Gradle
dependencies {
    implementation 'commons-io:commons-io:2.15.1'
}

选择与 JDK 版本兼容的最新稳定版即可,2.11+ 对 JDK8 友好,2.15+ 适配更高版本。


📂 FileUtils 常用功能
#

FileUtils 主要用于“文件与目录”的便捷操作。

1) 读写文件
#

// 读字符串(按编码)
String content = FileUtils.readFileToString(new File("a.txt"), StandardCharsets.UTF_8);

// 写字符串(覆盖或追加)
FileUtils.writeStringToFile(new File("b.txt"), "你好,Commons IO\n", StandardCharsets.UTF_8, true);

// 读写字节数组
byte[] bytes = FileUtils.readFileToByteArray(new File("pic.png"));
FileUtils.writeByteArrayToFile(new File("copy.png"), bytes);

2) 文件复制、移动、删除
#

// 复制文件或目录
FileUtils.copyFile(new File("a.txt"), new File("dest/a.txt"));
FileUtils.copyDirectory(new File("srcDir"), new File("destDir"));

// 移动(含覆盖目录处理)
FileUtils.moveFile(new File("a.txt"), new File("moved/a.txt"));
FileUtils.moveDirectory(new File("srcDir"), new File("movedDir"));

// 删除(安静删除,不抛异常)
FileUtils.deleteQuietly(new File("temp.log"));
// 强制删除目录
FileUtils.deleteDirectory(new File("outDir"));

3) 遍历与过滤
#

// 列出目录下满足扩展名的文件(递归)
Collection<File> files = FileUtils.listFiles(
        new File("project"), new String[]{"java", "md"}, true);

// 遍历文件并处理大小、修改时间
for (File f : files) {
    long size = FileUtils.sizeOf(f);          // 文件大小
    long dirSize = FileUtils.sizeOfDirectory(f.getParentFile());
}

4) 临时文件与磁盘空间
#

// 创建父目录
FileUtils.forceMkdir(new File("logs/app"));

// 可用空间查询(字节)
long free = FileUtils.freeSpaceKb("C:");

小贴士:对于大文件复制,配合 NIO 或 Buffered 流可能更高效;FileUtils 胜在简洁可靠。


🔗 IOUtils 常用功能
#

IOUtils 主要用于“流”的读写、拷贝和关闭等操作。

1) 流与字符串、字节的互转
#

// InputStream -> String / byte[]
String text = IOUtils.toString(new FileInputStream("a.txt"), StandardCharsets.UTF_8);
byte[] data = IOUtils.toByteArray(new FileInputStream("pic.jpg"));

// String / byte[] -> OutputStream
IOUtils.write("hello", new FileOutputStream("b.txt"), StandardCharsets.UTF_8);
IOUtils.write(data, new FileOutputStream("copy.jpg"));

2) 流拷贝与按行读取
#

// 高效复制(内部有缓冲)
try (InputStream in = new FileInputStream("large.bin");
     OutputStream out = new FileOutputStream("large.copy.bin")) {
    long n = IOUtils.copy(in, out);  // 返回复制的字节数
}

// 按行读取
try (InputStream in = new FileInputStream("log.txt");
     Reader reader = new InputStreamReader(in, StandardCharsets.UTF_8)) {
    List<String> lines = IOUtils.readLines(reader);
    lines.forEach(System.out::println);
}

3) 安静关闭与 NOP 输出
#

// 安静关闭(吞异常,谨慎使用)
Closeable c = new FileInputStream("a.txt");
IOUtils.closeQuietly(c);

// 一个丢弃所有数据的 OutputStream(调试场景)
OutputStream blackhole = IOUtils.nullOutputStream();

🆚 FileUtils vs IOUtils 该用谁?
#

  • 处理“文件/目录” → 选 FileUtils
  • 处理“流”读写、转换、复制 → 选 IOUtils
  • 复制文件:已知是文件对象 → FileUtils.copyFile。若你只有 InputStream → IOUtils.copy

✅ 实战示例:按行过滤复制文本
#

public static void filterCopy(Path src, Path dest) throws IOException {
    try (BufferedReader br = Files.newBufferedReader(src, StandardCharsets.UTF_8);
         Writer writer = new OutputStreamWriter(new FileOutputStream(dest.toFile()), StandardCharsets.UTF_8)) {
        for (String line; (line = br.readLine()) != null; ) {
            if (!line.isBlank()) {
                IOUtils.write(line + System.lineSeparator(), writer);
            }
        }
    }
}

✅ 实战示例:目录递归复制并统计大小
#

public static long copyAndSize(File srcDir, File destDir) throws IOException {
    FileUtils.copyDirectory(srcDir, destDir);
    return FileUtils.sizeOfDirectory(destDir);
}
Java学习之旅 - 这篇文章属于一个选集。
§ 8: 本文