• day19(上)_IO流2(BufferedReaer,BufferedWriter,BufferedInputStream,BufferedOutputStream)


    1.IO总结:

    IO体系

    IO总结:

    /*
    2.分析类中常用方法总结:
        InputStream和OutputStream常用方法:
          ①int available()
             返回文本中的字节数.
         
          ②public abstract int read()throws IOException
             从输入流中读取数据的下一个字节。
             返回 0 到 255 范围内的 int 字节值。
             如果因为已经到达流末尾而没有可用的字节,则返回值 -1。
            
            public abstract void write(int b)throws IOException
              将指定的字节写入此输出流。
              write 的常规协定是:向输出流写入一个字节。
              要写入的字节是参数 b 的八个低位。b 的 24 个高位将被忽略。 
            
            Q1.read为什么不返回byte,而write传入的参数为int而不是byte?不是读取一个字节/写入一个字节吗?
               这是因为文件在计算机中以二进制补码存储,
              在未读到文件结尾前读到1111 1111 1111(-1),导致读取字节提前结束.
               解决方法:
             在read方法中:
                 为了避免上面那种全为1的情况,对此类型提升为int
                 (32bit)
                 但是即使提升,发生符号扩展(补1)也就是
                 byte:                             11111111
                  int:11111111 11111111 11111111 11111111
                 &255:00000000 00000000 00000000 11111111
                 还是-1,那么这时候在不改变原byte值的情况下,让其&255改变了
                 前24bit,而不改变后8bit,此时int值为255
                 返回数据范围的是:
                      00000000 00000000 00000000 00000000(0)
                                  |
                      00000000 00000000 00000000 11111111(255)
            在write方法中:
                写入的是:00000000~11111111(int中byte值)
                内部发生类型强制类型转换->截断后8bit
        
        ③ public int read(byte[] b)throws IOException
            从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
            以整数形式返回实际读取的字节数,如果因为流位于文件末尾而没有可用的字节,
            则返回值 -1;
          
          public void write(byte[] b)throws IOException
             将 b.length个字节从指定的 byte 数组写入此输出流。
        ④ 
      关于刷新:
         public void flush()throws IOException//OutputStream中flush
            刷新此输出流并强制写出所有缓冲的输出字节。
            flush 的常规协定是:如果此输出流的实现已经缓冲了以前写入的任何字节,
            则调用此方法指示应将这些字节立即写入它们预期的目标。 
         
         对于字节写入流来说可以不用flush也会写入文件,因为
          如果没有使用具体制定缓冲区,不论什么数据都以字节操作,
          对字节这个最小单位操作,直接写入目的地.
        
       public void flush()throws IOException//BufferedOutputStream中flush
         刷新此缓冲的输出流。这迫使所有缓冲的输出字节被写出到底层输出流中。 
          
         该方法内部实现细节:
            protected byte buf[];
            protected OutputStream out;
          private void flushBuffer() throws IOException {
            if (count > 0) {
                out.write(buf, 0, count);//buf内部定义的字节缓冲区
                                         //2.将buf中的数据写入到了输出流中
                count = 0;                 
            }
        }
          
        public synchronized void flush() throws IOException {
            flushBuffer();//1.首先刷新了缓冲区
            out.flush();//3.把流中的数据刷新到文本中
          }
    
        ⑤
        关闭缓冲区实际上关闭的是使用缓冲区的流对象:
    
        //BufferedInputStream.java
           public void close() throws IOException {
            byte[] buffer;
            while ( (buffer = buf) != null) {
                if (bufUpdater.compareAndSet(this, buffer, null)) {
                    InputStream input = in;
                    in = null;
                    if (input != null)
                        input.close();//关闭的是 使用缓冲区的输入字节流对象
                                      //关闭此输入流并释放与该流关联的所有系统资源。
                    return;
                }
                // Else retry in case a new buf was CASed in fill()
            }
        }
    
            
    */
    /*
     Reader和Writer常用方法总结:
        ①public int read()throws IOException 
             读取单个字符。    
             返回:
             作为整数读取的字符,
             范围在 0 到 65535 之间 (0x0000-0xffff)(2byte范围),
             如果已到达流的末尾,则返回 -1 
          public void write(int c)throws IOException
            写入单个字符。要写入的字符包含在给定整数值的 16(2Byte)个低位中,
            16 高位被忽略。 
    
        ②public int read(char[] cbuf) throws IOException
          将字符读入数组。
          返回:读取的字符数,如果已达到流的末尾,则返回-1
          public void write(char[] cbuf)throws IOException
           字符数组中内容写入流中   
         ③public void write(String str)throws IOException
           写入字符串。 
        
        ④FileReader与InputStreamReader(转换流)
          当操作文件,用字节读取流关联文件:
            public FileReader(String fileName)throws FileNotFoundException
        
        //内部实现细节:
        
        public FileReader(String fileName) throws FileNotFoundException {
            super(new FileInputStream(fileName));//内部创建了字节读取流对象
        }                                        //传给了转换流
        public InputStreamReader(InputStream in) {
            super(in);
            try {
                sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object
            } catch (UnsupportedEncodingException e) {
                // The default encoding should always be available
                throw new Error(e);
            }
        }    
       同理FileWriter和OutputStreamWriter
       //内部实现细节:
          public FileWriter(String fileName) throws IOException {
            super(new FileOutputStream(fileName));
        } 
       public OutputStreamWriter(OutputStream out) {
            super(out);
            try {
                se = StreamEncoder.forOutputStreamWriter(out, this, (String)null);
            } catch (UnsupportedEncodingException e) {
                throw new Error(e);
            }
        }
      ⑤flush:
        public abstract void flush()throws IOException
        刷新该流的缓冲。如果该流已保存缓冲区中各种 write() 方法的所有字符,
        则立即将它们写入预期目标。
       字符流必须刷新/关闭(关闭之前会刷新),把流中的数据刷到文本中.    
    
    */
    
    /*
    BufferedReader,BufferedWriter,LineNumberReader
        从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。 
        缓冲区内部使用数组来实现.
    BufferedReader:
        public String readLine()throws IOException
        读取一个文本行。
        通过下列字符之一即可认为某行已终止:
        换行 ('\n')、回车 ('\r') 或回车后直接跟着换行。 
        
        返回:
        包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null
       掌握readLine方法的原理 
    BufferedWriter:
       public void newLine()throws IOException
        写入一个行分隔符。行分隔符字符串由系统属性 line.separator 定义,
        并且不一定是单个新行 ('\n') 符。
        注意:在不同平台下,行分隔符可能不同,例如:windows:"\r\n" linux:"\n"
        该方法是一个跨平台方法
    LineNumberReader:
        默认情况下,行编号从 0 开始。
        该行号随数据读取在每个行结束符处递增,
        并且可以通过调用 setLineNumber(int) 更改行号。
      如果行编号从0开始,文本第一行为1,第二行为2......
    close方法:底层依然关闭的是使用该缓冲的流对象,并且释放用到的系统底层资源
    */

    2.字节流的读取和写入方法:

    package filestream;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    class FileStreamDemo{
          //方法一:一个字节一个字节读
          public static void read_1(){
          FileInputStream fis=null;
          try{
           //fis=new FileInputStream("fos.txt"); 
           fis=new FileInputStream("TestUnicode.txt");
           int ch=0;
           System.out.println("read_1:");
           while((ch=fis.read())!=-1)
              System.out.println(ch);
          }
          catch(IOException e){
           e.printStackTrace();
          }
          finally{
           try{
            if(fis!=null)
              fis.close();
           }
           catch(IOException e){
            e.printStackTrace();
           }
          }
     }
    
         /*
        方法二:
        public int read(byte[] b)throws IOException
        */
      public static void read_2(){
          FileInputStream fis=null;
          try{
           fis=new FileInputStream("fos.txt"); 
           int bytes=0;
           byte[] b=new byte[1024];
           System.out.print("read_2:");
           while((bytes=fis.read(b))!=-1)
              System.out.println(bytes+"..."+new String(b,0,bytes));//只输出读取到的字节数
          }
          catch(IOException e){
           e.printStackTrace();
          }
          finally{
           try{
            if(fis!=null)
              fis.close();
           }
           catch(IOException e){
            e.printStackTrace();
           }
          }
     }
      /*
      public int available()throws IOException
       该方法返回了文本中字节数.
       例如:abcd,返回4
       注意一点:
       abcd
       e   abcd后有回车,返回7->\r\n各占1byte
       这种方法有个缺陷:
         如果返回值过大(读取1G(1024*1024)文件)
         那么此时new的数组过大,可能发生内存溢出
         在使用时注意
      */
     public static void read_3(){
          FileInputStream fis=null;
          try{
           fis=new FileInputStream("fos.txt"); 
           int bytes=0;
           byte[] b=new byte[fis.available()];//定义一个刚刚好的缓冲区
           fis.read(b);//一次性把数据读到b中
           System.out.println("read_3:"+new String(b));
          }
          catch(IOException e){
           e.printStackTrace();
          }
          finally{
           try{
            if(fis!=null)
              fis.close();
           }
           catch(IOException e){
            e.printStackTrace();
           }
          }
     }
     
    //写入方法: 
     public static void write(){
         FileOutputStream fos=null;
         FileInputStream fis=null;
            try{
              fis=new FileInputStream("1.txt");
              fos=new FileOutputStream("fos.txt");
              int aByte=0;
              while((aByte=fis.read())!=-1)
                 fos.write(aByte);
              //fos.write("abcd".getBytes());//将字符串转成字节数组写入
              /*
               这里即使不刷新/关闭,abcd依然会写入到fos.txt中,
               字符流:例如读汉字(2byte),先读一个字节临时存储(底层用的字节流的缓冲区),再读一个->查表->汉字
               字节流:如果没有使用具体制定缓冲区,不论什么数据都以字节操作,
                      对字节这个最小单位操作,直接写入目的地.
              */
            }
            catch(IOException e){
             e.printStackTrace();
           }
           finally{
            try{
             if(fos!=null)
                fos.close();//
    依然需要关闭此输出流并释放与此流有关的所有系统资源
            }
            catch(IOException e){
             e.printStackTrace();
            }
           }
       }
      
      
     public static void main(String[] args){
            write();
            read_1();
            read_2();
            read_3();
    
       }
    
    }

    字符流读写演示

    3.多媒体文件(mp3,电影...)拷贝

    /*
     FileInputStream 用于读取诸如图像数据之类的原始字节流
     FileOutputStream 用于写入诸如图像数据之类的原始字节的流。
    复制图片:
        思想:
        1.创建字节读取流对象关联一个图片文件
        2.创建字节写入流对象创建一个图片文件,用于存储获取到的图片数据
        3.通过循环读写,完成数据存储
        4.关资源
    */
    package copypic;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    class CopyPicture{
      public static void main(String[] args){
        FileInputStream fis=null;
        FileOutputStream fos=null;
       try{
        fis=new FileInputStream("c:\\Screen.png");
        fos=new FileOutputStream(".\\ScreenCopy.png");
        byte[] bbuf=new byte[1024];
        int bytes;
        while((bytes=fis.read(bbuf))!=-1)
          fos.write(bbuf,0,bytes);
       }
       catch(IOException e){
        e.printStackTrace();
       }
       finally{
        try{
         if(fis!=null)
           fis.close();
        }
       catch(IOException e){
         e.printStackTrace();
       }
       try{
         if(fos!=null)
           fos.close();
        }
       catch(IOException e){
         e.printStackTrace();
       }
       }
      }
    }
    /*
    不要用字符流拷贝媒体文件
    读取数据和写入数据可能发生不一致
    导致写入的编码和原编码不一致,拷贝失败.
    */

    4.字符流和字节流的缓冲区

    ①BufferedWriter与BufferedReader

    /*BufferedWriter为什么没有空构造函数?
    
        这是因为缓冲区的出现是为了提高流的操作效率而出现的
         所以在创建缓冲区之前,必须要先有流对象
         
    现实生活中例子:
      用蓄水池(缓冲区)储水(流对象),如果还没有水呢,要蓄水池干啥用呢
    ?
    
      先有水
    ->再有蓄水池 水和水杯也是. 比喻: 水一滴一滴的滴(FileReader的read()) 我想要喝水滴一滴喝一滴(write())很不爽 那么我用个杯子(缓冲区)接的差不多() 一饮而尽很爽~ */
    package bufferedwriter;
    import java.io.FileWriter;
    import java.io.BufferedWriter;
    import java.io.IOException;
    
    class BufferedWriterDemo{
      public static void main(String[] args){
       FileWriter fw=null;
       BufferedWriter bufw=null;
       try{
            //创建一个字符写入流对象
            fw=new FileWriter("buff.txt");     
            
            //为了提高字符写入流效率,加入缓冲技术
            //其实缓冲技术原理在缓冲里面封装了数组
            bufw=new BufferedWriter(fw);//public BufferedWriter(Writer out)
                                        //创建一个使用默认大小 输出缓冲区(8KB)的 缓冲字符输出流。
                                        
           for(int i=0;i<4;++i){
            bufw.write("coding"+i);//写入缓冲区
            bufw.newLine();
            bufw.flush();//写一次刷一次.假如停电,未刷新此时数据可能还在流(内存)中,还没有写到硬盘中.
           }
       }
       catch(IOException e){
          e.printStackTrace();   
       }
       finally{
         
         try{
         //其实关闭缓冲区,就是在关闭缓冲区中的流对象
         //真正调用底层资源,进行读写的是流对象
         
         bufw.close();//在关闭之前也会刷新缓冲
         /*
         源码:
          public void close() throws IOException {
            synchronized (lock) {
                if (out == null) {
                    return;
                }
                try {
                    flushBuffer();//刷新缓冲
                } finally {
                    out.close();//关闭使用缓冲的流对象
                    out = null;
                    cb = null;
                }
            }
        }
        */
        }
        
        catch(IOException e){
         e.printStackTrace();
        }
        
       }
    
      }
    }
    /*
     关于newLine方法
         在windows里面换行为"\r\n";
         而linux里面为:"\n"
         为了实现跨平台性,保证我们把同一个程序拿到不同平台下都能换行.
         BufferedWriter中提供了一个newLine方法.
         相当于在windows版的JDK源文件中:
         封装了write("\r\n");
         linux版的
         封装了write("\n");
    */

    /*
    关于缓冲区理解:
    如果是边读边写,就会很慢,也伤硬盘。
    缓冲区(字节数组/字符数组)就是内存里的一块临时存储数据的区域,把数据先存到缓冲区中,然后写入硬盘.
    */

    /*
     BufferedReader(缓冲字符输入流):
        从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和 行 的高效读取。
    */
    package bufferedreader;
    import java.io.BufferedReader;
    import java.io.FileReader;
    import java.io.IOException;
    
    class BufferedReaderDemo{
        public static void main(String[] args){
         
         //创建一个字符读取流对象和文件相关联
           BufferedReader bufr=null;
           try{
              
            //依然为了提高效率,加入缓冲技术,
            //将字符读取流对象作为参数传递给缓冲区对象的构造函数
            bufr=new BufferedReader(new FileReader("buff.txt"));
            String readStr;
            while((readStr=bufr.readLine())!=null)//从缓冲区中一行一行读,直到文件流末尾
                System.out.println(readStr);//打印字符串。如果参数为 null,则打印字符串 "null"。否则,
     //按照平台的默认字符编码将字符串的字符转换为字节,并完全以write(int)方法的方式写入这些字节。
            }
           catch(IOException e){
            e.printStackTrace();
           }
           finally{
              try{
                if(bufr!=null)
                  bufr.close();   
              
              }
              catch(IOException e){
               e.printStackTrace();
              }
            }
        }
    
    }

    利用字符流缓冲区复制文件:

    /*
    通过缓冲区复制一个.java文件
    其实就是BufferedReader与BufferedWriter综合运用
    */
    package copy;
    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.FileReader;
    import java.io.FileWriter;
    import java.io.IOException;
    class CopyTest{
      
      public static void readWrite(BufferedReader bufr,BufferedWriter bufw){
         try{
              String line=null;
              while((line=bufr.readLine())!=null){
                bufw.write(line);
                bufw.newLine();
                bufw.flush();
              }
          }
          catch(IOException e){
              e.printStackTrace();
          }
      } 
      
      public static void copy(String file,String destPath){
     
      BufferedReader bufr=null;    
      BufferedWriter bufw=null;
      try{
        bufr=new BufferedReader(new FileReader(file));
       
        if(file.indexOf('\\')==-1){//当前目录下的文件,也就是说可以不用写 .\\文件
           bufw=new BufferedWriter(new FileWriter(destPath+file));
           readWrite(bufr,bufw);
        }
        else{//需要获取路径中的文件名
         String[] fileName = file.split("\\\\");
         bufw=new BufferedWriter(new FileWriter(destPath+fileName[fileName.length-1]));
         readWrite(bufr,bufw);
        }
       
      }
      catch(IOException e){
       e.printStackTrace();
      }
      finally{
        try{
        if(bufr!=null)
          bufr.close();
        }
        catch(IOException e){
         throw new RuntimeException("读取关闭失败");
        }
        try{
        if(bufw!=null)
            bufw.close();
        }
        catch(IOException e){
         throw new RuntimeException("写入关闭失败");
        }
      }
    }
      
     
     public static void main(String[] args){
          
      copy("3_CopyTest.java","d:\\");
     }
    }
    /*
    readLine原理:
     画个示意图.
    拷贝过程原理图
    */

    readLine原理图:

        readLine原理

    以上拷贝过程示意图:

    读写缓冲区

    ②BufferedInputStream与BufferedOutputStream

    /*
    字节流两个缓冲区:
     BufferedInputStream
        BufferedInputStream为另一个输入流添加一些功能,
        即缓冲输入以及支持 mark 和 reset 方法的能力。
        在创建BufferedInputStream 时,会创建一个内部缓冲区数组。
     BufferedOutputStream
        该类实现缓冲的输出流。
        通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,
        而不必针对每次字节写入调用底层系统。 
    */
    package copymp3;
    import java.io.BufferedInputStream;
    import java.io.BufferedOutputStream;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    class ByteBuffered{
     /*
      方法一:使用int read()
                  从此输入流中读取下一个数据字节。
                  返回一个 0 到 255 范围内的 int 字节值
               void write(int b)
                 将指定的字节写入此缓冲的输出流。
     */
      public static void copy_1()throws IOException{
       BufferedInputStream bis=new BufferedInputStream(new FileInputStream("E:\\SONG\\Bandari - 爱尔兰风笛.mp3"));
       BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(".\\copy.mp3")); 
       int aByte;
       while((aByte=bis.read())!=-1)
          bos.write(aByte);
       bis.close();
       bos.close();
      }
      
    
     public static void main(String[] args)throws IOException{
      long start,end;   
      start=System.currentTimeMillis();
       copy_1();
      end=System.currentTimeMillis();
      System.out.println((end-start)+"ms");//计算下拷贝花费时间
      /*
      这里可以回顾下设计模式:模板方法模式(继承(下)中已阐述)
     */
     }
    }
  • 相关阅读:
    [Unity3D]蓝港面试题
    BZOJ 2186 SDOI2008 沙拉公主的困惑 数论
    JSONObject与JSONArray的使用
    一个int类型究竟占多少个字节
    软件开发的金字塔
    poj 1064 Cable master ,二分 精度!!!
    php实现工厂模式
    数据库索引的作用和长处缺点
    C++中使用class和structkeyword的不同
    提交时提示错误This Bundle is invalid.New apps and app updates submitted to the App Store must be built wit
  • 原文地址:https://www.cnblogs.com/yiqiu2324/p/3085362.html
Copyright © 2020-2023  润新知