Java---IO 流
- 🔍文件
- [🔍IO 流](#🔍IO 流)
-
- 理解流与文件
- 流的分类
- FileInputStream
- FileOutputStream
- 文件拷贝
- FileReader
- FileWriter
- 节点流与处理流
- BufferedReader
- BufferedWriter
- [BufferedInputStream + BufferedOutputStream](#BufferedInputStream + BufferedOutputStream)
- 对象处理流
- 标准输入输出流
- 转换流
- PrintStream
- PrintWriter
🔍文件
文件, 计算机存储数据的一种方式
创建文件
下面列举了创建文件的 3 种方式, 包括
- File(String pathname), 指定路径名创建
- File(File parent, String child), 指定父抽象路径 + 子路径名创建
- File(String parent, String child), 指定父路径名 + 子路径名创建
java
public class FileCreate {
public static void main(String[] args) {
// create1();
// create2();
// create3();
}
// 创建方式 1, File(String pathname)
public static void create1() {
String pathName = "e:/test1.txt";
File file = new File(pathName);
try {
boolean ret = file.createNewFile();
if(ret) {
System.out.println("文件创建成功");
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 创建方式 2, File(File parent, String child)
public static void create2() {
String parentPath = "e:/";
File parent = new File(parentPath);
String child = "test2.txt";
try {
File file = new File(parent, child);
boolean ret = file.createNewFile();
if(ret) {
System.out.println("文件创建成功");
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 创建方式3, File(String parent, String child)
public static void create3() {
String parent = "e:/";
String child = "test3.txt";
try {
File file = new File(parent, child);
boolean ret = file.createNewFile();
if(ret) {
System.out.println("文件创建成功");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
获取文件相关信息
具体文章参考获取文件相关信息
file.getName()
, 获取文件名file.getAbsolutePath()
, 获取文件绝对路径file.getParent()
, 获取文件父级目录file.length()
, 获取文件大小(单位: 字节)file.exists()
, 判断文件是否存在file.isFile()
, 判断是否为一个文件file.isDirectory()
, 判断是否为一个目录
目录相关操作
具体文章参考目录相关操作
file.exists()
, 判断目录是否存在file.delete()
, 删除目录file.mkdir()
, 创建单级目录file.mkdirs()
, 创建多级目录
注意🍂
- 执行删除目录命令时, 如果目录中存在文件, 则无法直接删除目录
- 创建多级目录也可以应用于创建单级目录的情况
🔍IO 流
I
, Input
的缩写, 表示输入
O
, Output
的缩写, 表示输出
输入流, 数据从文件输入到程序(文件 → 程序)
输出流, 数据从程序输出到文件(程序 → 文件)
举个栗子🌰
你可以将程序看作是一个人, 将文件看作是水
将人喝水(水被输入到人的肚子中)的过程理解为输入流
人在喝水过程中被呛到了(水被重新洒到了杯子)的过程理解为输出流
理解流与文件
流与文件之间的关系类似于快递小哥与商品之间的关系
- 将用户理解为程序
- 将快递小哥送快递的过程理解为输入流 / 输出流
- 将快递驿站理解为文件, 驿站中的快递(物品)理解为文件中的数据
快递小哥将快递(文件中的数据)送至用户家(程序) → 输入流
快递小哥将用户需要寄送的快递送至快递驿站 → 输出流
流负责数据的输入与输出
流的分类
- 依据操作的对象划分为: (1) 字节流 (2) 字符流
- 依据数据的流向划分为: (1) 输入流 (2) 输出流
抽象基类 | 字节流 | 字符流 |
---|---|---|
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
FileInputStream
使用 FileInputStream
中的 read()
方法, 一次只能读取一个字节的内容
使用 FileInputStream
中的 read(byte[] b)
方法, 一次可以读取指定字节大小的内容
java
public class ReadFile {
public static void main(String[] args) {
// readFile1();
// readFile2();
}
// 一次读取一个字节
public static void readFile1() {
String filePath = "e:/hello.txt";
int readSize = 0;
InputStream inputStream = null;
try {
inputStream = new FileInputStream(filePath);
// 从文件中一次读取一个字节的数据. 返回 -1, 表示读到文件末尾
while((readSize = inputStream.read()) != -1) {
System.out.print((char) readSize);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 一次读取多个字节
public static void readFile2() {
String filePath = "e:/hello.txt";
int readSize = 0;
// 从文件中一次读取 8 个字节的数据. 返回 -1, 表示读到文件末尾
byte[] buffer = new byte[8];
InputStream inputStream = null;
try {
inputStream = new FileInputStream(filePath);
while((readSize = inputStream.read(buffer)) != -1) {
// 注意不能写成 System.out.print(new String(buffer, 0, buffer.length));
// 这是因为后续的 byte[] buffer 中的值由于第一次读取后的内容已经被填充
System.out.print(new String(buffer, 0, readSize));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
FileOutputStream
使用 FileOutputStream
向指定的文件中写入内容时. 如果该文件不存在, 则会自动进行创建
使用 FileOutputStream
中的 write()
方法, 一次只能写入一个字节的内容
使用 FileOutputStream
中的 write(byte[] b)
方法, 一次可以写入指定字节大小的内容
FileOutputStream outputStream = new FileOutputStream(filePath)
再次执行程序时, 新写入的内容覆盖原有的内容
FileOutputStream outputStream = new FileOutputStream(filePath, true)
再次执行程序时, 新写入的内容会追加到原有内容的末尾
再次执行程序时, 指的是流被关闭后再次使用
java
public class WriteFile {
public static void main(String[] args) {
writeFile1();
// writeFile2();
}
// 一次写入一个字节
public static void writeFile1() {
OutputStream outputStream = null;
String filePath = "e:/test.txt";
try {
// 使用 FileOutputStream 向指定的文件中写入内容时
// 如果该文件不存在, 则会自动进行创建
outputStream = new FileOutputStream(filePath);
// 当再次执行程序时, 新写入的内容覆盖原有的内容
// outputStream = new FileOutputStream(filePath);
// 当再次执行程序时, 新写入的内容会追加到原有内容的末尾
// outputStream = new FileOutputStream(filePath, true);
outputStream.write('h');
outputStream.write('e');
outputStream.write('l');
outputStream.write('l');
outputStream.write('o');
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 一次写入多个字节
public static void writeFile2() {
OutputStream outputStream = null;
String filePath = "e:/test.txt";
try {
// 使用 FileOutputStream 向指定的文件中写入内容时
// 如果该文件不存在, 则会自动进行创建
outputStream = new FileOutputStream(filePath);
String str = "hello world";
outputStream.write(str.getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
文件拷贝
文件拷贝分为 2 步, 包括
- 读取源文件中的数据输入到程序(输入流)
- 从程序中输出数据到目标文件(输出流)
写入目标文件中的数据长度应该是读取到的长度, 不能是字节数组本身的长度
outputStream.write(buffer, 0, readSize)
注意🍂
拷贝文件时应每次读取部分数据时就写入目标文件, 不要一下子全部读取再写入目标文件
这是因为如果文件太大, 可能会导致执行过程崩溃
java
public class FileCopy {
public static void main(String[] args) {
copyFile();
}
// 文件拷贝
// 1. 读取源文件的数据
// 2. 将源文件的数据拷贝到目标文件路径
public static void copyFile() {
// srcFilePath 源文件路径(最好不要有中文)
String srcFilePath = "D:/Csdn截图上传/Redis_01.png";
// destFilePath 目标文件路径(最好不要有中文)
String destFilePath = "E:/Redis2.png";
InputStream inputStream = null;
OutputStream outputStream = null;
int readSize = 0;
// 每次读取 1024 字节的数据
byte[] buffer = new byte[1024];
try {
inputStream = new FileInputStream(srcFilePath);
// 使用 FileOutputStream 向指定的文件中写入内容时
// 如果该文件不存在, 则会自动进行创建
outputStream = new FileOutputStream(destFilePath);
while((readSize = inputStream.read(buffer)) != -1) {
// 将读取到的内容写入到目标文件
// outputStream.write(buffer);
outputStream.write(buffer, 0, readSize);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 资源释放
try {
if(inputStream != null) {
inputStream.close();
}
if(outputStream != null) {
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
FileReader
使用 FileReader
中的 read()
方法, 一次只能读取一个字符的内容
使用 FileReader
中的 read(char cbuf[])
方法, 一次可以读取指定字符大小的内容
java
public class ReadFile {
public static void main(String[] args) {
// readFile1();
// readFile2();
}
// 一次读取一个字符
public static void readFile1() {
Reader reader = null;
String filePath = "e:/test.txt";
int readSize = 0;
try {
reader = new FileReader(filePath);
// 从文件中一次读取一个字符的数据. 返回 -1, 表示读到文件末尾
while((readSize = reader.read()) != -1) {
System.out.print((char) readSize);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(reader != null) {
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 一次读取多个字符
public static void readFile2() {
Reader reader = null;
String filePath = "e:/test.txt";
int readSize = 0;
char[] buffer = new char[1024];
try {
reader = new FileReader(filePath);
// 从文件中一次读取多个字符的数据. 返回 -1, 表示读到文件末尾
while((readSize = reader.read(buffer)) != -1) {
System.out.print(new String(buffer, 0, readSize));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(reader != null) {
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
FileWriter
使用 FileWriter
向指定的文件中写入内容时. 如果该文件不存在, 则会自动进行创建
使用 FileWriter
中的 write()
方法, 一次只能写入一个字符的内容
使用 FileWriter
中的 write(char[] cbuf)
或 write(String str)
方法, 一次可以写入指定字符大小的内容
FileWriter writer= new FileWriter(filePath)
再次执行程序时, 新写入的内容覆盖原有的内容
FileWriter writer= new FileWriter(filePath, true)
再次执行程序时, 新写入的内容会追加到原有内容的末尾
再次执行程序时, 指的是流被关闭后再次使用
FileWriter 使用之后, 必须要关闭(close)或刷新(flush). 否则数据写入不到指定的文件中
java
public class WriteFile {
public static void main(String[] args) {
// writeFile1();
// writeFile2();
}
public static void writeFile1() {
Writer writer = null;
String filePath = "e:/note.txt";
try {
writer = new FileWriter(filePath);
writer.write('我');
writer.write('亦');
writer.write('无');
writer.write('他');
writer.write(',');
writer.write('为');
writer.write('手');
writer.write('属');
writer.write('尔');
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 表示使用过该字符流
if(writer != null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void writeFile2() {
Writer writer = null;
String filePath = "e:/note.txt";
try {
writer = new FileWriter(filePath);
String str = "我亦无他, 为手熟尔";
writer.write(str);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 表示使用过该字符流
if(writer != null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
节点流与处理流
节点流, 针对指定的数据源读写数据. 例如 FileReader
, FileWriter
针对的数据源就是文件
处理流, 也叫包装流. 是连接在已存在的流(节点流或处理流)之上, 为程序提供更为强大的读写功能. 例如 BufferedReader
, BufferedWriter
翻译一下就是节点流只能做指定的工作. 而处理流不仅能做指定的工作, 还能在指定的基础之上做额外的工作
类型
分类 | 字节输入流 | 字节输出流 | 字符输入流 | 字符输出流 | 流类型 |
---|---|---|---|---|---|
抽象基类 | InputStream | OutputStream | Reader | Writer | |
访问文件 | FileInputStream | FileOutputStream | FileReader | FileWriter | 节点流 |
访问数组 | ByteArrayInputStream | ByteArrayOutputStream | CharArrayReader | CharArrayWriter | 节点流 |
访问管道 | PipedInputStream | PipedOutputStream | PipedReader | PipedWriter | 节点流 |
访问字符串 | StringReader | StringWriter | 节点流 | ||
缓冲流 | BufferedInputStream | BufferedOutputStream | BufferedReader | BufferedWriter | 处理流 |
转换流 | InputStreamReader | OutputStreamWriter | 处理流 | ||
对象流 | ObjectInputStream | ObjectOutputStream | 处理流 | ||
抽象基类 | FilterInputStream | FilterOutputStream | FilterReader | FilterWriter | 处理流 |
打印流 | PrintStream | PrintWriter | 处理流 | ||
推回输入流 | PushbackInputStream | PushbackReader | 处理流 | ||
特殊流 | DataInputStream | DataOutputStream | 处理流 |
BufferedReader
java
public class BFReadFile {
public static void main(String[] args) throws IOException {
readFile();
}
public static void readFile() throws IOException {
String filePath = "e:/test.txt";
BufferedReader reader = new BufferedReader(new FileReader(filePath));;
String line = "";
while((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();
}
}
BufferedWriter
java
public class BFWriter {
public static void main(String[] args) throws IOException {
writeFile();
}
public static void writeFile() throws IOException {
String filePath = "e:/write.txt";
// 表示以追加方式写入
// BufferedWriter writer = new BufferedWriter(new FileWriter(filePath, true));
// 表示以覆盖方式写入
BufferedWriter writer = new BufferedWriter(new FileWriter(filePath));
writer.write("hello world");
// writer.write('\n');
writer.newLine(); // 插入一个和系统相关的换行符
writer.write("hello world");
// writer.write('\n');
writer.newLine(); // 插入一个和系统相关的换行符
writer.write("hello world");
// writer.write('\n');
writer.newLine(); // 插入一个和系统相关的换行符
writer.close();
}
}
BufferedInputStream + BufferedOutputStream
利用 BufferedInputStream
+ BufferedOutputStream
实现文件拷贝
java
public class BufferedCopy {
public static void main(String[] args) throws IOException {
copyFile();
}
// 拷贝文件
// 1. 读取源文件数据到程序(输入流)
// 2. 将原文件数据从程序输入到目标文件(输出流)
public static void copyFile() throws IOException {
// 源文件路径
String srcFilePath = "d:/活法.pdf";
// 目标文件路径
String descFilePath = "e:/稻盛和夫_活法.pdf";
BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(srcFilePath));
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(descFilePath));
int readSize = 0;
byte[] buf = new byte[1024];
// 1. 读取源文件数据到程序
while((readSize = inputStream.read(buf)) != -1) {
// 2. 将原文件数据从程序输入到目标文件(输出流)
outputStream.write(buf, 0, readSize);
// 刷新缓冲区
outputStream.flush();
}
inputStream.close();
outputStream.close();
}
}
对象处理流
对象处理流包括 ObjectInputStream
和 ObjectOutputStream
通常利用 ObjectInputStream
和 ObjectOutputStream
进行序列化与反序列化
对于序列化与反序列化的解释🍂
当我们保存数据时, 通常不会保存数据的类型
例如在 .txt
文件中保存写入 10 并保存, 此时并没有保存数据的类型. 此时我们无法确定 10 这个数据是整数还是字符串类型
在比如, 当我们保存一个浮点数 10.5 时, 虽然进行了保存, 但是无法确定这个浮点数的类型是 float
还是 double
类型
序列化的过程就是保存数据的类型 + 数据的值
反序列化的过程就是将保存数据的类型 + 数据的值
举个栗子🌰
java
// 示例代码
public class Student {
private int id;
private String name
}
定义一个学生类, 属性包括 id, name
如果只保存一个 id 的值 + name 的值. 例如 id = 10, name = "嘟嘟". 此时我们并没有办法判断这些属性描述的具体对象. 可以是学生, 老师, 也可能是一只宠物
序列化的过程就是保存了数据的类型 + 数据的值, 也就是说将值所描述的对象一同进行保存
而反序列化的过程就是在恢复数据时, 恢复保存了的数据 + 数据的值
实现序列化的过程需要该类实现下列的任意一个接口
- Serializable
- Externalizable
通常选择实现 Serializable
接口, 因为这只是一个标记接口, 里面并不包含抽象方法
ObjectOutputStream
java
public class ObjOutput {
public static void main(String[] args) throws IOException {
writeFile();
}
public static void writeFile() throws IOException {
String filePath = "e:/t1.txt";
ObjectOutputStream outputStream =
new ObjectOutputStream(new FileOutputStream(filePath));
outputStream.writeInt(100);
outputStream.writeChar('a');
outputStream.writeUTF("hello world");
outputStream.writeObject(new Student(1, "Tom"));
outputStream.close();
System.out.println("序列化完成");
}
}
Student 类🍂
java
public class Student implements Serializable {
private int id;
private String name;
public Student(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
ObjectInputStream
java
public class ObjInput {
public static void main(String[] args) throws IOException, ClassNotFoundException {
readFile();
}
public static void readFile() throws IOException, ClassNotFoundException {
String filePath = "e:/t1.txt";
ObjectInputStream inputStream =
new ObjectInputStream(new FileInputStream(filePath));
System.out.println(inputStream.readInt());
System.out.println(inputStream.readChar());
System.out.println(inputStream.readUTF());
System.out.println(inputStream.readObject());
inputStream.close();
System.out.println("反序列化完成");
}
}
对象处理流注意事项
- 读写顺序需要保持一致
- 序列化或反序列化的对象, 需要实现
Serializable
或Externalizable
接口 - 序列化的类中建议添加
SerialVersionUID
, 以便提高版本兼容性 - 序列化对象时, 默认将对象中的所有属性都进行序列化(被
static
或transient
修饰的成员不会序列化) - 序列化对象时, 要求对象中的属性也实现序列化的接口
- 序列化具备可继承性. 即某个类实现了序列化, 那么它的子类也默认实现了序列化
标准输入输出流
⭕System.in
标准输入 → 默认对应设备为键盘
System.in
编译类型为InputStream
System.in
运行类型为PrintStream
⭕System.out
标准输出 → 默认对应设备为显示器
System.out
编译类型为PrintStream
System.out
运行类型为PrintStream
转换流
转换流包括 InputStreamReader
和 OutputStreamReader
转换流通常用于解决乱码问题. 这是因为InputStreamReader
和 OutputStreamReader
的构造方法中都可以指定字符编码
InputStreamReader
是 Reader
的子类, 可以将 InputStream
(字节流) 包装为 Reader
(字符流)
OutputStreamReader
是 Writer
的子类, 可以将 OutputStream
(字节流) 包装为 Writer
(字符流)
InputStreamReader
java
public class InputReadFile {
public static void main(String[] args) throws IOException {
readFile();
}
public static void readFile() throws IOException {
String filePath = "e:/copyFile.pdf";
FileInputStream inputStream = new FileInputStream(filePath);
// 将 FileInputStream 转换为 InputStreamReader
// 指定编码格式为 UTF-8
InputStreamReader reader = new InputStreamReader(inputStream, "gbk");
// 将 InputStreamReader 转换为 BufferedReader
BufferedReader bfReader = new BufferedReader(reader);
while(bfReader.readLine() != null) {
System.out.println(bfReader.readLine());
}
bfReader.close();
}
}
OutputStreamWriter
java
public class OutputWriteFile {
public static void main(String[] args) throws IOException {
writeFile();
}
public static void writeFile() throws IOException {
String filePath = "e:/test.txt";
FileOutputStream outputStream = new FileOutputStream(filePath);
// 设置编码格式
// OutputStreamWriter writer = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
OutputStreamWriter writer = new OutputStreamWriter(outputStream, "gbk");
BufferedWriter bfWriter = new BufferedWriter(writer);
bfWriter.write("hello, world");
bfWriter.newLine();
bfWriter.write("做到才能得到");
bfWriter.close();
}
}
PrintStream
java
public class ExPrintStream {
public static void main(String[] args) throws IOException {
// print1();
// print2();
}
// 未设置打印位置
// 默认将打印结果输出到显示器
public static void print1() {
PrintStream stream = System.out;
stream.println("hello world");
stream.println("做到才能得到");
stream.close();
}
// 设置打印位置
public static void print2() throws IOException {
String filePath = "e:/test.txt";
System.setOut(new PrintStream(filePath));
System.out.println("输出结果到指定位置");
System.out.close();
}
}
PrintWriter
java
public class ExPrintWriter {
public static void main(String[] args) throws IOException {
// print1();
print2();
}
// 为指定输出位置
// 默认将打印结果输出到显示器
public static void print1() {
PrintWriter writer = new PrintWriter(System.out);
writer.println("hello world");
writer.println("hello myFriend");
writer.close();
}
// 指定输出位置
public static void print2() throws IOException {
String filePath = "e:/test.txt";
PrintWriter writer = new PrintWriter(new FileWriter(filePath));
writer.println("hello world");
writer.println("hello myFriend");
writer.close();
}
}
🌸🌸🌸完结撒花🌸🌸🌸