【JAVA基础】22 IO流

1. IO流概述及其分类

  • 概念

    • IO流用来处理设备之间的数据传输
    • Java对数据的操作是通过流的方式
    • Java用于操作流的类都在IO包中
  • 按流向分为两种:
    • 输入流
    • 输出流
  • 按操作类型分为两种:
    • 字节流 : 字节流可以操作任何数据,因为在计算机中任何数据都是以字节的形式存储的
    • 字符流 : 字符流只能操作纯字符数据,比较方便。
  • IO流常用父类
    • 字节流的抽象父类
      • java.io.InputStream
      • java.io.OutputStream
    • 字符流的抽象父类
      • java.io.Reader
      • java.io.Writer
  • IO程序书写
    • 使用前,导入IO包中的类
    • 使用时,进行IO异常处理
    • 使用后,释放资源

 

2. IO流:FileInputStream

java.io.FileInputStream

  • 构造方法:
    • FileInputStream(File file)   通过打开一个实际文件的连接来创建一个FileInputStream,该文件通过文件系统中的File对象file来指定
    • FileInputStream(FileDescriptor fdObj)   通过使用文件描述符fdObj创建一个FileInputStram,该文件描述符表示到文件系统中某个实际文件的现有连接
    • FileInputStream(String name)   通过打开一个到实际文件的连接来创建一个FileInputStream,该文件通过文件系统中的路径名name指定。
  • 方法:
    • int available()  返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数
    • void close()  关闭此文件输入流,并释放与此有关的所有系统资源
    • protected void finalize()   确保不在引用文件输入流时调用其close方法
    • FileChannel getChannel()    返回与此文件输入流有关的唯一FileChannel对象
    • FileDescriptor getFD()   返回表示到文件系统中实际文件的连接FileDescriptor对象,该文件系统正被此FileInputStream使用
    • int read()    从此输入流中读取一个数据字节
    • int read(byte[] b)   从此输入流中将最多b.length个字节的数据读入一个byte数组中
    • int read(byte[] ,int off, int len)   从此输入流中将最多len个字节的数据读入到byte数组中
    • int skip(long n)   从输入流中跳过并丢弃n个字节的数据
  • 方法演示:
    • read()  一次读取一个字节
      • package com.heima.stream;
        
        import java.io.File;
        import java.io.FileInputStream;
        import java.io.IOException;
        
        public class Demo1_FileInputStream {
        
            /**
             * @param args
             * @throws IOException,FileNotFoundException 
             */
            public static void main(String[] args) throws IOException {
                File file = new File("xxx.txt"); 
                if(!file.exists()) {
                    file.createNewFile();  // 抛出IOException。FileNotFoundException是IOException的子类,所以只需要抛出IOException即可
                }
                
                FileInputStream fis = new FileInputStream("xxx.txt");   //创建一个文件输入流对象,并关联xxx.txt // 抛出FileNotFoundException异常
                
                int b;
                while((b = fis.read()) != -1) {  //文件的结束标记是-1。将每次读到的字节赋值给b并判断是否是-1。
                    System.out.println(b); 
                }
                fis.close();  //关闭流释放资源
            }
        
        }
        View Code
      • 问题:read()方法读取的是一个字节,为什么返回值是int,而不是byte?
      • 因为字节输入流可以操作任意类型的文件,比如:图片、音频等,这些文件底层都是以二进制形式的存储的,如果每次读取都返回byte,有可能在读到中间的时候遇到111111111,那么,这111111111是byte类型的-1,我们的程序是遇到-1就会停止不读了,后面的数据就读不到了,所以在读取的时候用int类型接收,如果111111111会在其前面补上24个0凑足4个字节,那么byete类型的-1就变成int类型的255了,这样可以保证整个数据读完,而结束标记的-1就是int类型。
      • 示例:
        • 一个字节:1000 0001 是byte类型的-1的源码,其反码为1111 1110, -1的补码为11111111。
        • 通过转换为int类型,自动在前面加上24个0,即00000000 00000000 00000000 11111111,表示int类型的255。可以避免程序终止。
        • 在写入文件写入过程中,通过FileOutputStream中的write()写入时,一次写出一个字节,会将前面的24个0去掉。

 

3. IO流:FileOutputStream

java.io.FileOutputStream

  • 构造方法:
    • FileOutputStream(File file)   创建一个向指定File对象表示的文件中,写入数据的文件输出流
    • FileOutputStream(File file, boolean append)  创建一个向指定File对象表示的文件中写入数据的文件输出流
    • FileOutputStream(FileDescriptor fdObj)   创建一个向指定文件描述符处写入数据的输出文件流,该文件描述符表示到一个文件中的某个实际文件的现有连接。
    • FileOutputStream(String name)   创建一个向具有指定名称的文件中写入数据的输出文件流。
    • FileOutputStream(String name, boolean append)  创建一个向具有指定name的文件中写入数据的输出文件流
  • 方法:
    • void close()  关闭此文件输出流,并释放与此流相关的所有系统资源。
    • protected void finalize()  清理到文件的连接,并确保不再引用此文件输出流时调用此流的close方法。
    • FileChannel getChannel()    返回与此文件输出流有关的唯一FileChannel对象
    • FileDescriptor getFD()  返回与此流有关的文件描述符
    • void write(byte[] b)  将b.length 个字节从指定byte数组写入此文件输出流中
    • void write(byte[] b, int off, int len)  将指定byte数组中从偏移量off开始的len个字节写入此文件输出流。
    • void write(int b)  将指定字节写入此文件输出流。
  • 方法演示:
    • write()一次写出一个字节
      • package com.heima.stream;
        
        import java.io.FileNotFoundException;
        import java.io.FileOutputStream;
        import java.io.IOException;
        
        public class Demo2_FileOutputStream {
        
            /**
             * @param args
             * @throws IOException 
             */
            public static void main(String[] args) throws IOException {
                // 如果文件不存在,则创建该文件 
                // 如果文件不存在,则抛出 FileNotFoundException
                FileOutputStream fos = new FileOutputStream("yyy.txt"); 
                
                for (int i = 0; i < 10; i++) {
                    int v = (97 + i);
                    fos.write(v);
                }
                
                fos.close();
                
                
        
            }
        
        }
        View Code
      • 虽然写出的是一个int数,但是在写出的时候会将前面的24个0去掉,所以写出的一个byte。
    • 追加写入
      • FileOutputStream fos = new FileOutputStream("yyy.txt", true);
      • 如果没有“yyy.txt”,会创建一个“yyy.txt”文件;如果“yyy.txt”文件存在,则会在文件内容后面追加(若append的参数值为false,则会清空后再写入)

 

4. IO流:拷贝

  • 一次一个字节的读写——拷贝图片、音频文件
    • package com.heima.stream;
      
      import java.io.File;
      import java.io.FileInputStream;
      import java.io.FileNotFoundException;
      import java.io.FileOutputStream;
      import java.io.IOException;
      
      public class Demo3_Copy {
      
          /**
           * @param args
           * @throws IOException 
           */
          public static void main(String[] args) throws IOException {
              File pic = new File("E:\1.jpg");
              if(pic.exists()) {
                  FileInputStream fis = new FileInputStream(pic);
                  FileOutputStream fos = new FileOutputStream("E:\copy.jpg");
                  
                  int b;
                  while((b = fis.read()) != -1) {
                      fos.write(b);
                  }
                  fis.close();
                  fos.close();
              }
          }
      }
      View Code
    • FileInputStream读取,FileOutputStream写出
    • 原理:字节流一次读写一个字节,复制音频图片;
    • 弊端:效率太低。
  • 字节数组拷贝——available() 方法
    • int read(byte[] b)  一次读取一个字节数组
    • void write(byte[] b)   一次写出一个字节数组
    • int available()    获取读的文件所有的字节个数。返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数
      • package com.heima.stream;
        
        import java.io.File;
        import java.io.FileInputStream;
        import java.io.FileOutputStream;
        import java.io.IOException;
        
        public class Demo4_Available {
        
            /**
             * @param args
             * @throws IOException 
             */
            public static void main(String[] args) throws IOException {
                File pic = new File("E:\2.jpg");
                if(pic.exists()) {
                    FileInputStream fis = new FileInputStream(pic);
                    FileOutputStream fos = new FileOutputStream("E:\copy2.jpg");
                    
                    byte[] arr = new byte[fis.available()]; // 创建和文件大小一样的字节数组
                    fis.read(arr);
                    fos.write(arr);
                    fis.close();
                    fos.close();
                }
            }
        
        }
        View Code
      • 弊端:有可能会有内存溢出,不推荐使用
  • 定义小数组拷贝
    • write(byte[] b)
    • write(byte[] b, int off, int len) 写出有效的字节个数

 

5. 定义小数组拷贝

  •  定义小数组的标准格式
    • 案例演示
      • package com.heima.stream;
        
        import java.io.FileInputStream;
        import java.io.FileOutputStream;
        import java.io.IOException;
        
        public class Demo5_SmallArrayCopy {
        
            /**
             * @throws IOException 
             */
            public static void main(String[] args) throws IOException {
                FileInputStream fis = new FileInputStream("xxx.txt");
                FileOutputStream fos = new FileOutputStream("yyy.txt");
                byte[] arr = new byte[2];
                int len;
                while((len = fis.read(arr)) != -1) {
                    fos.write(arr,0,len);
                }
                fis.close();
                fos.close();
            }
        
        }
        View Code
      • write(byte[] b)  写入b数组大小的内容。弊端:如果输出文件中原有的数据字节大小大于b的大小,多出大小的字节内容不会清空。用write(byte[] b, int off, int len)则会避免这种问题。
      • write(byte[] b, int off, int len)   写入b数组大小的内容,从off偏移量开始写,写入长度为len。可以避免输出流指定的文件中的内容未完全覆盖。
    • 标准格式:
      • package com.heima.stream;
        
        import java.io.FileInputStream;
        import java.io.FileOutputStream;
        import java.io.IOException;
        
        public class Demo5_SmallArrayCopy {
        
            /**
             * @throws IOException 
             */
            public static void main(String[] args) throws IOException {
                FileInputStream fis = new FileInputStream("E:\1.jpg");
                FileOutputStream fos = new FileOutputStream("E:\copy_1.jpg");
                byte[] arr = new byte[1024 * 8];  //自定义字节数组
                int len;
                while((len = fis.read(arr)) != -1) {
                    fos.write(arr,0,len);   //写出字节数组写出有效个字节个数
                }
                fis.close();
                fos.close();
            }
        
        }
        View Code

6. BufferedInputStream和BufferedOutputStream

  • BufferedInputStream
    • 构造方法
      • BufferedInputStream(InputStream in)  创建一个BufferedInputStream,并保存其参数,即输入流in,以便将来使用。
      • BufferedInputStream(InputStream in, int size)   创建具有指定缓冲区大小的BufferedInputStream,并保存其参数,即输入流in,以便将来使用。
    • 方法:
      • int available()   返回可以从此输入流读取(或跳过)、且不受此输入流接下来的方法调用阻塞的估计字节数
      • void close()    关闭此输入流,并释放该流的所有系统资源
      • void mark()    参见InputStream的mark方法的常见规定
      • boolean markSupported()   测试此输入流是否支持mark和reset方法
      • int read()     
      • int read(byte[] b, int off, int len)  从此字节输入流中给定偏移量处开始将各字节读取到指定的byte数组中
      • void reset()    参见InputStream中的reset方法的常规规定
      • long skip(long n)
  • BufferedOutputStream
    • 构造方法:
      • BufferedOutputStream(OutputStream out)   创建一个新的缓冲输出流,以将数据写入指定的底层输出流  
      • BufferedOutputStream(OutputStream out, int size)   创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流
    • 方法:
      • void flush()   刷新此缓冲的输出流
      • void write(byte[] b, int off, int len)     将指定byte数组中从偏移量off开始的len个字节写入此缓冲的输出流   
      • void write(int b)   将指定的字节写入此缓冲的输出流
  • 使用BufferedInputStream和BufferedOutputStream包装文件输入流和输出流的拷贝
    • 示例:
      • package com.heima.stream;
        
        import java.io.BufferedInputStream;
        import java.io.BufferedOutputStream;
        import java.io.FileInputStream;
        import java.io.FileOutputStream;
        import java.io.IOException;
        
        public class Demo6_BufferCopy {
        
            /**
             * @throws IOException 
             */
            public static void main(String[] args) throws IOException {
                FileInputStream fis = new FileInputStream("E:\1.jpg");  // 创建输入流对象
                FileOutputStream fos = new FileOutputStream("E:\buffer_copy_1.jpg");  // 创建输出流对象
                BufferedInputStream bis = new BufferedInputStream(fis); // 创建缓冲区输入流对象,对输入流进行包装,让其更加强大
                BufferedOutputStream bos = new BufferedOutputStream(fos);  // 创建缓冲区输出流对象
                
                int b;
                while((b = bis.read()) != -1) {
                    bos.write(b);
                }
                
                // 只需要关闭包装后的缓冲区的对象
                bis.close();  
                bos.close();
            }
        
        }
        View Code
      • 效果:速度非常快
    • 缓冲思想:
      • 字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多;
      • BufferedInputStream中内置了一个缓冲区,有参数defaultBufferSize=8192,从BufferedInputStream中读取一个字节时,会一次性从文件中读取8192个,存在缓冲区,返回给程序一个;程序再次读取时,就不用找文件了,直接从缓冲区获取,直到缓冲区中所有的都被使用过,从重新从文件中读取8192个;
      • BufferedOutputStream中也内置了一个缓冲区(数组),默认指定了写入数组大小为8192;程序向流中写出字节时,不会直接写到文件,而是先写到缓冲区,直到缓冲区写满,BufferedOutputStream才会把缓冲区中的数据一次性写到文件里
      • 这是加入了数组这样的缓冲区效果;
      • java本身在设计的时候,也考虑了这样的设计思想(装饰设计模式),所以提供了字节缓冲区流。
    • 小数组的读写和 带Buffered的读取哪个更快?
      • 定义小数组如果是8192个字节大小和Buffered比较的话,定义小数组会略胜一筹,因为读和写操作是同一个数组,而Buffered操作的是两个数组。
  • flush和close方法的区别
    • flush() 方法
      • 用来刷新缓冲区的,刷新后可以再次写出
    • close()方法
      • 用来关闭释放资源的,如果是带缓冲区的流对象的close()方法,不但会关闭流,还会再关闭流之前刷新缓冲区,关闭后不能再写出
    • package com.heima.stream;
      
      import java.io.BufferedInputStream;
      import java.io.BufferedOutputStream;
      import java.io.FileInputStream;
      import java.io.FileOutputStream;
      import java.io.IOException;
      
      public class Demo7_FlushCloseDiff {
      
          /**
           flush() 和 close() 方法的区别
           * @throws IOException 
           */
          public static void main(String[] args) throws IOException {
              BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\1.jpg"));
              BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("E:\copy1.jpg"));
              
              int b;
              while((b = bis.read()) != -1) {
                  bos.write(b);
              }
              
              /*
               * 若没有flush()或close()方法, 那么最后的结果,还有最后一次的读的结果,写到了bos的缓冲区数组中去了,
               * 看结果,copy的结果可能会被原来的结果小一点。
               * 通过flush()方法,可以保证最后读取的输出流中在缓冲区的结果写到硬盘上,保证数据的一致性。
               */
              bos.flush();  // 在没有close()方法的时候,用flush()方法,可以刷新缓冲区,保证数据的完整一致
      //        bis.close();
      //        bos.close();
          }
      
      }
      View Code

7. 字节流读写中文

  • 字节流读取中文的问题
    • 字节流在读中文的时候有可能会读到半个中文,造成乱码
  • 字节流写出中文的问题
    • 字节流直接操作的字节,所以写出中文必须将字符串转换成字节数组
    • 写出回车换行write(" ".getBytes());
  • package com.heima.stream;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    public class Demo8_Chinese {
    
        /**
         * @throws IOException 
        
         */
        public static void main(String[] args) throws IOException {
            FileInputStream fis = new FileInputStream("xxx.txt");  //正文内容:你好,我们是可爱的中国人(Chinese)。
            FileOutputStream fos = new FileOutputStream("yyy.txt");
            
            byte[] test = "我们".getBytes();
            for (byte t : test) {
                System.out.println(t);
            }
            byte[] b = new byte[2];
            int len;
            fos.write("这是用来测试String的getBytes方法的".getBytes());
            fos.write("
    ".getBytes());
            while((len = fis.read(b)) != -1) {
                fos.write(b);
            }
            fos.write("这是用来测试String的getBytes方法的".getBytes());
            fis.close();
            fos.close();
            /*
             * 这是用来测试String的getBytes方法的
             * 你好,我们是可爱的中国人(Chinese)。≌馐怯美床馐許tring的getBytes方法的
             */
            // 数组设置的大小为2,中文在GBK编码下,一个中文是2个字节,一个英文是1个字节,中英混杂会产生乱码问题。
        }
    
    }
    View Code
  • 中文的读取和写入更多可以使用字符流的方式。

 

8. 流的标准处理异常

  • 流的标准处理异常代码:1.16版本及以前
    • try finally嵌套
    • package com.heima.stream;
      
      import java.io.FileInputStream;
      import java.io.FileNotFoundException;
      import java.io.FileOutputStream;
      import java.io.IOException;
      
      public class Demo9_Exception {
      
          /**
           * @param args
           * @throws IOException 
           */
          public static void main(String[] args) throws IOException {
              FileInputStream fis = null;
              FileOutputStream fos = null;
              try {
                  fis = new FileInputStream("xxx.txt");
                  fos = new FileOutputStream("yyy.txt");
                  
                  int b;
                  while((b = fis.read()) != -1) {
                      fos.write(b);
                  }
              } finally {
                  try {    // try finally的嵌套,目的是为了能关掉一个就关掉一个
                      if(fis != null) {
                          fis.close();
                      }
                  } finally {
                      if(fos != null) {
                          fos.close();
                      }
                  }
              }
          }
      }
      View Code
  • 流的标准处理异常代码:1.7版本
    • try close嵌套
    • package com.heima.stream;
      
      import java.io.FileInputStream;
      import java.io.FileOutputStream;
      import java.io.IOException;
      
      public class Demo10_Exception {
      
          /**
           * @throws IOException 
           */
          public static void main(String[] args) throws IOException {
              try(
                  FileInputStream fis = new FileInputStream("xxx.txt");
                  FileOutputStream fos = new FileOutputStream("yyy.txt");
              ){
                  int b;
                  while((b = fis.read()) != -1) {
                      fos.write(b);
                  }
              } 
          }
      }
      View Code
    • 原理:
      • 在try()中创建的流对象,必须实现了AutoCloseable这个接口,如果实现了,在try后面的{}(读写代码)执行后就会自动调用流对象的close方法将其关掉并释放所有相关系统资源
      • 能在try()块写入的方法,都必须是实现了AutoCloseable这个接口的。

 

9. 图片加密

  • 通过异或^ 方法,加密,解密(一个数异或2次,等于其本身)
  • package com.heima.test;
    
    import java.io.BufferedInputStream;
    import java.io.BufferedOutputStream;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    public class Test1 {
        public static void main(String[] args) throws IOException {
            // 加密写入
            demo1(); 
            
            // 解密
            demo2();
            
        }
    
        public static void demo1() throws FileNotFoundException, IOException {
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\1.jpg"));
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("E:\copy.jpg"));
            
            int b;
            while((b = bis.read()) != -1) {
                bos.write(b ^ 123 );  // 通过异或方法加密
            }
            bis.close();
            bos.close();
        }
        
        public static void demo2() throws FileNotFoundException, IOException {
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\copy.jpg"));
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("E:\copy2.jpg"));
            
            int b;
            while((b = bis.read()) != -1) {
                bos.write(b ^ 123 );  // 异或两次,就可以获得其本身
            }
            bis.close();
            bos.close();
        }
    
    }
    View Code

10. 拷贝文件练习

  • 在控制台录入文件的路径,将文件拷贝到当前项目下
    • package com.heima.test;
      
      import java.io.BufferedInputStream;
      import java.io.BufferedOutputStream;
      import java.io.File;
      import java.io.FileInputStream;
      import java.io.FileOutputStream;
      import java.io.IOException;
      import java.util.Scanner;
      
      public class Test2 {
      
          /**
          在控制台录入文件的路径,将文件拷贝到当前项目下
           * @throws IOException 
           */
          public static void main(String[] args) throws IOException {
              Scanner sc = new Scanner(System.in);
              System.out.println("请输入一个文件的路径名称:");
              String filePath = sc.nextLine();
              File file = new File(filePath);
              if(file.exists()) {
                  BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
                  BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file.getName()));
                  
                  int b;
                  while((b = bis.read()) != -1) {
                      bos.write(b);
                  }
                  bis.close();
                  bos.close();
                  
              }
          }
      
      }
      View Code
  • 将键盘录入的数据拷贝到当前项目的text.txt文件中,键盘录入数据,当遇到quit时就退出
    • package com.heima.test;
      
      import java.io.BufferedOutputStream;
      import java.io.FileNotFoundException;
      import java.io.FileOutputStream;
      import java.io.IOException;
      import java.util.Scanner;
      
      public class Test3 {
      
          /**
           将键盘录入的数据拷贝到当前项目的text.txt文件中,键盘录入数据,当遇到quit时就退出
           * @throws IOException 
           */
          public static void main(String[] args) throws IOException {
              Scanner sc = new Scanner(System.in);
              BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("text.txt"));
              
              boolean flag = false;
              while(!flag) {
                  System.out.println("请随意输入您想要输入的内容:");
                  String content = sc.nextLine();
                  
                  if("quit".equals(content)) { // 注意一定要用equals方法, ==判断是基本数据类型的值,判断引用数据类型的地址值,String类型的引用数据类型
                      flag = true;
                  
                  } else {
                      bos.write(content.getBytes());
                      bos.write("
      ".getBytes());
                  }
              }
              
              bos.close();
              
          }
      
      }
      View Code

11. 字符流FileReader,FileWriter

  •  字符流是什么?
    • 字符流是可以直接读写字符的IO流
    • 字符流读取字符,就要先读取到字节数据,然后转为字符;如果要写出字符,需要把字符转为字节再写出。
  • FileReader
    • java.io.FileReader
    • FileReader类的read()方法可以按照字符大小读取
    • 构造方法:
      • FileReader(File file)   在给定从中读取数据的File的情况下,创建一个新的FileReader
      • FileReader(FileDescriptor fd)   在给定从中读取数据的FileDescriptor的情况下,创建一个新的FileReader
      • FileReader(String fileName)    在给定从中读取数据的文件名的情况下创建一个新的FileReader
    • 方法:
      • 从类java.ip.InputStreamReader中继承 close, getEncoding, read, read, ready
      • 从类java.io.Reader中继承mark, markSupported, read, read, reset, skip
      • 从类java.lang.Object继承的方法 clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    • 示例:
      • package com.heima.reader;
        
        import java.io.File;
        import java.io.FileReader;
        import java.io.FileWriter;
        import java.io.IOException;
        
        public class Demo1_FileReader {
        
            public static void main(String[] args) throws IOException {
                File file = new File("xxx.txt");
                if(!file.exists()) {
                    file.createNewFile();
                } else {
                    FileWriter fw = new FileWriter(file);
                    fw.write("第一行");
                    fw.close();
                }
                
                FileReader fr = new FileReader(file);
                
                int ch;
                while((ch = fr.read()) != -1) {
                    System.out.println((char)ch);
                }
                
                fr.close();
            }
        }
        View Code
  • FileWriter
    • java.io.FileWriter
    • FileWriter类的write()方法可以自动把字符转为字节写出
    • 构造方法:
      • FileWriter(File file)  根据给定的File对象构造一个FileWriter对象
      • FileWriter(File file, boolean append)   根据给定的File对象构造一个FileWriter对象
      • FileWriter(FileDescriptor fd)   构造与某个文件描述符相关联的FileWriter对象
      • FileWriter(String fileName)   根据给定的文件名构造一个FileWriter对象
      • FileWriter(String fileName, boolean  append)   根据给定的文件名以及指示是否附加写入数据的boolean值来构造FileWriter对象
    • 方法:
      • 从类java.io.OutputStreamWriter继承的方法:close, flush, getEncoding, write, write, write
      • 从类java.io.Writer继承的方法:append, append, append, write, write
      • 从类java.lang.Object继承的方法:clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    • 示例:
      • package com.heima.reader;
        
        import java.io.FileWriter;
        import java.io.IOException;
        
        public class Demo2_FileWriter {
        
            /**
             * @throws IOException 
             */
            public static void main(String[] args) throws IOException {
                FileWriter fw = new FileWriter("xxx.txt",true);
                fw.write("第一行");
                fw.close();
            }
        
        }
        View Code
  • 字符流的拷贝
    • package com.heima.reader;
      
      import java.io.FileReader;
      import java.io.FileWriter;
      import java.io.IOException;
      
      public class Demo3_Copy {
      
          public static void main(String[] args) throws IOException {
              FileReader fr = new FileReader("xxx.txt"); 
              FileWriter fw = new FileWriter("yyy.txt");
      
              int ch;
              while((ch = fr.read()) != -1) {
                  fw.write(ch);
              }
              fr.close();
              fw.close();  
              // FileWriter中有一个2048字节大小的缓冲区;
              // 如果不close关流,则会留有最后一部分数据在缓冲区中;
              // 如果close关流后,会刷新缓冲区,再关闭
          }
      
      }
      View Code
  • 什么情况下使用字符流
    • 字符流也可以拷贝文本文件,但不推荐使用。因为读取时,会把字节转为字符,写出时还要把字符转回字节。
    • 程序需要读取一段文本,或者需要写出一段文本的时候可以使用字符流,即:只读 或 只写 的情况下使用;
      • 读取的时候是按照字符的大小读取的,不会出现半个中文;
      • 写出的时候可以直接将字符串写出,不用转换为字节数组。
  • 字符流是否可以拷贝非纯文本的文件
    • 不可以拷贝非纯文本的文件
    • 因为在读的时候会将字节转换为字符,在转换过程中,可能找不对对应的字符,就会用"?"代替,写出的时候会将字符转换成字节写出去
    • 如果是"?",直接写出,这样写出之后的文件就乱了,看不了了

12. 自定义字符数组的拷贝

  • package com.heima.reader;
    
    import java.io.FileReader;
    import java.io.FileWriter;
    import java.io.IOException;
    
    public class Demo4_Copy {
    
        /**
         * @throws IOException 
         */
        public static void main(String[] args) throws IOException {
            FileReader fr = new FileReader("xxx.txt"); 
            FileWriter fw = new FileWriter("yyy.txt");
    
            int len;
            char[] arr = new char[1024*8];  // 自定义1024*8 大小的字符数组     
            while((len = fr.read(arr)) != -1) {    
                fw.write(arr, 0, len);         
            }
            
            // 关闭流
            fr.close();      
            fw.close();
        }
    
    }
    View Code

13. 带缓冲的字符流

  • java.io.BufferedReader
    • 构造方法:
      • BufferedReader(Reader in)   创建一个使用默认大小缓冲区的缓冲字符输入流
      • BufferedReader(Reader in, int sz)  创建一个使用指定大小输入缓冲区的缓冲字符输入流
    • 方法:
      • void close()   关闭该流并释放与之关联的所有资源
      • void mark(int readAheadLimit)  标记流中的当前位置
      • boolean markSupported()   判断此流是否支持mark()操作(它一定支持)
      • int read()  读取单个字符;如果达到文件流末尾,返回-1
      • int read(char[] cbuf, int off, int len)   将字符读入数组的某一部分
      • String readLine()  读取一个文本行;如果达到文件流末尾,返回null
      • boolean ready()   判断此流是否已准备好被读取
      • void reset()   将流重置到最新的标记
      • long skip(long n)   跳过字符
      • 从类java.io.Reader继承的方法 read,read
      • 从类java.lang.Object继承的方法 clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait,wait,wait
    • BufferedReader中的read()方法,读取字符时会一次读取若干字符到缓冲区,然后逐个返回给程序,降低读取文件的次数,提高效率
  • java.io.BufferedWriter
    • 构造方法:
      • BufferedWriter(Writer out)   创建一个使用默认大小输出缓冲区的缓冲字符输出流
      • BufferedWriter(Writer out, int sz)   创建一个使用给定大小输出缓冲区的新缓冲字符输出流
    • 方法:
      • void close()  关闭此流,但要先刷新它
      • void  flush()   刷新该流的缓冲
      • void newLine()  写入一个行分隔符,可跨平台,由系统line.separator定义
      • void write(char[] cbuf, int off, int len)   写入字符数组的某一部分
      • void write(int c)   写入单个字符
      • void write(String s, inf off, int len)  写入字符串的某一部分
    • BufferedWriter中的write()方法,写出字符时会先写到缓冲区,缓冲区写满时才会写到文件,降低写文件的次数,提高效率
  • 示例:
    • package com.heima.reader;
      
      import java.io.BufferedReader;
      import java.io.BufferedWriter;
      import java.io.FileReader;
      import java.io.FileWriter;
      import java.io.IOException;
      
      public class Demo5_Buffered {
      
          public static void main(String[] args) throws IOException {
              BufferedReader br = new BufferedReader(new FileReader("xxx.txt"));  
              BufferedWriter bw = new BufferedWriter(new FileWriter("yyy.txt")); 
      
              int ch;             
              while((ch = br.read()) != -1) {     
                  bw.write(ch);             }
              
              br.close();   
              bw.close();  
          }
      
      }
      View Code
  •  readLine() 和 newLine() 方法
    • BufferedReader 的readLine() 方法可以读取一行字符(不包含换行符号)
    • BufferedWriter的 newLine() 方法可以输出一个跨平台的换行符号" "
    • 示例
      • package com.heima.reader;
        
        import java.io.BufferedReader;
        import java.io.BufferedWriter;
        import java.io.FileReader;
        import java.io.FileWriter;
        import java.io.IOException;
        
        public class Demo6_Buffered {
        
            /**
             BufferedReader 的readLine() 方法可以读取一行字符(不包含换行符号)
             BufferedWriter的 newLine() 方法可以输出一个跨平台的换行符号"
        "
             * @throws IOException 
             */
            public static void main(String[] args) throws IOException {
                BufferedReader br = new BufferedReader(new FileReader("xxx.txt"));
                BufferedWriter bw = new BufferedWriter(new FileWriter("yyy.txt"));
                
                String line;
                while((line = br.readLine()) != null)  {
                    bw.write(line);
                    bw.newLine();  // 写入行分隔符 line.separator
                }
                
                br.close();
                bw.close();
            }
        
        }
        View Code
  • 将文本反转
    • 将一个文本文档上的文本反转,第一行和倒数第一行交换,第二行和倒数第二行交换
    • package com.heima.test;
      
      import java.io.BufferedReader;
      import java.io.BufferedWriter;
      import java.io.FileReader;
      import java.io.FileWriter;
      import java.io.IOException;
      import java.util.ArrayList;
      import java.util.List;
      
      public class Test1 {
      
          /**
           * 将一个文本文档上的文本反转,第一行和倒数第一行交换,第二行和倒数第二行交换
           * 
           * 分析: 1. 创建输入输出流,要一行一行读,用到readLine()方法,BufferedReader和BufferedWriter 2.
           * 创建集合对象 3. 将读到的数据存储到集合中 4. 倒着遍历,将数据写入到文件上 5. 关流
           * 
           * @throws IOException
           */
          public static void main(String[] args) throws IOException {
              BufferedReader br = new BufferedReader(new FileReader("xxx.txt"));
              
      
              List<String> list = new ArrayList<>();
              String line;
              while ((line = br.readLine()) != null) {
                  list.add(line);
              }
              br.close();
              
              BufferedWriter bw = new BufferedWriter(new FileWriter("yyy.txt"));
              for (int i = list.size() - 1; i >= 0; i--) {
                  System.out.println(i);
                  bw.write(list.get(i));
              }
      
              
              bw.close();
      
          }
      
      }
      View Code

14. LineNumberReader

  • java.io.LineNumberReader 
    • 跟踪行号的缓冲字符输入流
    • 是BufferedReader的子类,具有相同的功能,并且可以统计行号
  • 构造方法:
    • LineNumberReader(Reader in)    使用默认输入缓冲区的大小创建新的行编号reader
    • LineNumberReader(Reader in, int sz)    创建新的行编号reader,将字符读入给定大小的缓冲区
  • 方法:
    • int getLineNumber()   获得当前行号
    • void mark(int readAheadLimit)    标记该流中的当前位置
    • int read()     读取单个字符
    • int read(char[] cbuf, int off, int len)   将字符读入数组中的某一部分
    • String readLine()   读取文本行
    • void reset()    将该流重新设置为最新的标记
    • void setLineNumber(int lineNumber)   设置当前行号
    • long skip(long n)    跳过字符
  • 示例:
    • package com.heima.reader;
      
      import java.io.FileReader;
      import java.io.IOException;
      import java.io.LineNumberReader;
      
      public class Demo7_LineNumberReader {
      
          /**
           * @param args
           * @throws IOException 
           */
          public static void main(String[] args) throws IOException {
              LineNumberReader lnr = new LineNumberReader(new FileReader("xxx.txt"));
              String line;
              lnr.setLineNumber(100); // 设置行号
              while((line = lnr.readLine()) != null) {
                  System.out.print(lnr.getLineNumber() + ": ");
                  System.out.println(line);
                  /*
                   * 输出:
                   * 101:第一行  ---> 行号从101开始
                   * 102:第二行
                   */
              }
              
              lnr.close(); 
          }
      
      }
      View Code

15. 装饰设计模式

  • 示例:
    • package com.heima.reader;
      
      public class Demo8_Wrap {
      
          /**
          装饰设计模式
           */
          public static void main(String[] args) {
              HeimaStudent hs = new HeimaStudent(new Student());
              hs.code();
          }
      }
      
      // 所有继承接口Coder的类,都要重写code方法
      interface Coder {   
          public void code();
      }
      
      class Student implements Coder {
      
          @Override
          public void code() {
              System.out.println("JavaSE");
              System.out.println("JavaWeb");
          }
      }
      
      class HeimaStudent implements Coder {
          // 1. 获取被装饰类的引用  
          private Student s; 
          
          // 2. 在构造方法中传入被装饰类的对象
          public HeimaStudent(Student s) {
              this.s =s ;
          }
          
          // 3. 对原有的功能进行升级
          @Override
          public void code() {
              s.code();
              System.out.println("ssh");
              System.out.println("数据库");
              System.out.println("大数据");
              System.out.println("...");
          }
          
      }
      View Code
  • 好处:
    • 耦合性降低
      • 如果HeimaStudent继承Student类,重写code方法,耦合性太高,只要父类Student改变有点,HeimaStudent就会发生变化;
      • 而使用装饰设计模式,则会降低耦合性
    • 可选择性高
      • 如FileReader,可以选择BufferedReader对其进行包装,也可以选择LineNumberReader对其进行包装

16. 使用指定的码表读写字符

  • FileReader是使用默认码表读取文件,如果需要使用指定码表读取,可以使用InputStreamReader(字节流, 编码表)
    • java.io.InputStreamReader  是字节流通向字符流的桥梁
      • 构造方法:
        • InputStreamReader(InputStream in)   创建一个使用默认字符集的InputStreamReader
        • InputStreamReader(InputStream in, Charset cs)   创建使用给定字符集的InputStreamReader
        • InputStreamReader(InputStream in, CharsetDecoder dec)    创建使用给定字符集解码器的InputStreamReader
        • InputStreamReader(InputStream in, String charsetName)    创建使用指定字符集的InputStreamReader
      • 方法:
        • void close()   关闭该流并释放与之关联的所有资源
        • String getEncoding()    返回此流使用的字符编码的名称
        • int read()    读取单个字符
        • int read(char[] cbuf, int offset, int length)   将字符读入数组中的某一部分
        • boolean ready()   判断此流是否已经准备好用于读取
  • FileWriter是使用默认码表写出文件,如果需要使用指定码表写出,那么可以使用OutputStreamWriter(字节流, 编码表)
    • java.io.OutputStreamWriter  是字符流通向字节流的桥梁
      • 构造方法:
        • OutputStreamWriter(OutputStream out)   创建使用默认字符编码的OutputStreamWriter
        • OutputStreamWriter(OutputStream out, Charset cs)   创建使用给定字符集的OutputStreamWriter
        • OutputStreamWriter(OutputStream out, CharsetEncoder enc)    创建使用给定字符集编码器的OutputStreamWriter
        • OutputStreamWriter(OutputStream out, String charsetName)   创建使用指定字符集的OutputStreamWriter
      • 方法:
        • void close()   关闭此流,但要先刷新它
        • void flush()    刷新该流的缓冲
        • String getEncoding()    返回此流使用的字符编码的名称
        • void write(char[] cbuf, int off, int len)    写入字符数组的某一部分
        • void write(int c)     写入单个字符
        • void write(String str, int off, int len)     写入字符串的某一部分
  • package com.heima.reader;
    
    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.FileReader;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.io.UnsupportedEncodingException;
    
    public class Demo9_TransIO {
    
        /**
          从utf-8编码的文本文件读取数据,转换成GBK编码的文本文件中去
         * @throws IOException 
         */
        public static void main(String[] args) throws IOException {
            demo1();
            demo2();
            demo3();
        }
    
        public static void demo3() throws UnsupportedEncodingException,
                FileNotFoundException, IOException {
            // 通过BufferedXxxx,实现更高效的读和写
            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 c;
            while((c = br.read()) != -1) {
                bw.write(c);
            }
            br.close();
            bw.close();
        }
    
        public static void demo2() throws UnsupportedEncodingException,
                FileNotFoundException, IOException {
            InputStreamReader isr = new InputStreamReader(new FileInputStream("utf-8.txt"),"utf-8");
            OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk.txt"),"gbk");
            
            int c;
            while((c = isr.read()) != -1) {
                osw.write(c);
            }
            isr.close();
            osw.close();
        }
    
        public static void demo1() throws FileNotFoundException, IOException {
            // 用FileReader,使用默认编码表进行读出和写入,两个文件编码不同时,会出现乱码
            FileReader fr = new FileReader("utf-8.txt");
            FileWriter fw = new FileWriter("gbk.txt");
            
            int c;
            while((c = fr.read()) != -1) {
                fw.write(c);
            }
            fr.close();
            fw.close();
        }
    }
    View Code
  • 图解:

17. 练习:获取文本上字符出现的次数

  •  获取一个文本上每个字符出现的次数,将结果写在times.txt上
  • package com.heima.test;
    
    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.FileReader;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.util.TreeMap;
    
    public class Test2 {
        
        /*
         *  获取一个文本上每个字符出现的次数,将结果写在times.txt上
         *  
         *  分析:
         *  1. 创建带缓冲的输入流对象
         *  2. 创建双列集合Map对象 TreeMap对象(可排序)
         *  3. 将读取到的字符存在双列集合中:存储时要判断:
         *      如果包含这个键,就+1;
         *      如果不包含这个键,就创建这个键,值为1。
         *  4. 关闭输入流
         *  5. 创建带缓冲的输出流对象
         *  6. 将TreeMap的键值对写到times.txt中
         *  7. 关闭输入流
         */
        public static void main(String[] args) throws IOException {
            // 1. 创建带缓冲的输入流对象
            BufferedReader br = new BufferedReader(new FileReader("xxx.txt"));
            
            // 2. 创建双列集合Map对象 TreeMap对象(可排序)
            TreeMap<Character, Integer> tm = new TreeMap<>();
            
            // 3. 将读取到的字符存在双列集合中:存储时要判断:
            //    如果包含这个键,就+1;
            //  如果不包含这个键,就创建这个键,值为1。
            int c;
            while((c = br.read()) != -1) {
                char key = (char)c;
                /*if(!tm.containsKey(key)) {
                    tm.put(key, 1);
                } else {
                    tm.put(key, tm.get(key) + 1);
                }*/
                tm.put(key, !tm.containsKey(key) ? 1 : tm.get(key) + 1);
            }
            
            // 4. 关闭输入流
            br.close();
            
            // 5. 创建带缓冲的输出流对象
            BufferedWriter bw = new BufferedWriter(new FileWriter("times.txt"));
            for (Character k: tm.keySet()) {
                switch (k) {
                case '	':
                    bw.write("\t" + "=" + tm.get(k));
                    break;
                case '
    ':
                    bw.write("\r" + "=" + tm.get(k));
                    break;
                case '
    ':
                    bw.write("\n" + "=" + tm.get(k));
                    break;
                default:
                    bw.write(k + "=" + tm.get(k));
                    break;
                }
                bw.newLine();
            }
            bw.close();
        }
    }
    View Code 

18. 练习:试用版软件

  • 当我们下载一个实用版软件,没有购买正版的时候,每执行一次就会提醒我们还有多少次使用机会。
  • 用学过的IO流知识,模拟试用版软件,试用10次机会,执行一次就提示一次您还有几次机会,如果次数到了提示请购买正版。
  • package com.heima.test;
    
    import java.io.BufferedReader;
    import java.io.FileReader;
    import java.io.FileWriter;
    import java.io.IOException;
    
    public class Test3 {
    
        /**
         当我们下载一个实用版软件,没有购买正版的时候,每执行一次就会提醒我们还有多少次使用机会。
        用学过的IO流知识,模拟试用版软件,试用10次机会,执行一次就提示一次您还有几次机会,如果次数到了提示请购买正版。
        
        分析:
        1. 创建带缓冲的输入流对象,因为要使用readLine方法,可以保证数据的原样性。
        2. 将读到的字符串转换为int数
        3. 对int数进行判断,如果 i>0,就将其i--;如果 i <= 0,就提示购买正版
        4. 在if判断中要将--的结果打印,并将结果通过输出流写到文件上
         * @throws IOException 
         */
        public static void main(String[] args) throws IOException {
            BufferedReader br = new BufferedReader(new FileReader("config"));
            
            String line = br.readLine();
            
            int count = Integer.parseInt(line);
            
            if(count > 0) {
                System.out.println("您还有" + count-- + "次机会");
                FileWriter fw = new FileWriter("config");
                fw.write(count + "");
                fw.close();
                
            } else {
                System.out.println("您的试用次数已到,请购买正版!");
            }
            br.close();
            
        }
    
    }
    View Code

19. IO流小结

  • 会用BufferedReader读取GBK码表和UTF-8码表的字符

  • 会用BufferedWriter写出字符到GBK码表和UTF-8码表的文件中

  • 会使用BufferedReader从键盘读取一行

 

 

 

原文地址:https://www.cnblogs.com/zoe233/p/13141487.html