三十六、File 类与 IO 流基础——文件操作的「第一步」

三十六、File 类与 IO 流基础------文件操作的「第一步」📁

😫 痛点引入 :Java 程序的数据都在内存里,一关机就没了?想操作磁盘上的文件却不知道从何下手?

别慌!今天带你从 File 类 开始,搞定文件路径、创建删除、判断获取,再入门 IO 字节流,让数据在内存和磁盘之间跑起来!🏃


一、File 类------文件和目录的「地图」🗺️

1.1 File 是什么

File 是文件和目录路径名的表示,在 java.io 包里。

关键理解 :File 封装的不是真正存在的文件,只是一个路径。你可以把它理解为「地址牌」------写了个地址,不代表那栋楼已经建好了 🏗️

1.2 路径的两种写法

路径类型 说明 示例
绝对路径 📍 从盘符开始的完整路径 D:\Java\JDK\bin
相对路径 📌 相对于项目根目录的简化路径 a/b/c.txt

⚠️ 注意 :IDEA 中默认的相对路径是当前项目的根目录。

1.3 路径分隔符

Windows 路径中的 \ 是转义字符,表示一个确切的 \ 需要写 \\:

java 复制代码
// 方式1:双反斜杠(Windows 风格)
File f1 = new File("D:\\Java\\JDK\\bin");

// 方式2:正斜杠(推荐,跨平台通用)
File f2 = new File("D:/Java/JDK/bin");

二、File 构造方法 🏗️

2.1 四种构造方式

构造方法 说明
File(String path) 将路径字符串封装为 File 对象
File(String parent, String child) 将两个路径拼接后封装
File(File parent, String child) 将 File 对象和路径拼接
File(URI uri) 将 URI 封装为 File 对象

2.2 代码示例

java 复制代码
import java.io.File;

public class Demo01 {
    public static void main(String[] args) {
        // 方式1:直接传路径字符串
        File f1 = new File("D:/Java/JDK/bin");
        System.out.println("方式1:" + f1);

        // 方式2:拼接两个路径字符串
        File f2 = new File("D:/Java", "JDK/bin");
        System.out.println("方式2:" + f2);

        // 方式3:File对象 + 子路径
        File f3 = new File(f1, "java");
        System.out.println("方式3:" + f3);
    }
}

三、File 创建与删除 ✂️

3.1 方法速查表

方法 功能 返回值
createNewFile() 创建文件 📄 boolean,成功 true
mkdir() 创建单级文件夹 📁 boolean,成功 true
mkdirs() 创建多级文件夹 📂 boolean,成功 true
delete() 删除文件或空文件夹 🗑️ boolean,成功 true

3.2 ⚠️ 注意事项

  1. mkdir() 只能创建最后一级文件夹,父级路径必须存在
  2. mkdirs() 可以创建多级文件夹,父级不存在会自动创建
  3. delete() 不走回收站!删了就没了!
  4. delete() 只能删除空文件夹(里面不能有任何内容)

3.3 代码示例

java 复制代码
import java.io.File;
import java.io.IOException;

public class Demo02 {
    public static void main(String[] args) throws IOException {
        // 1. 创建文件(绝对路径)
        File f1 = new File("D:/1.txt");
        System.out.println("创建文件:" + f1.createNewFile());  // true
        // 再次创建,文件已存在,返回false
        System.out.println("再次创建:" + f1.createNewFile());  // false

        // 2. 创建单级文件夹(相对路径,相对于项目根目录)
        File f2 = new File("a");
        System.out.println("创建单级文件夹:" + f2.mkdir());  // true

        // 3. 创建多级文件夹
        File f3 = new File("a/b/c/d");
        System.out.println("创建多级文件夹:" + f3.mkdirs());  // true

        // 4. 删除文件(不走回收站!⚠️)
        File f4 = new File("D:/1.txt");
        System.out.println("删除文件:" + f4.delete());  // true

        // 5. 删除文件夹(只能删空文件夹!)
        File f5 = new File("a/b/c/d");
        System.out.println("删除空文件夹:" + f5.delete());  // true
    }
}

四、File 判断与获取 🔍

4.1 判断方法

方法 功能
exists() 路径是否存在
isFile() 是否是文件
isDirectory() 是否是文件夹

4.2 获取方法

方法 功能
getAbsolutePath() 获取绝对路径
getPath() 获取封装的路径
getName() 获取文件/文件夹名称(最后一级)
length() 获取文件大小(字节数)
list() 获取文件夹下所有子项名称 → String[]
listFiles() 获取文件夹下所有子项 File 对象 → File[]

4.3 代码示例

java 复制代码
import java.io.File;

public class Demo03 {
    public static void main(String[] args) {
        File f = new File("a");

        // 判断功能
        System.out.println("路径是否存在:" + f.exists());
        System.out.println("是否是文件:" + f.isFile());
        System.out.println("是否是文件夹:" + f.isDirectory());

        // 获取功能
        System.out.println("绝对路径:" + f.getAbsolutePath());
        System.out.println("封装路径:" + f.getPath());
        System.out.println("名称:" + f.getName());

        // 获取文件夹下的子项
        if (f.isDirectory()) {
            System.out.println("--- list() 获取名称 ---");
            String[] names = f.list();
            for (String name : names) {
                System.out.println(name);
            }

            System.out.println("--- listFiles() 获取File对象 ---");
            File[] files = f.listFiles();
            for (File file : files) {
                System.out.println(file.getAbsolutePath());
            }
        }
    }
}

五、实战:筛选文件夹中的文件 💪

需求 :获取文件夹内以 .txt 为后缀且大小大于 1KB 的文件的绝对路径。

java 复制代码
import java.io.File;
import java.util.Scanner;

public class Demo04 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("请输入文件夹路径:");
        File f = new File(sc.next());

        // 第一步:判断文件夹是否存在
        if (!f.exists()) {
            System.out.println("文件夹不存在!");
            return;
        }

        // 第二步:获取文件夹下所有文件对象
        File[] files = f.listFiles();
        if (files == null) {
            System.out.println("文件夹为空!");
            return;
        }

        // 第三步:筛选 .txt 且 > 1KB 的文件
        int count = 0;
        for (File file : files) {
            if (file.getName().endsWith(".txt") && file.length() > 1024) {
                System.out.println("找到文件:" + file.getAbsolutePath());
                count++;
            }
        }
        System.out.println("共找到 " + count + " 个符合条件的文件");
    }
}

六、IO 流入门------数据传输的「管道」🚰

6.1 什么是 IO 流

约翰·冯·诺依曼 说过:永远站在内存的角度看待问题!

  • 输入流(Input):从外部往内存里进 → 读数据 📥

  • 输出流(Output):从内存往外出 → 写数据 📤

    磁盘文件 ──输入流──→ 内存(Java程序)──输出流──→ 磁盘文件
    (读数据) (写数据)

6.2 IO 流的分类

分类维度 类型 说明
按方向 输入流 / 输出流 读 / 写
按数据 字节流 / 字符流 字节(万能)/ 字符(文本)

6.3 字节流 vs 字符流

对比项 字节流 字符流
操作单位 字节(byte) 字符(char)
后缀标识 Stream Reader / Writer
适用场景 所有文件(万能)✅ 纯文本文件
中文处理 需要编码表 自动处理 ✅
推荐场景 不确定文件类型时优先用 操作文本时优先用

七、字节输入流 FileInputStream 📥

7.1 核心概念

FileInputStream 是 InputStream 的子类,用于从磁盘文件读取字节数据。

特点:

  • 可以读取文本、视频、音频、图片等所有类型的文件
  • 计算机中一切数据都以字节为单位存储

7.2 构造方法

构造方法 说明
FileInputStream(File f) 将 File 对象封装为字节输入流
FileInputStream(String path) 将路径字符串封装为字节输入流

⚠️ 注意 :只能封装文件路径,封装文件夹没有意义(文件夹本身没数据)。

7.3 读取方法

方法 功能 返回值
read() 读取一个字节 int,读到末尾返回 -1
read(byte[] arr) 读取数组大小的字节 int,本次读取的字节个数
close() 关闭流 void

7.4 代码示例

方式一:单字节读取

java 复制代码
import java.io.FileInputStream;
import java.io.IOException;

public class Demo05 {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("1.txt");

        int i;  // 记录读取到的字节信息
        while ((i = fis.read()) != -1) {
            System.out.print((char) i);  // 转为字符打印
        }

        fis.close();  // 关闭流!一定要关!
    }
}

方式二:字节数组读取(推荐 ⭐效率更高)

java 复制代码
import java.io.FileInputStream;
import java.io.IOException;

public class Demo06 {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("1.txt");

        byte[] b = new byte[4];  // 缓冲数组
        int len;  // 记录本次读取的字节个数

        while ((len = fis.read(b)) != -1) {
            // ⚠️ 一定要用 len 指定转换个数!
            // 最后一次读取可能不够数组大小,不指定会有残留数据
            System.out.print(new String(b, 0, len));
        }

        fis.close();
    }
}

💡 为什么一定要 new String(b, 0, len) 而不是 new String(b)?

复制代码
假设文件内容:abcde(5个字节)
数组大小:4

第一次读取:[a, b, c, d]  len=4  → new String(b,0,4) = "abcd" ✅
第二次读取:[e, b, c, d]  len=1  → new String(b,0,1) = "e" ✅
                                    new String(b)     = "ebcd" ❌ 残留数据!

八、字节输出流 FileOutputStream 📤

8.1 构造方法

构造方法 说明
FileOutputStream(File f) 封装为字节输出流(覆盖模式)
FileOutputStream(String path) 封装为字节输出流(覆盖模式)
FileOutputStream(String path, boolean append) append=true 追加模式 🔗

覆盖 vs 追加:

  • append=false(默认):每次写入相当于新建文件,原内容没了 ❌
  • append=true:在原文件内容后面拼接 ✅

8.2 写出方法

方法 功能
write(int b) 写出一个字节
write(byte[] b) 写出字节数组
write(byte[] b, int off, int len) 写出数组的一部分

8.3 代码示例

java 复制代码
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo07 {
    public static void main(String[] args) throws IOException {
        // 覆盖模式(默认)
        FileOutputStream fos = new FileOutputStream("2.txt");

        // 写出一个字节
        fos.write('a');   // 写出字符a
        fos.write(98);    // 写出数字98(对应字符b)

        // 写出字节数组
        byte[] b = {65, 66, 67, 68};
        fos.write(b);     // 写出ABCD

        // 写出数组的一部分
        fos.write(b, 1, 2);  // 写出BC

        // 写出字符串(先转为字节数组)
        fos.write("书源最帅".getBytes());

        fos.close();
    }
}

追加模式示例:

java 复制代码
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo08 {
    public static void main(String[] args) throws IOException {
        // 第二个参数为true → 追加模式,在原文件末尾拼接
        FileOutputStream fos = new FileOutputStream("2.txt", true);

        fos.write("这是追加的内容".getBytes());
        fos.close();
    }
}

九、文件复制------读写结合 📋

核心思路:读取源文件 → 写出到目标文件

java 复制代码
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo09 {
    public static void main(String[] args) throws IOException {
        // 源文件(读)
        FileInputStream fis = new FileInputStream("1.txt");
        // 目标文件(写)
        FileOutputStream fos = new FileOutputStream("3.txt");

        int i;
        while ((i = fis.read()) != -1) {
            fos.write(i);  // 读取一个字节,写出一个字节
        }

        // 先关输出流,再关输入流
        fos.close();
        fis.close();
    }
}

十、高效字节缓冲流 ⚡

10.1 为什么需要缓冲流?

普通字节流每次读写一个字节,都要访问磁盘,效率极低 🐌

缓冲流底层有一个 8192 字节(8KB) 的缓冲数组,一次性读写一大批数据,减少磁盘交互次数,效率飞升 🚀

复制代码
普通流:  磁盘 → 1字节 → 内存 → 1字节 → 磁盘  (频繁访问磁盘)
缓冲流:  磁盘 → 8192字节 → 缓冲区 → 取1个 → 内存  (批量读取)

10.2 构造方法

构造方法 说明
BufferedInputStream(InputStream is) 包装字节输入流
BufferedOutputStream(OutputStream os) 包装字节输出流

10.3 代码示例

java 复制代码
import java.io.*;

public class Demo10 {
    public static void main(String[] args) throws IOException {
        // 包装普通流,提升效率
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("1.txt"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("4.txt"));

        int i;
        while ((i = bis.read()) != -1) {
            bos.write(i);
        }

        bos.close();  // close() 会自动调用 flush()
        bis.close();
    }
}

10.4 ⚠️ 缓冲输出流的注意点

java 复制代码
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("3.txt"));
bos.write(97);  // 数据还在缓冲区,没有写到磁盘!

// 方式1:手动刷新
bos.flush();    // 强制将缓冲区内容写到磁盘

// 方式2:关闭流时自动刷新
bos.close();    // close() 内部会自动调用 flush()

10.5 性能对比(拷贝视频文件)

java 复制代码
import java.io.*;

public class Demo11 {
    public static void main(String[] args) throws IOException {
        test_缓冲流拷贝();
        test_普通流拷贝();
    }

    // 缓冲流拷贝
    private static void test_缓冲流拷贝() throws IOException {
        long start = System.currentTimeMillis();

        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("1.mp4"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("3.mp4"));

        int i;
        while ((i = bis.read()) != -1) {
            bos.write(i);
        }

        bos.close();
        bis.close();

        long end = System.currentTimeMillis();
        System.out.println("缓冲流拷贝耗时:" + (end - start) + "ms");  // 几十ms
    }

    // 普通流拷贝
    private static void test_普通流拷贝() throws IOException {
        long start = System.currentTimeMillis();

        FileInputStream fis = new FileInputStream("1.mp4");
        FileOutputStream fos = new FileOutputStream("2.mp4");

        int i;
        while ((i = fis.read()) != -1) {
            fos.write(i);
        }

        fos.close();
        fis.close();

        long end = System.currentTimeMillis();
        System.out.println("普通流拷贝耗时:" + (end - start) + "ms");  // 几千ms
    }
}

预期结果 :缓冲流比普通流快 几十倍!⚡


十一、IO 异常处理------流一定要关!🔒

11.1 传统方式(Java 6 及以前)

java 复制代码
import java.io.*;

public class Demo12 {
    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("1.txt");
            int i;
            while ((i = fis.read()) != -1) {
                System.out.print((char) i);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // finally 中关闭流,确保一定会执行
            try {
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

11.2 try-with-resources(Java 7+ ⭐推荐)

java 复制代码
import java.io.*;

public class Demo13 {
    public static void main(String[] args) {
        // 在 try() 中创建流对象,使用完自动关闭!
        try (
            FileInputStream fis = new FileInputStream("1.txt");
            FileOutputStream fos = new FileOutputStream("5.txt");
        ) {
            int i;
            while ((i = fis.read()) != -1) {
                fos.write(i);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 不需要手动 close(),自动关闭!✅
    }
}

11.3 两种方式对比

方式 代码量 是否自动关闭 推荐度
try-finally 多(嵌套 try) 手动关闭 ⭐⭐
try-with-resources 少 自动关闭 ✅ ⭐⭐⭐⭐⭐

本篇总结 📝

  1. File 类 🗺️:封装路径,创建/删除文件和文件夹
  2. 路径 📍:绝对路径(从盘符开始)、相对路径(相对项目根目录)
  3. IO 流 🚰:输入流(读)、输出流(写),永远站在内存角度
  4. 字节流 📦:FileInputStream/FileOutputStream,万能流,一切皆字节
  5. 缓冲流 ⚡:8KB 缓冲数组,减少磁盘交互,效率翻倍
  6. 文件复制 📋:读一个写一个,先关输出再关输入
  7. 异常处理 🔒:try-with-resources 自动关闭流(Java 7+)

🚀 下一篇预告 :《三十七、字符流与转换流------中文处理的「救星」》------ 用来搞定字符流读写、编码表、转换流,再也不怕中文乱码!


作者 :书源丶
发布平台 :CSDN
系列:「Java基础篇」

相关推荐
一 乐8 小时前
动漫书销售商城|基于springboot + vue动漫书销售商城(源码+数据库+文档)
java·数据库·vue.js·spring boot·毕业设计
卓怡学长8 小时前
w214基于jsp知道特产网
java·intellij-idea
步行cgn8 小时前
Spring 注解使用详解
java·spring
一条小小yu8 小时前
Spring IoC的理解
java·后端·spring
落魄实习生9 小时前
Agent Scope Java 2.x 系列【7】工具使用
java·开发语言·ai
旺仔学长 哈哈9 小时前
springboot钓鱼爱好者交流平台APP设计与实现
java·spring boot·mysql·充电桩管理系统
自强的小白10 小时前
核心功能(Service接口)
java·mybatis
乌暮11 小时前
深入理解 Java 泛型:把「万能盒子」用对、用稳
java·开发语言·后端·学习
大猫和小黄12 小时前
深入理解 Java Optional:从设计哲学到工程实战
java