IO 流最全讲解

原理与基本概念

一、概念

流 : 流动 、流向 从一端移动到另一端(源头与目的地) 程序 与 文件|数组|网络连接|数据库,以程序为中心

二、IO流分类

1、流向: 输入流与输出流

2、数据:

字节流:二进制,可以一切文件 包括 纯文本 doc 音频、视频等等 字符流:文本文件,只能处理纯文本

3、功能:

节点:包裹源头 处理:增强功能,提供性能

注意:我们这里学习的是传统 IO 流,并且是阻塞式 IO。

三、字符流与字节流 (重点) 与文件

1、字节流

输入流: InputStream read(byte[] b) 、read(byte[] b, int off, int len) +close() FileInputStream()

输出流: OutputStream write(byte[] b) write(byte[] b, int off, int len) +flush() +close() FileOutputStream

2、字符流

输入流:

Reader read(char[] cbuf) read(char[] cbuf, int off, int len) +close() FileReader()

输出流:

Writer write(char[] cbuf) write(char[] cbuf, int off, int len) +flush() +close() write(String str, int off, int len) FileWriter()

四、操作

1、举例:搬家 -->读取文件

1)、关联房子 --->建立与文件联系

2)、选择搬家 -->选择对应流

3)、搬家 -->读取|写出

  • a)、卡车大小 --->数组大小
  • b)、运输 -->读取、写出

4)、打发over -->释放资源

2、操作

1)建立联系

2)选择流

3)操作 数组大小+read 、write

4)释放资源

字节流

字节流:可以处理一切文件,包括二进制 音频、视频 、doc等 节点流: InputStream FileInputStream OutputStream FileOutputStream

一、读取文件

1、建立联系 File对象 源头

2、选择流 文件输入流 InputStream FileInputStream

3、操作 : byte[] car =new byte[1024]; +read+读取大小 输出

4、释放资源 :关闭

二、写出文件

1、建立联系 File对象 目的地

2、选择流 文件输出流 OutputStream FileOutputStream

3、操作 : write() +flush

4、释放资源 :关闭

三、文件拷贝(程序为桥梁)

1、建立联系 File对象 源头 目的地

2、选择流

文件输入流 InputStream FileInputStream

文件输出流 OutputStream FileOutputStream

3、操作 : 拷贝

     byte[] flush =new byte[1024]; 
     int len =0;
      while(-1!=(len=输入流.read(flush))){  
         输出流.write(flush,0,len)
      }
     输出流.flush();

4、释放资源 :关闭 两个流

代码如下:

public class CopyFileDemo {

    public static void main(String[] args) {
        String srcPath = "E:/others/helloWorld.java";
        String destPath = "E:/others/myCopy.java";
        try {
            copyFile(srcPath,destPath);
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("拷贝文件失败");
        }
    }

    /**
     * 文件的拷贝
     * @param srcPath   源文件路径
     * @param destPath  目录文件路径
     * @throws IOException
     */
    public static void copyFile(String srcPath,String destPath) throws IOException {
        //1、建立联系源(存在且为文件)+ 目的地(文件可以不存在)
        File src = new File(srcPath);
        File dest = new File(destPath);
        copyFile(src,dest);
    }

    /**
     * 文件的拷贝
     * @param src   源文件路径
     * @param dest  目录文件路径
     * @throws IOException
     */
    public static void copyFile(File src,File dest) throws IOException {
        if (!src.isFile()){
            throw new IOException("只能拷贝文件");
        }
        if (dest.isDirectory()){
            throw new IOException(dest.getAbsolutePath()+"不能建立与文件夹同名的文件");
        }
        //2、选择流
        InputStream is = new FileInputStream(src);
        OutputStream os = new FileOutputStream(dest);
        //3、文件拷贝   循环+读取+写出
        byte[] flush = new byte[1024];
        int len = 0;
        //读取
        while (-1!=(len=is.read(flush))){
            //写出
            os.write(flush,0,len);
        }
        //强制刷出
        os.flush();
        //先打开的流后关闭
        os.close();
        is.close();
    }
}

四、文件夹拷贝

1、递归查找子孙级文件|文件夹,并创建文件夹

2、文件复制(IO流复制)

3、分析将 A 文件夹 拷贝到 AA 文件夹中 A / \ a.txt b | b.txt

AA

|

A / \ a.txt b | b.txt

4、不能将父目录拷贝到子目录中

代码如下:

/**
 * 文件夹的拷贝
 * 1、文件 赋值 copyFile
 * 2、文件夹 创建 mkdirs()
 * 3、递归 查找子孙级
 */
public class CopyDir {
    public static void main(String[] args) {
        //源目录
        String srcPath = "e:/others/study";
        //目标目录
        String destPath = "e:/others/test";
        copyDir(srcPath,destPath);
    }

    public static void copyDir(String srcPath,String destPath){
        File src = new File(srcPath);
        File dest = new File(destPath);
        copyDir(src,dest);
    }

    public static void copyDir(File src,File dest){
        if (src.isDirectory()){
            dest = new File(dest,src.getName());
        }
        copyDirDetail(src,dest);
    }

    public static void copyDirDetail(File src,File dest){
        if (src.isFile()){
            try {
                copyFile(src,dest);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }else if (src.isDirectory()){
            //确保目标文件夹存在
            dest.mkdirs();
            //获取下一级目录|文件
            for (File sub:src.listFiles()){
                copyDirDetail(sub,new File(dest,sub.getName()));
            }
        }
    }
}

字符流

字符流 : 只能处理纯文本,全部为可见字符 .txt .html 节点流 :

  • Reader FileReader
  • Writer FileWriter

一、纯文本读取

1、建立联系

2、选择流 Reader FileReader

3、读取 char[] flush =new char[1024];

4、关闭

代码如下:

/**
 * 纯文本读取
 * @author Administrator
 *
 */
public class Demo01 {

    /**
     * @param args
     */
    public static void main(String[] args) {
        //创建源
        File src =new File("E:/xp/test/a.txt");
        //选择流
        Reader reader =null;
        try {
            reader =new FileReader(src);
            //读取操作
            char[] flush =new char[1024];
            int len =0;
            while(-1!=(len=reader.read(flush))){
                //字符数组转成 字符串
                String str =new String(flush,0,len);
                System.out.println(str);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            System.out.println("源文件不存在");
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("文件读取失败");
        }finally{
            try {
                if (null != reader) {
                    reader.close();
                }
            } catch (Exception e2) {
            }
        }
    }

}

二、纯文本写出

1、建立联系

2、选择流 Writer FileWriter

3、读取 write(字符数组,0,长度)+flush

  • write(字符串)
  • append(字符|字符串)

4、关闭

代码如下:

/**
 * 写出文件
 * @author Administrator
 *
 */
public class Demo02 {

    /**
     * @param args
     */
    public static void main(String[] args) {
        //创建源
        File dest =new File("e:/xp/test/char.txt");
        //选择流
        Writer wr =null;
        try {
            //默认 false 为覆盖文件。当指定为 true 时,追加文件,而不是覆盖文件。
            wr =new FileWriter(dest,true);
            //写出
            String msg ="追加...锄禾日当午\r\n码农真辛苦\r\n一本小破书\r\n一读一上午";
            wr.write(msg);
            wr.append("看电视剧 ");
            wr.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch (IOException e) {
            e.printStackTrace();
        }finally{
            try {
                if (null != wr) {
                    wr.close();
                }
            } catch (Exception e2) {
            }
        }
    }
}

处理流

处理流:增强功能、提供性能,节点流之上

一、缓冲流

1、字节缓冲流

BufferedInputStream

BufferedOutputStream

2、字符缓冲流

BufferedReader readLine()

BufferedWriter newLine()

二、转换流: 字节流 转为字符流 处理乱码(编码集、解码集)

1、编码与解码概念

编码 : 字符 ---编码字符集>二进制

解码 : 二进制 ---解码字符集->字符

2、乱码:

1)、编码与解码的字符集不统一

2)、字节缺少,长度丢失

3、文件乱码 InputStreamReader(字节输入流,"解码集") OutputStreamWriter(字符输出流,"编码集")

代码如下:

/**
 * 产生乱码的原因
 */
public class ConvertDemo01 {
    public static void main(String[] args) throws UnsupportedEncodingException {
        reason01();
        reason02();
    }

    public static void reason02() throws UnsupportedEncodingException {
        //如果编码解码过程中不指定的话,默认使用平台的字符集,我这里使用的 IDEA,默认字符集为 utf-8
        //解码 byte --> char
        String str = "中国";
        //编码 char --> byte
        byte[] data = str.getBytes();
        System.out.println(new String(data));

        //设定编码字符集
        data = str.getBytes("gbk");
        //编码解码字符集不统一出现乱码
        System.out.println(new String(data));

        //编码
        byte[] data2 = "中国".getBytes("utf-8");
        //解码
        str = new String(data2,"utf-8");
        System.out.println(str);
    }

    public static void reason01() {
        String str = "中国";
        byte[] data = str.getBytes();
        //字节数不完整
        System.out.println(new String(data,0,4));
    }
}

其他流

一、节点流

1、字节数组 字节 节点流

输入流:

ByteArrayInputStream read(byte[] b, int off, int len) + close()

输出流:

ByteArrayOutputStream write(byte[] b, int off, int len) +toByteArray() 不要使用多态

代码如下:

/**
 * 字节数组 节点流
 * 数组的长度有限,数据量不会很大
 */
public class ByteArrayDemo01 {

    public static void main(String[] args) throws IOException {
        read(write());
    }

    /**
     * 输入流 操作与 文件输入流操作一致
     * 读取字节数组
     */
    public static void read(byte[] src) throws IOException {
        //选择流
        InputStream is = new BufferedInputStream(
                new ByteArrayInputStream(src)
        );
        //操作
        byte[] flush = new byte[1024];
        int len = 0;
        while (-1 != (len = is.read(flush))){
            System.out.println(new String(flush,0,len));
        }
    }

    public static byte[] write() throws IOException {
        //目的地
        byte[] dest;
        //选择流
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        //操作写出
        String msg = "我是啦啦啦啦";
        byte[] info = msg.getBytes();
        bos.write(info,0,info.length);
        //获取数据
        dest = bos.toByteArray();
        //释放资源
        bos.close();
        return dest;
    }
}

二、处理流

1、基本类型+String 保留数据+类型

输入流:DataInputStream readXxx

输出流:DataOutputStream writeXxx

2、引用类型 (对象) 保留数据+类型

反序列化 输入流:ObjectInputStream readObject()

序列化 输出流:ObjectOutputStream writeObject()

注意: 1)、先序列化后反序列化;反序列化顺序必须与序列化一致

2)、不是所有的对象都可以序列化,实现了 java.io.Serializable 这个接口的类才可以被序列化

3)、不是所有的属性都需要序列化,有 transient 关键字的属性就不会被序列化。

3、打印流 PrintStream println() print()

4、三个常量 :

System.in |out|err

System.setIn() |setOut() |setErr()

关闭方法

编写工具类,实现关闭流的功能。

public class FileUtil {

    //基于多态实现关闭流的功能
    public static void close(Closeable ... io){
        for (Closeable temp:io){
            try {
                if (temp != null) {
                    temp.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //基于泛型方法实现关闭流的功能
    public static <T extends Closeable> void closeAll(T ... io){
        for (T temp:io){
            try {
                if (temp != null) {
                    temp.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

总结

一、步骤: 创建源 选择流 操作(读取|写出) 释放

二、流

节点流:离数据源|程序最近的流 1、字节流:可以处理一切(纯文本、音频、视频等) 1)、输入流 InputStream FileInputStream ByteArrayInputStream 操作:read(字节数组) a)、中间容器 byte[] flush=new byte[长度] b)、接收长度 int len =0; c)、循环读取 while(-1!=(len=流.read(flush))){} d)、操作:输出、拷贝 2)、输出流 OutputStream FileOutputStream ByteArrayOutputStream 操作:write(字节数组,0,长度) 输出 2、字符流:只能处理纯文本 1)、输入流:Reader FileReader 操作:read(字符数组) a)、中间容器 char[] flush=new char[长度] b)、接收长度 int len =0; c)、循环读取 while(-1!=(len=流.read(flush))){} d)、操作:输出、拷贝 2)、输出流:Writer FileWriter 操作:write(字符数组,0,长度) 输出 处理流: 装饰模式 提高性能增强功能 1、转换流:解码与编码字符集问题 1)、输入流:InputStreamReader ->解码 2)、输出流:OutputStreamWriter ->编码 2、缓冲流:提高性能 1)、输入流:BufferedInputStream BufferedReader 2)、输出流:BufferedOutputStream BufferedWriter 3、处理数据+类型 1)、基本数据类型 + 字符串:必须存在才能读取,读取与写出顺序必须一致 a)、输入流:DataInputStream readXxx b)、输出流:DataOutputStream writeXxx 2)、引用类型:Serializable transient a)、反序列化:ObjectInputStream readObject b)、序列化:ObjectOutputStream writeObject 4、打印流: PrintStream 5、System.in out err setIn setOut 以下流使用新增方法时不能发生多态 ByteArrayOutputStream: toByteArray() BufferedReader: readLine() BufferedWriter:newLine() DataInputStream DataOutputStream ObjectInputStream ObjectOutputStream PrintStream

三、重点
四、操作

0、打印文件|目录

1、文件拷贝

2、关闭流方法

3、文件分割与合并

原文发布于微信公众号 - Java知其所以然(gh_37a1335e2608)

原文发表时间:2018-08-19

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券