IO流
2883字约10分钟
2025-10-03
IO流
Java中的数据写入到本地磁盘中,叫输出流
Java程序读取本地磁盘,叫输入流
I/O(读/写)
管理文件和文件夹
创建文件
// 在D盘中的book文件夹中创建文件(1.txt)
File file = new File("D:\\book\\1.txt");
try {
// 执行这一行代码时,文件才正常创建
file.createNewFile();
System.out.println("创建成功");
} catch (IOException e) {
e.printStackTrace();
}
// 父文件夹路径 + 子文件路径
// 相当于拼接了
File file1 = new File("D:\\book");
String fileName = "2.txt";
File file2 = new File(file1, fileName);
try {
file2.createNewFile();
System.out.println("创建成功");
} catch (IOException e) {
e.printStackTrace();
}
String fileName1 = "D:\\book";
String fileName2 = "3.txt";
File file3 = new File(fileName1, fileName2);
try {
file3.createNewFile();
System.out.println("创建成功");
} catch (IOException e) {
e.printStackTrace();
}
删除文件
// 删除文件
File file = new File("D:\\book\\2.txt");
file.delete();
System.out.println("删除成功");
删除目录
// 删除目录(目录中没有文件的前提下)
File file1 = new File("D:\\book\\ceshi");
file1.delete();
System.out.println("删除成功");
创建目录
// 创建目录
File file3 = new File("D:\\ceshi1");
file3.mkdir();
System.out.println("文件夹创建成功");
创建多级目录
// 创建多级目录,mkdirs也可以创建单个文件夹
File file4 = new File("D:\\ceshi1\\a\\b");
file4.mkdirs();
System.out.println("文件夹创建成功");
获取文件信息
File file = new File("D:\\book\\1.txt");
System.out.println("文件名字:" + file.getName()); // 1.txt
System.out.println("文件绝对路径:" + file.getAbsolutePath()); // D:\book\1.txt
System.out.println("文件父级目录:" + file.getParent()); // D:\book
System.out.println("文件大小(字节):" + file.length()); // 5
System.out.println("文件/文件夹是否存在:" + file.exists()); // true
System.out.println("文件是不是一个文件:" + file.isFile()); // true
System.out.println("文件是不是一个文件夹:" + file.isDirectory()); // false
字节流与处理流
处理流不能不包装其他流而单独使用,因为处理流(过滤流)的设计本质是 “增强已有流的功能”,它自身并不直接与数据源 / 目的地交互,必须依赖被包装的基础流(可以是节点流或其他处理流)才能工作。
特性 | 字节流(基础流) | 处理流(过滤流) |
---|---|---|
数据源连接 | 直接连接数据源 / 目的地 | 不直接连接,包装其他流 |
功能 | 仅提供基本的字节读写功能 | 提供增强功能(缓冲、转换等) |
依赖关系 | 独立工作,不依赖其他流 | 必须依赖基础流才能工作 |
性能 | 直接操作,可能频繁 IO,性能较低 | 通常有优化(如缓冲),性能更好 |
典型用途 | 直接读写字节数据 | 数据加工、转换、提高效率 |
特性 | FileInputStream / FileOutputStream | FileReader / FileWriter | BufferedReader / BufferedWriter |
---|---|---|---|
数据类型 | 字节流(byte) | 字符流(char) | 字符流(char,处理流) |
读取单位 | 字节(8 位) | 字符(16 位,依赖编码) | 字符 / 行(基于缓冲) |
底层操作 | 直接操作文件字节 | 基于FileInputStream ,自动转换字节为字符(默认编码) | 包装其他字符流,提供缓冲和按行读取 |
编码处理 | 不处理编码,直接读字节 | 使用平台默认编码(可能导致乱码) | 依赖被包装的流的编码处理 |
主要用途 | 读取 / 写入二进制文件(图片、音频等) | 读取 / 写入文本文件(默认编码) | 高效读取 / 写入文本(缓冲 + 按行读取) |
文件输入流(读取)
FileInputStream 文件输入流
read()
读取文件字符,当读取到末尾时,返回一个 -1
read和readLine区别:read是读取字符,readLine是读取一行字符串
String fileName = "D:\\book\\hi.txt";
int num = 0;
char[] c = new char[11];
FileInputStream f = null;
try {
f = new FileInputStream(fileName);
// 读取方法一
while (num != -1) {
// 将文件中的字符赋值给num(ASCII码)
num = f.read();
// 在将num转换成字符输出
System.out.print((char)num); // hello,world
}
// while (f.read() != -1) {
//// 这种写法之所以读取的结果不完整
//// 是因为read()被调用了两次,所以最后的结果不正确
// System.out.print((char)f.read());
// }
// 读取方法二
while ((num = f.read(c)) != -1) {
// 将文件中的字符赋值给num
// 在将num转换成字符输出
// new String(字符数组, 从下标0开始写入, 写入长度)
System.out.print(new String(c, 0, num)); // hello,world
System.out.println(num); // 数组长度:11
}
} catch {
...
} finally {
try {
// 关闭文件流,释放内存
f.close();
} catch (IOException e) {
e.printStackTrace();
}
}
文件输出流(写入)
FileOutputStream 文件输出流
使用 FileOutputStream 写入文件时,必须要使用close()
方法结束,否则写入文件失败
String fileName = "D:\\book\\hello.txt";
FileOutputStream f = null;
try {
// 会覆盖掉文件中的内容
f = new FileOutputStream(fileName);
// 向文件中写入H值
f.write('H');
// 写入字符串
String text = "Hello";
// 将字符串转换成字符数组
f.write(text.getBytes());
// f.write(字符数组, 从下标0开始写入, 写入长度)
f.write(text.getBytes(), 0, text.length());
// 追加写入,不会覆盖原本类容
f = new FileOutputStream(fileName, true);
f.write(text.getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
f.close();
} catch (IOException e) {
e.printStackTrace();
}
}
拷贝文件(二进制文件)
// 老的文件路径
String oldFilename = "D:\\img.png";
// 新的文件路径
String newFilename = "D:\\img1.png";
FileInputStream fis = null;
FileOutputStream fos = null;
byte[] bt = new byte[1024];
int num = 0;
try {
fis = new FileInputStream(oldFilename);
fos = new FileOutputStream(newFilename);
// 边读边写
// fis.read(bt) 将读取的文件写入到bt字节数组
// fos.write 写入
// num 字节大小长度
while ((num = fis.read(bt)) != -1) {
fos.write(bt, 0, num);
}
System.out.println("拷贝成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fis.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
FileReader
FileReader 读取文件的方法和 FileInputStream 一样
- 优先用
FileInputStream
:读取二进制文件(图片、视频、压缩包等),或需要直接操作字节流的场景。 - 优先用
FileReader
:读取文本文件(如.txt
、.csv
),且希望直接处理字符(无需手动编码转换)的场景。
FileReader和FileInputStream的区别:
- **
FileInputStream
**属于 字节流(InputStream),用于读取 原始字节数据(如图片、音频、二进制文件等)。它以字节(byte
)为单位处理数据,直接操作二进制流。 - **
FileReader
**属于 字符流(Reader),用于读取 字符数据(如文本文件.txt
、.java
等)。它以字符(char
)为单位处理数据,会自动根据平台默认编码(或指定编码)将字节转换为字符
特性 | FileInputStream | FileReader |
---|---|---|
数据单位 | 字节(byte ) | 字符(char ) |
适用文件类型 | 二进制文件 | 文本文件 |
编码处理 | 不处理编码,直接读字节 | 自动转换字节为字符 |
继承关系 | 继承自 InputStream | 继承自 Reader |
小问题:因为 FileReader 默认使用的是UTF-8来读取文件的,当读取的文件编码是别的格式时,就会出现乱码的问题(乱码问题可以使用转换流来解决)
FileWriter
FileWriter 写入文件的方法和 FileOutputStream 一样
- 优先用
FileOutputStream
:写入二进制文件(图片、视频、可执行文件等),或需要精确控制字节数据的场景。 - 优先用
FileWriter
:写入文本内容(如日志、配置文件),尤其是需要直接操作字符串或字符的场景(无需手动处理编码转换)
注意点:
- 使用FileWriter写入文件时,必须要使用
close()
方法结束,否则写入文件失败 - FileOutputStream 则没有这个问题
BufferedReader
- 读取一行的文本
- 到达流的末尾时,返回
null
BufferedReader b = new BufferedReader(new FileReader("D:\\book\\hi.txt"));
String line;
// readLine() 是 BufferedReader 类提供的一个常用方法,用于读取一行文本
while ((line = b.readLine()) != null) {
System.out.println(line);
}
// 关闭流
b.close();
BufferedWriter
BufferedWriter b = new BufferedWriter(new FileWriter("D:\\book\\hi.txt"));
// 插入一行字符串
b.write("hello,world");
// 换行
b.newLine();
b.write("hello,world");
b.newLine();
b.write("hello,world");
// 关闭流
b.close();
Buffered拷贝(字节文件)
该方法不能拷贝二进制文件(如视频,音乐,图片等)
String line;
BufferedReader b1 = null;
BufferedWriter b2 = null;
try {
b1 = new BufferedReader(new FileReader("D:\\book\\hi.txt"));
b2 = new BufferedWriter(new FileWriter("D:\\book\\h2.txt"));
while ((line = b1.readLine()) != null) {
b2.write(line);
// 换行
b2.newLine();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
b1.close();
b2.close();
} catch (IOException e) {
e.printStackTrace();
}
}
该方法可以读取任何文件
BufferedOutputStream bos = null;
BufferedInputStream bis = null;
byte[] bytes = new byte[1024];
int num;
try {
// 读取
bis = new BufferedInputStream(new FileInputStream("D:\\book\\img.png"));
// 写入
bos = new BufferedOutputStream(new FileOutputStream("D:\\book\\img11.png"));
while ((num = bis.read(bytes)) != -1) {
bos.write(bytes, 0 ,num);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
bis.close();
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
序列化与反序列化
序列化:保存数据时,保存数据的值和数据类型
反序列化:恢复数据时,恢复数据的值和数据类型
要想对象实现序列化机制,需要该对象实现两个接口(二选一)
Serializable // 没有实现方法(推荐使用这个方法)
Externalizable // 有实现方法
序列化
public class ObjFile {
public static void main(String[] args) {
String filePath = "D:\\book\\log.dat";
ObjectOutputStream objfile = null;
try {
objfile = new ObjectOutputStream(new FileOutputStream(filePath));
// int
objfile.write(100);
// boolean
objfile.writeBoolean(true);
// char
objfile.writeChar('a');
// double
objfile.writeDouble(1.2);
// String
objfile.writeUTF("hello");
// 对象
objfile.writeObject(new Dog());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
objfile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
class Dog implements Serializable {}
反序列化
ObjectInputStream objint = null;
try {
// 反序列化的顺序要和序列化的顺序一致
objint = new ObjectInputStream(new FileInputStream("D:\\book\\log.dat"));
System.out.println(objint.readInt());
System.out.println(objint.readBoolean());
System.out.println(objint.readChar());
System.out.println(objint.readDouble());
System.out.println(objint.readUTF());
System.out.println(objint.readObject());
// 调用序列化里面的对象
Dog dog = (Dog)(objint.readObject());
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
objint.close();
} catch (IOException e) {
e.printStackTrace();
}
}
注意点
序列化和反序列化的顺序要一致
序列化和反序列化对象要实现 Serializable
序列化类添加SerialVersionUID属性,为了提高版本的兼容性
// 显式定义序列化版本号 private static final long serialVersionUID = 1L;
如果不定义
SerialVersionUID
,Java 会根据类的结构(字段、方法、继承关系等)自动计算一个版本号。此时,即使对类做非常微小的修改(比如新增一个字段、调整方法的顺序,甚至只是加了一行注释),系统计算出的版本号都可能发生变化。序列化会将类中的属性进行序列化,但static和transient修饰的成员除外
在序列化中创建了一个对象,则该对象需要实现
Serializable
方法public class Dog implements Serializable { A a = new A(); } class A implements Serializable {}
序列化具备继承性,但某个类继承序列化类,该类即使没有实现
Serializable
也可以被序列化
转换流
InputStreamReader
public static void main(String[] args) throws Exception {
String filepath = "D:\\book\\hi.txt";
// 以gbk编码的方式读取
InputStreamReader gbk = new InputStreamReader(new FileInputStream(filepath), "gbk");
BufferedReader b1 = new BufferedReader(gbk);
System.out.println(b1.readLine());
b1.close();
}
OutputStreamWriter
public static void main(String[] args) throws Exception {
String filepath = "D:\\book\\hello.txt";
OutputStreamWriter out1 = new OutputStreamWriter(
new FileOutputStream(filepath), "gbk");
// 以gbk编码的方式写入
out1.write("你好,Java");
out1.close();
}
打印流
PrintStream
public static void main(String[] args) throws Exception {
PrintStream out1 = System.out;
out1.println("nihao");
out1.write("java".getBytes());
// 指定打印的位置
System.setOut(new PrintStream("D:\\book\\log.dat"));
// 输出的内容将不在终端中显示,而是打印到上面指定的文件中
for (int i = 0; i < 10; i++) {
System.out.println("log测试1");
}
out1.close();
}
PrintWriter
public static void main(String[] args) throws Exception {
// 指定打印的位置
PrintWriter p = new PrintWriter(
new FileWriter("D:\\book\\log.dat"));
// 输出的内容将不在终端中显示,而是打印到上面指定的文件中
p.print("nihao");
p.close();
}
Properties
要求:读取的文件必须是 k-v
格式的,也就是键=值
,键值对没有空格,且不需要用引号引起来,默认为String类型
读取的文件
user=admin
pwd=123
读取代码
public static void main(String[] args) throws IOException {
Properties p1 = new Properties();
p1.load(new FileReader("D:\\book\\pwd.txt"));
p1.list(System.out);
// 读取user等于的值
String user = p1.getProperty("user");
System.out.println(user); // admin
}
写入代码
public static void main(String[] args) throws Exception {
Properties p1 = new Properties();
p1.setProperty("ip","172.0.0.1");
// 值1:修改文件的路径 | 值2:注释
p1.store(new FileOutputStream("D:\\book\\pwd.txt"), null);
// 追加模式
p1.store(new FileOutputStream("D:\\book\\pwd.txt", true), null);
}