zoukankan      html  css  js  c++  java
  • IO流的输入输出

    Java IO 体系

    字节流操作类和字符流操作类组成了Java IO体系。

    看下面一张图

    从上图可以看到,整个Java IO体系都是基于字符流(InputStream/OutputStream) 和 字节流(Reader/Writer)作为基类,它们是IO操作的四大抽象类,根据不同的数据载体或功能派生出来的。

    流的概念和作用

    流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。

    字符流和字节流

    字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。 字节流和字符流的区别:

    • 读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
    • 处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。

    • 字节流:一次读入或读出是8位二进制。
    • 字符流:一次读入或读出是16位二进制。

    设备上的数据无论是图片或者视频,文字,它们都以二进制存储的。二进制的最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节。意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据。

    结论:只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。

    ****************************************************************************

    操作IO的基本步骤

    * 1、创建数据源 
    * 2、选择流:字符流/字节流 
    * 3、操作 
    * 4、关闭数据缓冲

    ***********************************************************

    1、输入流字节流 InputStream

    • InputStream 是所有的输入字节流的父类,它是一个抽象类。
    • ByteArrayInputStreamStringBufferInputStreamFileInputStream 是三种基本的介质流,它们分别从Byte 数组StringBuffer、和本地文件中读取数据。
    • PipedInputStream 是从与其它线程共用的管道中读取数据,与Piped 相关的知识后续单独介绍。
    • ObjectInputStream 和所有FilterInputStream 的子类都是装饰流(装饰器模式的主角)。

    2、输出字节流 OutputStream

    • OutputStream 是所有的输出字节流的父类,它是一个抽象类。
    • ByteArrayOutputStreamFileOutputStream 是两种基本的介质流,它们分别向Byte 数组、和本地文件中写入数据。
    • PipedOutputStream 是向与其它线程共用的管道中写入数据。
    • ObjectOutputStream 和所有FilterOutputStream 的子类都是装饰流。
    • 文件流:FileInputStream/FileOutputStream

    这四个类是专门操作文件流的,用法高度相似,区别在于前面两个是操作字节流,后面两个是操作字符流。它们都会直接操作文件流,直接与OS底层交互。因此他们也被称为节点流

    注意使用这几个流的对象之后,需要关闭流对象,因为java垃圾回收器不会主动回收,只能由操作系统来回收。因为JVM 不是直接和文件打交道的,而是通过操作系统和文件打交道的,不过在Java7之后,可以在 try() 括号中打开流,最后程序会通知OS(操作系统)关闭流对象,不再需要显示地close。

    FileInputStream/FileOutputStream

    从文件向程序中写数据使用InputStream的FileInputStream,按字节数组读取

    代码:

    package com.sxt.io;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    
    /**
     * 四个步骤: 分段读取 文件字节输入流
     * 1、创建源
     * 2、选择流
     * 3、操作
     * 4、释放资源
     * @author liuzeyu12a
     *
     */
    public class IOTest03 {
    
        public static void main(String[] args) {
            //1、创建源
            File src = new File("abc.txt");
            //2、选择流
            InputStream  is =null;
            try {
                is =new FileInputStream(src);
                //3、操作 (分段读取)
                byte[] flush = new byte[1024*10]; //缓冲容器
                int len = -1; //接收长度
                while((len=is.read(flush))!=-1) {
                    //字节数组-->字符串 (解码)
                    String str = new String(flush,0,len);
                    System.out.println(str);
                }        
            
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                //4、释放资源
                try {
                    if(null!=is) {
                        is.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
    }
    View Code

    从程序向文件中写数据使用OutputStream的FileOutputStream,按字节数组写入

    代码:

     1 package com.sxt.io;
     2 
     3 import java.io.File;
     4 import java.io.FileNotFoundException;
     5 import java.io.FileOutputStream;
     6 import java.io.IOException;
     7 import java.io.OutputStream;
     8 
     9 /**
    10  * 文件字节输出流
    11  *1、创建源
    12  *2、选择流
    13  *3、操作(写出内容)
    14  *4、释放资源
    15  * @author liuzeyu12a
    16  *
    17  */
    18 public class IOTest04 {
    19 
    20     public static void main(String[] args) {
    21         //1、创建源
    22         File dest = new File("dest.txt");
    23         //2、选择流
    24         OutputStream os =null;
    25         try {
    26             os = new FileOutputStream(dest,true);
    27             //3、操作(写出)
    28             String msg ="IO is so easy
    ";
    29             byte[] datas =msg.getBytes(); // 字符串-->字节数组(编码)
    30             os.write(datas,0,datas.length);
    31             os.flush();
    32         }catch(FileNotFoundException e) {        
    33             e.printStackTrace();
    34         }catch (IOException e) {
    35             e.printStackTrace();
    36         }finally{
    37             //4、释放资源
    38             try {
    39                 if (null != os) {
    40                     os.close();
    41                 } 
    42             } catch (Exception e) {
    43             }
    44         }
    45     }
    46 
    47 }
    View Code

    综合以上两种文件操作,可以写一个文件拷贝的案例

    代码:

     1 package com.sxt.io;
     2 
     3 import java.io.File;
     4 import java.io.FileInputStream;
     5 import java.io.FileNotFoundException;
     6 import java.io.FileOutputStream;
     7 import java.io.IOException;
     8 import java.io.InputStream;
     9 import java.io.OutputStream;
    10 
    11 /**
    12  * 文件拷贝:文件字节输入、输出流
    13  *
    14  * @author liuzeyu12a
    15  *
    16  */
    17 public class Copy {
    18 
    19     public static void main(String[] args) {
    20         copy("src/com/sxt/io/Copy.java","copy.txt");
    21     }
    22     /**
    23      * 文件的拷贝 
    24      * 思考: 利用递归 制作文件夹的拷贝
    25      * @param srcPath
    26      * @param destPath
    27      */
    28     public static void copy(String srcPath,String destPath) {
    29         //1、创建源
    30             File src = new File(srcPath); //源头
    31             File dest = new File(destPath);//目的地
    32             //2、选择流
    33             InputStream  is =null;
    34             OutputStream os =null;
    35             try {
    36                 is =new FileInputStream(src);
    37                 os = new FileOutputStream(dest);        
    38                 //3、操作 (分段读取)
    39                 byte[] flush = new byte[1024]; //缓冲容器
    40                 int len = -1; //接收长度
    41                 while((len=is.read(flush))!=-1) {
    42                     os.write(flush,0,len); //分段写出
    43                 }            
    44                 os.flush();
    45             }catch(FileNotFoundException e) {        
    46                 e.printStackTrace();
    47             }catch (IOException e) {
    48                 e.printStackTrace();
    49             }finally{
    50                 //4、释放资源 分别关闭 先打开的后关闭
    51                 try {
    52                     if (null != os) {
    53                         os.close();
    54                     } 
    55                 } catch (IOException e) {
    56                     e.printStackTrace();
    57                 }
    58                 
    59                 try {
    60                     if(null!=is) {
    61                         is.close();
    62                     }
    63                 } catch (IOException e) {
    64                     e.printStackTrace();
    65                 }
    66             }
    67     }
    68     public static void copy2(String srcPath,String destPath) {
    69         //1、创建源
    70             File src = new File(srcPath); //源头
    71             File dest = new File(destPath);//目的地
    72             //2、选择流        
    73             try(InputStream  is=new FileInputStream(src);
    74                     OutputStream os = new FileOutputStream(dest);    ) {                
    75                 //3、操作 (分段读取)
    76                 byte[] flush = new byte[1024]; //缓冲容器
    77                 int len = -1; //接收长度
    78                 while((len=is.read(flush))!=-1) {
    79                     os.write(flush,0,len); //分段写出
    80                 }            
    81                 os.flush();
    82             }catch(FileNotFoundException e) {        
    83                 e.printStackTrace();
    84             }catch (IOException e) {
    85                 e.printStackTrace();
    86             }
    87     }
    88 }
    View Code

    3、字符输入流 Reader

    在上面的继承关系图中可以看出:

    Reader 是所有的输入字符流的父类,它是一个抽象类。

           * CharReader、StringReader 是两种基本的介质流,它们分别将Char 数组、String中读取数据。PipedReader 是从与其它线程共用的管道中读取数据。
       * BufferedReader 很明显就是一个装饰器,它和其子类负责装饰其它Reader 对象。
      * FilterReader 是所有自定义具体装饰流的父类,其子类PushbackReader 对Reader 对象进行装饰,会增加一个行号。
      * InputStreamReader 是一个连接字节流和字符流的桥梁,它将字节流转变为字符流。FileReader 可以说是一个达到此功能、常用的工具类,在其源代码中明显使用了将FileInputStream 转变为Reader 的方法。

    我们可以从这个类中得到一定的技巧。

      * Reader 中各个类的用途和使用方法基本和InputStream 中的类使用一致。后面会有Reader 与InputStream 的对应关系。

    为了人为操作方便,我们一般只对字符敏感,所以使用FileReader/FileWriter操作字符会更加方便。

    4、字符输出流Writer

    在上面的关系图中可以看出:

    Writer 是所有的输出字符流的父类,它是一个抽象类。

      * CharArrayWriter、StringWriter 是两种基本的介质流,它们分别向Char 数组、String 中写入数据。PipedWriter 是向与其它线程共用的管道中写入数据,
      * BufferedWriter 是一个装饰器为Writer 提供缓冲功能。
      * PrintWriter 和PrintStream 极其类似,功能和使用也非常相似。
      * OutputStreamWriter 是OutputStream 到Writer 转换的桥梁,它的子类FileWriter 其实就是一个实现此功能的具体类(具体可以研究一下SourceCode)。功能和使用和OutputStream 极其类似,后面会有它们的对应图。

    • 文件流:FileReader/FileWriter

    从文件向程序冲写数据使用Reader的FileReader,按字符数组读取

    package com.sxt.io;
    
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.IOException;
    import java.io.Reader;
    
    /**
     * 四个步骤: 分段读取 文件字符输入流
     * 1、创建源
     * 2、选择流
     * 3、操作
     * 4、释放资源
     * 
     * @author liuzeyu12a
     *
     */
    public class IOTest05 {
    
        public static void main(String[] args) {
            //1、创建源
            File src = new File("abc.txt");
            //2、选择流
            Reader  reader =null;
            try {
                reader =new FileReader(src);
                //3、操作 (分段读取)
                char[] flush = new char[1024]; //缓冲容器
                int len = -1; //接收长度
                while((len=reader.read(flush))!=-1) {
                    //字符数组-->字符串
                    String str = new String(flush,0,len);
                    System.out.println(str);
                }        
            
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                //4、释放资源
                try {
                    if(null!=reader) {
                        reader.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
    }
    View Code

    从文件向程序冲写数据使用Writer的FileWriter,按字符数组写出

     1 package com.sxt.io;
     2 
     3 import java.io.File;
     4 import java.io.FileNotFoundException;
     5 import java.io.FileWriter;
     6 import java.io.IOException;
     7 import java.io.Writer;
     8 
     9 /**
    10  * 文件字符输出流
    11  *1、创建源
    12  *2、选择流
    13  *3、操作(写出内容)
    14  *4、释放资源
    15  * @author liuzeyu12a
    16  *
    17  */
    18 public class IOTest06 {
    19 
    20     public static void main(String[] args) {
    21         //1、创建源
    22         File dest = new File("dest.txt");
    23         //2、选择流
    24         Writer writer =null;
    25         try {
    26             writer = new FileWriter(dest);
    27             //3、操作(写出)
    28             //写法一
    29 //            String msg ="IO is so easy
    尚学堂欢迎你";
    30 //            char[] datas =msg.toCharArray(); // 字符串-->字符数组
    31 //            writer.write(datas,0,datas.length);
    32             //写法二
    33             /*String msg ="IO is so easy
    尚学堂欢迎你";
    34             writer.write(msg);    
    35             writer.write("add");        
    36             writer.flush();*/
    37             
    38             //写法三
    39             writer.append("IO is so easy
    ").append("尚学堂欢迎你");
    40             writer.flush();
    41         }catch(FileNotFoundException e) {        
    42             e.printStackTrace();
    43         }catch (IOException e) {
    44             e.printStackTrace();
    45         }finally{
    46             //4、释放资源
    47             try {
    48                 if (null != writer) {
    49                     writer.close();
    50                 } 
    51             } catch (Exception e) {
    52             }
    53         }
    54     }
    55 
    56 }
    View Code

    5、另外我们再介绍两个字节流类,是为了操作字节方便所引入,分别是ByteArrayInputStream / ByteArrayOutputStream

    之前我们操作的源头都是来自了文件File ,现在我们将源头换成电脑中的一块内存,或者网络(服务器)上的一块内存,用字节数组表示。操作内存,这个时候JVM 就可以直接访问了,不用再经过操作系统了,此时的资源就有GC来回收了,可以不用手动关闭了。

    所有的对象都可以转成字节数组,转成字节数组后,在内存中就以二进制存在,方便网络上的数据传输。

    程序向内存中的字节数组读取数据ByteArrayInputStream

     1 package com.sxt.io;
     2 
     3 import java.io.ByteArrayInputStream;
     4 import java.io.FileNotFoundException;
     5 import java.io.IOException;
     6 import java.io.InputStream;
     7 
     8 /**
     9  * 四个步骤:字节数组输入流
    10  * 1、创建源  : 字节数组 不要太大
    11  * 2、选择流
    12  * 3、操作
    13  * 4、释放资源: 可以不用处理
    14  * 
    15  * @author liuzeyu12a
    16  *
    17  */
    18 public class IOTest07 {
    19 
    20     public static void main(String[] args) {
    21         //1、创建源
    22         byte[] src = "talk is cheap show me the code".getBytes();
    23         //2、选择流
    24         InputStream  is =null;
    25         try {
    26             is =new ByteArrayInputStream(src);
    27             //3、操作 (分段读取)
    28             byte[] flush = new byte[5]; //缓冲容器
    29             int len = -1; //接收长度
    30             while((len=is.read(flush))!=-1) {
    31                 //字节数组-->字符串 (解码)
    32                 String str = new String(flush,0,len);
    33                 System.out.println(str);
    34             }        
    35         
    36         } catch (IOException e) {
    37             e.printStackTrace();
    38         }finally {
    39             //4、释放资源
    40             try {
    41                 if(null!=is) {
    42                     is.close();
    43                 }
    44             } catch (IOException e) {
    45                 e.printStackTrace();
    46             }
    47         }
    48     }
    49 
    50 }
    View Code

    程序向内存中写数据后,再从内存中使用toByteArray()方法得到字节数组

    package com.sxt.io;
    
    import java.io.ByteArrayOutputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    
    /**
     * 字节数组输出流 ByteArrayOutputStream
     *1、创建源  : 内部维护
     *2、选择流  : 不关联源
     *3、操作(写出内容)
     *4、释放资源 :可以不用
     *
     * 获取数据:  toByteArray()
     * @author liuzeyu12a
     *
     */
    public class IOTest08 {
    
        public static void main(String[] args) {
            //1、创建源
            byte[] dest =null;
            //2、选择流 (新增方法)
            ByteArrayOutputStream baos =null;
            try {
                baos = new ByteArrayOutputStream();
                //3、操作(写出)
                String msg ="show me the code";
                byte[] datas =msg.getBytes(); // 字符串-->字节数组(编码)
                baos.write(datas,0,datas.length);
                baos.flush();
                //获取数据
                dest = baos.toByteArray();
                System.out.println(dest.length +"-->"+new String(dest,0,baos.size()));
            }catch(FileNotFoundException e) {        
                e.printStackTrace();
            }catch (IOException e) {
                e.printStackTrace();
            }finally{
                //4、释放资源
                try {
                    if (null != baos) {
                        baos.close();
                    } 
                } catch (Exception e) {
                }
            }
        }
    
    }
    View Code

    这里要注意的是ByteArrayOutputStream使用的是子类新增的方法,幷没有发生多态。当我们程序向内存中写数据时,写多大的内存是有内存自己分配的,所以可以不能加目的地,并且要注意写入的数据不能太大。

    6、一个综合的案例,实现图片读取到字节数组,再由字节数组写出到图片,其实就是图片的拷贝。

    分析:

    由文件到程序,再有程序到字节数组,必须经过程序这个中间站.

    图片到字节数组:

    图片到程序  FileInputStream

    程序到字节数组 ByteArrayOutputStream

    字节数组到图片:

    字节数组程序 ByteArrayInputStream

    程序到文件 FileOutputStream

    代码实现:

     1 package com.sxt.io;
     2 
     3 import java.io.ByteArrayInputStream;
     4 import java.io.ByteArrayOutputStream;
     5 import java.io.File;
     6 import java.io.FileInputStream;
     7 import java.io.FileNotFoundException;
     8 import java.io.FileOutputStream;
     9 import java.io.IOException;
    10 import java.io.InputStream;
    11 import java.io.OutputStream;
    12 
    13 /**
    14  *1、 图片读取到字节数组
    15  *2、 字节数组写出到文件
    16  * @author liuzeyu12a
    17  *
    18  */
    19 public class IOTest09 {
    20 
    21     public static void main(String[] args) {
    22         //图片转成字节数组
    23         byte[] datas = fileToByteArray("p.png");
    24         System.out.println(datas.length);
    25         byteArrayToFile(datas,"p-byte.png");        
    26     }
    27     /**
    28      * 1、图片读取到字节数组
    29      * 1)、图片到程序  FileInputStream
    30      * 2)、程序到字节数组    ByteArrayOutputStream
    31      */
    32     public static byte[] fileToByteArray(String filePath) {
    33         //1、创建源与目的地
    34         File src = new File(filePath);
    35         byte[] dest =null;
    36         //2、选择流
    37         InputStream  is =null;
    38         ByteArrayOutputStream baos =null;
    39         try {
    40             is =new FileInputStream(src);
    41             baos = new ByteArrayOutputStream();
    42             //3、操作 (分段读取)
    43             byte[] flush = new byte[1024*10]; //缓冲容器
    44             int len = -1; //接收长度
    45             while((len=is.read(flush))!=-1) {
    46                 baos.write(flush,0,len);         //写出到字节数组中            
    47             }        
    48             baos.flush();
    49             return baos.toByteArray();
    50         } catch (FileNotFoundException e) {
    51             e.printStackTrace();
    52         } catch (IOException e) {
    53             e.printStackTrace();
    54         }finally {
    55             //4、释放资源
    56             try {
    57                 if(null!=is) {
    58                     is.close();
    59                 }
    60             } catch (IOException e) {
    61                 e.printStackTrace();
    62             }
    63         }
    64         return null;        
    65     }
    66     /**
    67      * 2、字节数组写出到图片
    68      * 1)、字节数组到程序 ByteArrayInputStream
    69      * 2)、程序到文件 FileOutputStream
    70      */
    71     public static void byteArrayToFile(byte[] src,String filePath) {
    72         //1、创建源
    73         File dest = new File(filePath);
    74         //2、选择流
    75         InputStream  is =null;
    76         OutputStream os =null;
    77         try {
    78             is =new ByteArrayInputStream(src);
    79             os = new FileOutputStream(dest);
    80             //3、操作 (分段读取)
    81             byte[] flush = new byte[5]; //缓冲容器
    82             int len = -1; //接收长度
    83             while((len=is.read(flush))!=-1) {
    84                 os.write(flush,0,len);            //写出到文件    
    85             }        
    86             os.flush();
    87         } catch (IOException e) {
    88             e.printStackTrace();
    89         }finally {
    90             //4、释放资源
    91             try {
    92                 if (null != os) {
    93                     os.close();
    94                 } 
    95             } catch (Exception e) {
    96             }
    97         }
    98     }
    99 }
    View Code

    在jdk7 以后我们可以使用try...with对流的关闭操作进行简化

    将以上代码进行封装简化

     1 package com.sxt.io;
     2 
     3 import java.io.ByteArrayInputStream;
     4 import java.io.ByteArrayOutputStream;
     5 import java.io.Closeable;
     6 import java.io.FileInputStream;
     7 import java.io.FileNotFoundException;
     8 import java.io.FileOutputStream;
     9 import java.io.IOException;
    10 import java.io.InputStream;
    11 import java.io.OutputStream;
    12 
    13 /**
    14  * try ...with...resource
    15  * @author liuzeyu12a
    16  *
    17  */
    18 public class FileUtils2 {
    19 
    20     public static void main(String[] args) {
    21         //文件到文件
    22         try {
    23             InputStream is = new FileInputStream("abc.txt");
    24             OutputStream os = new FileOutputStream("abc-copy.txt");
    25             copy(is,os);
    26         } catch (IOException e) {
    27             e.printStackTrace();
    28         }
    29         
    30         //文件到字节数组
    31         byte[] datas = null;
    32         try {
    33             InputStream is = new FileInputStream("p.png");
    34             ByteArrayOutputStream os = new ByteArrayOutputStream();
    35             copy(is,os);
    36             datas = os.toByteArray();
    37             System.out.println(datas.length);
    38         } catch (IOException e) {
    39             e.printStackTrace();
    40         }
    41         //字节数组到文件
    42         try {
    43             InputStream is = new ByteArrayInputStream(datas);
    44             OutputStream os = new FileOutputStream("p-copy.png");
    45             copy(is,os);
    46         } catch (IOException e) {
    47             e.printStackTrace();
    48         }
    49     }
    50     /**
    51      * 对接输入输出流
    52      * try ...with...resource
    53      * @param is
    54      * @param os
    55      */
    56     public static void copy(InputStream is,OutputStream os) {        
    57             try(is;os) {            
    58                 //3、操作 (分段读取)
    59                 byte[] flush = new byte[1024]; //缓冲容器
    60                 int len = -1; //接收长度
    61                 while((len=is.read(flush))!=-1) {
    62                     os.write(flush,0,len); //分段写出
    63                 }            
    64                 os.flush();
    65             }catch(FileNotFoundException e) {        
    66                 e.printStackTrace();
    67             }catch (IOException e) {
    68                 e.printStackTrace();
    69             }
    70     }    
    71 }
    View Code

    部分图片文字参考

    https://www.cnblogs.com/runningTurtle/p/7088125.html

    https://www.cnblogs.com/zhaoyanjun/p/6292384.html

  • 相关阅读:
    jvm内存模型学习
    sparkstreaming容错
    yum命里详解
    rpm包的安装及卸载
    git命令记录
    ElaticSearch基本查询
    入门elaticsearch
    Java网络编程学习(一)
    JAVA多线程之生产消费模型
    Java线程同步详解
  • 原文地址:https://www.cnblogs.com/liuzeyu12a/p/10396698.html
Copyright © 2011-2022 走看看