Java【文件和IO】File 类, 字节IO流的使用


前言

各位读者好, 我是小陈, 这是我的个人主页, 希望我的专栏能够帮助到你:
📕 JavaSE基础: 基础语法, 类和对象, 封装继承多态, 接口, 综合小练习图书管理系统等
📗 Java数据结构: 顺序表, 链表, 堆, 二叉树, 二叉搜索树, 哈希表等
📘 JavaEE初阶: 多线程, 网络编程, TCP/IP协议, HTTP协议, Tomcat, Servlet, Linux, JVM等(正在持续更新)

之前几篇文章陆续介绍了 Java 多线程的相关知识, 本篇继续介绍文件操作和 IO 流相关的基础内容

文件, 狭义上讲, 电脑中 C 盘 D 盘中的文件, 广义上讲, 很多操作系统为了实现接口的统一性,将所有的 IO 设备都抽象成了文件
IO 是指 : Input输入 和 Output输出
IO 设备就是 : 可以把数据输入到计算机, 或者把计算机中的数据输出的外部设备


提示:是正在努力进步的小菜鸟一只,如有大佬发现文章欠佳之处欢迎批评指点~ 废话不多说,直接上干货!

识狭义上的文件 : 针对硬盘这种持久化存储的 I/O 设备,当我们想要进行数据保存时,往往不是保存成一个整体,而是独立成一个个的单位进行保存,这个独立的单位就被抽象成文件的概念,就类似办公桌上的一份份真实的文件一般。

狭义上的文件包括文件夹和普通文件, 例如下图 : D 盘就是一个文件夹, 打开之后包含若干个文件夹和普通文件. 这些文件夹再打开可能还包含若干文件夹和普通文件
在这里插入图片描述
可以发现, 文件的存储结构实际上就是树形结构, 文件夹都是"非叶子节点", 普通文件都是"叶子节点" , 文件夹的专业术语是目录


一、File 类

Java 标准库封装了一个 File 类来操作文件, 如果把电脑上的文件当作电视机, File 类的对象就是遥控器, 通过路径把 File 对象实例出来(于遥控器和电视机连接上了), 就可以利用 File 对象(遥控器)来操作文件(电视机)

不完全准确的类比, 但是意思差不多

注意 : 实例化出来 File 对象, 并不代表电脑上就真实存在这个文件

1, 构造方法

        File file = new File("D:\\Program Files\\Tencent\\QQ\\Bin\\test.txt");

构造方法的参数是该目标文件的路径, 这个路径可以是绝对路径, 也可以是相对路径, 都是字符串

绝对路径 : 从盘符开始, 到目标文件的完整路径
相对路径 : 从某个目录(工作目录)开始, 到目标文件的路径, 相对路径中, "."表示当前目录, ".."表示上一级目录

如果以当前 Java 项目所在的目录为工作目录, 在这个目录下创建test.txt这个文件 相对路径就可以表示成 : ./test.txt
以相对路径的方式创建 file 对象的代码: File file = new File(“./test.txt”);


2, 成员方法

返回值方法名说明
StringgetParent()返回 File 对象的父目录文件路径
StringgetName()返回 FIle 对象的纯文件名称
StringgetPath()返回 File 对象的文件路径
StringgetAbsolutePath()返回 File 对象的绝对路径
StringgetCanonicalPath()返回 File 对象的修饰过的绝对路径
        System.out.println(file.getName() + " --> 文件名");
        System.out.println(file.getParent() + " --> 父目录文件路径");
        System.out.println(file.getPath() + " --> 路径");
        System.out.println(file.getAbsolutePath() + " --> 绝对路径");
        System.out.println(file.getCanonicalFile() + " --> 修饰后的绝对路径");

运行结果 :
在这里插入图片描述
下面三行输出看起来没有什么区别, 但是如果把构造方法里的路径换成相对路径, 区别就很明显了, 各位读者可以自行尝试


返回值方法名说明
booleanexists()判断 File 对象描述的文件是否真实存在
booleanisDirectory()判断 File 对象代表的文件是否是一个目录
booleanisFile()判断 File 对象代表的文件是否是一个普通文件
booleancreateNewFile()根据 File 对象,自动创建一个空文件。成功创建后返回 true
booleandelete()根据 File 对象,删除该文件。成功删除后返回 true
voiddeleteOnExit()根据 File 对象,标注文件将被删除,删除动作会到 JVM 运行结束时才会进行
        System.out.println(file.exists() + " --> 是否存在");
        System.out.println(file.createNewFile() + " --> 是否成功创建(第一次)");
        System.out.println(file.createNewFile() + " --> 是否成功创建(第二次)");
        
        System.out.println(file.isDirectory() + " --> 是否为目录");
        System.out.println(file.isFile() + " --> 是否为普通文件");

        System.out.println(file.delete() + " --> 是否成功删除(第一次)");
        System.out.println(file.delete() + " --> 是否成功删除(第二次)");

运行结果 :
在这里插入图片描述


下面是创建目录的方法

返回值方法名说明
booleanmkdir()创建 File 对象代表的目录
        File dir = new File("D:\\Program Files\\Tencent\\QQ\\Bin\\test");

        System.out.println(dir.exists() + " --> 是否存在");
        System.out.println(dir.isDirectory() + " --> 是否为目录");
        System.out.println(dir.isFile() + " --> 是否为普通文件");

        System.out.println(dir.mkdir() + " --> 是否为成功创建");
        System.out.println(dir.delete() + " --> 是否为成功删除");

        System.out.println(dir.exists() + " --> 是否存在");

运行结果 :
在这里插入图片描述


下面是创建多级目录的方法

返回值方法名说明
booleanmkdirs()创建 File 对象代表的目录,如果必要,会创建中间目录
        File dir = new File("D:\\Program Files\\Tencent\\QQ\\Bin\\test2/aaa/bbb");

由于本机没有test2 和 aaa 这两个文件, 这个方法会创建出来从 test2 到 bbb 这三层目录, 我们可以用以下两个方法观察

返回值方法名说明
String[]list()返回 File 对象代表的目录下的所有文件名
File[]listFiles()返回 File 对象代表的目录下的所有文件,以 File 对象表示
        File dir = new File("D:\\Program Files\\Tencent\\QQ\\Bin\\test2/aaa/bbb");
        System.out.println(dir.mkdirs() + " --> 是否为成功创建");

        File dir2 = new File("D:\\Program Files\\Tencent\\QQ\\Bin\\test2");
        String[] results1 = dir2.list();
        File[] results2 = dir2.listFiles();
        System.out.println(Arrays.toString(results1));
        System.out.println(Arrays.toString(results2));

运行结果 :
在这里插入图片描述


以上就是 File 类的常用方法介绍, 下面介绍字节流的 IO 操作

  • 明确"流"的概念
    “流”, 可以联想到"水流", 把水龙头打开就会有水流出来, 并且可以控制流出来的水的快慢
    水流的最小单位是水滴, 而字节流 IO 的最小单位就是一个字节

  • 明确"输入"和"输出"的方向
    输入输出都是以 CPU 的视角来看的, CPU 运行时是从内存中读取数据, 而文件在硬盘(外存)上,
    文件输入, 是硬盘 --> 内存 --> CPU , 数据流入到 CPU(读取文件中的数据)
    文件输出, 是 CPU --> 内存 --> 硬盘, 数据从 CPU 流出(把数据写入到文件)

  • 明确对应的类
    字节流 IO 操作使用的类是 : InputStream 和 OutputStream

二、字节流输入输出

1, 字节流输入 InputStream

1.1, 每次输入一个字节

InputStream 这个类是一个抽象类, 不能被直接实例化, 应该实例化它的子类

文件只是 IO 设备的其中一种, 还有其他 IO 设备(比如网卡…), 所以针对文件的 IO 操作的类是 FileInputStream, 需要在构造方法中指定路径来表示操作的具体文件是哪个(或者是 File 对象)

注意 :
实例化出来对象之后就相当于文件被打开, (产生了文件描述符), 使用完文件之后需要调用 close 方法关闭文件, 否则可能会产生文件资源泄露的严重bug

由于字节流字符流的 IO 操作的类都继承了 Closeable 这个接口, 所以有了一种新的语法 :

        try(InputStream inputStream = new FileInputStream("D:/hello.txt")) {
            
            }
        }

try(实例化对象) { }, 当大括号中的代码执行完后, 就会自动的调用 close 方法

调用 inputStream.read() 就可以读取一个字节的数据, 如果读到文件末尾, 返回值为 -1, 我们不知道文件中有多少字节的数据, 所以利用 while 循环 :

        // 输入流  从硬盘读到内存
        try(InputStream inputStream = new FileInputStream("D:/hello.txt")) {
            while(true) {
                int ret = inputStream.read();
                if(ret == -1) {
                	// 说明读完了
                    break;
                }
                System.out.println(ret);
            }
        }

在这里插入图片描述
如果文件中的数据是字母和标点符号, 根据数字查询 ASCII 码表即可, 如果是汉字, 需要把数字转换成十六进制再查询 UTF-8 码表

1.2, 每次输入多个字节

要想一次输入多个字节, 就创建一个字节数组, 调用 inputStream.read(字节数组), 最多读取数组长度大小个字节, 返回数组长度, 读完了返回 -1

        // 输入流  从硬盘读到内存
        try(InputStream inputStream = new FileInputStream("D:/hello.txt")) {
            byte[] array = new byte[1024];
            while(true) {
                int ret = inputStream.read(array);
                if(ret == -1) {
                    // 说明读完了
                    break;
                }
                for(int i = 0; i < ret; i++) {
                    System.out.println(array[i]);
                }
            }
        }

这种方式从文件读取数据的次数更少, 性能要更好


2, 字节流输出 OutputStream

2.1, 每次输出一个字节

和 InputStream 类似, 针对文件的输出对应的类是 FileOutputStream

调用 outputStream.write(整形), 可以每次输出一个字节

        try(OutputStream outputStream = new FileOutputStream("D:/hello.txt")) {
            outputStream.write(97);
            outputStream.write(98);
            outputStream.write(99);
        }

这样就可以在文件中依次写入"abc", 如图 :
在这里插入图片描述

接着再写入一些数据到文件

        try(OutputStream outputStream = new FileOutputStream("D:/hello.txt")) {
            outputStream.write(100);
            outputStream.write(101);
            outputStream.write(102);
        }

查看文件中, 如图 :
在这里插入图片描述

发现原本的"abc" 三个字符没有了, 这是因为第二次写入的数据把之前的数据覆盖了, 只需要在实例化 outputStream 的构造方法中多给一个参数 “true”, 表示在之前的数据末尾 append 要写入的数据

// 输出流 从内存写到硬盘
        try(OutputStream outputStream = new FileOutputStream("D:/hello.txt",true)) {
            outputStream.write(100);
            outputStream.write(101);
            outputStream.write(102);
        }

查看文件中的数据如图 :
在这里插入图片描述


2.2, 每次输出多个字节

和 InputStream 类似, 要想每次输出多个字节, 需要创建一个字符数组, 调用 outputStream.write(字符数组), 就可以把字符数组中的所有数据写入到文件

        // 输出流 从内存写到硬盘
        try(OutputStream outputStream = new FileOutputStream("D:/hello.txt", true)) {
            byte[] array = {103, 104, 105};
            outputStream.write(array);
        }

查看文件中的数据, 如图 :
在这里插入图片描述



总结

以上就是本篇的全部内容, 都相对比较简单, 只是方法比较繁多, 需要小伙伴们多加练习

如果本篇对你有帮助,请点赞收藏支持一下,小手一抖就是对作者莫大的鼓励啦😋😋😋~


上山总比下山辛苦
下篇文章见

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

灵魂相契的树

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值