
十三、文件、IO 流、Properties
Pupper一、文件
:::tip
文件流:
- 输入流: 是指 数据从 文件 到 内存 的路径;
- 输出流: 是指 数据从 内存 到 文件 的路径;
:::
1. 创建文件
命令 | 说明 |
---|---|
new File(String pathname) | 根据路径构建 一个 File 对象 |
new File(File parent, String child) | 根据父目录文件 + 子路径构建 |
new File(String parent, String child) | 根据父目录 + 子路径构建 |
createNewFile | 创建新文件 |
1 | // 文件创建 - 案例 |
文件常用方法:
| getName | 获取文件名称 |
|————————-|———————————|
| getAbsolutePath | 获取文件绝对路径 |
| getParent | 获取文件父目录 |
| length | 获取文件大小(字节) |
| exists | 判断文件是否存在 |
| isFile | 判断是否为文件 |
| isDirectory | 判断是否为目录 |
2. 目录操作 和 文件删除
命令 | 说明 |
---|---|
mkdir | 创建一级目录 |
mkdirs | 创建多级目录 |
delete | 删除空目录或文件 |
:::danger
Java 中,将目录看做是一个特殊的文件,因此,文件中的方法在目录中也实用。
:::
1 | // 目录创建及删除 - 案例 |
二、IO 流
:::tip
IO (Input 和 Output 的缩写)流: 用于处理数据传输,如读、写文件,网络通信等;
- Java 程序中,对于数据的输入和输出操作以“流(stream)”的方式进行;
:::
:::info
流的分类:
- 按操作数据的单位不同分为: 字节流(8 bit,二进制文件),字符流(文本文件);
- 按数据的流向不同分为: 输入流、输出流;
- 按流的角色不用分为: 节点流、处理流或包装流
:::
节点流
分类 | 字节输入流 | 字节输出流 | 字符输入流 | 字符输出流 |
---|---|---|---|---|
抽象基类 | 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 |
- 斜体 + 下划线为 抽象类,无法创建实例, 红色表示节点流,必须直接与制定的物理节点关联;
1. 字节流
:::note
- 读取 或 写入 文件时,返回 -1 ,表示已经读取完毕;
:::
1. 输入流
1 | // 字节流 输入(读取文件) |
2. 输出流
:::note
- new FileOutputStream(filePath) : 这种创建方式会 覆盖 之前的内容;
- new FileOutputStream(filePath, true) : 这种创建方式不会覆盖之前的内容,会 追加 在最后方;
- str.getBytes : 字符串 —-> 字节数组
- 字节数组部分写入, 从 off 开始, 向右偏移 len 个字符,
- 如果偏移量过多,会报下标越界错误
:::
- 如果偏移量过多,会报下标越界错误
1 | // 字节流 输出(写入文件) |
1 | // 文件 拷贝 - 案例 |
2. 字符流
1. 输入流
:::note
FileReader 相关方法:
- new FileReader(File/String)
- read : 每次读取单个字符,返回该字符,读完文件返回 -1;
- read(char[]): 批量读取多个字符到数组,返回读取到的字符数,读完文件返回 -1;
————————————————————————————————————————————————
- new String(char[]): 将 char[] 转换成 String;
- new String(char[], off, len) :将 char[] 的指定部分转换成 String;
:::
1 | // 字符流 输入(读取文件) |
2.输出流
:::note
FileWriter 相关方法:
- new FileWriter(File/String) : 覆盖模式
- new FileWriter(File/String, true):追加模式
- write(int):写入单个字符
- write(char[]):写入指定数组
- write(char[],off, len):写入指定数组的指定部分
- write(string): 写入整个字符串
- write(String,off,len):写入字符串的指定部分
————————————————————————————————————————————————
- toCharArray:将 String 转为 char[]
FileWriter 使用后,必须要关闭(close)或 刷新(flush),否则写入不到指定文件
:::1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37public class FileWriter1 {
public static void main(String[] args) {
}
/**
* 从一个文件中读取,写入另一个文件
*/
public void writer1() throws IOException {
// 获取文件路径
String absolutePath = new File("").getAbsolutePath();
System.out.println(absolutePath);
// 创建文件对象
File demo1 = new File(absolutePath + "/src/com/io_/demo1.txt");
if (!(demo1.exists())){
if (demo1.createNewFile()){
System.out.println("文件创建成功");
}
}
FileReader fileReader = new FileReader(absolutePath + "/src/com/io_/hello.txt");
FileWriter w1 = new FileWriter(absolutePath + "/src/com/io_/demo1.txt", true);
int data = 0;
char[] c = new char[1024];
// 读取文件
while ((data = fileReader.read(c)) != -1) {
w1.write(new String(c, 0, data));
}
// 关闭文件
fileReader.close();
w1.close();
}
}3. 处理流
:::tip
节点流: 从一个特定的数据源读写数据;
处理流(也叫包装流): “连接” 在已经存在的流之上,为程序提供更为强大的读写能力;
:::
:::note
节点流 和 处理流 的区别:节点流是底层流,直接跟数据源相接;
- 处理流 包装了节点流,既可以消除不用节点流的实现差异,也可以提供更方便的方法来完成输入输出
- 处理流 对 节点流进行包装,使用了修饰器设计模式,不会直接与数据相连;
————————————————————————————————————————————————
处理流的主要功能:
- 性能的提高:主要以增加缓冲的方式来提高输入输出的效率;
- 操作的便捷: 处理流可能提供了一系列便捷的方法来一次输入输出大批量的数据,使用更加灵活方便;
- BufferedReader 和 BufferedWriter
- BufferedInputStream 和 BufferedOutputStream
读取完成后 返回 null;
:::1.字符 处理流
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public class Reader1 {
public static void main(String[] args) {
}
public void read1() throws IOException {
// 获取文件路径
String absolutePath = new File("").getAbsolutePath();
System.out.println(absolutePath);
// 创建文件对象
FileReader fileReader = new FileReader(absolutePath + "/src/com/io_/hello.txt");
BufferedReader br = new BufferedReader(fileReader);
String data;
// 读取文件 br.readLine() 按行读取
while ((data = br.readLine()) != null) {
System.out.println(data);
}
// 关闭文件, 只需关闭处理流即可
br.close();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40public class Writer1 {
public static void main(String[] args) {
}
/**
* 从一个文件中读取,写入另一个文件
*/
public void writer1() throws IOException {
// 获取文件路径
String absolutePath = new File("").getAbsolutePath();
System.out.println(absolutePath);
// 创建文件对象
File demo1 = new File(absolutePath + "/src/com/io_/demo1.txt");
if (!(demo1.exists())){
if (demo1.createNewFile()){
System.out.println("文件创建成功");
}
}
FileReader fileReader = new FileReader(absolutePath + "/src/com/io_/hello.txt");
FileWriter w1 = new FileWriter(absolutePath + "/src/com/io_/demo1.txt", true);
BufferedReader r = new BufferedReader(fileReader);
BufferedWriter w = new BufferedWriter(w1);
String data;
// 读取文件
while ((data = r.readLine()) != null) {
w.write(data);
// 换行
w.newLine();
}
// 关闭文件
r.close();
w.close();
}
}2.字节 处理流
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37public class BufferedReaderWriter1 {
public static void main(String[] args) {
}
public void writer1() throws IOException {
// 获取文件路径
String absolutePath = new File("").getAbsolutePath();
System.out.println(absolutePath);
// 创建文件对象
File demo1 = new File(absolutePath + "/src/com/io_/demo.mp4");
if (!(demo1.exists())){
if (demo1.createNewFile()){
System.out.println("文件创建成功");
}
}
FileInputStream fileInput = new FileInputStream(absolutePath + "/src/com/io_/iShot_2022-06-06_15.39.50.mp4");
FileOutputStream fileOutput = new FileOutputStream(absolutePath + "/src/com/io_/demo.mp4", true);
BufferedInputStream i = new BufferedInputStream(fileInput);
BufferedOutputStream o = new BufferedOutputStream(fileOutput);
byte[] b = new byte[1024];
int data = 0;
// 读取文件
while ((data = i.read(b)) != -1) {
o.write(b, 0, data);
}
// 关闭文件
o.close();
i.close();
}
}3. 对象 处理流
:::tip
序列化 和 反序列化:序列化: 在保存数据时,同时保存数据的值 和 数据的类型;
- ObjectOutputStream (写入文件)
- 反序列化: 在回复数据时,同时回复数据的值 和 类型;
- ObjectInputStream (读取文件)
需要让某个对象支持序列化机制,则需要让其类是可序列化的,因此必须实现以下接口之一:
- Serializable (标记接口,推荐使用)
- Externalizable(该接口有方法需要实现,不推荐使用)
:::
:::danger
注意事项:
序列化 与 反序列化 的顺序必须一致;
- 要实现 序列化 与 反序列化对象, 类 必须实现 Serializable 接口
- 序列化 的类中建议添加 SerialVersionUID,为了提高版本的兼容性;
- 序列化对象时,默认将里面所有属性都进行序列化,但除了 static 或 transient 修饰的成员
- 序列化对象时, 要求里面的属性的类型也需要实现序列化接口;
- 序列化具备可继承性,也就是如果某个类实现了序列化,则它所有的子类也已经默认实现了序列化;
:::1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82public class ObjectInput1 {
public static void main(String[] args) {
}
// 序列化
public void objectOutputStream1() throws IOException {
// 获取文件路径
String filePath = new File("").getAbsolutePath() + "/src/com/io_/demo1.json";
new File(filePath).createNewFile(); // 创建文件
// 创建对象序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
// 基本数据类型的包装类 实现了 Serializable
oos.writeInt(100);
oos.writeBoolean(true);
oos.writeChar('a');
oos.writeUTF("李焕英"); // 字符串
Dog dog = new Dog("旺财", 10);
oos.writeObject(dog);
oos.close();
}
// 反序列化
public void objectInputStream1() throws IOException, ClassNotFoundException {
// 获取文件路径
String filePath = new File("").getAbsolutePath() + "/src/com/io_/demo1.json";
// 创建对象序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
// 反序列化
// 读取的顺序 必须 和 序列化的顺序一致
System.out.println(ois.readInt());
System.out.println(ois.readBoolean());
System.out.println(ois.readChar());
System.out.println(ois.readUTF());
System.out.println(ois.readObject());
ois.close();
}
}
class Dog implements Serializable{
private String name;
private int age;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}4. 标准输入、输出流
| | | 编译类型 | 运行类型 | 默认设备 |
|——————|—————|——————-|——————————-|—————|
| System.in | 标准输入 | InputStream | BufferedInputStream | 键盘 |
| System.out | 标准输出 | PrintStream | PrintStream | 显示器 |
5.转换流
:::tip
- InputStreamReader:Reader 的子类,可以将 InputStream(字节流)包装成 Reader(字符流);
- OutputStreamWriter:Writer 的子类,实现将 OutputStream(字节流)包装成 Writer(字节流);
- 当处理纯文本数据时,如果使用字符流效率更高,并且可以有效解决中文问题,所以建议将字节流转换成字符流;
可在使用时置顶编码格式(如:utf-8,gdk,gb2312等)
:::1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public class InputStreamReader1 {
public void transition1() throws IOException {
String filePath = new File("").getAbsolutePath() + "/src/com/io_/hello.txt";
FileInputStream fis = new FileInputStream(filePath);
// 将字节流 转为 字符流,编码为 utf-8
InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
// 将字符流 转为 BufferedReader
BufferedReader br = new BufferedReader(isr);
String s;
while ((s = br.readLine()) != null){
System.out.println(s);
}
br.close();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class OutputStreamWriter1 {
public void transition1() throws IOException {
String filePath = new File("").getAbsolutePath() + "/src/com/io_/hello.txt";
FileOutputStream fos = new FileOutputStream(filePath);
// 将字符流 转为 字节流,编码为 utf-8
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fos, StandardCharsets.UTF_8));
bw.write("你好,李焕英");
// 将字符流 转为 BufferedReader
bw.close();
}
}6.打印流
:::tip
PrintStream : 字节打印流,默认输出为 显示器;
PrintWriter : 字符打印流,
:::1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25public class PrintStream1 {
public static void main(String[] args) {
}
public void print1() throws IOException {
PrintStream out = System.out;
out.print("你好,你不好");
// 因为 print 底层使用的是 write,所以可以直接调用 write 进行打印/输出
out.write("天王盖地虎".getBytes(StandardCharsets.UTF_8));
// 创建文件
String filePath = new File("").getAbsolutePath()+"/src/com/io_/demo2.txt";
new File(filePath).createNewFile();
// 修改打印 设备, 到文件 demo2.txt
System.setOut(new PrintStream(filePath));
// 不会打印到控制台, 会保存到文件
System.out.println("宝塔管水貂");
out.close();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29public class PrintWriter1 {
public static void main(String[] args) {
}
public void print1() throws IOException {
// PrintWriter writer = new PrintWriter(System.out);
// writer.print("你好,你不好");
// 创建文件
String filePath = new File("").getAbsolutePath()+"/src/com/io_/demo3.txt";
File file = new File(filePath);
if (!(file.exists())) {
if (new File(filePath).createNewFile()){
System.out.println("文件创建成功");
}
}else{
System.out.println("文件已存在");
}
// 修改打印 设备, 到文件 demo3.txt
PrintWriter printWriter = new PrintWriter(filePath);
// 不会打印到控制台, 会保存到文件
printWriter.print("宝塔管水貂");
printWriter.close();
}
}三、Properties 类
:::tip
Properties 类 : 专门用于读写配置文件的集合类;
- 配置文件格式:键 = 值
- 键值对不需要有 空格,值不需要用引号,默认类型时 String;
:::
Properties 常用方法:
- 键值对不需要有 空格,值不需要用引号,默认类型时 String;
load | 加载配置文件的键值对到 Properties 类 |
---|---|
list | 将数据显示到指定设备 |
getProperty(key) | 根据键获取值 |
setProperty(key,value) | 设置键值对到 Properties 对象 |
store | 将 Properties 中的键值对存储到配置文件(中文以 unicode 码存储) |
1 | public class Properties1 { |
- 0鼓励
- 0菜狗
- 0正确
- 0错误
- 0思考
- 0无聊