File类(IO流、字节流、缓冲流)

目录

File和IO的概述

File的创建功能

绝对路径和相对路径

File的方法

File的删除方法

File获取和判断方法

File的listFile方法

 File练习一

File练习二

File练习三

IO的概述

IO的分类

字节流-字节输出流(写数据)

FileOutputStream构造方法

字节流的注意事项

字节流-一次写多个数据

字节流-两个问题

字节流-trycatch捕获异常

字节流小结

字节流-字节输入流(读数据)

字节流-读多个字节

字节流-文件复制

 字节流-定义小数组拷贝

缓冲流

缓冲流一次读写一个字节

缓冲流-一次读写一个字节底层原理

缓冲流-一次读写一个字节数组

小结


File和IO的概述

目前我们如何存储数据? 有什么弊端?
    经常使用变量, 数组, 集合等
    弊端是数据不能永久化存储, 只要代码运行结果, 所有数据都会丢失

计算机中有没有一个硬件, 可以支持数据永久化存储? 
    有的, 硬盘可以
        
对文件读写的前提条件?
    要知道文件在哪
    
什么是IO流?
        可以对硬盘中的数据进行读写
        双击打开文件 -> 读取硬盘中的数据 -> 读
        按下文件保存 -> 将数据保存在硬盘 -> 写
    
File类的作用?
    读写数据时, 告诉虚拟机要读写的文件/文件夹在哪
    也可以对文件进行常规操作

File的创建功能

File: 是文件和目录路径的抽象表示
        文件和目录可以通过File封装成对象
        File封装的对象仅仅是一个路径名, 可以存在, 也可以不存在

构造方法:

        File(String pathname);     通过指定的字符串路径创建对象

        File(String parent,String child);  通过指定的字符串路径,拼接创建file对象

        File(FIle pathname,String child);通过指定的File对象和字符串路径,拼接创建File对象

代码示例:

// 1. File(String pathname); 通过指定的字符串路径创建File对象 (常用)
        System.out.println(new File("C:\\itheima\\a.txt")); //可以存在也可以不存在

        // 2. File(String parent, String child); 通过指定的字符串路径, 拼接创建File对象
        System.out.println(new File("C:\\itheima", "a.txt")); 
        
        // 3. File(File pathname, Stringchild); 通过指定的File对象和字符串路径, 拼接创建File对象
        System.out.println(new File(new File("C:\\itheima"), "a.txt")); 
    }

绝对路径和相对路径

绝对路径: 从盘符开始的路径
            File file1 = new File("C:\\itheima\\a.txt");

相对路径: 相对当前项目
            File file2 = new File("a.txt");

相对路径: 模块下的路径
            File file3 = new File("模块名\\a.txt");

File的方法

public boolean createNewFile();

创建一个新的空文件夹

(路径要正确,文件名不能重复,只能创建文件)

public boolean mkdir();

创建一个单级文件夹

(不常用,只能创建单级文件夹)

public boolean mkdirs();

创建一个多级文件夹

(常用,可以创建单级和多级,只能创建文件夹)

//3. public boolean mkdirs(); 创建一个多级文件夹
    private static void method03() {
        //注意一: 可以创建单级文件夹, 也可以创建多级文件夹
        System.out.println(new File("C:\\itheima\\day11\\com\\itheima\\test").mkdirs()); //true

        //注意二: 不管调用者有没有书写文件后缀, 只能创建文件夹
        System.out.println(new File("C:\\itheima\\demo.test").mkdirs()); //true
    }

    //2. public boolean mkdir(); 创建一个单级文件夹
    private static void method02() {
        //注意一: mkdir只能创建单级文件夹, 不能创建多级
        System.out.println(new File("C:\\itheima\\aaa\\bbb\\ccc").mkdir()); //false 路径错误

        //注意二: 不管调用者有没有书写文件后缀, 只能创建文件夹
        System.out.println(new File("C:\\itheima\\aaa.txt").mkdir()); //在itheima下创建aaa.txt文件夹
    }

    //1. public boolean createNewFile(); 创建一个新的空文件
    private static void method01() throws IOException {
        //注意一: 如果路径中的文件夹不存在, 报错, java.io.IOException: 系统找不到指定的路径
        //File file = new File("C:\\itheimahaha\\a.txt");

        //注意二: 如果文件存在, 创建失败返回false; 如果文件不存在, 创建成功返回true
        //File file = new File("C:\\itheima\\a.txt");

        //注意三: 不管调用者有没有书写文件后缀, 只能创建文件
        //File file = new File("C:\\itheima\\aaa");

        File file = new File("C:\\itheima\\a.txt"); //如果a.txt不存在, 创建成功返回true
        System.out.println(file.createNewFile());
    }

File的删除方法

        public boolean delete();     删除文件或文件夹(只能删除文件和"空"文件夹,而且不走回收站)

public static void main(String[] args) {
            //注意一: 如果删除文件夹, 文件夹必须是空的
            //File file = new File("C:\\itheima");
            //System.out.println(file.delete()); //false

            File file = new File("C:\\itheima\\demo");
            System.out.println(file.delete()); //true

            File file1 = new File("C:\\itheima\\a.txt");
            System.out.println(file1.delete()); //true
        }

File获取和判断方法

public boolean isDirectory();判断路径是否为文件夹
public boolean isFile();判断路径是否为文件     
public boolean exists();判断路径是否存在
public boolean getName();返回路径表示的文件或文件名

代码示例:

public static void main(String[] args) {
            //1. public boolean isDirectory(); 判断路径是否为文件夹
            File file = new File("C:\\itheima\\day11");
            System.out.println(file.isDirectory()); //true day11是文件夹

            //2. public boolean isFile(); 判断路径是否为文件
            System.out.println(file.isFile()); //false day11不是文件

            //3. public boolean exists(); 判断路径是否存在
            System.out.println(file.exists()); //true
            File file1 = new File("C:\\itheima\\day88");
            System.out.println(file1.exists()); //false day88不存在

            //4. public String getName(); 返回路径表示的文件或文件夹名
            System.out.println(file.getName()); //day11
            //a.txt 如果是文件名会带后缀
            System.out.println(new File("C:\\itheima\\a.txt").getName()); 
        }

File的listFile方法

File的高级获取方法listFile

        public File[ ] listFiles();         返回路径表示的目录的文件夹和文件的对象数组

public static void main(String[] args) {
            File file = new File("C:\\itheima");
            File[] files = file.listFiles();
            for (File path : files) {
                System.out.println(path); //打印C盘itheima包中的所有内容包括隐藏项目
                /*
                    注意事项:
                        *.调用者不存在时: 返回null
                        1.调用者是一个文件时: 返回null
                        2.调用者是一个空文件夹时: 返回一个长度为0的数组
                        3.调用者是一个有内容的文件夹时: 返回路径表示的目录中的文件夹, 
                          和文件的对象数组, 包括隐藏隐藏项目 (本案例所示)
                        4.调用者是一个有权限才能进入的文件夹时: 返回null
                 */
            }
        }

 File练习一

练习一: 在当前模块下的aaa文件夹下创建一个a.txt

代码示例
    public class Demo {
        public static void main(String[] args) throws IOException {
            File file = new File("day11\\aaa");
            if(!file.exists()){
                //如果aaa文件夹不存在, 创建该文件夹
                file.mkdirs();
            }
            //拼接路径创建, 继续创建文件a.txt
            File newFile = new File(file,"a.txt");
            System.out.println(newFile.createNewFile()); //true 可以不接收
        }
    }

File练习二

练习二: 删除一个多级文件夹
    1. 删除文件夹中所有内容
    2. 删除该文件夹

解决所有文件夹和递归相结合的思路
    1. 进入: 得到src文件夹里面所有内容的File对象
    2. 遍历: 得到src文件夹里面每一个文件和文件夹的File对象
    3. 判断1: 如果是文件, 直接删除
    4. 判断2: 如果是文件夹, 递归继续判断
    5. 删除该文件夹
    
代码示例
    public class Demo {
        public static void main(String[] args) {
            //创建File对象指定路径
            File file = new File("C:\\itheima");
            //调用方法删除
            deleteDor(file);
        }

        public static void deleteDor(File src) {
            //1. 进入: 得到src文件夹里面所有内容的File对象
            File[] files = src.listFiles();
            //2. 遍历: 得到src文件夹里面每一个文件和文件夹的File对象
            for (File file : files) {
                //3. 判断1: 如果是文件,直接删除
                if (file.isFile()) {
                    file.delete();
                } else {
                    //4. 判断2: 如果是文件夹,递归继续判断
                    deleteDor(file);
                }
            }
            //5. 删除该文件夹
            src.delete();
        }
    }

File练习三

练习三: 统计一个文件中, 每种文件的个数并打印
打印格式如下:
    txt:3个
    doc:4个
    jpg:6个
    
代码示例
    public class Demo {
        public static void main(String[] args) {
            // 统计一个文件夹中, 每种文件出现的次数
            // 如果使用变量统计, 弊端是同时只能统计一种文件

            // 创建File对象指定路径
            File file = new File("day11");
            // 利用map集合进行数据统计, 键代表文件后缀名, 值代表出现次数
            HashMap<String, Integer> map = new HashMap<>();
            // 调用方法
            getCount(map, file);
            // 打印集合
            System.out.println(map); //{txt=1, java=28, iml=1}
        }

        private static void getCount(HashMap<String, Integer> map, File file) {
            // 1.进入,获取所有文件和文件夹对象数组
            File[] files = file.listFiles();
            // 2.遍历获取到所有文件和文件夹对象
            for (File f : files) {
                // 3.如果是文件
                if (f.isFile()) {
                    // 4.通过.切割, 获取切割后的数组
                    String fileName = f.getName();
                    String[] arr = fileName.split("\\.");
                    // 5.只统计有且仅有一个后缀的文件
                    if (arr.length == 2) {
                        // 6.获取后缀
                        String fineEndName = arr[1];
                        // 7.在集合判断该后缀是否存在
                        if (map.containsKey(fineEndName)) {
                            // 8.后缀名存在, 根据key将已经出现的次数value获取
                            Integer count = map.get(fineEndName);
                            // 9.该后缀的文件又出现一次
                            count++;
                            // 10.更新出现次数
                            map.put(fineEndName, count);
                        } else {
                            // 11.后缀名不存在, 添加到集合, 1代表第一次出现
                            map.put(fineEndName, 1);
                        }
                    }
                } else {
                    // 12.如果是文件夹, 递归
                    getCount(map, f);
                }
            }
        }
    }

IO的概述

目前我们如何存储数据? 有什么弊端?

    经常使用变量,数组,集合等
    弊端是数据不能永久化储存,只要代码运行结果,所有数据都会丢失

思考问题?

在数据传输过程中, 是谁在读? 是谁在写? 这个参照物是什么?
         IO的数据传输可以看做是一种数据的流动,按照流动的方向,已内存为参照物,进行读
         写操作,简单来说就是内存在读,内存在写
                       I 表示input,读      从硬盘到内存
                      O 表示output,写     从内存到硬盘

学习IO流的目的?

    目标1: 将数据写到文件中,实现数据的永久化存储
    目标2: 读取文件中已存在的数据

IO的分类

IO流按"流向"分类
            1. 输入流
            2. 输出流
    
IO流按"类型"分类 (一般来说按类型分类比较多)
            1. 字节流 -> 操作所有类型的文件 (包括视频音频图片等)
            2. 字符流 -> 只能操作纯文本 (包括java文件,txt文件等)

什么是纯文本文件?
            在windows中使用记事本打开"能读懂的", 就是纯文本文件 (使用字符流操作)
            在windows中使用记事本打开"读不懂的", 就是非纯文本文件 (使用字节流操作)
    
以下文件使用什么流操作?
            office - 非纯文本文件 - 字节流
            avi    - 非纯文本文件 - 字节流
            map3   - 非纯文本文件 - 字节流
            图片    - 非纯文本文件 - 字节流
            txt    - 纯文本文件 - 字符流

字节流-字节输出流(写数据)

字节流写数据的步骤

        1. 创建字节流输出流对象

        2. 写数据

        3. 释放资源

FileOutputStream构造方法

        FileOutputStream(File file);        写数据到指定的File对象的文件

        FileOutputStream(String name);  写数据到指定名称的文件

代码实现:

public static void main(String[] args) throws IOException {
     //1. 创建字节输出流对象 (告诉JVM要往哪个文件中写数据)
     // FileOutputStream fos = new FileOutputStream(new File("C:\\Demo01.txt"));
     FileOutputStream fos = new FileOutputStream("C:\\itheima\\Demo01.txt");
            /*
              FileOutputStream带参构造源码
                  public FileOutputStream(String name) throws FileNotFoundException {
                      // 会判断传递进来的字符串参数是不是null
                      // 不是null就封装为File对象, 所以两种构造一个意思
                      this(name != null ? new File(name) : null, false);
                  }
             */
            //2. 写数据
            fos.write(97);
            //3. 释放资源
            fos.close();
        }

字节流的注意事项

字节流写数据注意事项
    1. 如果文件不存在, 则会自动创建
    2. 如果文件路径不存在, 则会报错java.io.FileNotFoundException(系统找不到指定的路径。)
    3. 如果文件存在, 则会先清空, 再写入数据
    4. 写出的整数是在码表中对应的字母
    5. 每次操作完流必须释放资源, 相当于告诉计算机这个文件使用完毕了

代码示例:

public static void main(String[] args) throws IOException {
         //注意一: 如果文件不存在, 则会自动创建
        //注意二: 如果文件路径不存在, java.io.FileNotFoundException(系统找不到指定的路径)
         //FileOutputStream fos = new FileOutputStream("C:\\itheima11\\Demo01.txt");

         //注意三: 如果文件存在, 则会先清空, 再写入数据
         FileOutputStream fos = new FileOutputStream("C:\\itheima\\Demo01.txt");
         //注意四: 写出的整数是在码表中对应的字母
         fos.write(97); //a
         fos.write(98); //b
         fos.write(99); //c
         //注意五: 每次操作完流必须释放资源, 相当于告诉计算机这个文件使用完毕了              
         fos.close();

        }

字节流-一次写多个数据

字节流写数据的三种方式

void write(int b); 一次写一个
void write(byte[ ] b);一次写一个字节数组
void write(byte[ ] b, int off, int len);一次写一个字节数组的一部分
public static void main(String[] args) throws IOException {
            FileOutputStream fos = new FileOutputStream("day11\\Demo02.txt");

            //1. void write(int b); 一次写一个

            //2. void write(byte[] b); 一个写一个字节数组
            //byte[] bytes = {55,56,57};
            //fos.write(bytes); //789

            //3. void write(byte[] b, int off, int len); 一个写一个字节数组的一部分
            byte[] bytes = {55,56,57,58,59,60,61,62,63,64,65};
            //参数(字节数组,从几索引开始,写几个);
            fos.write(bytes,0,5); //789:;

            //释放资源
            fos.close();
        }

字节流-两个问题

问题1: 字节流写数据, 如何实现换行?
    windows: \r\n -> 需要转为字节数字,通过String的.getBytes()方法实现
    linux: \n
    max: \r
                fos.write("\r\n".getBytes());
    
问题2: 字节流写数据,如何实现追加写入呢?
        FileOutputStream fos = new FileOutputStream("...", true);
        构造方法第二个参数, 不写默认为false表示不能追加, 写true表示可以追加写入, 不会再清空旧文本

字节流-trycatch捕获异常

如果出现异常, 如何让close方法一定会执行呢?
    finally: 在异常处理时, 提供finally块来执行所有清除(释放)操作
    特点: 被finally控制的语句一定会执行, 除非JVM退出

格式:
        try  {
            //可能出现异常的代码块
        }  catch(异常)  {
            //处理异常的代码
        }  finally  {
            //清除(释放)操作
        }

public static void main(String[] args) {
        	//提升fos作用域
            FileOutputStream fos = null;
            try {
                //可能出现异常的代码块
                fos = new FileOutputStream("day11\\Demo04.txt");
                fos.write(97);
            } catch (IOException e) {
                //处理异常的代码
                e.printStackTrace();
            }finally {
                //清除(释放)操作
                //注意: 对fos做非空判断, 避免空指针异常
                if(fos != null){
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

字节流小结

字节流写数据步骤
    1. 创建字节输出流对象

        FileOutputStream(File file); //写数据到指定File对象的文件
        FileOutputStream(String name); //写数据到指定名称的文件

     2. 写数据

        (1)字节流写数据注意事项

            1. 如果文件不存在, 则会自动创建
            2. 如果文件路径不存在, java.io.FileNotFoundException(系统找不到指定的路径。)
            3. 如果文件存在, 则会先清空, 再写入数据
            4. 写出的整数是在码表中对应的字母
            5. 每次操作完流必须释放资源, 相当于告诉计算机这个文件使用完毕了字节流写
                数据的三种方式
            6. void write(int b); 一次写一个
            7. void write(byte[] b); 一个写一个字节数组
            8. void write(byte[] b, int off, int len); 一个写一个字节数组的一部分

        (2)换行和追加写入问题

    windows: \r\n -> 需要转为字节数字,通过String的.getBytes()方法实现    
    构造方法第二个参数, 不写默认为false表示不能追加, 写true表示可以追加写入, 不会再清空旧文本

     3. 释放资源

        finally: 在异常处理时, 提供finally块来执行所有清除(释放)操作
        特点: 被finally控制的语句一定会执行, 除非JVM退出
        格式: 
            try{
                //可能出现异常的代码块
            }catch(异常){
                //处理异常的代码
            }finally{
                   //清除(释放)操作
                //流.close();
            }

字节流-字节输入流(读数据)

字节流读数据步骤
    1. 创建字节输入流对象
    2. 读数据
    3. 释放资源

字节流写数据注意事项
    1. 如果文件不存在, 报错java.io.FileNotFoundException
    2. 一次读一个数据, 返回值就是字节, 如果不看字节, 需要强转为char类型

public static void main(String[] args) throws IOException {
            //1. 创建字节输入流对象
            //注意一: 如果文件不存在, 报错java.io.FileNotFoundException
            //FileInputStream fis = new FileInputStream("day11\\Demo005.txt");
            FileInputStream fis = new FileInputStream("day11\\Demo05.txt");

            //2. 读数据
            //System.out.println(fis.read()); //97
            //注意二: 一次读一个数据, 返回值就是字节, 如果不看字节, 需要强转为char类型
            System.out.println((char)fis.read()); //a

            //3. 释放资源
            fis.close();
        }

字节流-读多个字节

public static void main(String[] args) throws IOException {
            //读取模块下Demo06.txt文件中的多个数据
            FileInputStream fis = new FileInputStream("day11\\Demo06.txt");

            //文件中多个字节怎么读? 使用循环尝试
    //        while (true){
    //            System.out.println(fis.read());
    //        }

            //读取完有效数据后, 一直打印-1, -1代表文件到达了结尾
            //定义变量, 让变量记录读取的结果, 如果变量不等于-1满足,
                // 证明文件还没有读完, 那就继续读

            int b;
            while ((b = fis.read()) != -1) {
                //System.out.println(b); //只需要将b强转为char即可
                System.out.println((char) b);
            }

            //国际惯例
            fis.close();
        }

字节流-文件复制

需求: 将目标路径下的a.avi复制到当前模块下
分析: 
	复制文件, 就是就是将数据从一个文件中读取出来(数据源), 然后写入到另一个文件(目的地)
    数据源 -> 读数据 -> FileInputStream    
    目的地 -> 写数据 -> FileOutputStream    

代码示例
    public class Demo {
        public static void main(String[] args) throws IOException {
            //数据源 -> 读数据 -> FileInputStream
            FileInputStream fis = new FileInputStream("C:\\itheima\\a.avi");
            //目的地 -> 写数据 -> FileOutputStream
            FileOutputStream fos = new FileOutputStream("day11\\a.avi");
            //将数据从一个文件中读取出来(数据源), 然后写入到另一个文件(目的地)
            int b;
            while ((b = fis.read()) != -1) {
                fos.write(b);
            }
            //国际惯例
            fis.close();
            fos.close();
        }
    }

 字节流-定义小数组拷贝

如果操作的数据过大, 那么速度会有什么影响? 慢!  
            硬盘(数据源) ->
            创建输入流对象 ->
            内存(read() -> int b -> write()) ->
            创建输出流对象 ->
            目的地
                    一个字节上述过程会走一次

如何提升拷贝速度的效率?
    为了解决速度问题, 字节流通过创建字节数组, 可以"一次性读取数个数据并写多个数据"

一次读取一个字节数组的方法:
    public int read(byte[] b); 从输入流读取最多b.length个字节的数据
    返回的是读到的, 实际有效字节的个数

代码示例:(重点)

public static void main(String[] args) throws IOException {
            //数据源 -> 读数据 -> FileInputStream
            FileInputStream fis = new FileInputStream("C:\\itheima\\a.avi");
            //目的地 -> 写数据 -> FileOutputStream
            FileOutputStream fos = new FileOutputStream("day11\\a.avi");

            // 1.创建"鸡蛋篮子", 也就是一次读取的数据集合
            byte[] bytes = new byte[1024];
            // 2.len代表本次读到的有效数据个数(读到了几个有效字节)
            int len;
            // 3.如果没到-1代表没到文件结尾, 继续读
            while ((len = fis.read(bytes)) != -1) {
                // 4.从0开始, 写入读到的(bytes)所有有效字节(len)
                fos.write(bytes, 0, len);
            }

            //国际惯例
            fis.close();
            fos.close();
        }

小数组拷贝原理:

主要理解变量len的作用
    读数据时, len代表代表本次读到的有效数据个数, 也就是读到了几个有效字节
    写数据时, len代表写的个数

缓冲流

目前的读写效率很低
    读一个字节, 写一个字节
    读一个小数组, 写一个小数组中读到的所有数据

字节缓冲流的作用 :  提升读写效率

BufferOutputStream: 字节缓冲输出流, 构造接收一个字节流对象, 而不是文件路径
        BufferOutputStream(OutputStream out);


BufferedInputStream: 字节缓冲输入流, 构造接收一个字节流对象, 而不是文件路径
        BufferedInputStream(InputStream in)

字节缓冲流仅仅提供了缓冲区(一个数组), 用来提升读写效率
字节缓冲流不能直接操做文件中的数据, 真正操做流中数据的, 还是基本的字节流

缓冲流一次读写一个字节

public static void main(String[] args) throws IOException {
            //数据源
            BufferedInputStream bis = new BufferedInputStream(new 
                                    FileInputStream("C:\\itheima\\lesson.mp4"));
            //目的地
            BufferedOutputStream bos = new BufferedOutputStream(new 
                   FileOutputStream("C:\\Users\\Administrator\\Desktop\\lesson.mp4"));
            //一次读一个字节
            int b;
            while ((b = bis.read()) != -1) {
                bos.write(b);
            }
            //国际惯例
            bis.close();
            bos.close();
        }

缓冲流-一次读写一个字节底层原理

1. BufferedInputStream底层: 创建了默认长度为8192的字节数组, BufferedOutputStream底层一样
    // 默认长度
    private static int DEFAULT_BUFFER_SIZE = 8192;
    public BufferedInputStream(InputStream in) {
        this(in, DEFAULT_BUFFER_SIZE);
    }
    // this
    public BufferedInputStream(InputStream in, int size) {
    super(in);
    if (size <= 0) {
        throw new IllegalArgumentException("Buffer size <= 0");
    }
    buf = new byte[size]; // size=8192
}

2. close方法底层: 会将字节流关闭

缓冲流-一次读写一个字节数组

需求: 字节缓冲流结合字节数组提升拷贝效率

代码示例
    public class Demo {
        public static void main(String[] args) throws IOException {
            //数据源
            BufferedInputStream bis = new BufferedInputStream(new 
                                    FileInputStream("C:\\itheima\\lesson.mp4"));
            //目的地
            BufferedOutputStream bos = new BufferedOutputStream(new 
                  FileOutputStream("C:\\Users\\Administrator\\Desktop\\lesson.mp4"));
            //读写
            byte[] bytes = new byte[1024];
            /*
        解释一下数组长度为什么定义为1024
           1. 计量存储和传输的计量是以字节为单位
           2. 1024个字符的数组,这是一个缓冲,java推荐使用8K作为缓冲,也就是8192个字节
           3. BufferedInputStream和BufferedOutputStream底层就是这样么做的
           4. 缓冲区(数组)容量并非固定不变, 可以自行定义, 为提高计算机的运算效率, 将其定义
               为计算机最为熟悉的长度1024B(或者倍数也可以), 这是程序优化的一部分
        总结:
           如果像之前定义int b一个一个字节读取, 在底层相当于一个一个字节"倒手", 效率肥肠
           低那么通过结合字节数组, 在底层相当于一个一个数组"倒手", 效率肥肠高, 但是数组长
           度最好是1024的倍数, 算是对代码的一种优化
             */
            int len;
            while ((len = bis.read(bytes)) != -1) {
            //从bytes中获取数据写到目的地, 每次从索引0开始, 写len读到的有效字节个数这么多
                bos.write(bytes, 0, len);
            }
            //国际惯例
            bis.close();
            bos.close();
        }
    }

小结

字节流: 可以操作(拷贝)所有类型的文件
字节缓冲流: 提升效率, 不能直接操作文件, 需要用字节流当带参构造参数
    
拷贝文件四种方式: 本质是底层传输时"倒手"的数据个数不同, 个数越大效率越高
    1. 字节流一次读一个字节
    2. 字节流一次读一个字节数组
    3. 字节缓冲流一次读一个字节
    4. 字节缓冲流一次读一个字节数组


版权声明:本文为Achao_myFriend原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。