字符流 【重点】
字节流的体系结构
字符输入流
Reader
|-InputStreamReader 转换流
|-FileReader 基本流
|-BufferedReader 缓冲流

    字符输出流
        Writer
            |-OutputStreamWriter    转换流
                |-FileWriter        基本流
            |-BufferedWriter        缓冲流

编码表
    什么是编码表
        是一张现实中的文字符号等(字符)与计算机能识别的二进制(字节)之间对应表

        现实中的             计算机能识别的
        a                   01100001
        我                  11100001 10001010
        !                   01001000

    什么编解码
        编码:字符转换为字节   a->01100001
        解码:字节转换为字符   01100001->a

        编解码操作一定要查询编码表
        编解码操作使用的编码表必须是同一张,否则会出现乱码问题

    常见的编码表
        ASCII:最早的编码表,美国人和英国人的文字符号等的对应关系,不包含中文
        GBK:中文码表,一个中文占2个字节,英文、数字和符号占1个字节
        UTF-8:包含中文,一个中文占3个字节,英文、数字和符号占1个字节
        ISO-8859-1:西欧/拉丁编码,不包含中文


    "我爱java"(字符串) ->'我','爱','j','a','v','a'(字符)

    '我'(1个字符) -> {-19, -29, -89}(3个字节)
    字符是由字节组成的,有些是一个字节组成,有些是多个字节组成

基本流
    FileReader
        构造方法
            FileReader(String name)
            FileReader(File file)


        成员方法
            int read():一次读取一个字符,并返回读取到的字符,如果读取到了文件末尾,返回-1
            int read(char[] chs):一次读取多个字符,并返回读取到数组中的有效字符个数,如果读取到了文件末尾,返回-1
            void close()



            //如果文件存在,那么就不会报错.
            //如果文件不存在,那么就直接报错.
            FileReader fr = new FileReader("bytestream\\a.txt");

            int read = fr.read();
            //一次读取一个字符,返回值就是本次读到的那个字符数据.
            //也就是字符在码表中对应的那个数字.
            //如果我们想要看到的是字符数据,那么一定要强转成char

            System.out.println((char)read);

            //释放资源
            fr.close();


    FileWriter
        构造方法
                FileWriter(String name)
                FileWriter(String name, boolean append)
                FileWriter(File file)
                FileWriter(File file, boolean append)


            成员方法
                 void write(int b):写出字符数据 int->char
                 void write(char[] b)
                 void write(char[] b, int off, int len)

                 void write(String s)
                    * write("\r\n") 换行
                 void write(String s, int off, int len)


                 void close()



                //1.创建字符输出流的对象
                                //注意点:1、如果文件不存在,会帮我们自动创建出来,但是要求文件路径要存在
                                //      2、如果文件存在,会把文件清空,再写出到文件
                                        3、可以写相对路径,也可以写绝对路径
                                        4、关联的路径永远只能是文件路径,不能是文件夹路径
                FileWriter fw = new FileWriter("C:\\itheima\\a.txt");

                //2,写数据
                // 传递一个整数时,那么实际上写到文件中的,是这个整数在码表中对应的那个字符.
                fw.write(98);// 98(十进制)->00000000 00000000 00000000 01100010(int、二进制)
                              // -> 00000000 01100010(char、二进制) ->写入到文件中,但是文本编辑器是一个编解码

                //3,释放资源
                fw.close(); //告诉操作系统,我现在已经不要再用这个文件了

        flush和close方法的区别
            close方法
                1、是先刷新,在关闭流
                2、close关闭流转换就不能继续使用流
            flush方法
                1、只有刷新功能
                2、可以多次调用,可以继续使用流

缓冲流
    底层提供了缓冲区(数组),提高读写的效率

     BufferedWriter
        构造方法:不能直接关联写出的文件
            BufferedWriter(Writer out)


        写和释放资源的操作与字符基本流一模一样
    BufferedReader
        构造方法:不能直接关联读取的文件
            BufferedReader(Reader in)


        读和释放资源的操作与字符基本流一模一样


     特有功能
        BufferedWriter类
            * void newLine():一个跨平台的换行

        BufferedReader类
            * String readLine():一次读取一行数据(不包含回车换行符),并返回,如果读取到文件末尾,返回null


    字符缓冲流的好处:
        1、提高读写的效率
        2、提供了特有功能方便操作

转换流
    InputStreamReader
        字符流 = 字节流 + 编码表

        FileReader = FileInputStream + 默认编码(UTF-8)
        InputStreamReader = FileInputStream + 指定编码表


    OutputStreamWriter
        FileWriter = FileOutputStream + 默认编码(UTF-8)
        OutputStreamWriter = FileOutputStream + 指定编码表

    作用:
        1、读写的过程中进行编码转换
        2、将字节流转换为字符流

Properties集合 【重点】
什么是Properties?
是一个双列集合,间接实现了Map接口。
没有泛型,存储的键和值都是字符串类型
它提供和IO流相结合的方法

Properties
    共性功能            【了解】


    特有功能            【重点】
         Object setProperty(String key, String value)   相当于put方法
         String getProperty(String key)                相当于get方法         【重点】
         Set<String> stringPropertyNames()              相当于keySet方法
         void load(字节/字符输入流 inStream)              读取                【重点】
         void store(字节/字符输出流, String comments)     写出



    java中的配置方式
        1、配置文件方式
            .properties文件方式
            .xml文件方式

        2、注解配置方式


    Properties的使用步骤:
        1、创建Properties集合对象
        2、通过load方法读取.properties文件中的配置信息(格式:键=值)
        3、通过getProperty方法根据键获取我们需要的值。


        // 1、创建Properties集合对象
        Properties prop = new Properties();
        // 2、通过load方法读取.properties文件中的配置信息(格式:键=值)
        InputStream in = PropertiesDemo3.class.getClassLoader().getResourceAsStream("info.properties");
        prop.load(in);

        // 3、通过getProperty方法根据键获取我们需要的值。
        String username = prop.getProperty("username");// zhangsan
        String password = prop.getProperty("password");

其他流 - 对象操作流 【了解】
用于操作对象的流,可以将对象直接写出到文件,也可以将文件中的对象读取出来
ObjectOutputStream:对象输出流,将对象直接写出到文件
构造方法
ObjectOutputStream(OutputStream out)

            * 构造方法中不能直接关联文件路径,要传递基本流对象,基本流关联文件路径

    成员方法
         void writeObject(Object obj)
            * Object obj要写出的对象


ObjectInputStream:对象输入流,将文件中的对象读取出来
    构造方法
        ObjectInputStream(InputStream in)
            * 构造方法中不能直接关联文件路径,要传递基本流对象,基本流关联文件路径

    成员方法
          Object readObject()
            * 返回的是读取到的对象


序列化和反序列化的概念
    序列化:将对象直接写出到文件
    反序列化:将文件中的对象读取出来

Serializable:标识/标记型接口

    用对象序列化流序列化了一个对象后,假如我们修改了对象所属的类文件,读取数据会不会出问题呢?
        会出现问题,InvalidClassException无效的类异常
        该类的序列版本号与从流中读取的类描述符的版本号不匹配(写的时候的类与读取到时候看到的类不同了)

    如果出问题了,如何解决呢?
        在类中定义一个serialVersionUID常量
        ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;

        serialVersionUID常量:序列化版本号,你可以认为这是类的版本号,读和写的时候看类是否是相同的,
        就是根据serialVersionUID常量的值来判断的

        如果不显式定义serialVersionUID常量,那么系统会根据类的内容去生成一个默认的serialVersionUID


    如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
        transient:瞬态关键字