【Java从入门到大牛】File和IO流上篇

本文由 程序喵正在路上 原创,CSDN首发!
系列专栏:Java从入门到大牛
首发时间:2023年8月9日
欢迎关注点赞收藏留言
一以贯之的努力 不得懈怠的人生

目录

  • 存储数据的方案
  • File
    • 创建对象
    • 常用方法1:判断文件类型、获取文件信息
    • 常用方法2:创建文件、删除文件
    • 常用方法3:遍历文件夹
  • 前置知识:方法递归
    • 认识递归的形式
    • 应用、执行流程、算法思想
    • 案例:文件搜索
    • 案例:删除非空文件夹
    • 案例:啤酒问题
  • 前置知识:字符集
    • 常见字符集介绍
    • 总结
    • 字符集的编码、解码操作
  • IO流
  • IO流-字节流
    • 文件字节输入流:每次读取一个字节
    • 文件字节输入流:每次读取多个字节
    • 文件字节输入流:一次读取完全部字节
    • 文件字节输出流:写字节出去
    • 案例:文件复制
  • 释放资源的方式
    • try-catch-finally
    • try-with-resource

存储数据的方案

double money = 9999.5;							// 变量
int[] age = new int[100];						// 数组
Student s = new Student();						// 对象
List<Student> students = new ArrayList<>();		// 集合

上面这些都是内存中的数据容器,它们记住的数据,在断点或者程序终止时会丢失

有些数据想长久保存起来,该怎么办 ?

  • 文件是非常重要的存储方式,在计算机硬盘中
  • 即便断电或者程序终止了,存储在硬盘文件中的数据也不会丢失

File

File 是 java.io 包下的类,File 类的对象,用于代表当前操作系统的文件(可以是文件或者文件夹)

注意:File 类只能对文件本身进行操作,不能读写文件里面存储的数据

IO流

用于读写数据的,可以读写文件或者网络中的数据…

File

创建对象

创建File类的对象
【Java从入门到大牛】File和IO流上篇_第1张图片

  • File 对象既可以代表文件,也可以代表文件夹
  • File 封装的对象仅仅是一个路径名,这个路径可以是存在的,也允许是不存在的

绝对路径、相对路径

  • 绝对路径:从盘符开始

    File f1 = new File("D:\\soft\\QQ");
    
  • 相对路径:不带盘符,默认直接到当前工程下的目录寻找文件

    File f2 = new File("模块名\\a.txt");
    

具体应用

import java.io.File;

/**
 * 目标:掌握File创建对象,代表具体文件的方案
 */
public class FileTest1 {
    public static void main(String[] args) {
        // 创建一个File对象,指代某个具体的文件
         File f1 = new File("D:/resource/a.txt");      // 方式一:最常用
//         File f1 = new File("D:\\resource\\ab.txt");           // 方式二:第一个反斜杠用来转义
//         File f1 = new File("D:" + File.separator +"resource" + File.separator + "ab.txt");    // 方式三:File.separator表示分隔符 "\"
        System.out.println(f1.length());    // 文件大小

        // 创建一个File对象,指代某个文件夹
        File f2 = new File("D:/resource");
        System.out.println(f2.length());    // 文件夹的大小,不是计算其包括的所有文件的大小

        // 注意:File对象可以指代一个不存在的文件路径
        File f3 = new File("D:/resource/b.txt");
        System.out.println(f3.length());
        System.out.println(f3.exists());    // false

        // 我现在要定位的文件是在模块中,应该怎么定位呢?
        // 绝对路径:带盘符的,不推荐使用
        // File f4 = new File("D:\\code\\javasepromax\\file-io-app\\src\\c.txt");
        // 相对路径(重点):不带盘符,默认是直接去工程下寻找文件的
        File f4 = new File("file-io-app\\src\\itheima.txt");
        System.out.println(f4.length());
    }
}

常用方法1:判断文件类型、获取文件信息

File提供的判断文件类型、获取文件信息功能
【Java从入门到大牛】File和IO流上篇_第2张图片

具体应用

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;

/**
     目标:掌握File提供的判断文件类型、获取文件信息功能
 */
public class FileTest2 {
    public static void main(String[] args) throws UnsupportedEncodingException {
        // 1.创建文件对象,指代某个文件
        File f1 = new File("D:/resource/a.txt");

        // 2、public boolean exists():判断当前文件对象,对应的文件路径是否存在,存在返回true
        System.out.println("f1.exists: " + f1.exists());

        // 3、public boolean isFile() : 判断当前文件对象指代的是否是文件,是文件返回true,反之
        System.out.println("f1.isFile: " + f1.isFile());

        // 4、public boolean isDirectory(): 判断当前文件对象指代的是否是文件夹,是文件夹返回true,反之
        System.out.println("f1.isDirectory: " + f1.isDirectory());

        // 5.public String getName():获取文件的名称(包含后缀)
        System.out.println("f1.getName: " + f1.getName());

        // 6.public long length():获取文件的大小,返回字节个数
        System.out.println("f1.length: " + f1.length());

        // 7.public long lastModified():获取文件的最后修改时间
        long time = f1.lastModified();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        System.out.println("f1.lastModified: " + sdf.format(time));

        // 8.public String getPath():获取创建文件对象时,使用的路径
        File f2 = new File("D:\\resource\\ab.txt");
        File f3 = new File("file-io-app\\src\\itheima.txt");
        System.out.println("f2.getPath: " + f2.getPath());
        System.out.println("f3.getPath: " + f3.getPath());

        // 9.public String getAbsolutePath():获取绝对路径
        System.out.println("f2.getAbsolutePath: " + f2.getAbsolutePath());
        System.out.println("f3.getAbsolutePath: " + f3.getAbsolutePath());
    }
}

【Java从入门到大牛】File和IO流上篇_第3张图片

常用方法2:创建文件、删除文件

File类创建文件的功能
【Java从入门到大牛】File和IO流上篇_第4张图片

File类删除文件的功能
【Java从入门到大牛】File和IO流上篇_第5张图片

注意:delete 方法默认只能删除文件和空文件夹,删除后的文件不会进入回收站

具体应用

import java.io.File;

/**
 * 目标:掌握File创建和删除文件相关的方法
 */
public class FileTest3 {
    public static void main(String[] args) throws Exception {
        // 1、public boolean createNewFile():创建一个新文件(文件内容为空),创建成功返回true,反之
        File f1 = new File("D:/code/aa.txt");
        System.out.println(f1.createNewFile());

        // 2、public boolean mkdir():用于创建文件夹,注意:只能创建一级文件夹
        File f2 = new File("D:/code/aaa");
        System.out.println(f2.mkdir());

        // 3、public boolean mkdirs():用于创建文件夹,注意:可以创建多级文件夹
        File f3 = new File("D:/code/bbb/ccc/ddd/eee/fff/ggg");
        System.out.println(f3.mkdirs());

        // 3、public boolean delete():删除文件,或者空文件,注意:不能删除非空文件夹
        System.out.println(f1.delete());
        System.out.println(f2.delete());
        File f4 = new File("D:/code");
        System.out.println(f4.delete());
    }
}

常用方法3:遍历文件夹

File类提供的遍历文件夹的功能
【Java从入门到大牛】File和IO流上篇_第6张图片

使用listFiles方法时的注意事项:

  • 当主调是文件,或者路径不存在时,返回 null
  • 当主调是空文件夹时,返回一个长度为 0 的数组
  • 当主调是一个有内容的文件夹时,将里面所有一级文件和文件夹的路径放在 File 数组中返回
  • 当主调是一个文件夹,且里面有隐藏文件时,将里面所有文件和文件夹的路径放在 File 数组中返回,包含隐藏文件
  • 当主调是一个文件夹,但是没有权限访问该文件夹时,返回 null

具体应用

import java.io.File;
import java.io.IOException;
import java.util.Arrays;

/**
 * 目标:掌握File提供的遍历文件夹的方法
 */
public class FileTest4 {
    public static void main(String[] args) throws IOException {
        File f1 = new File("D:/code/aaa");
        f1.mkdir();

        for (int i = 1; i < 4; i++) {
            File f2 = new File("D:/code/aaa/a" + i + ".txt");
            if(!f2.createNewFile()) System.out.println("Failed to createNewFile!");
        }

        // 1、public String[] list():获取当前目录下所有的 "一级文件名称"到一个字符串数组中去返回
        String[] names = f1.list();
        for (String name : names) {
            System.out.println(name);
        }

        // 2、public File[] listFiles():(重点)获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回(重点)
        File[] files = f1.listFiles();
        for (File file : files) {
            System.out.println(file.getAbsolutePath());
        }

        File f3 = new File("D:/code/aaa");
        File[] files1 = f3.listFiles();
        System.out.println(Arrays.toString(files1));
    }
}

【Java从入门到大牛】File和IO流上篇_第7张图片

前置知识:方法递归

认识递归的形式

什么是方法递归 ?

  • 递归是一种算法,在程序设计语言中广泛应用
  • 从形式上看,方法调用自身的形式称为方法递归(recursion)

递归的形式

  • 直接递归:方法自己调用自己
  • 间接递归:方法调用其他方法,其他方法又回调方法自己

使用方法递归时需要注意的问题:

递归如果没有控制好终止,会出现递归死循环,导致栈内存溢出错误

应用、执行流程、算法思想

案例导学-计算 n 的阶乘

需求:计算 n 的阶乘,例如 5 的阶乘 = 1 * 2 * 3 * 4 * 5

分析

  1. 假如我们认为存在一个公式是 f(n) = 1 * 2 * 3 * … * (n-1) * n
  2. 那么公式等价形式就是:f(n) = f(n-1) * n
  3. 如果求的是 1-5 的阶乘,我们如何手算出它的结果:f(5) = f(4) * 5,f(4) = f(3) * 4,…,f(2) = f(1) * 2,f(1) = 1,然后依次递归回来算出结果

具体实现

/**
 * 目标:掌握递归的应用,执行流程和算法思想。
 */
public class RecursionTest1 {
    public static void main(String[] args) {
        System.out.println("5的阶乘是:" + f(5));
    }

    public static int f(int n){
        // 终结点
        if(n == 1){
            return 1;
        }else {
            return f(n - 1) * n;
        }
    }
}

【Java从入门到大牛】File和IO流上篇_第8张图片

递归算法三要素:

  1. 递归的公式:f(n) = f(n-1) * n
  2. 递归的终结点:f(1)
  3. 递归的方向必须走向终结点

案例-猴子吃桃问题

猴子第一天摘下若干桃子,当即吃了一半,觉得好不过瘾,于是又多吃了一个;第二天又吃了前天剩余桃子数量的一半,觉得好不过瘾,于是又多吃了一个;以后每天都是吃前天剩余桃子数量的一半,觉得好不过瘾,又多吃了一个;等到第10天的时候发现桃子只有1个了。

需求:请问猴子第一天摘了多少个桃子?

分析

整体来看,每一天都是做同一个事件,典型的规律化问题,考虑递归三要素:

  • 递归公式: f(n) = (f(n + 1) + 1) * 2
  • 递归终结点:f(10) = 1
  • 递归方向:从第一天到第十天
/*
    猴子吃桃问题
 */
public class RecursionTest2 {
    public static void main(String[] args) {
        System.out.println("猴子第一天摘了" + f(1) + "个桃子");

        // 验证结果
        double num = f(1);
        for (int i = 1; i < 11; i++) {
            System.out.print("第" + i + "天一开始有" + num + "个桃子");
            num = num / 2 - 1;
            if (num >= 0) System.out.println(", 吃完还剩" + num + "个桃子");
        }
    }

    public static double f(double n) {
        if (n == 10) {
            return 1;
        } else {
            return (f(n + 1) + 1) * 2;
        }
    }
}
/*
    思路分析:
    f(2) = f(1) / 2 - 1;
    f(3) = f(2) / 2 - 1;
    f(4) = f(3) / 2 - 1;
    ...
    f(9) = f(8) / 2 - 1;
    f(10) = f(9) / 2 - 1 = 1;
 */

【Java从入门到大牛】File和IO流上篇_第9张图片

案例:文件搜索

案例:文件搜索

需求:从 D 盘中,搜索 “QQ.exe” 这个文件,找到后直接输出其位置

分析

  1. 先找出 D 盘下的所有一级文件对象
  2. 遍历全部一级文件对象,判断是否是文件
  3. 如果是文件,判断是否是自己想要的
  4. 如果是文件夹,需要继续进入到该文件夹,重复上述过程

代码实现

import java.io.File;

/**
 * 目标:掌握文件搜索的实现
 */
public class RecursionTest3 {
    public static void main(String[] args) throws Exception {
          searchFile(new File("D:/") , "QQ.exe");
    }

    /**
     * 去目录下搜索某个文件
     * @param dir  目录
     * @param fileName 要搜索的文件名称
     */
    public static void searchFile(File dir, String fileName) throws Exception {
        // 1、把非法的情况都拦截住
        if(dir == null || !dir.exists() || dir.isFile()){
            return; // 代表无法搜索
        }

        // 2、dir不是null,存在,一定是目录对象
        // 获取当前目录下的全部一级文件对象
        File[] files = dir.listFiles();

        // 3、判断当前目录下是否存在一级文件对象,以及是否可以拿到一级文件对象
        if(files != null && files.length > 0){
            // 4、遍历全部一级文件对象。
            for (File f : files) {
                // 5、判断文件是否是文件,还是文件夹
                if(f.isFile()){
                    // 是文件,判断这个文件名是否是我们要找的
                    if(f.getName().contains(fileName)){
                        System.out.println("找到了:" + f.getAbsolutePath());

						// 拓展:启动QQ
                        Runtime runtime = Runtime.getRuntime();
                        runtime.exec(f.getAbsolutePath());
                    }
                }else {
                    // 是文件夹,继续重复这个过程(递归)
                    searchFile(f, fileName);
                }
            }
        }
    }
}

【Java从入门到大牛】File和IO流上篇_第10张图片

案例:删除非空文件夹

需求

删除非空文件夹

分析

  1. File 默认不可以直接删除非空文件夹
  2. 所有我们需要遍历文件夹,先删除里面的内容,再删除自己

代码实现

import java.io.File;

/**
 * 删除非空文件夹
 */
public class RecursionTest4 {
    public static void main(String[] args) throws Exception {
        createTestFile();
        File dir = new File("D:/test007");
        deleteDir(dir);     // 可以先注释这一行,到 D盘查看非空文件夹是否创建成功
    }

    // 创建用来删除的非空文件夹
    public static void createTestFile() throws Exception {
        // 创建多级文件夹
        File f1 = new File("D:/test007/aaa/bbb/ccc/ddd/eee");
        if (!f1.mkdirs()) return;

        // 创建一级文件夹
        File f2 = new File("D:/test007/fff");
        if (!f2.mkdir()) return;

        // 再随便添加一些文件
        for (int i = 1; i < 4; i++) {
            File f = new File("D:/test007/t00" + i + ".txt");
            if (!f.createNewFile()) return;
        }

        File f3 = new File("D:/test007/aaa/a.jpg");
        if (!f3.createNewFile()) return;

        System.out.println("非空文件夹创建成功!");
    }

    // 删除非空文件夹
    public static void deleteDir(File dir) {
        if (dir == null || !dir.exists()) {
            return;
        }

        // 是一个文件
        if (dir.isFile()) {
            if(dir.delete()) System.out.println("文件" + dir.getName() + "删除成功!");
            return;
        }

        // 是文件夹
        File[] files = dir.listFiles();     // 尝试取出所有一级文件对象

        // 取出一级文件对象失败
        if (files == null) {
            return;
        }

        // 取出成功,但是空文件夹
        if (files.length == 0) {
            if(dir.delete()) System.out.println("文件" + dir.getName() + "删除成功!");
            return;
        }

        // 是非空文件夹
        for (File file : files) {
            if (file.isFile()) {    // 是文件,直接删除
                if (file.delete()) System.out.println("文件" + file.getName() + "删除成功!");
            } else {
                deleteDir(file);    // 是文件夹,递归删除
            }
        }

        // 最后还剩下一个空文件夹,记得删除
        if(dir.delete()) System.out.println("文件" + dir.getName() + "删除成功!");
    }
}

执行结果

【Java从入门到大牛】File和IO流上篇_第11张图片

案例:啤酒问题

需求

啤酒 2 元 1 瓶,4 个盖子可以换一瓶,2 个空瓶可以换一瓶,请问 10 元钱可以喝多少瓶酒,剩余多少空瓶和盖子

代码实现

public class RecursionTest5 {
    public static int totalNumber;      // 总酒数
    public static int lastBottleNumber; // 每轮剩余瓶子数
    public static int lastCoverNumber;  // 每轮剩余盖子数
    public static void main(String[] args) {
        buy(10);
        System.out.println("总酒数: " + totalNumber);
        System.out.println("剩余瓶子数: " + lastBottleNumber);
        System.out.println("剩余盖子数: " + lastCoverNumber);
    }

    // 买酒
    public static void buy(int money) {
        // 有多少钱先买酒
        int buyNumber = money / 2;      // 可买酒数
        totalNumber += buyNumber;

        // 瓶子和盖子还可以换酒
        int allBottleNumber = buyNumber + lastBottleNumber;     // 当前总瓶子数
        int allCoverNumber = buyNumber + lastCoverNumber;       // 当前总盖子数
        int allMoney = 0;       // 记录下次买酒还有多少钱

        // 瓶子换酒
        if (allBottleNumber >= 2) {
            allMoney += (allBottleNumber / 2) * 2;
        }
        lastBottleNumber = allBottleNumber % 2;

        // 盖子换酒
        if (allCoverNumber >= 4) {
            allMoney += (allCoverNumber / 4) * 2;
        }
        lastCoverNumber = allCoverNumber % 4;

        // 如果钱还够买酒, 继续买
        if (allMoney >= 2) {
            buy(allMoney);
        }
    }
}

执行结果

【Java从入门到大牛】File和IO流上篇_第12张图片

前置知识:字符集

常见字符集介绍

【Java从入门到大牛】File和IO流上篇_第13张图片

标准ASCII字符集

  • ASCII(American Standard Code for Information Interchange):美国信息交换标准代码,包括了英文、符号等
  • 标准 ASCII 使用 1 个字节存储一个字符,首尾是 0,总共可表示 128 个字符
    【Java从入门到大牛】File和IO流上篇_第14张图片

GBK字符集(汉字内码扩展规范,国标)

  • 汉字编码字符集,包含了 2 万多个汉字等字符,GBK 中一个中文字符编码成两个字节的形式存储
  • 注意:GBK 兼容了 ASCII 字符集
  • GBK 规定:汉字的第一个字节的第一位必须是 1

Unicode字符集(统一码,也叫万国码)

Unicode 是国际组织规定的,可以容纳世界上所有文字、符号的字符集

UTF-8字符集

  • 是 Unicode 字符集的一种编码方案,采取可变长编码方案,共分为四个长度区:1 个字节、2 个字节、3 个字节、4 个字节

  • 英文字符、数字等只占 1 个字节(兼容标准 ASCII 编码),汉字字符占用 3 个字节

  • 注意:技术人员在开发时都应该使用 UTF-8 编码!

    【Java从入门到大牛】File和IO流上篇_第15张图片

总结

  • ASCII 字符集:只有英文、数字、符号等,占 1 个字节
  • GBK 字符集:汉字占 2 个字节,英文、数字占 1 个字节
  • UTF-8 字符集:汉字占 3 个字节,英文、数字占 1 个字节
  • 字符编码时使用的字符集,和解码时使用的字符集必须一致,否则会出现乱码
  • 英文、数字一般不会乱码,因为很多字符集都兼容了 ASCII 编码

字符集的编码、解码操作

什么是编码 ?

把字符按照指定的字符集编码成字节

什么是解码 ?

把字节按照指定的字符集编码解码成字符

Java 代码完成对字符的编码
【Java从入门到大牛】File和IO流上篇_第16张图片
Java 代码完成对字符的解码

【Java从入门到大牛】File和IO流上篇_第17张图片

具体应用

import java.util.Arrays;

/**
 * 目标:掌握如何使用Java代码完成对字符的编码和解码
 */
public class Test {
    public static void main(String[] args) throws Exception {
        // 1、编码
        String data = "a我b";
        byte[] bytes = data.getBytes(); // 默认是按照平台字符集(UTF-8)进行编码的。
        System.out.println(Arrays.toString(bytes));

        // 按照指定字符集进行编码
        byte[] bytes1 = data.getBytes("GBK");
        System.out.println(Arrays.toString(bytes1));

        // 2、解码
        String s1 = new String(bytes); // 按照平台默认编码(UTF-8)解码
        System.out.println(s1);

        String s2 = new String(bytes1, "GBK");
        System.out.println(s2);
    }
}

【Java从入门到大牛】File和IO流上篇_第18张图片

IO流

什么是IO流 ?

输入输出流,用来读写数据的

【Java从入门到大牛】File和IO流上篇_第19张图片

如何学习IO流 ?

  1. 先搞清楚 IO 流的分类、体系
  2. 再挨个学习每个 IO 流的作用、用法

IO流的分类

【Java从入门到大牛】File和IO流上篇_第20张图片

IO 流总体来看就有四大流:字节输入流、字节输出流、字符输入流、字符输出流

  • 字节输入流 InputStream:以内存为基准,来自磁盘文件 / 网络中的数据以字节的形式读入到内存中去的流
  • 字节输出流 OutputStream:以内存为基准,把内存中的数据以字节写出到磁盘文件或者网络中去的流
  • 字符输入流 Reader:以内存为基准,来自磁盘文件 / 网络中的数据以字符的形式读入到内存中去的流
  • 字符输出流 Writer:以内存为基准,把内存中的数据以字符写出到磁盘文件或者网络中去的流

IO流-字节流

文件字节输入流:每次读取一个字节

IO流的体系

【Java从入门到大牛】File和IO流上篇_第21张图片

FileInputStream(文件字节输入流)

作用:以内存为基准,可以把磁盘文件中的数据以字节的形式读入到内存中去
【Java从入门到大牛】File和IO流上篇_第22张图片

注意事项

使用 FileInputStream 每次读取一个字节,读取性能较差,并且读取汉字输出会乱码

具体应用

准备一个测试文本文件:

【Java从入门到大牛】File和IO流上篇_第23张图片

import java.io.*;

/**
 * 目标:掌握文件字节输入流,每次读取一个字节
 */
public class FileInputStreamTest1 {
    public static void main(String[] args) throws Exception {
        // 1、创建文件字节输入流管道,与源文件接通
        // InputStream is = new FileInputStream(new File("file-io-app/src/test1.txt"));
        // 简化写法:推荐使用
        InputStream is = new FileInputStream(("file-io-app/src/test1.txt"));

        // 2、开始读取文件的字节数据
        // public int read():每次读取一个字节返回,如果没有数据了,返回-1
        int b; // 用于记住读取的字节
        while ((b = is.read()) != -1){
            System.out.print((char) b);
        }

        // 读取数据的性能很差!
        // 读取汉字输出会乱码!!无法避免的!!
        // 流使用完毕之后,必须关闭!释放系统资源!
        is.close();
    }
}

【Java从入门到大牛】File和IO流上篇_第24张图片

文件字节输入流:每次读取多个字节

FileInputStream(文件字节输入流)

作用:以内存为基准,可以把磁盘文件中的数据以字节的形式读入到内存中去
【Java从入门到大牛】File和IO流上篇_第25张图片

注意事项

使用 FileInputStream 每次读取多个字节,读取性能得到了提升,但读取汉字输出还是会乱码

具体应用

import java.io.FileInputStream;
import java.io.InputStream;

/**
 * 目标:掌握使用FileInputStream每次读取多个字节
 */
public class FileInputStreamTest2 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个字节输入流对象代表字节输入流管道与源文件接通
        InputStream is = new FileInputStream("file-io-app/src/test1.txt");

        // 2、开始读取文件中的字节数据:每次读取多个字节
        //  public int read(byte b[]) throws IOException
        //  每次读取多个字节到字节数组中去,返回读取的字节数量,读取完毕会返回-1
        byte[] buffer = new byte[3];
        int len; // 记住每次读取了多少个字节
        while ((len = is.read(buffer)) != -1){
            // 注意:读取多少,倒出多少。
            String rs = new String(buffer, 0 , len);
            System.out.print(rs);
        }
        // 性能得到了明显的提升!!
        // 但是这种方案也不能避免读取汉字输出乱码的问题!!

        is.close(); // 关闭流
    }
}

【Java从入门到大牛】File和IO流上篇_第26张图片

文件字节输入流:一次读取完全部字节

方式一:自己定义一个字节数组与被读取的文件大小一样大,然后使用该字节数组,一次读完文件的全部字节
【Java从入门到大牛】File和IO流上篇_第27张图片

方式二:Java 官方为 InputStream 提供了如下方法,可以直接把文件的全部字节读取到一个字节数组中返回
【Java从入门到大牛】File和IO流上篇_第28张图片

直接把文件数据全部读取到一个字节数组可以避免乱码,是否还存在问题 ?

  • 如果文件过大,创建的字节数组也会过大,就可能引起内存溢出的问题

读写文本内容更适合用字符流,字节流适合做数据的转移,如:文件复制等

具体应用

【Java从入门到大牛】File和IO流上篇_第29张图片

import java.io.*;

/**
 * 目标:使用文件字节输入流一次读取完文件的全部字节
 */
public class FileInputStreamTest3 {
    public static void main(String[] args) throws Exception {
        // 1、一次性读取完文件的全部字节到一个字节数组中去
        // 创建一个字节输入流管道与源文件接通
        InputStream is1 = new FileInputStream("file-io-app/src/test2.txt");

        // 2、方式一:准备一个字节数组,大小与文件的大小正好一样大
        File f = new File("file-io-app/src/test2.txt");
        long size = f.length();
        byte[] buffer1 = new byte[(int) size];

        int len = is1.read(buffer1);
        System.out.println(new String(buffer1));

        System.out.println("---------------------------------");

        // 方式二:使用Java提供的方法
        InputStream is2 = new FileInputStream("file-io-app/src/test2.txt");
        byte[] buffer2 = is2.readAllBytes();
        System.out.println(new String(buffer2));

        // 关闭流
        is1.close();
        is2.close();
    }
}

【Java从入门到大牛】File和IO流上篇_第30张图片

文件字节输出流:写字节出去

FileOutputStream(文件字节输出流)

作用:以内存为基准,把内存中的数据以字节的形式写出到文件中

【Java从入门到大牛】File和IO流上篇_第31张图片

具体应用

import java.io.*;

/**
 * 目标:掌握文件字节输出流FileOutputStream的使用
 */
public class FileOutputStreamTest4 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个字节输出流管道与目标文件接通
        // 覆盖管道:覆盖之前的数据
//        OutputStream os =
//                new FileOutputStream("file-io-app/src/testOut.txt");

        // 追加数据的管道
        OutputStream os =
                new FileOutputStream("file-io-app/src/testOut.txt", true);

        // 2、开始写字节数据出去
        os.write(97); // 97就是一个字节,代表a
        os.write('b'); // 'b'也是一个字节

        byte[] bytes = "我爱你中国abc".getBytes();
        os.write(bytes);
        os.write("\n".getBytes());          // 换行符
        os.write(bytes, 0, 15);     // 一个汉字占3个字节

        os.close(); // 关闭流
    }
}

【Java从入门到大牛】File和IO流上篇_第32张图片

案例:文件复制

【Java从入门到大牛】File和IO流上篇_第33张图片

字节流非常适合做一切文件的复制操作。任何文件的底层都是字节,字节流做复制,是一字不漏地转移完全部字节,只要复制后的文件格式一致就没问题

具体应用

import java.io.*;

/**
 * 目标:使用字节流完成对文件的复制操作
 */
public class CopyTest5 {
    public static void main(String[] args) throws Exception {
        // 需求:复制照片
        // 1、创建一个字节输入流管道与源文件接通
        InputStream is = new FileInputStream("file-io-app/src/1.png");
        // 2、创建一个字节输出流管道与目标文件接通
        OutputStream os = new FileOutputStream("file-io-app/src/copy.png");

        // 3、创建一个字节数组,负责转移字节数据
        byte[] buffer = new byte[1024];
        // 4、从字节输入流中读取字节数据,写出去到字节输出流中。读多少写出去多少
        int len; // 记住每次读取了多少个字节
        while ((len = is.read(buffer)) != -1){
            os.write(buffer, 0, len);
        }

        os.close();
        is.close();
        System.out.println("复制完成!!");
    }
}

【Java从入门到大牛】File和IO流上篇_第34张图片

释放资源的方式

try-catch-finally

try {
	...
} catch(IOException e) {
	e.printStackTrace();
} finally {
	...
}

finally 代码区的特点:无论 try 中的程序是正常执行了,还是出现了异常,最后都一定会执行 finally 区,除非 JVM 终止

作用:一般用于在程序执行完成后进行资源的释放操作(专业级做法)

具体应用

/**
 * 目标:认识try-catch-finally
 */
public class Test1 {
    public static void main(String[] args) {
        try {
            System.out.println(10 / 2);
            // return;          // 跳出方法的执行
            // System.exit(0);  // 虚拟机
        }catch (Exception e){
            e.printStackTrace();
        } finally {
            System.out.println("===finally执行了一次===");
        }

        System.out.println(div(10, 2));
    }

    public static int div(int a, int b){
        try {
            return a / b;
        }catch (Exception e){
            e.printStackTrace();
            return -1; // 代表的是出现异常
        }finally {
            // 千万不要在finally中返回数据!
            return 111;
        }
    }
}

手动释放资源

/**
 * 目标:掌握释放资源的方式
 */
public class Test2 {
    public static void main(String[] args)  {
        InputStream is = null;
        OutputStream os = null;
        try {
            // 1、创建一个字节输入流管道与源文件接通
            is = new FileInputStream("file-io-app/src/1.png");
            // 2、创建一个字节输出流管道与目标文件接通
            os = new FileOutputStream("file-io-app/src/copy.png");

            // 3、创建一个字节数组,负责转移字节数据
            byte[] buffer = new byte[1024];
            // 4、从字节输入流中读取字节数据,写出去到字节输出流中
            int len; // 记住每次读取了多少个字节
            while ((len = is.read(buffer)) != -1){
                os.write(buffer, 0, len);
            }
            System.out.println("复制完成!!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 释放资源的操作
            try {
                if(os != null) os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

            try {
                if(is != null) is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

try-with-resource

JDK 7 开始提供了更简单的资源释放方案:try-with-resource

try(定义资源1; 定义资源2; ...) {
	可能出现异常的代码;
} catch(异常类名 变量名) {
	异常的处理代码;
}

上述资源使用完毕后,会自动调用其 close() 方法,完成对资源的释放

  • () 中只能放置资源,否则报错

  • 什么是资源呢?

  • 资源一般指的是最终实现了 AutoCloseable 接口

    public abstract class InputStream implements Closeable {}
    public abstract class OutputStream implements Closeable, Flushable {}
    public interface Closeable extends AutoCloseable {}
    

具体应用

public class MyConnection implements AutoCloseable{
    @Override
    public void close() throws Exception {
        System.out.println("释放了与某个硬件的链接资源~~~~");
    }
}
import java.io.*;

/**
 * 目标:掌握释放资源的方式:try-with-resource
 */
public class Test3 {
    public static void main(String[] args)  {
        try (
                // 注意:这里只能放置资源对象(流对象)
                // 1、创建一个字节输入流管道与源文件接通
                InputStream is = new FileInputStream("file-io-app/src/1.png");
                // 2、创建一个字节输出流管道与目标文件接通
                OutputStream os = new FileOutputStream("file-io-app/src/copy.png");

                MyConnection conn = new MyConnection();
                ){

            // 3、创建一个字节数组,负责转移字节数据
            byte[] buffer = new byte[1024];
            // 4、从字节输入流中读取字节数据,写出去到字节输出流中
            int len; // 记住每次读取了多少个字节
            while ((len = is.read(buffer)) != -1){
                os.write(buffer, 0, len);
            }
            System.out.println("复制完成!!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

【Java从入门到大牛】File和IO流上篇_第35张图片

你可能感兴趣的:(Java从入门到大牛,java,开发语言)