字符集(Character Set)
字符集是对字符进行统一编号的一套规则。
计算机底层只能存储二进制数据,二进制可以映射为整数,因此字符通过编号的方式在计算机中存储。
常见字符集:
- ASCII
- GBK / GB2312
- UTF-8(Unicode)
字符集决定了字符 ↔ 字节之间的映射规则,是理解 I/O 流中文乱码问题的基础。
I/O 流概述
I/O(Input / Output)流用于完成数据在内存与外部介质之间的传输。
File类只能表示和操作文件本身,不能读写文件内容。
对文件内容的读写必须依赖 I/O 流。
I/O 流的分类
按数据流向划分
-
输入流(InputStream / Reader)
以内存为基准,将数据从磁盘文件或网络读入内存 -
输出流(OutputStream / Writer)
以内存为基准,将数据从内存写出到磁盘文件或网络
按数据单位划分
-
字节流
- 以字节(8 bit)为最小单位
- 适合处理二进制文件(图片、音视频、压缩包等)
-
字符流
- 以字符为最小单位
- 适合处理纯文本文件,自动处理字符编码
FileInputStream —— 文件字节输入流
将磁盘文件中的数据按字节读入内存。
构造器
FileInputStream(File file)FileInputStream(String path)
常用方法
public int read(); // 读取一个字节,读取结束返回 -1
示例:逐字节读取
FileInputStream in = new FileInputStream("text.txt");
int code;
while ((code = in.read()) != -1) {
System.out.println((char) code);
}
in.close();
该方式效率较低,且在读取多字节字符(如中文)时会出现乱码问题。
示例:字节数组读取
FileInputStream in = new FileInputStream("text.txt");
byte[] buffer = new byte[1024];
int len = in.read(buffer);
String result = new String(buffer, 0, len);
System.out.println(result);
in.close();
缓冲数组使用注意点
若多次复用同一个字节数组,必须使用 len 限定有效数据范围:
new String(buffer, 0, len);
处理乱码的常见方式
- 使用字符流(推荐)
- 明确指定字符编码
- 使用
readAllBytes()(适用于小文件)
byte[] bytes = in.readAllBytes();
String result = new String(bytes, StandardCharsets.UTF_8);
FileOutputStream —— 文件字节输出流
将内存中的数据按字节写出到磁盘文件。
构造器
FileOutputStream(File file)FileOutputStream(String path)FileOutputStream(File file, boolean append)FileOutputStream(String path, boolean append)
常用方法
write(int b)write(byte[] buffer)write(byte[] buffer, int offset, int length)
示例
FileOutputStream out = new FileOutputStream("text.txt", true);
out.write("Hello IO\n".getBytes());
out.flush();
out.close();
默认情况下,输出流会覆盖原文件内容。 设置
append=true可实现追加写入。
使用字节流复制文件
字节流适合复制任意类型文件。
基本步骤
- 创建输入流
- 创建输出流
- 循环读写字节数组
- 关闭资源
FileInputStream in = new FileInputStream("src.jpg");
FileOutputStream out = new FileOutputStream("dest.jpg");
byte[] buffer = new byte[8192];
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
in.close();
out.close();
FileReader —— 字符输入流
以字符为单位,将文本文件数据读入内存,自动处理编码。
构造器
FileReader(File file)FileReader(String path)
方法
read()read(char[] buffer)
FileReader reader = new FileReader("text.txt");
char[] buffer = new char[1024];
int len = reader.read(buffer);
System.out.println(new String(buffer, 0, len));
reader.close();
FileWriter —— 字符输出流
将内存中的字符数据写出到文件。
构造器
FileWriter(File file)FileWriter(String path)FileWriter(File file, boolean append)
方法
write(int c)write(char[] buffer)write(String str)
缓冲流(Buffered Stream)
缓冲流通过**内部缓冲区(默认 8KB)**减少系统 I/O 次数,提高性能。
字节缓冲流
BufferedInputStreamBufferedOutputStream
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.txt"));
字符缓冲流
BufferedReaderBufferedWriter
支持按行读写:
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
br.close();
对象的序列化与反序列化
概念
- 序列化:将对象转换为字节流写入文件
- 反序列化:从字节流还原对象
对象必须实现 Serializable 接口。
示例
ObjectOutputStream oos =
new ObjectOutputStream(new FileOutputStream("obj.dat"));
oos.writeObject(user);
oos.close();
ObjectInputStream ois =
new ObjectInputStream(new FileInputStream("obj.dat"));
User u = (User) ois.readObject();
ois.close();
transient 关键字
被 transient 修饰的成员变量不参与序列化,常用于敏感数据。
serialVersionUID
private static final long serialVersionUID = 1L;
用于保证序列化版本一致性。
打印流(PrintStream)
特点:
- 方便输出各种数据类型
- 不抛出 IOException
- 打印结果与内容一致
PrintStream ps = new PrintStream("out.txt");
ps.println(100);
ps.println("Hello");
ps.close();
输出重定向
System.setOut(new PrintStream("log.txt"));
System.out.println("redirect output");
Properties —— 属性集文件
Properties 是基于 Map<String,String> 的集合,常用于配置文件。
写入属性文件
Properties p = new Properties();
p.setProperty("user", "root");
p.setProperty("password", "123456");
p.store(new FileOutputStream("config.properties"), null);
读取属性文件
Properties p = new Properties();
p.load(new FileInputStream("config.properties"));
System.out.println(p.getProperty("user"));