Day21 --IO流对象 字符流 递归

来源:互联网 发布:陕西广电网络是国企嘛 编辑:程序博客网 时间:2024/05/22 17:27
 a.
    字符流 (中间有个转换的过程)
        概述
            * 可以直接读写字符(中文)的IO流
            * 字符流读取数据,首先先读取到字节数据,然后将其转换成字符
                * 字节 - 字符
            * 字符流写入数据,需要将字符转换为字节再写入
                * 字符 - 字节
        
        FileReader
            概述
                * 字符输入流 顶层抽象父类:Reader。
                * InputStreamReader 直接子类 FileReader。
            方法
                * read(); 一次读取一个字符  
                * 达到结束末尾,返回 -1
                * 读取出来的是字节,如果想是字符,就向下转型:char(c);
                * 通过项目平台默认的码表一次读取一个字符
                
        FileWriter
            Writer
                * Writer类中有一个2k(1024*2) 的小缓冲区,如果不关闭流对象,就会将内容写到缓冲区中,关流才能将缓冲区中的数据刷出,在关闭流对象。
                * byte占1个字节,char占2个字节,所以就是(1024*2)即2k的小缓冲区。
                
            概述
                * 字符输出流 顶层抽象父类:Writer。
                * OutputStreamWriter 直接子类 FileWriter。
            方法
                * write(int c); 一次写入一个字符(自动将字符转为字节写入)
                * 底层通过编码表将字节翻译成对应的汉字
                * 写入 阿拉伯数字会自动转成编码中对应的数据
            
    

b.
    字符流的拷贝
        Writer
                * Writer类中有一个2k(1024*2) 的小缓冲区,如果不关闭流对象,就会将内容写到缓冲区中,关流才能将缓冲区中的数据刷出,再关闭流对象。
                * byte占1个字节,char占2个字节,所以就是(1024*2)即2k的小缓冲区。
                
        代码
        FileReader fr = new FileReader("汉字.txt");
        FileWriter fw = new FileWriter("copy_汉字2.txt");
        
        int len;
        char[] b = new char[1024 * 8];
        while( (len = fr.read(b)) !=-1){  
            fw.write(b, 0, len);
        }
//        Writer类中有一个2k(1024*2) 的小缓冲区,如果不关闭流对象,就会将内容写到缓冲区中,关流才能将缓冲区中的数据刷出,再关闭流对象。
        fr.close();
        fw.close();
        
        字符流的拷贝过程
            * FileReader 字节转字符
            * FileWriter 字符转字节

字符流中没有available()方法


c.
    什么情况下使用字符流
        * 只有在 只读或只写的情况下,建议使用字符流。
        * 虽然字符流可以拷贝文本文件,但是不推荐这样使用。
        * 因为:读取文本文件时,会先转成字节,再手动强转 成字符,才能输出中文。
            * 读取的时候,可以按照字符的大小读取,不会出现半个中文
        
        * 写入文件的时候,将字符自动转换成字节写入(系统会根据默认码表将字节转换成对应中文并输入到文件中)。
            * 写入文件的时候,可以直接将字符串写入,不需要转成字节数组。而使用字节流写入字符串文件的时候,需要转成字节数组,即getBytes();
    

    字符流的copy过程 和 字节流的copy过程
        * 参考图片 
        * D:\CSDN_Android_\Code\Day21_IO流对象 字符流 递归\字符流 和 字节流 copy文本文件的过程.png
    
    使用字节流读取中文字符需要读取两次,因为一个中文由两个字节组成,而使用字符流只需要读取一次。
    System.out的类型是PrintStream,属于OutputStream类别。

d.
字符流是否可以拷贝【非】纯文本文件(音频,图片)
        * 不可以,因为可能出现乱码。只能操作纯文本文件。
        * 因为在读取文件的时候会将 字节转成字符,在转换的过程中,从码表中查找,如果没有对应的字符(值),就用 ? 来代替。写出的时候会将字符转成成字节写出来。
        * 如果是?, 直接写出来,这样写出来的图片或音频文件就是乱码,看不了,也听不了了。
    

e.
    带缓冲区的字符流
         概述
            * BufferedReader中的read()方法读取字符时,会一次性读取若干字符到缓冲区中,然后逐个返回给程序,降低读取文件的次数,提高了效率。
            * BufferedWriter中的write()方法写出字符时,会先写到缓冲区中,当缓冲区中写满后,再写到copy的文件上,降低了写文件的次数,提高了效率。
        
            * 缓冲区大小:8192(1024*8); 8*2=16k,也就是缓冲区大小是16k。这里之所以要*2,是因为char字符占2个字节。
        
f.
    带缓冲区流中的特殊方法
        概述
            * readLine(); 
                * 读取一个文本行(不是只读取一行)。
                * 是BufferedReader中的一个方法。
                * 不包含换行符号。
                * 如果读到文件中的 换行 ('\n')、回车 ('\r')的时候,就换行。
                * 为什么返回null? 其实底层也是返回-1的,只不过readLine()返回值类型是字符串,-1会被字符串接收,所以就返回null。
                
            * newLine();  
                * 写入一个回车换行符。
                * 如果bw.write(line);的话,copy文件中后的内容是 全部都显示在一行的,不会换行输出的。
                * 是BufferedWriter中的一个方法。
                    * newLine()是一个可以跨平台的换行符号: “\r\n”
                        * bw.write("\r\n"); 只支持win系统
                        * bw.newLine(); 跨平台,在任何操作系统中都可以进行换行操作。 
                            *  用于对写出数据进行换行操作(和源文件数据格式一致)。 跨平台的换行写法。 建议使用这个
        
newLine() 与 "\r\n"的区别:
    * newLine() 是跨平台的换行写法。
    * "\r\n" 只支持win系统的换行。


g.
    将文本反转
         * 将一个文本文档上的文本反转,第一行和倒数第一行交换,第二行和倒数第二行交换
        * 分析:
            * 创建输入输出流,因为读写的是一行。使用会使用到readLine,即缓存流
            * 创建集合对象,用于临时存储读到的数据。
            * 将读到的数据存储到集合中 
            * 倒着遍历集合,将数据写到文件上
            * 关流

        * 代码

//        1. 创建输入输出流,因为读写的是一行。使用会使用到readLine,即缓存流
        BufferedReader br = new BufferedReader( new FileReader("将文本反转.txt"));
        BufferedWriter bw = new BufferedWriter( new FileWriter("reverse_将文本反转.txt"));//该文件没有,系统会自动创建
        
//        2. 创建集合对象,用于临时存储读到的数据。
        ArrayList<String> arrayList = new ArrayList<>();
    
//        3. 将读到的数据存储到集合中 
        String len;
        while( ( len = br.readLine()) != null){
//            将其存储到集合中
            arrayList.add(len);
        }
        
//        4. 倒着遍历集合,将数据写到文件上
        for(int i = arrayList.size()-1;  i >= 0;  i--){
            bw.write(arrayList.get(i)); // 获取到每一个集合元素,将其写入到文件中
            bw.newLine();
        }
        
//        5. 关流
        br.close();
        bw.close();

流对象尽量完开,早关。
    * 什么时候用,什么时候创建
    * 用完就关


h.
    LineNumberReader
        概述
            * 跟踪行号的缓冲字符输入流
            * 是BufferedReader的子类
            * getLineNumber(); 可以获取当前行号
            * setLineNumber(); 可以设置当前行号
            
        代码
         LineNumberReader lnr = new LineNumberReader(new FileReader("将文本反转.txt"));
         String line;
         lnr.setLineNumber(50);  // 从51的行号开始写入。
         while( ( line = lnr.readLine()) !=null){
             System.out.println(lnr.getLineNumber()+":"+line);
         }
         lnr.close();


i.
    装饰设计模式
        设计模式概述
            * 最初来自建筑行业,如中式餐厅 和 西式餐厅 的不同
            * 实际开发过程中,我们发现代码都具备一定的相似性,所以我们把这些向上的内容经过不断向上抽象,最终形成一种模型,按照这种模型做出来的东西就是实现了某些规则或具备某些特性,这种模型就是设计模式
            
            
        装饰模式
            * 将功能不强的代码通过包装,增强代码功能
        好处
            * 耦合性不强,被装饰的类变化 与 装饰类的变化无关
            * 因为不是继承关系,子类不会随着父类的改变而改变
        分类
            * 创建型
                * 需要创建对象。如:单例设计模式,共存模式
            * 结构型
                * 维护对象与对象之间的关系,对功能进行扩张。如:装饰者模式
            * 行为型    
                * 对象能做什么事情。如:适配器设计模式,模板方法实际模式

        过程
            1. 首先先获取被装饰类的引用
            2. 在装饰类的构造方法中传入被装饰类的对象
            3. 对原有类功能进行升级
        
        代码
        public class 装饰设计模式 {
            public static void main(String[] args) {
                HeiMaStudent hms = new HeiMaStudent(new Student());
                hms.code();
           }
        }
    
        // 定义接口
        interface Coder{
            public void code();
        }
    
        // 1. 大学生
        class Student implements Coder{
        
            @Override
            public void code() {
                System.out.println("大学生在学校学 JavaEE");
                System.out.println("大学生在学校学 Anroid");
            }
        }

        // 2. 对学生进行包装 -- 功能的扩张
        public class HeiMaStudent implements Coder{
            // 获取被装饰类的引用
            private Student s;
            // 在构造方法中传入被装饰类的对象
            public HeiMaStudent(Student stu){
                this.s = stu;
            }
        // 3. 对原有功能进行升级
        @Override
        public void code() {
            s.code();
            System.out.println("经过培训后 Java三大框架");
            System.out.println("经过培训后 数据结构");
            System.out.println("经过培训后 面试要点");
            }
        }


g.
    使用指定的码表读写【字符】
        概述
            * FileReader是使用默认码表进行读取字符文件
            * 如果需要指定码表进行读取,那么就要使用InputStreamReader(字节流,编码表);
                * 第一个参数:为什么使用字节流而不是字符流?
                    * 因为字符流中有一个内置GBK码表,会将字节转字符,如果用gbk码表转utf-8码表内容会乱码,所以使用字节流来读。
                * 第二个参数:
                    * 使用什么码表来读取
                    * 使用什么码表来写出

            * FileWriter使用默认码表进行写入字符文件
            * 如果需要指定码表进行写入,那么就使用OutputStreamWriter(字节流,编码表);
            
        转换流由来
            * 字符流与字节流之间的桥梁,方便了字符流与字节流之间的操作。    
            * 当字节流中的数据都是字符时,转成字符流操作更为高效。        

        转换流
            * InputStreamReader   字节流通向字符流的桥梁 解码的过程,给程序员看
            * OutputStreamWriter 字符流通向字节流的桥梁  编码的过程,给机器看
        

        过程
            * utf8.txt - FileInputStrem - InputStreamReader - BufferedReader - int r(程序) - BufferedWriter - OutputStreamWriter - FileOutputStream - gbk.txt

        最高效的写法
        private static void demo03_使用Buffered进行不同码表之间的文件读写()throws IOException {
        // 指定utf-8码表 读字符。因为原先是默认码表GBK 读 UTF-8.txt的utf-8码表内容,所以读出来会乱码,所以现在指定utf-8码表读去utf-8文件
        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("UTF-8.txt"), "utf-8"));
    
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("GBK.txt"),"gbk"));
        
        int r;
        while( (r = br.read()) != -1){
            bw.write(r);
        }
        br.close();
        bw.close();

h.
    获取文本上每个字符出现次数 【面试题】
        获取一个文本上每个字符出现的次数,将结果写在times.txt上

    试用版软件
        当我们下载一个试用版软件,没有购买正版的时候,每执行一次就会提醒我们
        还有多少次使用机会用学过的IO流知识,模拟试用版软件,试用10次机会,执行
        一次就提示一次您还有几次机会,如果次数到了提示请购买正版    
    分析
        * 

i.
    递归
        概述
            * 自己调用自己
        弊端
            * 如果递归次数过多,容易栈内存溢出。
                * 内存溢出,会有异常出现。
            * 为什么有时候没有异常,但结果为0,表示超出int取值范围。
        好处 
            * 不用知道循环次数。
            
        构造方法不可以用递归调用,因为会无限调用自己的构造函数
        
        递归调用有返回值吗?
         * 可以有,也可以没有。

        
        案例
            * 5的阶乘   
            * 5 * 4 * 3 * 2 * 1
        代码
        public static int fun(int num){
        if(num == 1){
            return 1;
        }else{
            return num * fun(num -1);  
            // 5 * 4
            // 4 * 3
            // 3 * 2
            // 2 * 1
        }

    递归练习
        * 从键盘输入接收一个文件夹路径,打印出该文件夹下所有的.java文件名


【面试题】
字符高效率中有哪些特殊方法?
    BufferedReader中有readLine(); 每次读取一个文本行
    BufferedWriter中有newLine(); 换行输出(适用各个版本)

【无论字节输出流 还是 字符输出流都一个特点:当构造参数没有写第二个参数true时,每次运行都会将输出流的文件情况后,再重新写入数据。 所以输入流 和输出流再配合时候的时候,尽量两个不要写在一起,用到哪个流就再写哪个流;】