Java中的IO流小结
Java中的IO
文件的操作
- 有三种构造器, 可以"捆绑"到某个路径, 然后调用
file.createNewFile()
可以创造文件 - 一些常用的文件方法, 包括但不限于获取名字, 路径, 父级目录, 大小, 是否存在, 删除等...
- 一些常用的目录方法, 这里其实目录也可以当作一种特殊的文件,
file.mkdirs()
可以创建多级目录,file.mkdir()
可以创建一级目录
IO流
Java程序中, 对于数据的输入/输出以"流"的方式进行, java.io包下提供了各种"流"类和接口, 用以获取不同种类的数据, 并通过方法输入或者输出数据
常用的类
一. 节点流
节点流是底层流, 直接跟数据源相连
一. FileInputStream/FileOutputStream
FileInputStream
用来读取文件的内容, 常用的方法为int read(byte[] b)
, 如果没有, 返回-1, 否则返回实际读取的长度
@Test
public void readTest() {
String path = "D:\\mypile\\acm\\hello.txt";
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(path);
byte[] bytes = new byte[256];
int len;
while ((len = fileInputStream.read(bytes)) != -1) {
System.out.print(new String(bytes, 0, len));
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
fileInputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
FileOutputStream
用来写入文件, 大概写法同上, 如果文件不存在就会创建文件, 有一个注意点就是我们构造器后面可以传入一个Boolean变量来将append
设置为true或者false, 如果append
为 true, 就代表我们不会覆盖文件, 一直往后写, 如果是false就会不断覆盖
二. FileReader/FileWriter
FileReader
,FileWriter
是 字符的输入和输出流, 因为是按照字符读取, 所以传入的是char数组, 大概方法和字节流一样
注意:
- 注意汉字的编码形式, 比如在utf-8中, 一个汉字占三个字节, 所以一般利用字符流
- 对于写入文件的
FileWriter
, 写入后记得flush()
或者close
, 要不然写不进去
二. 包装流
处理流包装了节点流, 既可以消除不同节点流的实现差异, 也可以提供更方便的方法来完成输入输出. 处理流对节点流进行包装, 使用了修饰器设计模式, 不会直接与数据源相连, 操作更加便捷, 性能更加高
- Java提供了包装流/处理流:
BufferedReader/BufferedWriter
, 功能更加强大, 其类中有属性Reader/Writer
, 所以封装了一个节点流, 可以放上述所说的子类
一.BufferedReader/BufferedWriter
BufferedReader/BufferedWriter
都是字符流, 关闭的时候只需要关闭外部流即可, 一般用于文本文件的读写, 如果音频图片等应该使用字节流
@Test
public void readTest() throws IOException {
String path = "D:\\mypile\\acm\\hello.txt";
BufferedReader bufferedReader = new BufferedReader(new FileReader(path));
// 按行读取
String line;
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
// 只需关闭外部流, 内部流也会关闭
bufferedReader.close();
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(path, true));
bufferedWriter.write("\n你好世界\n");
bufferedWriter.close();
}
二.BufferedInputStream/BufferedOutputStream
处理流也有可以处理二进制文件的流, 也就是字节流: BufferedInputStream/BufferedOutputStream
@Test
public void readT2() throws IOException {
String srcPath = "D:\\mypile\\acm\\hello.txt";
String destPath = "D:\\mypile\\acm\\hello2.txt";
BufferedInputStream bufferedInputStream;
BufferedOutputStream bufferedOutputStream;
bufferedInputStream = new BufferedInputStream(new FileInputStream(srcPath));
bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(destPath));
byte[] buff = new byte[1024];
int len = 0;
while ((len = bufferedInputStream.read(buff)) != -1) {
bufferedOutputStream.write(buff, 0, len);
}
if (bufferedInputStream != null) {
bufferedInputStream.close();
}
if (bufferedOutputStream != null) {
bufferedOutputStream.close();
}
}
字节是根本, 所以字节流也可以处理文本文件
三. 对象处理流
有的时候我们需要保存具体的数据类型或者对象到文件中, 读出的时候也要保留数据类型或者对象的类型, 简而言之就是希望能够将基本数据类型或者对象进行序列化 或者 反序列化操作
- 序列化: 在保存数据时, 保存数据的值和数据类型
- 反序列化: 恢复数据时, 恢复数据的值和数据类型
需要让某个对象支持序列化机制, 则必须让其类是可序列化的, 为了让某个类可序列化, 该类就要实现如下两个结构之一
Serializable // 这是一个标记接口, 是一个空接口, 所以一般实现这个
Externalizable // 有方法需要实现
一. ObjectInputStream/ObjectOutputStream
ObjectInputStream/ObjectOutputStream
是对象处理流, 可以将数据序列化存取
class Dog implements Serializable {
}
@Test
public void ObjectStreamTest() throws IOException, ClassNotFoundException {
String filePath = "D:\\mypile\\acm\\serial.txt";
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
// 序列化数据到文件中
oos.writeInt(100); // int -> Integer (实现了Serializable接口)
oos.writeBoolean(true);
oos.writeUTF("序列化练习");
oos.writeObject(new Dog());
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
// 读取的顺序一定要和保存的顺序保持一致, 否则会出现异常
System.out.println(ois.readInt());
System.out.println(ois.readBoolean());
System.out.println(ois.readUTF());
Object o = ois.readObject();
System.out.println(o.getClass());
ois.close();
}
注意
- 读写顺序要一致
- 要求序列化或者反序列化的对象, 需要实现
Serializable
接口 - 序列化的类中建议添加
SerialVersionUID
, 为了提高版本的兼容性 - 序列化对象时, 默认将里面的所有的属性都序列化, 除了
static
或者transient
修饰的成员 - 序列化对象时, 要求里面的属性的类型也需要实现序列化接口, 例如内部类
- 序列化具备可继承性, 也就是如果某个类已经实现序列化, 则他的子类也已经默认实现了序列化, 例如
Number
和Integer
- 注意序列化的类在读和写这两个位置必须是共有的
四. 标准输入输出流
System.in
编译类型是InputStream
, 但是运行类型是BufferedInputStream
, 属于字节流, 表示的是标准输入, 键盘
System.out
编译类型是PrintStream
, 运行类型也是PrintStream
, 表示标准输出, 显示器
五. 转换流
可以把字节流转换成字符流, 我们对字节流指定编码方式, 这样的话就可以解决乱码问题
1. InputStreamReader/OutputStreamWriter
可以将InputStream/OutputStream
这样的字节流包装成字符流Reader/Writer
同时指定编码方式
@Test
public void transformTest() throws IOException {
// 使用 OutputStreamWriter 转换流
// 将字节流 FileOutputStream 转换成 字符流 OutputStreamWriter
String path = "D:\\mypile\\acm\\transform.txt";
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(path), "utf-8"));
bufferedWriter.write("你好, 世界");
bufferedWriter.close();
// 使用InputStreamReader 转换流
// 将字节流FileInputStream 转成 字符流 InputStreamReader 指定编码utf-8
InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(path), "utf-8");
// 把InputStreamReader 传给 BufferedReader
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
// 读取
String line = bufferedReader.readLine();
System.out.println(line);
bufferedReader.close();
}
六. 打印流
打印流只有输出流,
PrintStream
属于字节流的子类,PrintWriter
是字符流
使用方法
@Test
public void PrintTest() throws IOException {
PrintStream out = new PrintStream(System.out);
out.print("hello");
// print底层其实调用的是writer, 所以可以直接用write
out.write("hello".getBytes());
// 可以修改打印流输出的位置
String path = "D:\\mypile\\acm\\print.txt";
System.setOut(new PrintStream(path));
System.out.println("hello");
out.close();
PrintWriter printWriter = new PrintWriter(System.out);
printWriter.print("hello");
// 重定向
PrintWriter printWriter1 = new PrintWriter(path);
// 必须close才能写进去文件, close = flush + 关闭流
printWriter.close();
printWriter1.close();
}
七. Properties
Hashtable的一个子类, 轻松地读取以及写入配置文件
配置文件的格式:
键=值
...
使用方法
@Test
public void PropertiesTest() throws IOException {
String path = "src\\root.properties";
// 创建对象
Properties properties = new Properties();
// 加载指定的配置文件
properties.load(new FileReader(path));
// 把配置信息显示到控制台
properties.list(System.out);
// 获取指定的值, 根据key
System.out.println(properties.getProperty("password"));
System.out.println(properties.getProperty("root"));
//创建新的配置
properties.setProperty("charset", "gbk");
properties.setProperty("user", "tom");
properties.list(System.out);
// 也可以先创建properties再创建文件, 可以使用store方法
// 修改key对应的value
properties.setProperty("user", "john");
properties.list(System.out);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)