IO流

一、IO流概述

1.1 IO流概述(Input Output Stream)

  1. 在java程序中,对于数据的输入输出操作以”流“(stream)的方式进行

  2. java的流类一般位于java.io包中

  3. 流是一个抽象,动态的概念,是一连串连续动态的数据集合

1.2 IO流分类

1. 按流的方向分类

  • 输入流:数据流向是数据源到程序(以InputStream,Reader结尾的流)

  • 输出流:数据流向是程序到目的地(以OutputStream,Writer结尾的流)

  • 输入输出流的划分是相对程序而言的,并不是相对数据源

2.按处理的数据单元分类

  • 字节流:以字节为单位获取数据,命名一般以Stream结尾,顶级类:InputStream,OutputStream
  • 字符流:以字符为单位获取数据,命名一般以Reader/Writer结尾,顶级类:Reader,Writer

3.按处理对象不同分类

  • 节点流:可以直接从数据源或目的地读写数据,如FileInputStream,FileReader
  • 处理流:不直接连接到数据源或目的地,是”处理流的流“。通过对其他流的处理来提高程序的性能,如:BufferedInputStream,BufferedReader等。处理流也叫包装流
  • 节点流位于IO流的第一线,所有操作必须通过他们进行;处理流可以对节点流进行包装,来提高性能和程序的灵活性

1.3 IO流体系结构

1.字节流

  • InputStream,OutputStream是最基本的输入输出字节类,其他所有的字节输入输出类都继承于这两个类
  • 这两个类都是抽象类,不能创建他们的实例,只能使用他们的子类
  • FilterInputStream,FilterOutputStream是所有包装类的父类

2.Reader,Writer

  • 最基本的字符输入输出类,其他所有的字符输入输出类都继承于这两个类

  • 这两个类都是抽象类,不能创建他们的实例,只能使用他们的子类

二、File类的使用

  • File类代表文件或文件夹

  • 有两个作用:

    • (1)获取文件或文件夹的属性

    • (2)实现对文件或文件夹的创建和删除

代码示例一(获取文件或文件夹的属性):

package com.zhang.file;

import java.io.File;
import java.util.Date;

/**
 * 获取文件或文件夹的属性
 */
public class FileDemo01 {
    public static void main(String[] args) {
        //File file=new File("D:\\Learn\\test.txt");//反斜杠时要写成双斜杠
        File file = new File("D:/Learn");
        System.out.println(file.exists());
        System.out.println(file.getName());
        System.out.println(file.getAbsolutePath());//绝对路径
        System.out.println(file.canRead());
        System.out.println(file.canWrite());
        System.out.println(file.canExecute());
        System.out.println(file.length());
        System.out.println(file.isFile());
        System.out.println(file.isDirectory());
        System.out.println("==========================================");
        File[] files = file.listFiles();
        System.out.println(files.length);
        for (File f : files) {
            System.out.print(f.getName() + "\t" + new Date(f.lastModified()).toLocaleString()+"\t");
            if (f.isDirectory()) {
                System.out.println("文件夹");
            }else{
                System.out.println("文件"+"\t"+f.length());
            }
        }
    }
}

true
Learn
D:\Learn
true
true
true
0
false
true
==========================================
4
20201216官网导出项目包	2021-1-3 21:43:26	文件夹
2_2020最新Java面试题及答案(带完整目录).pdf	2021-8-17 10:05:57	文件	11168753
Java基础	2021-8-19 12:25:27	文件夹
test.txt	2021-8-19 14:07:47	文件	10

Process finished with exit code 0

代码示例二:(实现对文件或文件夹的创建和删除):

  • file.createNewFile();//创建文件

  • file.getParentFile();//得到上级目录

  • dir.mkdir();//创建单级文件夹

  • dir.mkdirs();//创建多级文件夹

package com.zhang.file;

import java.io.File;
import java.io.IOException;

/**
 * 实现对文件或文件夹的创建和删除
 * 存在文件则删除,不存在则创建
 */
public class FileDemo02 {
    public static void main(String[] args) {
        File file = new File("D:\\Learn\\test.txt");
        //存在文件则删除,不存在则创建
        if (file.exists()) {
            System.out.println("存在文件!");
            file.delete();
        } else {
            System.out.println("不存在文件!");
            try {
                System.out.println("创建文件!");
                file.createNewFile();//创建文件
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

package com.zhang.file;

import java.io.File;
import java.io.IOException;

/**
 * 实现对文件或文件夹的创建和删除
 * 存在文件则删除,不存在则创建
 * File不能对文件的内容进行操作,内容操作要用IO流
 */
public class FileDemo03 {
    public static void main(String[] args) {
        File file = new File("D:/Learn/abc/def/test.txt");
        if (file.exists()) {
            System.out.println("存在文件!");
            file.delete();
        } else {
            System.out.println("不存在文件!");
            File dir = file.getParentFile();
            System.out.println("dir::::"+dir);//dir::::D:\Learn\abc\def
            if (!dir.exists()) {
                System.out.println("不存在文件夹,需创建");
               // dir.mkdir();//创建单级文件夹
                 dir.mkdirs();//创建多级文件夹

            }
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

三、文件流

1.文件字节流(FileInputStream,FileOutputStream)

  • FileInputStream,FileOutputStream是字节流,节点流,源文件和目的地都是文件

  • 复制文件需要创建一个输入流和输出流完成文件的读写

  • 需要创建一个中转站,借助循环和中转站完成复制

  • 流使用完毕要关闭,这和垃圾回收没有关系

代码示例1(读一个字节):

package com.zhang.ios;

import java.io.*;

public class IosDemo01 {
    public static void main(String[] args) throws IOException {
        //1.创建输入流和输出流
        File file1 = new File("D:/Learn/test.txt");
        File file2 = new File("D:/Learn/test2.txt");
        InputStream is = new FileInputStream(file1);
        OutputStream os = new FileOutputStream(file2);
        //2.使用输入流和输出流完成文件的复制
        //定义一个中转站n
        int n = is.read();//读一个字节,n为-1时,代表读到末尾
        while (n != -1) {
            os.write(n);
            n = is.read();
        }
        //3.关闭输入流和输出流
        is.close();
        os.close();
    }
}

缺点:中转站太小,速度慢,效率低,复制大文件时效果明显,可将中转站由一个字节变为一个字节数组,减少读取硬盘的次数

代码示例2(读一组字节):

package com.zhang.ios;

import java.io.*;

public class IosDemo02 {
    public static void main(String[] args) throws IOException {
        //1.创建输入流和输出流
       /* File file1 = new File("D:/Learn/test.txt");
        File file2 = new File("D:/Learn/test2.txt");
        InputStream is = new FileInputStream(file1);
        OutputStream os = new FileOutputStream(file2);*/

        //简化上述语句:方法一
       /* InputStream is = new FileInputStream( new File("D:/Learn/test.txt"));
        OutputStream os = new FileOutputStream( new File("D:/Learn/test2.txt"));*/

        //简化上述语句 方法二-->直接写路径,底层还是new File();
        InputStream is = new FileInputStream("D:/Learn/test.txt");
        OutputStream os = new FileOutputStream("D:/Learn/test2.txt");

        //OutputStream os = new FileOutputStream(file2,true);//加true时,在文件内容后面追加内容,默认false,false为覆盖
        //2.使用输入流和输出流完成文件的复制
        //定义一个中转站arr(一个字节数组)
        byte[] arr = new byte[1024];
        int length = is.read(arr);//读内容到字节数组,并返回读取的字节数
        while (length != -1) {//-1时读到文件末尾
            os.write(arr, 0, length);//将字节数组的内容写入文件,只传一个参数时,文件大小一定为1kb,是有问题的
            length = is.read(arr);//再读内容到字节数组
        }
        //3.关闭输入流和输出流
        is.close();
        os.close();
    }
}

异常处理:

package com.zhang.ios;

import java.io.*;

/**
 * 异常处理,即使前面代码出错,也要关闭流,将close()放到finally里
 * is = new FileInputStream(file1);os = new FileOutputStream(file2);放到一个代码块里,
 * is出现异常后没必要在走os
 */
public class IosDemo03 {
    public static void main(String[] args) {
        //1.创建输入流和输出流
        File file1 = new File("D:/Learn/test.txt");
        File file2 = new File("D:/Learn/test2.txt");
        InputStream is = null;
        OutputStream os = null;
        try {
            is = new FileInputStream(file1);
            os = new FileOutputStream(file2);
            //2.使用输入流和输出流完成文件的复制
            //定义一个中转站n
            int n = is.read();//读一个字节
            while (n != -1) {
                os.write(n);
                n = is.read();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //3.关闭输入流和输出流
            try {
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (os != null) {
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


    }
}

异常处理总结

  1. 创建流,使用流要使用一次try-catch语句。
  2. 关闭流要分开进行异常处理。

jdk7异常处理新特征:

  1. try-with-resources:不用显式的进行资源的关闭,只要将资源的实例化对象放到try后面的()中,作用范围是当前try语句,执行完毕(正常完成或发生意外)后就会自动进行关闭,可省略finally语句,更简洁实用
  2. 只有实现了AutoCloseable接口的类才支持上述写法
package com.zhang.ios;

import java.io.*;

/**
 * 实现了AutoCloseable接口的类,不用显式的进行资源的关闭,只要将资源的实例化对象放到try后面的()中,
 * 作用范围是当前try语句,执行完毕(正常完成或发生意外)后就会自动进行关闭,可省略finally语句,更简洁实用
 */
public class IosDemo04 {
    public static void main(String[] args) {
        //1.创建输入流和输出流
        File file1 = new File("D:/Learn/test.txt");
        File file2 = new File("D:/Learn/test2.txt");
        try (InputStream is = new FileInputStream(file1); OutputStream os = new FileOutputStream(file2);) {

            //2.使用输入流和输出流完成文件的复制
            //定义一个中转站n
            int n = is.read();//读一个字节
            while (n != -1) {
                os.write(n);
                n = is.read();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

jdk9异常处理新特征:

try之前定义好对象,try()中引入创建好的对象,如果有多个对象,用;分割。如果try之前定义的对象会抛异常,不推荐使用此种方法,因为需要在方法签名中throws异常,或需要try-catch

package com.zhang.ios;

import java.io.*;

/**
 * java9中,try之前定义好对象,try()中引入创建好的对象,如果有多个对象,用;分割。
 * 如果try之前定义的对象会抛异常,不推荐使用此种方法,因为需要在方法签名中throws异常
 * 下面的例子就不推荐此种方式
 */
public class IosDemo05 {
    public static void main(String[] args) throws FileNotFoundException {
        //1.创建输入流和输出流
        File file1 = new File("D:/Learn/test.txt");
        File file2 = new File("D:/Learn/test2.txt");
        InputStream is = new FileInputStream(file1);
        OutputStream os = new FileOutputStream(file2);
        try (is;os) {
            //2.使用输入流和输出流完成文件的复制
            //定义一个中转站n
            int n = is.read();//读一个字节
            while (n != -1) {
                os.write(n);
                n = is.read();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

2.文件字符流

  • 使用字节流可以读写任意类型的文件,字符流只可以读写文本文件(使用记事本可以打开的文件),doc不是文本文件
  • 使用字符流的好处:处理非英文方便。一个汉字按字节流读的话需要读多次
  • 其实只有字节流,没有字符流,字符流的底层还是字节流
  • FileReader,FileWriter是字符流,是节点流

代码示例1(一个字符):

package com.zhang.ios;

import java.io.*;

public class IosDemo05 {
    public static void main(String[] args) throws IOException {
        //创建输入输出流
        Reader reader = new FileReader("D:/Learn/test.txt");
        Writer writer = new FileWriter("D:/Learn/test2.txt");
        //创建中转站
        int n = reader.read();//读一个字符
        while (n != -1) {
            writer.write(n);//写一个字符
            n = reader.read();//读一个字符

        }
        //关闭流
        reader.close();
        writer.close();
    }
}

代码示例1(一个字符数组):

package com.zhang.ios;

import java.io.*;

public class IosDemo06 {
    public static void main(String[] args) throws IOException {
        //创建输入输出流
        Reader reader = new FileReader("D:/Learn/test.txt");
        Writer writer = new FileWriter("D:/Learn/test2.txt");
        //创建中转站
        char[] ch = new char[1024];
        int length = reader.read(ch);//读一个字符数组
        while (length != -1) {
            writer.write(ch,0,length);//写一个字符数组
            length = reader.read(ch);//读一个字符数组
        }
        //关闭流
        reader.close();
        writer.close();
    }
}

四、缓冲流

1.缓冲字节流(BufferedInputStream,BufferedOutPutStream)

  • 使用缓冲流可以提高读写的速度,BufferedInputStream,BufferedOutputStream

  • 关闭高层流即可,关闭高层流的底层就是在关闭底层流

  • 缓冲流为什么可以提高查询速度?

    引入了输入输出两个缓冲区(缓冲区在内存上),大大减少了读取硬盘的次数

  • 如何刷新输出缓冲区(刷新的时候将缓冲区的内容写到硬盘上)?

    • 方法一:bos.close();底层是先刷新在关闭流
    • 方法二:直接bos.flush()
    • 方法三:缓冲区满,自动刷新

代码示例:

package com.zhang.buffered;

import java.io.*;

/**
 * 1.使用缓冲流可以提高读写的速度,BufferedInputStream,BufferedOutputStream
 * 2.关闭高层流即可,关闭高层流的底层就是在关闭底层流
 * 3.缓冲流为什么可以提高查询速度?
 *  引入了输入输出两个缓冲区(缓冲区在内存上),大大减少了读取硬盘的次数
 * 4.如何刷新输出缓冲区?
 *  方法一:bos.close();底层是先刷新在关闭流
 *  方法二:直接bos.flush()
 *  方法三:缓冲区满,自动刷新
 */
public class Demo01 {
    public static void main(String[] args) throws IOException {
        //1.创建输入流和输出流
        /*File file1 = new File("D:/Learn/test.txt");
        File file2 = new File("D:/Learn/test2.txt");
        InputStream is = new FileInputStream(file1);
        OutputStream os = new FileOutputStream(file2);
        BufferedInputStream bis = new BufferedInputStream(is);
        BufferedOutputStream bos = new BufferedOutputStream(os);*/

        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:/Learn/test.txt"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:/Learn/test2.txt"));

        //2.使用输入流和输出流完成文件的复制
        //定义一个中转站n
        int n = bis.read();//读一个字节
        while (n != -1) {
            bos.write(n);
            n = bis.read();
        }
        //3.关闭输入流和输出流
        bis.close();
        bos.close();
    }
}

2.缓冲字符流(BufferedReader,BufferedWriter)

  • 优点:速度快,简化编程

  • readLine()底层原理:

    底层还是一个一个字符读,append()放入到StringBuilder(或者char[])中,遇到换行符将StringBuilder(或者char[])转换成String并返回

  • 不同的操作系统换行符不同,要通过bufferedWriter.newLine() 换行

    • Unix系统,每行结尾只有<换行>,即“\n”
    • Windows系统,每行结尾是<回车><换行>,即“\r\n”
    • Mac系统,每行结尾只有<回车>,即“\r”

代码示例:

package com.zhang.buffered;

import java.io.*;

public class Demo02 {
    public static void main(String[] args) throws IOException {
        //创建输入输出流
        BufferedReader bufferedReader = new BufferedReader(new FileReader("D:/Learn/test.txt"));
        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("D:/Learn/test2.txt"));
        //中转站 按行读写
        String line = bufferedReader.readLine();
        while (line != null) {
            bufferedWriter.write(line);
            bufferedWriter.newLine();//换行
            line = bufferedReader.readLine();
        }
        //关闭流
        bufferedReader.close();
        bufferedWriter.close();
    }
}

五、数据流和对象流

  • 数据流和对象流可以很方便的实现对各种基本数据类型和引用类型的的读写
  • 只有字节流,没有字符流
  • 都是处理流,不是节点流
  • 数据流只能操作基本数据类型和字符串,对象流还可以操作对象
  • 写入的是二进制数据,无法通过记事本等直接查看
  • 写入的数据需要使用对应的输入流来读

1.数据流(DataInputStream,DataOutputStream)

代码示例:

package com.zhang.dataobjectios;

import java.io.*;

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

    public static void read() throws IOException {
        //创建输入流
        InputStream inputStream = new FileInputStream("D:/Learn/test.txt");
        BufferedInputStream bis = new BufferedInputStream(inputStream);
        DataInputStream dis = new DataInputStream(bis);
        //读数据
        System.out.println(dis.readBoolean());
        System.out.println(dis.readChar());
        System.out.println(dis.readFloat());
        System.out.println(dis.readInt());
        System.out.println(dis.readUTF());
       
        //关闭流
        dis.close();

    }

    public static void write() throws IOException {
        //创建输出流
        OutputStream outputStream = new FileOutputStream("D:/Learn/test.txt");
        BufferedOutputStream bos = new BufferedOutputStream(outputStream);
        DataOutputStream dos = new DataOutputStream(bos);
        //写入数据
        dos.writeBoolean(true);
        dos.writeChar('a');
        dos.writeFloat(1.4f);
        dos.writeInt(1);
        dos.writeUTF("测试");
        //关闭流
        dos.close();
    }
}

2.对象流(ObjectInputStream,ObjectOutputStream)

  • 写入对象时,如new Student()对象,Student需实现Serializable接口,否则写入文件时报错(java.io.NotSerializableException: com.zhang.dataobjectios.Student)

代码示例:

package com.zhang.dataobjectios;

import java.io.*;
import java.util.Date;

public class DataDemo02 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //write();
        read();
    }

    public static void read() throws IOException, ClassNotFoundException {
        //创建输入流
        InputStream inputStream = new FileInputStream("D:/Learn/test.txt");
        BufferedInputStream bis = new BufferedInputStream(inputStream);
        ObjectInputStream ois = new ObjectInputStream(bis);
        //读数据
        System.out.println(ois.readBoolean());
        System.out.println(ois.readChar());
        System.out.println(ois.readFloat());
        System.out.println(ois.readInt());
        System.out.println(ois.readUTF());
        Date date = (Date) ois.readObject();
        System.out.println(date);
        Student student = (Student) ois.readObject();
        System.out.println(student);


        //关闭流
        ois.close();

    }

    public static void write() throws IOException {
        //创建输出流
        OutputStream outputStream = new FileOutputStream("D:/Learn/test.txt");
        BufferedOutputStream bos = new BufferedOutputStream(outputStream);
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        //写入数据
        oos.writeBoolean(true);
        oos.writeChar('a');
        oos.writeFloat(1.4f);
        oos.writeInt(1);
        oos.writeUTF("测试");
        oos.writeObject(new Date());
        oos.writeObject(new Student(1, "zhangsan", 89.0, 23));//Student类需实现Serializable接口
        //关闭流
        oos.close();
    }
}

3.序列化和反序列化

(1)什么是序列化和反序列化
  • 序列化:Serialization 将对象的状态信息转换为可以存储或传输的形式的过程

    对象(内存)------------->字节数组,字节序列(外存,网络)

  • 反序列化:DeSerialization

    字节数组,字节序列(外存,网络)------------->对象(内存)

(2)什么时候需要序列化和反序列化

​ 存储或传输,如存储到硬盘(外存),传输到网络

(3)如何实现序列化和反序列化

​ 相应的类实现Serializable接口,如上述例子的Student类

​ writeObject实现了序列化

​ readObject()实现了反序列化

(4)序列化细节
  • 序列化接口没有任何方法 ; 作为分支判断的条件,如果 obj instanceof Serializable,则。。。走相应代码

  • static属性不参与序列化,如果属性被static修饰了,读对象内容时会读到null

  • 如果不希望某个属性参与序列化,需要使用transient修饰;不参与序列化,会读到null

  • Exception in thread "main" java.io.InvalidClassException: com.zhang.dataobjectios.Student; local class incompatible: stream classdesc serialVersionUID = 4509852492044774476, local class serialVersionUID = 6744612193840171701 即写入文件成功后,修改了Student的属性,重新读文件时报这种错(修改属性会新生成序列号)

    • 解决方案:给出固定的序列化版本号

六、其他流

(1)打印流(PrintStream,PrintWriter)

  • PrintStream字节流,PrintWriter字符流;都是处理流

  • 只有输出流,没有输入流

  • System.out,System.err是PrintStream的实例变量

  • System.out.println() 方法是PrintStream的,可以将各种类型的数据写入文件,不能保留原来的类型,都变成String类型,且是可读的

  • PrintWriter: servlet动态网页用到

    代码示例一(PrintStream)

package com.zhang.ios.iosother;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Scanner;

/**
 * System.out.println() 方法是PrintStream的,可以将各种类型的数据写入文件,不能保留原来的类型,都变成String类型,且是可读的
 * System.in.read();   public final static InputStream in = null;
 * System.err.print(1);    public final static PrintStream err = null;
 *
 * PrintWriter: servlet动态网页用到
 */
public class Demo01 {
    public static void main(String[] args) throws IOException {
       /* PrintStream ps = System.out;//    public final static PrintStream out = null;
        ps.println("d");
        ps.println('a');
        ps.println(1.2f);*/
        PrintStream ps = new PrintStream("D:/Learn/test.txt");
        ps.print(1);
        ps.print(true);
        ps.close();

        new Scanner(System.in);//从键盘读取数据    public final static InputStream in = null;
        InputStream is = new FileInputStream("D:/Learn/test.txt");
        System.out.println(new Scanner(is).next());//从文件读取
         System.err.print(1);//打印到控制台,红色字体,错误提示的    public final static PrintStream err = null;

    }
}

代码示例二(PrintWriter):

  • PrintWriter..println();可直接按行写数据
  • PrintWriter的底层还是BufferedWriter
package com.zhang.ios.iosother;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;

public class Demo02 {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("D:/Learn/test.txt"));
        //BufferedWriter bw = new BufferedWriter(new FileWriter("D:/Learn/test2.txt"));
        //用PrintWriter可以不用换行
        PrintWriter pw = new PrintWriter("D:/Learn/test2.txt");
        String s = br.readLine();
       /* while (s != null) {
            bw.write(s);
            bw.newLine();
            s = br.readLine();
        }
        bw.close();*/

        while (s != null) {
            pw.println(s);
            s = br.readLine();
        }
        pw.close();
        br.close();
    }
}

PrintWriter源码:
public PrintWriter(String fileName) throws FileNotFoundException {
        this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName))),
             false);
    }

(2) 转换流(InputStreamReader,OutPutStreamWriter)-适配器模式

  • 实现字节流->字符流的转换,是适配器设计模式的应用

  • 只能字节流转换为字符流

    package com.zhang.ios.iosother;
    
    import java.io.*;
    
    public class Demo03 {
        public static void main(String[] args) throws IOException {
            InputStream inputStream = System.in;//从键盘读
            Reader reader = new InputStreamReader(inputStream);//InputStreamReader 转换流:字节流->字符流
            BufferedReader br = new BufferedReader(reader);
    
            PrintWriter pw = new PrintWriter("D:/Learn/test2.txt");
            String str = br.readLine();
    
            while (!str.equals("bye")) {
                pw.println(str);
                str = br.readLine();
            }
            pw.close();
            br.close();
        }
    }
    
    

(3)字节数组流

  • ByteArrayInputStream,ByteArrayOutputStream,是节点流,数据源是字节数组

  • 用法和FileInputStream,FileOutputStream一致

  • CharArrayReader,CharArrayWriter是字符节点流

(4)IO流的设计模式

  • 装饰模式,动态组装流,减少子类的数量
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:/Learn/test.txt"));

BufferedInputStream(处理流)相当于是FileInputStream(节点流)的装饰
  
  • 面试题:IO流体系结构使用了什么设计模式?(1:装饰模式,2:适配器模式)

七、复制文件夹

  • 问题一:使用字节流还是字符流?

    • 使用字节流,可能含有图片,视频等文件
  • 如何提高复制速度

    • BufferedInputStream,BufferedOutPutStream//缓冲流

    • byte[] arr = new byte[1024];//大的中转站

  • 涉及到的技能点

    • IO流:文件的复制,IO流只能去写文件,不能读写文件夹
    • 递归:各级文件和文件夹的递归复制
    • File类:文件夹的定义和创建
  • 问题的迭代

    • 复制一个文件
    • 复制一个文件夹下的所有文件
    • 复制一个文件下的所有文件和子文件夹

    代码示例:

    package com.zhang.ios.copydir;
    
    import java.io.*;
    
    public class CopyDir {
        public static void main(String[] args) {
            copyDir("D:/Learn", "D:/Learn2");
        }
    
        /**
         * 复制文件夹
         *
         * @param sourceDir
         * @param destDir
         */
        public static void copyDir(String sourceDir, String destDir) {
            //源文件夹不存在,提示
            File dir = new File(sourceDir);
            if (!dir.exists()) {
                System.err.print("源文件夹不存在!");
                return;
            }
            //目标文件夹不存在,创建
            File dir2 = new File(destDir);
            if (!dir2.exists()) {
                dir2.mkdirs();
            }
    
            File[] files = dir.listFiles();//获取源文件下的文件(文件夹)
            for (File file : files) {
                //复制文件夹下的文件到新文件夹下
                if (file.isFile()) {
                    copyFile(sourceDir + "/" + file.getName(), destDir + "/" + file.getName());
                }
                //复制文件夹下的文件夹到新文件夹下
                if (file.isDirectory()) {
                    copyDir(sourceDir + "/" + file.getName(), destDir + "/" + file.getName());
                }
            }
    
        }
    
        /**
         * 复制文件
         *
         * @param sourceFile
         * @param destFile
         */
        public static void copyFile(String sourceFile, String destFile) {
            //创建输入输出流
            try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourceFile));
                 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));) {
                //读写数据
                //中转站
                byte[] buf = new byte[1024];
                int len;
                while ((len = bis.read(buf)) != -1) {
                    bos.write(buf, 0, len);
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            //关闭流,自动关闭
        }
    
    }
    
    

posted @ 2021-08-20 18:12  wlbsm  阅读(218)  评论(0编辑  收藏  举报