文章目录
学习日记(IO 流详细内容下篇)
一、缓冲流
1. 概述
缓冲流也称为高效流、高级流,之前学习的字节流可以称为原始流。
作用:缓冲流自带缓冲区(8KB),可以提高原始字节流、字符流读写数据的性能。
缓冲流分类:字节缓冲输入流、字节缓冲输出流、字符缓冲输入流、字符缓冲输出流。
2. 字节缓冲流
字节缓冲流包括字节缓冲输入流(BufferedInputStream)和字节缓冲输出流(BufferedOutputStream)。
原理:
- 字节缓冲输入流自带了 8K 的缓冲池,可以直接从缓冲池取数据,性能较好。
- 字节缓冲输出流自带了 8K 的缓冲池,数据就直接写入到缓冲池中去了,写数据的性能提高了。
字节缓冲输入流和字节缓冲输出流分别提高了字节输入流和字节输出流读取和写入数据的性能,在读写功能上无变化。
- 把原始的字节输入流包装成高级的字节缓冲输入流:
InputStream bis = new BufferedInputStream(is);
。 - 把原始的字节输出流包装成高级的字节缓冲输出流:
OutputStream bos = new BufferedOutputStream(os);
。
需求:文件拷贝:将 E:\桌面\2022级新生学号.xls(957 KB) 拷贝到 E:\桌面\一些文件 中。
注意:字节数组 buffer 现在是从缓冲区(内存中)读写数据,比之前在硬盘中读写数据要快。
总结:字节缓冲输入流 + 字节缓冲输出流 + 以字节数组读取和写入数据(拷贝),这样的方式是性能最优的组合。
3. 字符缓冲流
字符缓冲流包括字符缓冲输入流(BufferedReader)和字符缓冲输出流(BufferedWriter)。
原理:字符缓冲流自带 8K 的缓冲区。
(1)字符缓冲输入流
作用:提高字符输入流读取数据的性能,除此之外,多了按行读取数据的功能。
把原始的字符输入流包装成高级的字符缓冲输入流:BufferedReader br = new BufferedReader(fr);
。
方法名 | 说明 |
---|---|
public String readLine() throws IOException | 读取一行数据返回,返回值类型为 String,无行可读返回 null |
需要注意两点:可以但是不能用多态的写法,因为需要调用子类独有的方法;每读完一行,需要换行,用 pringln 而不是 print。
(2)字符缓冲输出流
作用:提高字符输出流写入数据的性能,除此之外,多了换行功能。
把原始的字符输出流包装成高级的字符缓冲输出流:BufferedWriter bw = new BufferedWriter(fw);
。
方法名 | 说明 |
---|---|
public void newLine() throws IOException | 换行 |
4. 案例
需求:将文件 基础语法\src\data6.txt 中的以下信息恢复顺序后,写入到 E:\桌面\一些文件 中。
6.粒粒皆辛苦? 2.唐 李绅 5.谁知盘中餐, 4.汗滴禾下土。 1.《悯农(其二)》 3.锄禾日当午,
分析:
- 读取文件,按行读取中文字符,并且之后要进行排序,因此用字符缓冲输入流;
- 将每行数据存入到一个 List 集合中;
- 对集合中的元素进行自定义排序;
- 遍历 List 集合,把排序之后的每个元素通过字符缓冲输出流写入到目标文件。
加大自定义排序难度!
拓展需求:在以上要求的基础上,文件的内容变为以下信息:
六.粒粒皆辛苦? 二.唐 李绅 五.谁知盘中餐, 四.汗滴禾下土。 一.《悯农(其二)》 三.锄禾日当午,
分析:自定义一个集合,预先存放“一”到”六“的顺序,然后再自定义排序。
用到的方法 | 说明 |
---|---|
public int indexOf(String str) | String 方法,表示获取字符串中某个字符的索引 |
int indexOf(Object o) | List 方法,表示获取 List 集合中某个元素的索引 |
package com.residue.IOstream2;
import java.io.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Test01 {
public static void main(String[] args) {
try (
Reader fr = new FileReader("基础语法\\src\\data6.txt");
BufferedReader br = new BufferedReader(fr);
Writer fw = new FileWriter("E:\\桌面\\一些文件\\悯农(其二).txt");
BufferedWriter bw = new BufferedWriter(fw);
) {
List<String> list = new ArrayList<>();
//将每行数据存入到一个 List 集合中
String len;
while ((len = br.readLine()) != null) {
list.add(len);
}
System.out.println(list);
//对集合中的元素进行自定义排序;
List<String> order = new ArrayList<>();
Collections.addAll(order, "一", "二", "三", "四", "五", "六");
list.sort(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return order.indexOf(o1.substring(0, o1.indexOf("."))) -
order.indexOf(o2.substring(0, o2.indexOf(".")));
}
});
System.out.println(list);
//遍历 List 集合,把排序之后的每个元素通过字符缓冲输出流写入到目标文件
for (String s : list) {
bw.write(s);
bw.newLine();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
二、转换流
字符流直接读取文本内容,只有当文件和代码编码一致时,才不会乱码,否则读取将出现乱码,如:文件是 GBK 编码,代码是 UTF-8,读取中文字符将出现乱码。
转换流分为字符输入转换流(InputStreamReader)和字符输出转换流(OutputStreamWriter)。
1. 字符输入转换流
作用:可以解决字符流读取不同编码乱码的问题。
步骤:
- 先提取 GBK 文件的原始字节流;
- 把原始字节流以 GBK 编码转换成字符流,如:
Reader isr = new InputStreamReader(is, "GBK");
。 - 把低级的字符输入流包装为高级的字符缓冲输入流。
- 按照之前对字符流的处理步骤进行读取。
用到的构造器 | 说明 |
---|---|
public InputStreamReader(InputStream in, String charsetName) | 把原始字节流以指定的编码方式转换成字符流 |
注意:如果没有指定编码方式,则以默认的编码方式,将字节流转换为字符流。
2. 字符输出转换流
作用:可以指定写出去的字符编码。
步骤:
- 先定义一个字节输出流;
- 把原始的字节输出流转换成字符输出流,如:
Writer osw = new OutputStreamWriter(os, "GBK");
。 - 把低级的字符输出流包装为高级的字符缓冲输出流。
- 按照之前对字符流的处理步骤进行写入。
用到的构造器 | 说明 |
---|---|
public OutputStreamWriter(OutputStream out, String charsetName) | 把原始的字节输出流转换成字符输出流 |
3. 总结
转换流总的思想就是,先从字节流开始,以指定的编码方式转换为字符流,然后再包装为高级的字符流,最后进行读写操作。原先是直接定义字符流,然后包装为高级的字符流进行读写,现在是先定义的字节流,然后多了一步字节流到字符流的转化。
三、序列化对象
分为对象序列化(ObjectOutputStream)和对象反序列化(ObjectInputStream)。
1. 对象序列化
对象序列化作用:把内存中的对象存储到磁盘文件中去。
步骤:
-
创建原始字节输出流,用对象字节输出流管道包装,如:
ObjectOutputStream oos = new ObjectOutputStream(os);
; -
调用序列化方法 writeObject ;
-
释放资源。
序列化方法 | 说明 |
---|---|
public final void writeObject(Object obj) throws IOException | 把内存中的对象存储到(写入到)文件中去 |
注意:序列化对象要求,对象必须实现 Serializable 序列化接口。
2. 对象反序列化
对象反序列化作用:把存储到磁盘文件中的对象数据恢复成内存中的对象。
步骤:
-
创建原始字节输入流,用对象字节输入流管道包装,如:
ObjectInputStream ois = new ObjectInputStream(is);
; -
调用反序列化方法 readObject ;
-
释放资源。
序列化方法 | 说明 |
---|---|
public final Object readObject() | 把存储到磁盘文件中的对象数据恢复成内存中的对象 |
3. 补充知识
- 如果不想让对象的某个属性序列化,可以用
transient
来修饰。
- 声明序列化的版本号:
private static final long serialVersionUID = 1;
,后面的常数为版本号。序列化的版本号与反序列化的版本号必须一致才不会出错。
当序列化的版本号升级后,必须重新进行序列化,然后反序列化才不会报错。
四、打印流
作用:打印流可以实现方便、高效(里面基于缓冲流 BufferedWriter)地打印数据到文件中,可以实现打印什么数据就是什么数据。打印流是高级流。
分类:PrintStream、PrintWriter。
步骤:
- 通过构造器创建一个打印流对象;
- 调用 print 或 println 方法打印。
构造器 | 说明 |
---|---|
public PrintStream(OutputStream out) | 打印流直接通向字节输出流管道 |
public PrintStream(File file) | 打印流直接通向文件对象 |
public PrintStream(String fileName) | 打印流直接通向文件路径 |
public PrintStream(String fileName, String csn) | 打印流直接通向文件路径,并指定文件编码方式 |
打印方法 | 说明 |
---|---|
public void println(XXX x) | 打印任意类型的数据 |
注意:如果要在原先基础上加内容,则应该打印流直接通向字节输出流管道,并在字节输出流中加参数 true。
PrintStream 和 PrintWriter 的区别:
- 打印数据功能上一模一样,都是使用方便,性能高效(核心)。
- PrintStream 继承 OutputStream,支持写(write 方法)字节数据的方法(一般不用,因为有 println)。
- PrintWriter 继承 Writer,支持写(write 方法)字符数据的方法(一般不用,因为有 println)。
打印流的优点:在打印功能上方便、性能高效。
五、Properties
1. 概述
Properties 其实就是一个 Map 集合,但是一般不会当集合使用。
核心作用:Properties 代表的是一个属性文件,可以把自己对象中的键值对信息存入到一个属性文件中去,也可以读取属性文件中的键值对信息到 Properties 对象。
属性文件:后缀是 .properties
结尾的文件,里面的内容都是 key = value
,后续做系统配置信息的。
常用 API | 说明 |
---|---|
public Object setProperty(String key, String value) | 保存键值对(put) |
public String getProperty(String key) | 获取键对应的值(get) |
注意:Properties 集合中的键和值都是 String 类型。
2. 使用 Properties 把键值对信息存储到属性文件
方法名 | 说明 |
---|---|
public void store(Writer writer, String comments) | 把键值对信息存储到属性文件 |
3. 使用 Properties 读取属性文件中的键值对信息到 Properties 对象
方法名 | 说明 |
---|---|
public void load(Reader reader) | 读取(加载)属性文件中的键值对信息到 Properties 对象 |
六、IO 框架
1. 概述
-
commons-io 是 apache 开源基金组织提供的一组有关 IO 操作的类库,可以提高 IO 功能开发的效率。
-
commons-io 工具包提供了很多 IO 操作的类,有两个主要的类:
FileUtils
以及IOUtils
。 -
commons-io 官网:点此进入:
https://commons.apache.org/proper/commons-io/
。
2. 将核心 jar 包导入到工程中
jar 包和 API 文档在本文的末尾。
- 第一步:下载好 jar 包
- 第二步:在模块下新建一个 lib 文件,将 jar 包拷贝到 lib 文件下
- 第三步:将 jar 文件加到依赖库
- 第四步:在类中导入包就可以使用
3. 常用 API
commons-io 的常用 API 可以从 API 文档中去查,用到哪个 API 再具体去查。
类 | 常用 API | 说明 |
---|---|---|
IOUtils | public static int copy(InputStream inputStream, OutputStream outputStream) | 完成文件的复制 |
FileUtils | public static void copyFileToDirectory(File srcFile, File destDir) | 完成文件复制到某个文件夹下 |
FileUtils | public static void copyDirectoryToDirectory(File sourceDir, File destinationDir) | 完成文件夹复制到某个文件夹下 |
FileUtils | public static String readFileToString(File file, String charsetName) | 读取文件中的数据,返回字符串 |
注意:有很多的 API,用到哪个 API 再具体去查。
注意:
- 缓冲流就是在原始流的基础上,增加了缓冲区,进行了改进,这样提高了原始字节流、字符流读写数据的性能。
- 考虑一个问题时要有清晰的思维和逻辑,从大到小,一一解决。
- 字符串 indexOf 方法的使用。
- GBK 文件的字节流中,一个中文字符用 2 个字节表示;UTF-8 文件的字节流中,一个中文字符用 3 个字符表示。英文字符都是用 1 个字节表示。
- 文件拷贝性能最优的操作组合:字节缓冲输入流 + 字节缓冲输出流 + 以字节数组读取和写入数据(拷贝)。
- 写数据的最优方法:打印流,还可以支持写入不同编码的数据。
- 读数据的最优方法:字符缓冲输入流,按行读取数据。
- 如果想要读数据,但是现在是字节输入流,则可以通过转换流(字符输入转换流)将字节流转换为字符流,然后再包装成高级的字符缓冲输入流,按行读取数据。
- IO 框架的 jar 包以及 API 文档下载:点此下载,提取码:
3285
。