当前位置: 首页 > article >正文

JavaEE:文件内容操作(一)

文章目录

  • 文件内容的读写---数据流
    • 字节流和字符流
    • 打开和关闭文件
      • 文件资源泄漏
        • try with resources


文件内容的读写—数据流

文件内容的操作,读文件和写文件,都是操作系统本身提供了API,在Java中也进行了封装.

Java中封装了操作文件的这些类,我们给它们起了个名字,叫做"文件流" / “IO流”.

IO流的特点:
我要从文件中读取100字节的数据,有以下几种读法.

  1. 直接一口气,把100字节都读完.
  2. 一次读50字节,分两次.
  3. 一次读10字节,分10次.
  4. 一次读1字节,分100次.

字节流和字符流

Java实现IO流的类有很多,可以分成两个大的类别.

  1. 字节流(二进制): 读写数据的基本单位,就是字节.
    InputStream
    OutputStream
  2. 字符流(文本): 读写数据的基本单位,就是字符.(字符流内部做的工作会更多一些,会自动的查询码表,把二进制数据,转换成对应的字符)
    Reader
    Writer

上述这四个类,都是"抽象类",实际上真正干活的,并非是上面这四个.
另外,Java中提供了很多很多类,来实现上述的这个四个抽象类.

在这里插入图片描述

打开和关闭文件

使用InputStream来去读取文件:
我们先手动在当前目录下创建一个文件,文件内容为hello.
在这里插入图片描述

创建一个FileInputStream对象,通过指定文件路径来获取输入流。

package javaEE.fileIO;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

public class J {
    // FileNotFoundException这个异常,是IOException的子类(IOException的特殊情况)
    public static void main(String[] args) throws IOException {
        // 因为InputStream是一个抽象类,我们不能直接new,只能new一个实现这个类的子类.
        // FileInputStream的括号内既可以指定绝对路径,也可以指定File对象.
        InputStream inputStream = new FileInputStream("./test.txt");
        // 在上述操作中,还隐含了一个操作"打开文件".
        // 与打开相对的还有一个关闭操作
        inputStream.close();
    }
}

InputStream 只是一个抽象类,要使用还需要具体的实现类。关于 InputStream 的实现类有很多,基本可以认为不同的输入设备都可以对应一个 InputStream 类,我们现在只关心从文件中读取,所以使用FileInputStream.

上述代码,只写成这样,其实是有风险的.
如果中间出现一些return操作或者抛出异常的操作,这些都会导致close()执行不到.

较好的写法还在后面~

文件资源泄漏

如果我们不使用close关闭,会咋样呢?
打开文件实际上实在该进程的文件描述符中,创建了一个新的表项.

之前写过进程 与 PCB(进程控制)的关系,这里就不再解释了.
文件描述符:描述了1该进程,都要操作那些文件,文件描述符,可以认为是一个数组,数组的每个元素就是一个struct file对象(Linux内核),每个结构体就描述了对应操作的文件的信息,数组的下标,就称为"文件描述符".

每一次打开一个文件,就相当于在数组上占用了一个位置,而在系统内核中,文件描述符数组,是固定长度并且不可扩容的.
除非主动调用close,关闭文件,此时,才会释放出空间.如果代码里一直打开,不去关闭,就会使这里的资源越来越少.
如果把数组搞满了,后续再打开文件,就会打开失败.

这个问题称为"文件资源泄漏".
这种问题,也是属于对程序员非常不友好的问题.
当文件资源泄漏时,程序的逻辑,都是能正常执行的,看不出来有啥不对的地方.
这样的问题,不容易被发现.
泄漏不是一瞬间就泄漏完,而是一个持续的过程.整个问题直到所有的资源泄漏完毕,这一刻才会集中的爆发出来.

public static void main(String[] args) throws IOException {
        // 因为InputStream是一个抽象类,我们不能直接new,只能new一个实现这个类的子类.
        // FileInputStream的括号内既可以指定绝对路径,也可以指定File对象.
        InputStream inputStream = new FileInputStream("./test.txt");
        // 在上述操作中,还隐含了一个操作"打开文件".
        // 与打开相对的还有一个关闭操作
        inputStream.close();
    }

在上述代码中,虽然要求确实是使用完毕之后要关闭,但是局限于本代码,不写close也行.因为close之后,紧接着进程就结束了.
刚才说的close是释放文件描述符表里的元素,进程结束,意味着PCB就真个销毁,PCB上的文件描述符,就会整个释放.

InputStream inputStream = new FileInputStream("./test.txt");
inputStream.close();

上述代码,只写成这样,其实是有风险的.
如果中间出现一些return操作或者抛出异常的操作,这些都会导致close()执行不到.

为了确保close能被执行到,所以要把它放在finally里面.

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

public class J {
    public static void main(String[] args) throws IOException {
        // 由于inputStream在try里面,而finally不能共享到try中的作用域,所以我们要把InputStream定义在try外面.
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream("./test.txt");
        } finally {
            inputStream.close();
        }
    }
}

都写try和finally了,其实还可以写上catch.

    public static void main(String[] args){
        // 由于inputStream在try里面,而finally不能共享到try中的作用域,所以我们要把InputStream定义在try外面.
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream("./test.txt");
        } catch (IOException e) {
             e.printStackTrace();
        } finally {
            // 又因为close会抛出一个IO异常,所以要再加上一层try
            try {
                inputStream.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

这么写代码确实更严谨了,但是看起来更复杂了.
其实,针对文件关闭这样的操作,我们还有一种更优雅的写法,即简单又可靠~

try with resources
public class J {
    public static void main(String[] args) throws FileNotFoundException {
        // 使用这种写法,我们不变写finally也不必写close了~
        try (InputStream inputStream = new FileInputStream("./test.txt")) {

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

这其实是Java中引入的一种特殊的写法,叫做"try with resources".
也就是在try的括号里面,写上要管理的资源,这里的资源就会在try代码块结束的时候,自动去执行关闭操作.
这里()中创建的资源可以是多个.

需要注意的是:必须是实现了Closeable的接口,才能放到try的括号内~在这里插入图片描述

好像本文没咋写读取文件内容,光写close了.
emmm,留到下一篇文章讲吧~

本文到这里就结束啦~


http://www.kler.cn/news/304872.html

相关文章:

  • docker--刚开始学不知道如何操作拉取,或拉取失败(cmd)
  • EmguCV学习笔记 C# 11.5 目标检测
  • 期货量化现在是要比股票量化更适合高频交易,程序化交易
  • 电脑桌面数据误删如何恢复?提供一份实用指南
  • spark sql详解
  • MVC 控制器
  • Qt-QLCDNumber显示类控件(26)
  • 如何简化机器人模型,加速仿真计算与可视化
  • 基于less和scss 循环生成css
  • Java中的高级I/O操作:NIO和AIO的比较
  • 大数据-129 - Flink CEP 详解 Complex Event Processing - 复杂事件处理
  • 哪个虚拟机软件在 Mac 上更好用,Mac 虚拟机会影响性能吗?
  • 计算机网络30——Linux-gdb调试命令makefile
  • [Linux#48][网络] 令牌环网 | IPv4 | socket 套接字 | TCP | UDP | 网络字节序列
  • Pytest配置文件pytest.ini如何编写生成日志文件?
  • AI创意引擎:优化Prompt提示词的高效提问技巧
  • 相机光学(三十八)——VCM(Voice Coil Motor)音圈马达
  • 数据分析-20-时间序列预测之基于PyTorch的LSTM数据准备及模型训练流程
  • Java后端编程语言进阶篇
  • 第158天:安全开发-Python-Socket编程反弹Shell分离免杀端口探针域名爆破
  • 前端知识点
  • Apache 的CollectionUtils各种集合操作好用的方法总结
  • SQLite的入门级项目学习记录(三)
  • 鸿蒙轻内核A核源码分析系列六 MMU协处理器(2)
  • Clickhouse使用笔记
  • Linux云计算 |【第三阶段】PROJECT1-DAY3
  • 若依后端正常启动但是uniapp移动端提示后端接口异常
  • 【已解决】SpringBoot3项目整合Druid依赖:Druid监控页面404报错
  • [leetcode-python]杨辉三角2
  • 打印自身的程序