字节流的简单操作 (fileInputStream, fileOutputStream)

本文介绍了Java中的流(Stream)概念,包括字节流和字符流的区别。重点讲解了字节流的代表类InputStream和OutputStream,如FileInputStream和FileOutputStream的使用方法,包括读写文件的操作,并通过实例演示了文件拷贝的过程。同时强调了流对象操作文件的核心步骤:打开、读取、写入和关闭,并解释了不关闭文件可能带来的资源泄露问题及其原因。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上篇中的File能进行一些常规的文件操作,但是没有读写两个核心操作

流(Stream)
就是Java中针对文件操作,又进行了进一步的抽象
流是一组类/一组API,描述了如何来进行文件读写操作
不同的类的读写方式都会存在差异

在这里插入图片描述
图中黄色部分为常用类.

所有的IO流相关的类,一共分成两个大的部分

  1. 字节流 读写数据以字节位基本单位 (byte)
  2. 字符流 读写数据以字符位基本单位 (char)

当我们处理文本文件的时候,使用字符流
当我们处理二进制文件的时候,使用字节流

区分二进制文件和文本文件的简单方式:
用记事本打开文件,如果能看懂,那就是文本文件
如果看不懂,那就是二进制文件

字节流

  1. InputStream:
    输入,从输入设备读取数据到内存中.
  2. OutputStream:
    输出.把内存中的数据写入到输出设备中

如果发现某个类的名字中带有inputStream/OutputStream 说明这就是字节流
但是也有个例外,PrintStream不带上面的内容,但他也是字节流

字符流

  1. Reader:输入
  2. Writer:输出

如果发现某个类的名字中带有Reader或这Writer,说明这个类就是字符流~
但是也有特例:

  1. InputStreamReader
  2. OutputStreamWriter

这两个是字符流,主要是看一个单词的后面一部分,最后一部分是主体,前面的部分都是修饰的

以字节流为例,使用流对象读写文件

需要知道的两个知识点:
FileInputStream(文件输入流)

  • FileInputStream(File file) : 向file对象的文件读取数据
  • FileInputStream(String path) : 向path文件读取数据

读取数据: 一个一个字节读取

public static void main(String[] args) throws IOException {
        FileInputStream fileInputStream = new FileInputStream("e:/BaiduNetdiskDownload/test.txt");
        int len = -1;
        //每次只读一个字节,如果没读完就返回下一个数据字节,如果读完了就返回-1,读完了就退出循环
        while((len = fileInputStream.read()) != -1) {
            System.out.print((char)len);
        }
        fileInputStream.close();

    }

输出:
test

读取数据: 先把字节存入到缓存区数组中,一次读一个指定长度的字节数组

public static void main(String[] args) throws IOException {
        File file = new File("e:/BaiduNetdiskDownload/test.txt");//创建对象描述文件
        FileInputStream fileInputStream = new FileInputStream("e:/BaiduNetdiskDownload/test.txt");//创建一个字节输入流对象
        //每次只读一个字节,如果没读完就返回下一个数据字节,如果读完了就返回-1,读完了就退出循环
        byte[] bytes = new byte[1024];//根据文件大小来创建数组
        StringBuffer sb = new StringBuffer();
        int len = -1;
        while((len = fileInputStream.read(bytes)) != -1) {
            sb.append(new String(bytes, 0, len));
        }
        fileInputStream.close();
    }

FileOutputStream(文件输出流)

  • FileOutputStream(File file) : 向file对象的文件写入数据
  • FileOutputStream(File file, boolean append) : 向file对象的文件追加数据
  • FileOutputStream(String path) : 向指定的文件写入数据
  • FileOutputStream(String path, boolean append) : 向指定的文件追加数据

当append的值为true时,向文件写入的数据就会追加到原数据后面,如果为false则会重写文件数据,默认为false
写入方法: 一个字节一个字节的写入

public static void main(String[] args) throws IOException {
        FileOutputStream outputStream = new FileOutputStream("e:/BaiduNetdiskDownload/test2.txt");
        //将要写入的数据
        String string = "Hello Earth";
        //将写入的数据转化成字节数组
        byte[] bytes = string.getBytes();
        for(byte b: bytes) {
            //每次写一个字节,写入到目标文件中
            outputStream.write(b);
        }
        //最后记得关闭目标文件
        outputStream.close();
    }

写入方法: 一次写一个字符数组

public static void main(String[] args) throws IOException {
        FileOutputStream outputStream = new FileOutputStream("e:/BaiduNetdiskDownload/test3.txt");
        String string = "Hello Mars";
        byte[] bytes = string.getBytes();
        outputStream.write(bytes);
        outputStream.close();
    }

操作场景:
拷贝文件:e:/BaiduNetdiskDownload/wallpaper.jpg 的数据到 e:/BaiduNetdiskDownload/wallpaper2.jpg 中

public static void main(String[] args) throws IOException {
        copyFile("e:/BaiduNetdiskDownload/wallpaper.jpg", "e:/BaiduNetdiskDownload/wallpaper2.jpg");
    }
    private static void  copyFile(String srcPath, String destPath) throws IOException {
        //0.先打开文件,才能够读写,(创建inputSteam/OutputStream对象的过程)
        //很多编程语言要对文件进行输入输出之前必须要先打开,java直接new相关对象就可以进行操作了
        //这是文件输入流
        FileInputStream fileInputStream = new FileInputStream(srcPath);
            //实例化这个对象的时候,可以使用字符串路径来实例化,也可以使用一个File对象来实例化,并且要处理异常
        //这是文件输出流
        FileOutputStream fileOutputStream = new FileOutputStream(destPath);

        //1.需要读取srcPath对应的文件内容
        byte[] buffer = new byte[1024];
        //单次读取的内容是存在上限的(缓存区的长度),要想把整个文件都读完,需要搭配循环来使用
        int len = -1;
        //如果读到的length时-1,说明读取结束,循环就结束了
        while((len = fileInputStream.read(buffer) )!= -1) {
            //读取成功,就把读到的内容写到另一个文件中即可
            //因为len的值不一定就是和缓冲区一样长
            fileOutputStream.write(buffer, 0, len);
        }
        //2.把读取到的内容写入到destPath对应的文件中
        File file = new File("BaiduNetdiskDownload/wallpaper.jpg");
        //3.关闭文件
        fileInputStream.close();
        fileOutputStream.close();
    }

其中fileInputStream.read时,有三个版本的read,分别对应三种方式:
一次读1个字节
一次读若干个字节,尝试把这个数组填满
一次读若干个字节,尝试填充b这个数组,从off下标开始,最多填充length个元素
这里我用的第二个方式.
在这里插入图片描述
并且这个read拥有int返回值,这表示返回值表示这次read操作实际读取到几个字节~
buffer实际长度是1024,但是这一次不一定能填满
如果文件一共10个字节,显然不能填满,返回值就是10
当文件已经读完了,就会返回-1

read的时候还可能会抛出IOException异常.
FileNotFoundException时IOException异常的子类,
所以就不应再写File…异常了


这里的fileOutputStream.write()也有三个版本

在这里插入图片描述
一次写1个字节
一次写若干个字节,尝试把b这个数组填满
一次写若干个字节,尝试填充b这个数组,从off下标开始,最多填充length个元素

运行代码,得到
在这里插入图片描述
再看看相应的目录下已经有了复制后的 wallpaper2.jpg 文件
在这里插入图片描述


总结:
流对象的核心操作:
1.打开文件(构造方法)
2.read:把文件从数据读到内存中
3.write:把数据从内存中写入到文件中.
4.close:关闭文件(和打开对应的文件)

如果不关闭文件,就会造成文件资源泄露

  • 一个一个的应用程序对应的进程在内核中都会对应着一个或者很多PCB,这些PCB通过双向链表链接.
  • 当一个进程中,打开一个文件的时候,就会在PCB中进行一定的记录
  • 在PCB里有一个属性,这个属性叫做文件描述符表,这张表是一个数组
  • 这个数组的每一个元素叫做file_struct (结构体),描述了文件的相关属性
  • Java中的File是在JVM描述文件,file_struct是在内核中描述文件(功能类似于File类)
  • 代码中每次打开文件就是在文件描述符表中创建了一个项
  • 代码中每次关闭文件就是在文件描述符表中删除了一个项
  • 那么关键就在于,这个文件描述符表元素个数是有上限的
  • 如果代码中一直反复打开新的文件,而没有关闭,那么文件描述符表就会被打满
  • 一旦满了,后面再想打开新的文件就会打开失败
  • 不过一个进程的文件描述符表的上限是可配置的

文件描述符表是PCB的一个属性
一个进程可能有多个PCB(每个PCB对应一个线程)
同一个进程的若干个PCB之间其实共享这同一个文件描述符表

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值