3

引言

Java IO(Input输入/Output输出)框架扮演着至关重要的角色,它是数据交换和文件处理的基石。
数据输入到计算机内存的过程即输入,反之输出到外部存储(比如数据库,文件,远程主机)的过程即输出。数据传输过程类似于水流,因此称为 IO 流。
image.png

一、Java IO体系概览

Java IO 流有40多个类,他们都是从如下 4 个抽象类基类中派生出来的。

抽象基类字节流字符流
输入流InputStreamReader
输出流OutputStreamWriter

字节流和字符流有什么区别

  • 字节流:以字节(8位)为单位进行数据的读写。它是计算机中最基本的数据传输单元,适用于处理包括文本、图像、音频、视频等在内的二进制数据。
  • 字符流:以字符为单位进行数据的读写。字符流在处理时,会根据指定的字符编码(如UTF-8、GBK等)将字符转换为字节进行处理。由于一个字符可能占用多个字节(具体取决于编码方式),主要用于处理文本数据。

IO 流的分类

类型名称说明
按功能分输入流,输出流输入流的类以InputStream, Reader为后缀。输出流的类以OutputStream, Writer未后缀。
按照类型分字节流、字符流节点流的类以InputStream,OutputStream为后缀,字符流以Reader,Writer为后缀
按角色或功能层次节点流(低级流、原始流)、包装流(处理流、高级流)节点流 它是直接从数据源或目的地读写数据的流。除了对原数据进行读取的节点流外,其他都是包装流

下图展示IO流系统图-常用的类
image.png

二、IO操作之文件与目录的操作

介绍File类,包括如何创建、删除文件或目录,检查文件属性等。

package file;

import org.junit.Test;

import java.io.File;
import java.io.IOException;

public class studyOfFile {
    @Test
    public void createFile() throws IOException {
        String filePath = "/Users/wu/file";
        File file = new java.io.File(filePath);
        /*创建文件*/
        file.createNewFile();

        /*查看文件的一下属性*/
        System.out.println("创建文件成功 ~");
        System.out.println("文件名字: " + file.getName());
        System.out.println("文件是否存在: " + file.exists());
        System.out.println("文件是否是文件: " + file.isFile());
        System.out.println("文件的路径: " + file.getAbsolutePath());
        System.out.println("文件是否拥有读权限 " + file.canRead());
        System.out.println("文件是否存在: " + file.exists());

        /*删除文件*/
        file.delete();
        System.out.println("删除文件成功 ~");
        System.out.println("文件是否存在: " + file.exists());
    }

    @Test
    public void createDirectory() {
        String directory = "/Users/wu/studyIo";
        File file = new java.io.File(directory);
        /*创建目录*/
        file.mkdir();

        /*查看目录的一下属性*/
        System.out.println("创建目录成功 ~");
        System.out.println("文件是否是文件: " + file.isFile());
        System.out.println("文件是否是目录: " + file.isDirectory());

        /*删除目录*/
        file.delete();
        System.out.println("删除目录成功 ~");
        System.out.println("目录是否存在: " + file.exists());
    }
}

文件操作执行结果:
image.png
目录操作的执行结果:
image.png

三、以案例学习OI流

在本小节将展示一下内容:
字节流:FileInputStream和FileOutStream
字符流:FileReader和FileWiter
包装流:BufferedInputStream

<-- FileReader和FileWiter的用法和下面的例子很类似,就不在写demo了-->

FileInputStream例子

这个例子将演示如何使用FileInputStream来读取文件内容,并使用FileOutputStream将读取的内容写入另一个文件。

package demo;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class ByteStreamExample {
    public static void main(String[] args) {
        String sourcePath = "/Users/wu/studyIo/source.txt";
        String targetPath = "/Users/wu/studyIo/target.txt";

        FileInputStream fis = null;
        FileOutputStream fos = null;

        try {
            /*创建一个source.txt文件 用于从source.txt文件中读取去数据*/
            fis = new FileInputStream(sourcePath);
            /*创建一个target.txt文件 用于将读到的数据写到target.txt文件中*/
            fos = new FileOutputStream(targetPath);


            /*设置缓冲区的大小 一次读取1024字节*/
            byte[] buffer = new byte[1024];
            int length;

            /*read方法放回读取的字节数,读取完毕返回-1*/
            while ((length = fis.read(buffer)) > -1) {
                /*边读边写*/
                fos.write(buffer, 0, length);
            }

            System.out.println("文件复制完成。");

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    /*记得关闭 释放资源*/
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fos != null) {
                try {
                    /*真正执行写的操作,否则写入不成功*/
                    fos.write(0);

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

这里我遇到了一个问题:
fis = new FileInputStream(sourcePath);按理可以直接创建文件的。因为它的源代码是这样写的
image.png
我会报错
image.png
手动创建上两个文件就不会报错了。
至于为什么不创建文件而报错,我也没想明白。

BufferedInputStream例子

这个例子将展示如何使用BufferedInputStream来提高文件读取的效率。

package demo;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;

public class BufferedStreamExample {
    public static void main(String[] args) {
        String sourcePath = "/Users/wu/studyIo/source.txt";
        BufferedInputStream bis = null;

        try {
            bis = new BufferedInputStream(new FileInputStream(sourcePath));

            int data = bis.read();

            while (data != -1) {
                System.out.print((char) data);
                data = bis.read();
            }

            System.out.println("\n文件读取完成。");

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

运行结果:
image.png
这里面会出现乱码问题,原因是用的是BufferedInputStream他是字节流,一个中文是占3个字节,它将他分成了一个一个字节读,就会乱码,把BufferedInputStream换为BufferedReader字符流就可以解决乱码问题。(要考虑文件编码格式)

四、以对象处理流来学习序列化和反序列化

序列化:保持数据时,保持数据的值和数据类型
反序列化:恢复数据时,恢复数据的值和数据类型

package demo;

import java.io.*;

public class SerializationDemo {
    public static void main(String[] args) {
        String sourcePath = "/Users/wu/studyIo/employee.txt";

        // 序列化过程
        try (FileOutputStream fileOut = new FileOutputStream(sourcePath);
             ObjectOutputStream out = new ObjectOutputStream(fileOut)) {

            Employee emp = new Employee("John Doe", 30);
            out.writeObject(emp);
            System.out.println("Serialized data is saved in employee.ser");

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

        // 反序列化过程
        Employee emp = null;
        try (FileInputStream fileIn = new FileInputStream(sourcePath);
             ObjectInputStream in = new ObjectInputStream(fileIn)) {

            emp = (Employee) in.readObject();
            System.out.println("Deserialized Employee...");
            System.out.println("Employee : " + emp);

        } catch (IOException i) {
            i.printStackTrace();
            return;
        } catch (ClassNotFoundException c) {
            System.out.println("Employee class not found");
            c.printStackTrace();
            return;
        }
    }
}

五、补充字符转换流,打印流和随机访问流

补充流

六、IO异常处理

  1. 用try-catch-finally语句块处理异常。
  2. 类名后面添加 throws IOException 声明可以有IO异常发生,但不做处理。

参考资料

哔哩哔哩 -->韩顺平老师讲的IO专题视频


吴季分
390 声望13 粉丝