IO流:
缓冲流: BufferedInputStream / BufferedOutputStream, BufferedReader / BufferedWriter
在基本流上增加缓冲区 char[] byte[], 提高读写效率
转换流: InputStreamReader / OutputStreamWriter
字节转字符: FileReader 读 char <- byte 硬盘
字符转字节: FileWriter 写 char -> byte 硬盘
序列化流: ObjectInputStream / ObjectOutputStream
序列化: 内存中的对象 写-> 硬盘上的文件中
反序列化: 内存中的对象 <-读 硬盘上的文件中
打印流: PrintStream
可以自动换行, 原样输出 System.out.println();
基本流: FileXxx
包装流:
包装流只是在基本流的外面增加了一些便捷的功能, 最终进行读写的还是基本流但是包装流增强了基本流的功能
BufferedReader br = new BufferedReader(new FileReader());
缓冲流
缓冲流的原理:
底层也是使用基本流(FileXxx)来读写,但缓冲流内部定义了一个缓冲数组, 在读的时候类似于我们一次读一个数组的方式, 减少了磁盘操作次数, 提高了程序效率
缓冲字节输出流: BufferedOutputStream
字节缓冲流
|_ BufferedInputStream # 缓冲字节输入流
|_ BufferedOutputStream # 缓冲字节输出流
字符缓冲流
|_ BufferedReader # 缓冲字符输入流
|_ BufferedWriter # 缓冲字符输出流
java.io.BufferedOutputStream类: 缓冲字节输出流
// 构造方法
BufferedOutputStream(OutputStream out): 使用基本流创建一个缓冲字节输出流
BufferedOutputStream(OutputStream out, int size): 使用基本流创建一个缓冲字节输出流,设置缓冲区大小
BufferedOutputStream使用步骤:
1.创建FileOutputStream对象, 构造方法中绑定要输出的目的地
2.创建BufferedOutputStream对象, 构造方法中传递FileOutputStream对象
3.使用BufferedOutputStream对象中的方法 write(), 把数据写入到内部缓冲区中
4.使用BufferedOutputStream对象中的方法 flush(), 把内部缓冲区中的数据,刷新到文件中
5.释放资源(会先调用flush方法刷新数据, 第4步可以省略)
// 1. 创建缓冲字节输出流对象, 内部传入基本流对象
FileOutputStream fos = new FileOutputStream("文件路径");
BufferedOutputStream bos = new BufferedOutputStream(fos);
//// 2. 写数据
bos.write("你好".getBytes());
// bos.flush(); // 可以省略
// 释放资源
bos.close();
缓冲字节输入流: BufferedInputStream
java.io.BufferedInputStream类: 缓冲字节输入流
// 构造方法
BufferedInputStream(InputStream in): 使用基本流创建一个缓冲字节输入流
BufferedInputStream(InputStream in, int size): 使用基本流创建一个缓冲字节输入流, 设置缓冲区大小
使用步骤:
1.创建FileInputStream对象, 构造方法中绑定要读取的数据源
2.创建BufferedInputStream对象, 构造方法中传递FileInputStream对象
3.使用BufferedInputStream对象中的方法 read(), 读取文件
4.释放资源 close()
FileInputStream fis = new FileInputStream("文件路径");
BufferedInputStream bis = new BufferedInputStream(fis);
byte[] bytes = new byte[1024];
int len;
while ((len = bis.read(bytes)) != -1) {
System.out.println(new String(bytes, 0, len));
}
bis.close()
缓冲流的效率测试: 复制文件
缓冲流 + 一次读写一个字节数组 效率最高
缓冲字符输出流: BufferedWriter
java.io.BufferedWriter类:
// 构造方法
BufferedWriter(Writer out): 使用基本流创建一个缓冲字符输出流
BufferedWriter(Writer out, int size): 使用基本流创建一个缓冲字符输出流, 设置缓冲区大小
// 特有方法
void newLine(): 写入一个换行符, 换行符自动根据当前系统确定
例子:
public class Test {
public static void main(String[] args) throws IOException {
// 创建缓冲字符输出流对象, 构造方法内部传递基本流对象
BufferedWriter bw = new BufferedWriter(new
FileWriter("day10\\testBufferedWriter1.txt"));
// 循环10次写入内容和换行
for (int i = 0; i < 10; i++) {
// 写入内容
bw.write("传智播客");
// 写入换行符
bw.newLine();
}
// 刷新 (可以省略)
// bw.flush();
// 释放资源
bw.close();
}
}
缓冲字符输入流: BufferedReader
java.io.BufferedReader类: 缓冲字符输入流
// 构造方法
BufferedReader(Reader in): 使用基本流创建一个缓冲字符输入流
BufferedReader(Reader in, int size): 使用基本流创建一个缓冲字符输入流, 设置缓冲区大小
// 特有方法
String readLine(): 一次读一行字符串, "不包含换行符". 读到文件末尾返回null
例子:
public class Test {
public static void main(String[] args) throws IOException {
// 创建缓冲字符输入流对象, 构造方法中传递基本流对象
BufferedReader br = new BufferedReader(new
FileReader("day10\\testBufferedWriter1.txt"));
// 循环读取
String line; // 定义String变量, 用于保存每次读到的一行字符串
while ((line = br.readLine()) != null) {
// 将读到的字符串打印出来, 因为字符串中不带换行符, 所以我们打印需要用println
System.out.println(line);
}
// 释放资源
br.close();
}
}
练习: 文本排序
步骤分析:
1. 创建ArrayList集合, 泛型用String
2. 创建BufferedReader对象, 构造方法中传递FileReader对象, 指向模块下的in.txt
3. 创建BufferedWriter对象, 构造方法中传递FileWriter对象, 指向模块下的out.txt
4. 使用BufferedReader对象中的方法readLine, 以行的方式读取文本
5. 把读取到的文本存储到ArrayList集合中
6. 使用Collections集合工具类的方法sort, 对集合中的元素按照自定义规则排序
7. 遍历ArrayList集合, 获取每一个元素
8. 使用BufferedWriter对象中的方法write, 把遍历得到的元素写入到文本中(内存缓冲区)
9. 写换行
10. 释放资源
转换流
字符编码和字符集
编码: 字符 -> 字节 'a' -> 97
解码: 字节 -> 字符 97 -> 'a'
编码表: 字符和二进制数字的对应规则
字符集和编码表: 字符集包含编码表
ASCII字符集
ASCII编码表
ASCII扩展编码表
ISO-8859-1字符集: Tomcat Web服务器程序
Latin-1: 拉丁字符. 没有中文. 每个字符由1个byte组成
GB字符集
补充:
乱码问题: FileReader读取GBK编码
补充:
转换流原理: 字符流和转换流的关系
GB2312编码表: 每个字符由2个byte组成
GBK编码表: 每个字符由2个byte组成
GB18030编码表: 每个字符由1, 2, 4个byte组成
Unicode字符集
UTF-8: ASCII字符占1个byte, 拉丁字符占2个byte, 中文占3个byte, Unicode辅助字符占4个byte
UTF-16
UTF-32
乱码问题: FileReader读取GBK编码
乱码原因: 读写编码不一致
GBK文件中存储的是"你好"在GBK中对应的byte
而IDEA中使用FileReader读取文件时, 是将byte按照UTF-8编码表查找字符, 结果找不到, 就显示了问号
转换流原理: 字符流和转换流的关系
java.io.Reader
|_ InputStreamReader: 转换字符输入流 byte --查指定码表--> char, char[], String
|_ FileReader byte --查UTF-8码表--> char
java.io.Writer
|_ OutputStreamWriter: 转换字符输出流 char, char[], String --查指定码表--> byte
|_ FileWriter char --查UTF-8码表--> byte
OutputStreamWriter类介绍及使用
java.io.OutputStreamWriter类: 输出转换流. 字符流通往字节流的桥梁
// 构造方法
OutputStreamWriter(OutputStream out): 使用默认编码表创建转换流
OutputStreamWriter(OutputStream out, String charsetName): 使用指定编码表创建转换流
// 使用默认UTF-8
OutputStreamWriter o = new OutputStreamWriter(new FileOutputStream("a.txt"));
o.write("dsfdsfdsaf")
// 使用指定的GBK
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("a.txt"), "GBK");
osw.write("你") -> 查"GBK"码表 -> -1,-2 -> FileOutputStream -> a.txt文件
写数据: 字符流 --------------------------> 字节流
InputStreamReader类介绍及使用
java.io.InputStreamReader类: 输入转换流. 字节流通往字符流的桥梁
// 构造方法
InputStreamReader(InputStream in): 使用默认编码表创建转换流
InputStreamReader(InputStream in, String charsetName): 使用指定编码表创建转换流
// 使用默认UTF-8
InputStreamReader r = new InputStreamReader(new FileInputStream("a.txt"));
// 使用指定的GBK
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"), "GBK");
'你' <- 查"GBK"码表 <- isr.read() <- -1,-2 <- FileInputStream <- a.txt文件
读数据: 字符流 <---------- 字节流
转换流小结:
FileReader = InputStreamReader + FileInputStream, 按照IDEA中UTF-8将读取的字节转换为字符
FileWriter = OutputStreamWriter + FileOutputStream, 按照IDEA中UTF-8将写出的字符转换为字节
转换流InputStreamReader, OutputStreamWriter在读写时, 可以指定按照哪个编码表来进行转换
所以:
字符流 = 编码表 + 字节流
转换流主要用于解决Web开发中的乱码问题
序列化流(对象流)
序列化和反序列化概述
序列化: 内存中的对象转换为字节序列, 以流的方式写入到磁盘的文件中
对象 -> 字节
反序列化: 磁盘文件中的字节序列, 以流的方式读取到内存中变成对象
字节 -> 对象
通过序列化流, 我们可以将内存中的数据方便的存储到磁盘上, 下次程序启动后也能从磁盘读取恢复之前的对象状态
OutputStream
|_ ObjectOutputStream类: 对象字节输出流
InputStream
|_ ObjectInputStream类: 对象字节输入流
对象序列化流: ObjectOutputStream
java.io.ObjectOutputStream类: 对象字节输出流
// 构造方法
ObjectOutputStream(OutputStream out)
// 特有成员方法
void writeObject(Object obj): 将对象写出
// 创建对象输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("student.txt"));
// 写对象
Student s = new Student("小美女", 18);
oos.writeObject(s);
// 释放资源
oos.close();
注意:
被读写的对象的类必须实现"java.io.Serializable"接口, 否则会抛出"NotSerializableException"
对象反序列化流: ObjectInputStream
java.io.ObjectInputStream类: 对象字节输入流
// 构造方法
ObjectInputStream(InputStream in)
// 特有成员方法
Object readObject(): 读取对象
// 创建对象输入流
ObjectInputStream oos = new ObjectInputStream(new FileInputStream("student.txt"));
// 读对象
Object o = oos.readObject();
Student s = (Student)o;
System.out.println(s);
// 释放资源
oos.close();
transient瞬态关键字: 避免属性序列化
static 修饰的成员变量属于类不属于对象, 所以不能序列化
transient 修饰的成员变量, 不能被序列化
transient 应用场景:
如果对象的某个属性不希望被序列化, 可以使用 transient 修饰, 这样就不会被对象流写到文件中
InvalidClassException异常: 原因和解决方案
serialVersionUID序列号的作用:
序列化操作时, 会根据类生成一个默认的 serialVersionUID 属性, 写入到文件中.
该版本号是根据类中成员计算出来的一个数值. 当类的成员发生改变时, 序列版本号也会发生变化
当代码中的类和读取的对象序列版本号不一致时, 就会抛出InvalidClassException
为了避免这种问题, 我们可以手动写死这个序列号, 这样无论成员如何变化, 序列版本号都是一样的
序列化流的应用场景:
以后我们会用到"缓存", 缓存就是一些数据, 是存储在内存中的, 比如存储某个用户登录的状态
而内存中的数据, 服务器一重启, 数据就没了, 所以我们需要在服务器重启前, 将内存中的数据写入到磁盘保存
这时就可以用 序列化流
服务器重启后, 先通过 反序列化流 读取到内存, 这样可以保持之前的状态
打印流
打印流PrintStream: 概述和使用
PrintStream特点:
1. 只有输出流, 没有输入流
2. PrintStream不会抛出IOException
3. 有特殊方法 print(), println(), 可以输出任意类型的值, 原样输出
java.io.PrintStream类: 字节打印流
// 构造方法
PrintStream(File file): 创建字节打印流, 输出到一个文件
PrintStream(OutputStream out): 创建字节打印流, 输出到一个字节输出流
PrintStream(String fileName): 创建字节打印流, 输出到一个文件路径
注意事项:
如果用 write(97) 方法, 会查编码表 97 -> a
如果用 print(97), println(97), 则原样输出 97 int -> '9''7' -> byte -> 文件 97
System.out.println();
PrintStream out;
java.lang.System类:
// 静态方法
static void setOut(PrintStream out): 设置System.out的输出目的地为参数的打印流
IO框架体系结构
PrintStream特点:
1. 只有输出流, 没有输入流
2. PrintStream不会抛出IOException
3. 有特殊方法 print(), println(), 可以输出任意类型的值, 原样输出
java.io.PrintStream类: 字节打印流
// 构造方法
PrintStream(File file): 创建字节打印流, 输出到一个文件
PrintStream(OutputStream out): 创建字节打印流, 输出到一个字节输出流
PrintStream(String fileName): 创建字节打印流, 输出到一个文件路径
注意事项:
如果用 write(97) 方法, 会查编码表 97 -> a
如果用 print(97), println(97), 则原样输出 97 int -> '9''7' -> byte -> 文件 97
System.out.println();
PrintStream out;
java.lang.System类:
// 静态方法
static void setOut(PrintStream out): 设置System.out的输出目的地为参数的打印流
|
|