Java零基础入门:深入Java核心:文件IO与序列化全解

1. 什么是IO?为什么需要它?

在编程世界里,**I/O(Input/Output,输入/输出)**​ 是指计算机与外部设备(如硬盘、网络、键盘、屏幕)之间进行数据交换的过程。

在Java中,所有的I/O操作都被抽象为"流(Stream)"的概念。你可以把流想象成一根管道,数据就像水流一样,从一端流向另一端。


2. File类:文件的"身份证"

在读写文件之前,我们需要先认识文件本身。java.io.File 类并不代表文件的内容,而是代表文件或目录的路径名抽象表示。

2.1 常用操作

import java.io.File;

import java.io.IOException;

public class FileDemo {

public static void main(String\[\] args) {

// 创建一个File对象(此时文件还不一定存在)

File file = new File("test.txt");

try {

// 创建新文件

if (file.createNewFile()) {

System.out.println("文件创建成功!");

}

// 获取文件信息

System.out.println("文件名:" + file.getName());

System.out.println("绝对路径:" + file.getAbsolutePath());

System.out.println("是否可读:" + file.canRead());

System.out.println("文件大小:" + file.length() + " 字节");

} catch (IOException e) {

e.printStackTrace();

}

}

}

💡 重点记忆:

  • File 只是"文件的名片",不是文件内容。
  • 常用方法:exists(), isFile(), isDirectory(), mkdir(), delete()。

3. 流的分类:字节流 vs 字符流

Java的流体系非常庞大,但我们可以按两个维度来划分:

类型 抽象基类 处理单位 适用场景
字节流​ InputStream / OutputStream 字节(8位) 图片、视频、所有二进制文件
字符流​ Reader / Writer 字符(16位) 文本文件(.txt, .java, .csv等)

3.1 为什么要有字符流?

因为不同国家有不同的文字编码(UTF-8、GBK等)。如果用字节流读取中文文本,可能会出现乱码。字符流内部会进行编码转换,保证你读到的是正确的字符。


4. 实战编码:文本读取与统计

4.1 需求说明

编写一个程序:

  1. 读取一个文本文件(如 input.txt)
  2. 统计文件的行数 和字符数(不含换行符)
  3. 将统计结果写入一个新的文件(output.txt)

4.2 使用 BufferedReader 和 BufferedWriter

为什么要用 Buffered?因为直接读写硬盘非常慢!缓冲流会在内存中开辟一个缓冲区,一次性读写一大块数据,效率提升数十倍。

完整代码示例

import java.io.*;

public class FileStatsDemo {

public static void main(String\[\] args) {

String inputFile = "input.txt";

String outputFile = "output.txt";

// 为了确保演示能运行,先创建一个测试文件

createTestFile(inputFile);

int lineCount = 0;

int charCount = 0;

// 使用 try-with-resources 自动关闭资源(非常重要!)

try (

BufferedReader br = new BufferedReader(new FileReader(inputFile));

BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile))

) {

String line;

// 逐行读取

while ((line = br.readLine()) != null) {

lineCount++; // 行数累加

charCount += line.length(); // 字符数累加

}

// 写入统计结果

bw.write("===== 文件统计报告 =====");

bw.newLine(); // 写入换行符

bw.write("源文件:" + inputFile);

bw.newLine();

bw.write("总行数:" + lineCount);

bw.newLine();

bw.write("总字符数:" + charCount);

bw.newLine();

bw.write("=======================");

System.out.println("统计完成!结果已写入 " + outputFile);

} catch (IOException e) {

System.err.println("发生IO错误:" + e.getMessage());

}

}

private static void createTestFile(String fileName) {

try (BufferedWriter bw = new BufferedWriter(new FileWriter(fileName))) {

bw.write("Hello, 世界!");

bw.newLine();

bw.write("这是第二行文本。");

bw.newLine();

bw.write("Java IO 很有趣。");

} catch (IOException e) {

e.printStackTrace();

}

}

}

运行结果(output.txt内容):

===== 文件统计报告 =====

源文件:input.txt

总行数:3

总字符数:24

=======================

4.3 代码优化点解析

  1. try-with-resources:JDK 7+ 特性,无论是否发生异常,都会自动调用 close() 方法,防止内存泄漏。
  2. BufferedReader.readLine():一次读一行,返回 null 表示读到文件末尾,非常方便做行数统计。
  3. BufferedWriter.newLine():写入系统相关的换行符,跨平台兼容。

5. 对象序列化:让对象"永生"

前面我们处理的都是文本和字节。但如果我们想把一个Java对象 (比如一个 User 对象)保存到硬盘上,或者通过网络发送,该怎么办?这就需要序列化。

5.1 什么是序列化?

  • 序列化(Serialization):把Java对象转换成字节序列的过程。
  • 反序列化(Deserialization):把字节序列恢复为Java对象的过程。

5.2 实现 Serializable 接口

要让一个类可以被序列化,最简单的方式是实现 java.io.Serializable 接口。这是一个标记接口,里面没有任何方法,只是告诉JVM:"这个类的对象可以被序列化"。

import java.io.Serializable;

// 实现Serializable接口

public class User implements Serializable {

// 强烈建议显式声明serialVersionUID

private static final long serialVersionUID = 1L;

private String name;

private int age;

private String email;

// transient 关键字:表示该字段不参与序列化

private transient String password;

public User(String name, int age, String email, String password) {

this.name = name;

this.age = age;

this.email = email;

this.password = password;

}

@Override

public String toString() {

return "User{name='" + name + "', age=" + age +

", email='" + email + "', password='" + password + "'}";

}

}

5.3 序列化与反序列化实战

import java.io.*;

public class SerializationDemo {

public static void main(String\[\] args) {

String filePath = "user.ser"; // .ser 是序列化文件的常见后缀

// 1. 序列化:将对象写入文件

serializeObject(filePath);

// 2. 反序列化:从文件读取对象

deserializeObject(filePath);

}

private static void serializeObject(String filePath) {

User user = new User("张三", 25, "zhangsan@example.com", "123456");

try (ObjectOutputStream oos = new ObjectOutputStream(

new FileOutputStream(filePath))) {

oos.writeObject(user);

System.out.println("对象已序列化:" + user);

System.out.println("密码字段(序列化前):" + user.getPassword()); // 注意:这里需要加getter才能访问,上面代码没写,演示用toString即可

} catch (IOException e) {

e.printStackTrace();

}

}

private static void deserializeObject(String filePath) {

try (ObjectInputStream ois = new ObjectInputStream(

new FileInputStream(filePath))) {

User restoredUser = (User) ois.readObject();

System.out.println("对象已反序列化:" + restoredUser);

System.out.println("密码字段(反序列化后):" + restoredUser.getPassword()); // 输出 null

} catch (IOException | ClassNotFoundException e) {

e.printStackTrace();

}

}

}

运行输出分析:

对象已序列化:User{name='张三', age=25, email='zhangsan@example.com', password='123456'}

密码字段(序列化前):123456

对象已反序列化:User{name='张三', age=25, email='zhangsan@example.com', password='null'}

密码字段(反序列化后):null

可以看到,transient 修饰的 password 字段在反序列化后变成了 null,因为它没有被写入文件。


6. 序列化核心注意事项(面试常考!)

  1. serialVersionUID :
    • 如果你不显式定义,JVM会根据类结构自动生成一个。一旦你修改了类(比如加了个字段),自动生成的UID会变,导致反序列化失败抛出 InvalidClassException。
    • 最佳实践:永远显式声明 private static final long serialVersionUID = 1L;。
  2. transient 关键字 :
    • 用于修饰不需要序列化的字段(如密码、敏感信息、临时缓存)。
  3. 父类继承 :
    • 如果父类实现了 Serializable,子类自动可序列化。
    • 如果父类没实现,子类序列化时,父类的字段会被忽略(需父类有无参构造器才能反序列化)。
  4. 静态变量不会被序列化 :
    • 因为静态变量属于类,不属于对象实例。

7. 本章总结

知识点 核心类/接口 关键点
文件操作 File 抽象路径,不操作内容
字节流 FileInputStream / FileOutputStream 万能流,适合二进制
字符流 FileReader / FileWriter 专治文本乱码
缓冲流 BufferedReader / BufferedWriter 效率高,推荐日常使用
对象序列化 ObjectOutputStream / ObjectInputStream 对象持久化
标记接口 Serializable 开启序列化能力
控制字段 transient 排除敏感字段

8. 每日一练

  1. 基础:编写程序,复制一张图片(使用字节流 FileInputStream/FileOutputStream)。
  2. 进阶:编写一个简单的"日志系统",将程序运行的日志(带时间戳)用 BufferedWriter 追加写入 app.log 文件。
  3. 挑战:创建一个 Student 类(包含姓名、成绩列表),实现序列化。将3个Student对象存入文件,然后读取出来计算平均分。
相关推荐
拿本唠嗑AI研究1 小时前
本体工程实战指南:从建模到可维护的完整落地路径
java·人工智能·python·jena·ontology
Smoothcloud润云1 小时前
PyTorch推理服务模型热切换与失败回滚实战:降低版本更新中断
服务器·人工智能·pytorch·python·深度学习·gpu
lambo mercy1 小时前
Day3:结构化输出
java·数据库·oracle
jsjzsl22 小时前
递归自改进系统(RSI)的本质是自发对称破缺:智能爆炸有界—— AI研发切记盲目投入
人工智能·python·算法
程序员清风2 小时前
最短路径算法:BFS、Dijkstra 与 Floyd
java·算法·宽度优先
2601_954811822 小时前
机器视觉教学套件选型指南:五维技术参数对比与课程体系评估
python·数码相机
小马不起床2 小时前
零基础玩转 FastAPI_05:后端课程模块
python·fastapi
jimy12 小时前
c++的 Type Traits (一)
开发语言·c++
小王2043 小时前
Day 41:模型注意力机制 — SE / CBAM / ECA
python·深度学习·机器学习