21、Java 文件与流操作全解析

Java 文件与流操作全解析

1. 目录与文件操作

1.1 目录分隔符注意事项

在不同的操作系统中,目录分隔符有所不同。Windows 使用反斜杠 \ 作为子目录之间的分隔符,而 Unix 和 Linux 使用正斜杠 / 。在 Java 中,如果在 Windows 机器上运行,Java 会将正斜杠转换为反斜杠。若使用反斜杠,需注意要使用两个反斜杠 \\ ,因为第一个反斜杠是转义字符。

1.2 列出目录内容

可以通过调用 list() 方法返回目录的内容。该方法返回一个字符串数组,其中包含存储在目录中的文件的名称。不过, list() 方法只能用于目录,因此在调用该方法之前,应先调用 isDirectory() 方法来确定文件是否为目录。 isDirectory() 方法在文件为目录时返回 true ,否则返回 false

以下是一个示例代码,展示了如何使用 isDirectory() 方法和 list() 方法来检索和显示目录的内容:

import java.io.File;
class Demo {
    public static void main(String args[] ) {
        String dir = " /temp";
        File file1 = new File(dir);
        if (file1.isDirectory()) {
            System.out.println( "Directory:" + dir);
            String str[] = file1.list();
            for (int i=0; i <str.length; i++) {
                File file2 = new File (dir + "/" + str[i]);
                if (file2.isDirectory()) {
                    System.out.println("Directory: "+ str[i]);
                }
                else {
                    System.out.println(str[i]);
                }
            }
        } else {
            System.out.println("Not a directory ");
        }
    }
}

这个示例的操作步骤如下:
1. 定义目录路径并创建 File 对象。
2. 检查该对象是否为目录。
3. 如果是目录,调用 list() 方法获取目录内容。
4. 遍历内容数组,判断每个元素是否为子目录并进行相应输出。

1.3 操作流程

graph TD;
    A[定义目录路径] --> B[创建File对象];
    B --> C{是否为目录};
    C -- 是 --> D[调用list()方法];
    D --> E[遍历内容数组];
    E --> F{是否为子目录};
    F -- 是 --> G[输出为目录];
    F -- 否 --> H[输出文件名];
    C -- 否 --> I[输出非目录信息];

2. Java 流概述

2.1 流的概念

在 Java 程序中,流与描述水流或电子在电路中流动的概念类似,只不过 Java 中的流由字节组成。Java 有几个基于四个抽象类构建的流类,分别是 InputStream OutputStream Reader Writer 。当使用 Java 的具体流类与字节流进行交互时,会间接使用这些类。

2.2 常用文件操作类型

通常,程序以三种方式存储数据:作为未封装在类中的单个数据片段、作为封装在类中的数据,或作为存储在数据库中的数据。下面将重点介绍四种常用的文件操作:写入文件、读取文件、追加文件以及读写对象到文件。

操作类型 描述
写入文件 创建文件输出流,使用打印写入器将数据写入文件
读取文件 打开文件,以不同方式读取数据
追加文件 在文件末尾添加新数据
读写对象 存储和检索封装在类中的数据

3. 写入文件

3.1 操作步骤

要将数据写入文件,需按以下步骤操作:
1. 创建文件输出流:使用 FileOutputStream 类的构造函数,并传入要打开的文件名。如果文件不在当前目录中,可包含完整路径。构造函数返回对文件输出流的引用。
2. 创建打印写入器:调用 PrintWriter 类的构造函数,并传入文件输出流的引用。构造函数返回对 PrintWriter 的引用。
3. 写入数据:使用 PrintWriter 引用调用 print() println() 方法将数据写入文件。
4. 关闭文件:完成写入后,调用 close() 方法关闭文件。

3.2 示例代码

以下是一个示例,展示了如何打开文件输出流、创建打印写入器并将数据写入文件:

import java.io.*;
import java.util.*;
public class Demo {
    public  static void main( String args[] ) {
        String studentFirstName = "Bob ";
        String studentLastName = " Smith ";
        String finalGrade = "A";
        try {
            PrintWriter out = new PrintWriter(
                    new FileOutputStream("Student.dat"));
            out.print(studentFirstName);
            out.print(studentLastName);
            out.println(finalGrade);
        } catch (IOException e) {
            System.out.println(e);
        } finally {
            out.close();
        }
    }
}

在这个示例中,所有涉及打开文件输出流和写入文件的语句都包含在 try 块中,以捕获可能出现的异常,如磁盘空间不足。

3.3 写入文件流程

graph TD;
    A[定义要写入的数据] --> B[创建FileOutputStream];
    B --> C[创建PrintWriter];
    C --> D[写入数据];
    D --> E{是否写入完成};
    E -- 是 --> F[关闭文件];
    E -- 否 --> D;
    B --> G{是否出现异常};
    G -- 是 --> H[捕获异常并输出信息];
    G -- 否 --> D;

4. 读取文件

4.1 读取方式

有多种方式从文件中读取数据:
- 一次读取一个字节的数据。
- 一次读取特定数量的字节。
- 一次读取一行字节,一行由一系列以换行符对应的字节结尾的字节组成。
- 以字符串形式读取数据。

4.2 操作步骤

为了读取文件,可按以下步骤操作:
1. 打开文件:使用 FileReader 类的构造函数,并传入要打开的文件名。如果文件不在当前目录中,应包含完整路径。
2. 创建缓冲读取器:使用 BufferedReader 构造函数,并传入用于打开文件的 FileReader 引用,以减少读取文件的开销。
3. 读取数据:调用 BufferedReader readLine() 方法读取一行数据,该方法在到达文件末尾时返回 null
4. 关闭文件:完成读取后,调用 close() 方法关闭文件。

4.3 示例代码

以下是一个示例,展示了如何创建文件读取器和缓冲读取器,并从文件中读取数据:

import java.io.*;
import java.util.*;
public class Demo {
    public  static void main( String args[] ) {
        String line;
        try {
            BufferedReader in = new BufferedReader(
                    new FileReader("Student.dat"));
            while (( line = in.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            System.out.println(e);
        } finally {
            in.close();
        }
    }
}

在这个示例中,使用 while 循环不断读取文件内容,直到到达文件末尾。

4.4 读取文件流程

graph TD;
    A[定义变量] --> B[创建FileReader];
    B --> C[创建BufferedReader];
    C --> D{是否到达文件末尾};
    D -- 否 --> E[读取一行数据];
    E --> F[输出数据];
    F --> D;
    D -- 是 --> G[关闭文件];
    B --> H{是否出现异常};
    H -- 是 --> I[捕获异常并输出信息];
    H -- 否 --> D;

5. 追加文件

5.1 追加原理

通常,写入文件时数据会覆盖原有内容。若要在文件末尾添加新数据,可将 FileOutputStream 构造函数的第二个参数设置为 true 。默认情况下,使用一个参数的构造函数时,字节会写入文件开头;而使用两个参数且第二个参数为 true 时,字节会写入文件末尾。若文件不存在,会创建该文件。

5.2 示例代码

以下是一个示例,展示了如何追加数据到文件:

import java.io.*;
import java.util.*;
public class Demo {
    public  static void main( String args[] ) {
        String studentFirstName = "Mary";
        String studentLastName = " Jones ";
        String grade = "A";
        try {
            PrintWriter out = new PrintWriter(
                    new FileOutputStream("Student.dat", true));
            out.print(studentFirstName);
            out.print(studentLastName);
            out.println(grade);
        } catch (IOException e) {
            System.out.println(e);
        } finally {
            out.close();
        }
    }
}

这个示例与写入文件的示例类似,只是使用了 FileOutputStream 的两个参数构造函数,并将第二个参数设置为 true

5.3 追加文件流程

graph TD;
    A[定义要追加的数据] --> B[创建FileOutputStream(追加模式)];
    B --> C[创建PrintWriter];
    C --> D[追加数据];
    D --> E{是否追加完成};
    E -- 是 --> F[关闭文件];
    E -- 否 --> D;
    B --> G{是否出现异常};
    G -- 是 --> H[捕获异常并输出信息];
    G -- 否 --> D;

6. 读写对象到文件

6.1 背景与原理

在现实世界中,许多要存储在文件中的数据元素是类的数据成员。当需要保留类的实例时,可将实例保存到文件中,此时仅存储实例变量,方法不保存。为了能够读写对象到文件,需按以下步骤操作。

6.2 操作步骤

写入对象
  1. 实现 Serializable 接口:在要写入文件的类中实现该接口,以便将类的实例转换为字节流。
  2. 创建类的实例并赋值:创建类的实例,并为实例变量赋值。
  3. 打开文件:使用 FileOutputStream 类的构造函数打开文件。
  4. 创建对象输出流:使用 ObjectOutputStream 类的构造函数,并传入文件输出流的引用。
  5. 写入对象:调用 writeObject() 方法,并传入要写入文件的对象的引用。
  6. 关闭文件:调用 close() 方法关闭文件。
读取对象
  1. 打开文件:使用 FileInputStream 类的构造函数打开文件。
  2. 创建对象输入流:使用 ObjectInputStream 类的构造函数,并传入文件输入流的引用。
  3. 读取对象:调用 readObject() 方法,该方法返回 Object 类的实例,需将其转换为具体的类。
  4. 关闭文件:调用 close() 方法关闭文件。

6.3 示例代码

以下是一个示例,展示了如何读写 Student 类的对象到文件:

import java.io.*;
import java.util.*;
public class Demo {
    public static void main( String args[] ) {
        Student[] writeStudentInfo = new Student[3];
        Student[] readStudentInfo = new Student[3];
        writeStudentInfo [0] = new Student (
                "Bob", "Smith", "B");
        writeStudentInfo [1] = new Student (
                "Mary", "Jones", "A");
        writeStudentInfo [2] = new Student (
                "Tom", "Jones", "B+");
        try {
            ObjectOutputStream out = new ObjectOutputStream(
                    new FileOutputStream("Student.dat", true));
            for (int y = 0; y < 3; y ++) {
                out.writeObject(writeStudentInfo[y]);
            }
            out.close();
            ObjectInputStream in = new ObjectInputStream(
                    new FileInputStream("Student.dat"));
            for (int x = 0; x < 3; x++) {
                readStudentInfo[x] =
                        (Student) in.readObject();
            }
            in.close();
            for (int i = 0; i < 3; i++) {
                System.out.println(
                        readStudentInfo [i].studentFirstName);
                System.out.println(
                        readStudentInfo [i].studentLastName);
                System.out.println(
                        readStudentInfo [i].studentGrade);
            }
        } catch (Exception e) {
            System.out.println(e);
        }
    }
}
class Student implements Serializable {
    // 类的具体实现可根据需求添加
    String studentFirstName;
    String studentLastName;
    String studentGrade;
    public Student(String studentFirstName, String studentLastName, String studentGrade) {
        this.studentFirstName = studentFirstName;
        this.studentLastName = studentLastName;
        this.studentGrade = studentGrade;
    }
}

6.4 读写对象流程

graph LR;
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;

    A([开始]):::startend --> B(定义对象数组):::process;
    B --> C(创建对象实例并赋值):::process;
    C --> D(创建FileOutputStream):::process;
    D --> E(创建ObjectOutputStream):::process;
    E --> F(写入对象到文件):::process;
    F --> G(关闭ObjectOutputStream):::process;
    G --> H(创建FileInputStream):::process;
    H --> I(创建ObjectInputStream):::process;
    I --> J(从文件读取对象):::process;
    J --> K(关闭ObjectInputStream):::process;
    K --> L(输出对象信息):::process;
    L --> M([结束]):::startend;

    D --> N{是否出现异常}:::decision;
    N -- 是 --> O(捕获异常并输出信息):::process;
    N -- 否 --> E;
    H --> P{是否出现异常}:::decision;
    P -- 是 --> Q(捕获异常并输出信息):::process;
    P -- 否 --> I;

通过以上内容,我们详细介绍了 Java 中文件与流的各种操作,包括目录操作、写入文件、读取文件、追加文件以及读写对象到文件。这些操作在实际开发中非常常见,掌握它们能帮助我们更好地处理文件数据。

7. 操作总结与对比

7.1 不同文件操作对比

操作类型 主要类使用 关键方法 操作特点
写入文件 FileOutputStream PrintWriter print() println() close() 可创建新文件,覆盖原有内容
读取文件 FileReader BufferedReader readLine() close() 可按字节、行或字符串读取
追加文件 FileOutputStream PrintWriter print() println() close() 在文件末尾添加新数据
读写对象 FileOutputStream ObjectOutputStream FileInputStream ObjectInputStream writeObject() readObject() close() 存储和检索类的实例

7.2 操作步骤对比列表

操作类型 步骤
写入文件 1. 定义数据;2. 创建 FileOutputStream ;3. 创建 PrintWriter ;4. 写入数据;5. 关闭文件
读取文件 1. 定义变量;2. 创建 FileReader ;3. 创建 BufferedReader ;4. 读取数据;5. 关闭文件
追加文件 1. 定义数据;2. 创建 FileOutputStream (追加模式);3. 创建 PrintWriter ;4. 追加数据;5. 关闭文件
读写对象 写入:1. 实现 Serializable 接口;2. 创建对象实例并赋值;3. 创建 FileOutputStream ;4. 创建 ObjectOutputStream ;5. 写入对象;6. 关闭流。读取:1. 创建 FileInputStream ;2. 创建 ObjectInputStream ;3. 读取对象;4. 关闭流

8. 异常处理与注意事项

8.1 异常处理

在文件操作中,可能会出现各种异常,如 IOException 等。为了确保程序的健壮性,需要对这些异常进行处理。在前面的示例代码中,我们使用了 try-catch-finally 结构来捕获和处理异常。例如,在写入文件时,如果磁盘空间不足,会抛出 IOException ,我们可以在 catch 块中捕获该异常并输出相应信息。

8.2 注意事项

  • 路径问题 :在不同操作系统中,目录分隔符不同,要注意使用正确的分隔符。在 Java 中,Windows 下使用反斜杠时需要转义。
  • 资源关闭 :完成文件操作后,一定要调用 close() 方法关闭文件流,避免资源泄漏。可以使用 finally 块确保无论是否发生异常,文件流都会被关闭。
  • 对象序列化 :在读写对象时,类必须实现 Serializable 接口,否则会抛出 NotSerializableException 异常。

9. 实际应用场景

9.1 数据持久化

在很多应用中,需要将数据持久化存储到文件中,以便下次使用。例如,一个学生管理系统可以将学生信息存储到文件中,在程序启动时读取文件中的数据进行初始化。

9.2 日志记录

程序运行过程中的日志信息可以记录到文件中,方便后续的问题排查和分析。可以使用追加文件的方式,不断将新的日志信息添加到日志文件中。

9.3 数据备份

定期将重要的数据备份到文件中,以防止数据丢失。可以使用读写对象的方式,将复杂的数据结构存储到文件中。

10. 总结

10.1 核心要点回顾

  • 掌握了不同操作系统下目录分隔符的使用。
  • 学会了使用 File 类进行目录和文件的操作,如列出目录内容。
  • 熟悉了 Java 流的概念和常用的流类,包括 InputStream OutputStream Reader Writer
  • 掌握了写入文件、读取文件、追加文件和读写对象到文件的具体操作步骤和代码实现。
  • 了解了异常处理和资源管理的重要性。

10.2 未来拓展

可以进一步学习 Java 的 NIO(New I/O)包,它提供了更高效的文件和网络操作方式。还可以结合数据库操作,实现更复杂的数据存储和管理。

通过以上对 Java 文件与流操作的全面解析,希望读者能够在实际开发中灵活运用这些知识,处理各种文件数据。

graph LR;
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;

    A([开始学习]):::startend --> B(了解文件操作基础):::process;
    B --> C(掌握写入文件操作):::process;
    C --> D(掌握读取文件操作):::process;
    D --> E(掌握追加文件操作):::process;
    E --> F(掌握读写对象操作):::process;
    F --> G(处理异常和注意事项):::process;
    G --> H(应用到实际场景):::process;
    H --> I(拓展学习NIO等):::process;
    I --> J([持续提升]):::startend;

    B --> K{是否理解}:::decision;
    K -- 否 --> L(重新学习基础):::process;
    K -- 是 --> C;
    C --> M{是否掌握}:::decision;
    M -- 否 --> N(复习写入操作):::process;
    M -- 是 --> D;
    D --> O{是否掌握}:::decision;
    O -- 否 --> P(复习读取操作):::process;
    O -- 是 --> E;
    E --> Q{是否掌握}:::decision;
    Q -- 否 --> R(复习追加操作):::process;
    Q -- 是 --> F;
    F --> S{是否掌握}:::decision;
    S -- 否 --> T(复习读写对象操作):::process;
    S -- 是 --> G;

这个流程图展示了学习 Java 文件与流操作的过程,从基础学习到实际应用,再到拓展学习,同时包含了复习和巩固的环节,帮助读者更好地掌握这些知识。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值