黑马程序员技术交流社区

标题: 【石家庄校区】缓冲流 转换流 序列化流 打印流 [打印本页]

作者: 键盘有花生油    时间: 2018-12-2 14:27
标题: 【石家庄校区】缓冲流 转换流 序列化流 打印流
本帖最后由 小石姐姐 于 2018-12-6 16:48 编辑
day10 缓冲流 转换流 序列化流 打印流
[Java] 纯文本查看 复制代码
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());

缓冲流
缓冲流的原理
    但缓冲流内部定义了一个缓冲数组, 在读的时候类似于我们一次读一个数组的方式, 减少了磁盘操作次数, 提高了程序效率
缓冲字节输出流: BufferedOutputStream
[size=12.0000pt]总结:

[Java] 纯文本查看 复制代码
字节缓冲流
    |_ 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步可以省略)
        
    FileOutputStream fos = new FileOutputStream("文件路径");
    BufferedOutputStream bos = new BufferedOutputStream(fos);
    bos.write("你好".getBytes());
    // bos.flush();  // 可以省略
    bos.close();

缓冲字节输入流: BufferedInputStream
[size=12.0000pt]总结:

[Java] 纯文本查看 复制代码
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();

[size=12.0000pt]补充:
[size=11.0000pt]
[size=11.0000pt]​
缓冲流的效率测试: 复制文件

缓冲字符输出流: BufferedWriter
[size=12.0000pt]总结:
[Java] 纯文本查看 复制代码
[mw_shl_code=java,true]知识点:

基本流 一次读写一个字节 一个字节
基本流 一次读写一个字节数组 一个字节数组
缓冲流 一次读写一个字节 一个字节数组
缓冲流 一次读写一个字节数组 两个字节数组
效率分别是怎样的, 哪种方式效率最高?
总结:

缓冲流 + 一次读写一个字节数组 效率最高


[Java] 纯文本查看 复制代码
java.io.BufferedWriter类:
    // 构造方法
    BufferedWriter(Writer out): 使用基本流创建一个缓冲字符输出流
    BufferedWriter(Writer out, int size): 使用基本流创建一个缓冲字符输出流, 设置缓冲区大小
    // 特有方法
    void newLine(): 写入一个换行符, 换行符自动根据当前系统确定
[/mw_shl_code]
缓冲字符输入流: BufferedReader
[size=12.0000pt]知识点:
[size=11.0000pt]
[size=11.0000pt]BufferedReader如何创建对象[size=11.0000pt]
[size=11.0000pt]BufferedReader特有的读取一行字符串的方法如何使用
[size=12.0000pt]总结:

[Java] 纯文本查看 复制代码

java.io.BufferedReader类: 缓冲字符输入流
    // 构造方法
    BufferedReader(Reader in): 使用基本流创建一个缓冲字符输入流
    BufferedReader(Reader in, int size): 使用基本流创建一个缓冲字符输入流, 设置缓冲区大小
    // 特有方法
    String readLine(): 一次读一行字符串, "不包含换行符". 读到文件末尾返回null

转换流
字符编码和字符集
[size=12.0000pt]总结:
[Java] 纯文本查看 复制代码

编码: 字符 -> 字节  'a' -> 97
解码: 字节 -> 字符  97 -> 'a'

编码表: 字符和二进制数字的对应规则
   
字符集和编码表: 字符集包含编码表
    ASCII字符集
        ASCII编码表
        ASCII扩展编码表
    ISO-8859-1字符集: Tomcat Web服务器程序
        Latin-1: 拉丁字符. 没有中文. 每个字符由1个byte组成
    GB字符集
        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
    ANSI: 表示使用系统默认编码表

乱码问题: FileReader读取GBK编码
[size=12.0000pt]总结:
[Java] 纯文本查看 复制代码

乱码原因: 读写编码不一致
    GBK文件中存储的是"你好"在GBK中对应的byte
    而IDEA中使用FileReader读取文件时, 是将byte按照UTF-8编码表查找字符, 结果找不到, 就显示了问号

乱码:
    ???         找不到字符
    空格 看不见   找不到字符
    兯ρ乯        能找到字符, 但是找错了

转换流原理: 字符流和转换流的关系
[Java] 纯文本查看 复制代码
[mw_shl_code=java,true]知识点:
FileReader和InputStreamReader有什么关系
FileWriter和OutputStreamWriter有什么关系
转换流有什么作用


总结:

[mw_shl_code=java,true]
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
[/mw_shl_code][/mw_shl_code]
OutputStreamWriter类介绍及使用
[Java] 纯文本查看 复制代码
知识点:

创建OutputStreamWriter对象时, 如何指定使用的编码表
总结:

[mw_shl_code=java,true]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文件
         写数据: 字符流 --------------------------> 字节流
[/mw_shl_code]
InputStreamReader类介绍及使用
[Java] 纯文本查看 复制代码
知识点:
创建InputStreamReader对象时, 如何指定使用的编码表

总结:

[mw_shl_code=java,true]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文件
                         读数据: 字符流 <---------- 字节流   
[/mw_shl_code]
序列化流(对象流)
序列化和反序列化概述

总结:

序列化: 内存中的对象转换为字节序列, 以流的方式写入到磁盘的文件中
    对象 -> 字节
反序列化: 磁盘文件中的字节序列, 以流的方式读取到内存中变成对象
    字节 -> 对象

通过序列化流, 我们可以将内存中的数据方便的存储到磁盘上, 下次程序启动后也能从磁盘读取恢复之前的对象状态

OutputStream
    |_ ObjectOutputStream: 对象字节输出流
InputStream
    |_ ObjectInputStream: 对象字节输入流
对象序列化流: ObjectOutputStream
[size=12.0000pt]知识点:
[size=11.0000pt]
ObjectOutputStream如何创建对象[size=11.0000pt]
调用什么方法将对象写入到文件[size=11.0000pt]
[size=11.0000pt][重要[size=11.0000pt]![size=11.0000pt]] 被序列化的对象所属的类[size=11.0000pt], 有什么特殊的要求
[size=12.0000pt]总结:

[Java] 纯文本查看 复制代码
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
[size=12.0000pt]知识点:
[size=11.0000pt]
ObjectInputStream如何创建对象[size=11.0000pt]
ObjectInputStream如何将对象从文件中读取到程序中
[size=12.0000pt]总结:

[Java] 纯文本查看 复制代码
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瞬态关键字: 避免属性序列化
[size=12.0000pt]总结:
[size=11.0000pt]
[size=11.0000pt]static[size=11.0000pt] 修饰的成员变量属于类不属于对象[size=11.0000pt], 所以不能序列化[size=11.0000pt]
[size=11.0000pt]transient[size=11.0000pt] 修饰的成员变量[size=11.0000pt], 不能被序列化[size=11.0000pt]
[size=11.0000pt]​[size=11.0000pt]
[size=11.0000pt]transient[size=11.0000pt] 应用场景[size=11.0000pt]:[size=11.0000pt]
[size=11.0000pt]    如果对象的某个属性不希望被序列化[size=11.0000pt], 可以使用[size=11.0000pt] [size=11.0000pt]transient[size=11.0000pt] 修饰[size=11.0000pt], 这样就不会被对象流写到文件中
InvalidClassException异常: 原因和解决方案
[size=12.0000pt]知识点:

[Java] 纯文本查看 复制代码
[mw_shl_code=java,true]如果将对象序列化到文件中后, 代码中的类发生了变化, 能否正常反序列化文件中的对象?
[重要!]"InvalidClassException"什么情况下发生, 如何避免

public class Person {
    int age;
}

oos.writeObject(new Person("张三", 13));
Object p = ois.readObject();

总结:

serialVersionUID序列号的作用:
    序列化操作时, 会根据类生成一个默认的 serialVersionUID 属性, 写入到文件中.
    该版本号是根据类中成员计算出来的一个数值. 当类的成员发生改变时, 序列版本号也会发生变化
    当代码中的类和读取的对象序列版本号不一致时, 就会抛出InvalidClassException

为了避免这种问题, 我们可以手动写死这个序列号, 这样无论成员如何变化, 序列版本号都是一样的
补充: IDEA设置生成序列版本号:
Setting -> Editor -> Inspections -> Java -> Serialization issues -> 勾选Serializable class without 'serialVersionUID'

然后在类名上按Alt + Enter提示就会有Add 'serialVersionID' field[/mw_shl_code]
打印流
打印流PrintStream: 概述和使用
[size=12.0000pt]总结:

[Java] 纯文本查看 复制代码
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框架体系结构

[Java] 纯文本查看 复制代码
// 体系结构:
字节流
    |_ InputStream                 # 字节输入流
    |   |_ FileInputStream         # 专门操作文件的字节输入流
    |   |_ BufferedInputStream     # 带有缓冲区的字节输入流, 效率高
    |   |_ ObjectInputStream       # 对象输入流
    |
    |_ OutputStream                # 字节输出流
        |_ FileOutputStream        # 专门操作文件的字节输出流
        |_ BufferedOutputStream    # 带有缓冲区的字节输出流, 效率高
        |_ ObjectOutputStream      # 对象输出流
        |_ PrintStream             # 字节打印流
字符流
    |_ Reader                      # 字符输入流
    |   |_ BufferedReader          # 带有缓冲区的字符输入流, 效率高
    |   |_ InputStreamReader       # 将字节流转换为字符流输入的转换输入流
    |       |_ FileReader          # 专门操作文件的字符输入流   
    |
    |_ Writer                      # 字符输出流
        |_ BufferedWriter          # 带有缓冲区的字符输出流, 效率高
        |_ OutputStreamWriter      # 将字符流转换为字节流输出的转换输出流
        |     |_ FileWriter         # 专门操作文件的字符输出流

今日API

[Java] 纯文本查看 复制代码
java.io.BufferedOutputStream类: 缓冲字节输出流
    // 构造方法
    BufferedOutputStream(OutputStream out): 使用基本流创建一个缓冲字节输出流
    BufferedOutputStream(OutputStream out, int size): 使用基本流创建一个缓冲字节输出流, 设置缓冲区大小

java.io.BufferedInputStream类: 缓冲字节输入流
    // 构造方法
    BufferedInputStream(InputStream in): 使用基本流创建一个缓冲字节输入流
    BufferedInputStream(InputStream in, int size): 使用基本流创建一个缓冲字节输入流, 设置缓冲区大小
   
java.io.BufferedWriter类:
    // 构造方法
    BufferedWriter(Writer out): 使用基本流创建一个缓冲字符输出流
    BufferedWriter(Writer out, int size): 使用基本流创建一个缓冲字符输出流, 设置缓冲区大小
    // 特有方法
    void newLine(): 写入一个换行符, 换行符自动根据当前系统确定
   
java.io.BufferedReader类: 缓冲字符输入流
    // 构造方法
    BufferedReader(Reader in): 使用基本流创建一个缓冲字符输入流
    BufferedReader(Reader in, int size): 使用基本流创建一个缓冲字符输入流, 设置缓冲区大小
    // 特有方法
    String readLine(): 一次读一行字符串, "不包含换行符". 读到文件末尾返回null
   
java.io.OutputStreamWriter类: 输出转换流. 字符流通往字节流的桥梁
    // 构造方法
    OutputStreamWriter(OutputStream out): 使用默认编码表创建转换流
    OutputStreamWriter(OutputStream out, String charsetName): 使用指定编码表创建转换流
   
java.io.InputStreamReader类: 输入转换流. 字节流通往字符流的桥梁
    // 构造方法
    InputStreamReader(InputStream in): 使用默认编码表创建转换流
    InputStreamReader(InputStream in, String charsetName): 使用指定编码表创建转换流
   
java.io.ObjectOutputStream类: 对象字节输出流
    // 构造方法
    ObjectOutputStream(OutputStream out)
    // 特有成员方法
    void writeObject(Object obj): 将对象写出
   
java.io.ObjectInputStream类: 对象字节输入流
    // 构造方法
    ObjectInputStream(InputStream in)
    // 特有成员方法
    Object readObject(): 读取对象
   
java.io.PrintStream类: 字节打印流
    // 构造方法
    PrintStream(File file): 创建字节打印流, 输出到一个文件
    PrintStream(OutputStream out): 创建字节打印流, 输出到一个字节输出流
    PrintStream(String fileName): 创建字节打印流, 输出到一个文件路径
   
java.lang.System类:
    // 静态方法
    static void setOut(PrintStream out): 设置System.out的输出目的地为参数的打印流









欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2