JavaEE 初阶篇-深入了解 I/O 流(FileInputStream 与 FileOutputStream 、Reader 与 Writer)

🔥博客主页: 【小扳_-CSDN博客】**
❤感谢大家点赞👍收藏⭐评论✍**

文章目录

[1.0 I/O 流概述](#1.0 I/O 流概述)

[2.0 文件字节输入流(FileInputStream)](#2.0 文件字节输入流(FileInputStream))

[2.1 创建 FileInputStream 对象](#2.1 创建 FileInputStream 对象)

[2.2 读取数据](#2.2 读取数据)

[2.3 关闭流](#2.3 关闭流)

[3.0 文件字节输出流(FileOutputStream)](#3.0 文件字节输出流(FileOutputStream))

[3.1 创建 FileOutputSrteam 对象](#3.1 创建 FileOutputSrteam 对象)

[3.2 写入数据](#3.2 写入数据)

[3.3 写入字节数组](#3.3 写入字节数组)

[3.3 关闭流](#3.3 关闭流)

[4.0 用字节输入流与字节输出流来实现文件复制](#4.0 用字节输入流与字节输出流来实现文件复制)

[5.0 释放资源的方式](#5.0 释放资源的方式)

[6.0 文件字符输入流(FileReader)](#6.0 文件字符输入流(FileReader))

[6.1 创建 FileReader 对象](#6.1 创建 FileReader 对象)

[6.2 读取字符](#6.2 读取字符)

[6.3 读取字符数组](#6.3 读取字符数组)

[7.0 文件字符输出流(FileWriter)](#7.0 文件字符输出流(FileWriter))

[7.1 创建 FileWriter 对象](#7.1 创建 FileWriter 对象)

[7.2 写入字符](#7.2 写入字符)


1.0 I/O 流概述

I/O 流是 Java 中用于处理输入和输出的机制,它提供了一种统一的方式来处理不同来源和目的地的数据。在 Java 中,I/O 流主要用于与文件、网络、内存等进行数据的读取和写入操作。

I 指 Input,称为输入流:负责把数据读到内存中。

O 指 Output,称为输出流:负责把数据从内存中写到磁盘或者网络等等。

I/O 流可以分为字节流和字符流两种类型。字节流以字节为单位读写数据,适用于处理二进制文件或字节数据;字符流以字符为单位读写数据,适用于处理文本文件或字符数据。

2.0 文件字节输入流(FileInputStream)

FileInputStream 是 Java 中用于从文件中读取字节数据的输入流类。它继承 InputStream 类,提供了一些方法来读取文件中的字节数据。

2.1 创建 FileInputStream 对象

可以使用 FileInputStream 的构造函数来创建对象,需要传入要读取的文件路径或者文件对象作为参数。

代码如下:

java 复制代码
FileInputStream fis = new FileInputStream("path/to/file.txt");

简单来说,创建了 FileInputStream 对象相当于创建了连通文件与内存之间的管道,进行字节流的流通。但是,该管道只能从文件流到内存中。

2.2 读取数据

FileInputStream 提供了 read() 方法来读取文件中的字节数据。每次调用 read() 方法会读取一个字节,并返回读取的字节数据(以 int 类型表示,范围为 0 到 255),如果已到达文件末尾,则返回 -1。

举个例子:

将该文件中的内容读取到内存中。

代码如下:

java 复制代码
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class demo1 {
    public static void main(String[] args) throws IOException {
        InputStream inputStream = new FileInputStream("D:\\software\\code\\2023_java\\2023_java_code\\code_24_4_21\\src\\MyFile\\MyText.text");
        //每次读取一个字节
        int n;
        //直到返回-1,就意味这已经将全部内容读取完毕了
        while ( (n = inputStream.read()) != -1 ){
            //因为读取出来的是字节,读出来的都是整数,那么可以将其转换为字符
            System.out.print((char) n);
        }

        //关闭资源
        inputStream.close();
    }
}

运行结果:

除了单个字节的读取, FileInputStream 还提供了 read(byte[] b) 方法来一次性读取多个字节到指定的字节数组中,其中返回值为读取的字节个数。

简单来说,可以将 b 数组当作成一个容器,那么 b 数组一下子可以从文件中装指定大小个字节,并且返回 b 容器中装了多少了字节,一旦返回值为 -1 ,则代表已经将文件中的内容读取完毕了。

该方法的效率远比一个一个字节读取到内存中的方法高效很多。

代码如下:

java 复制代码
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class demo2 {
    public static void main(String[] args) throws IOException {
        InputStream inputStream = new FileInputStream("D:\\software\\code\\2023_java\\2023_java_code\\code_24_4_21\\src\\MyFile\\MyText.text");

        //指定容器大小为 1024 个字节(1kb)
        byte[] b = new byte[1024];
        int n ;
        while ((n = inputStream.read(b)) != -1){
            //为了更好的观察,转换成字符串,不可能每一次都恰好可以装满1kb,只解析 0 到 n 个字节。
            String s = new String(b,0,n);
            System.out.println(s);
        }

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

运行结果:

注意事项:

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

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

使用字节流读取中文,如何保证输出不乱码,怎么解决?

定义一个与文件一样大小的字节数组,一次性读取完文件的全部字节。

代码如下:

java 复制代码
import java.io.*;

public class demo3 {
    public static void main(String[] args) throws IOException {
        File file = new File("D:\\software\\code\\2023_java\\2023_java_code\\code_24_4_21\\src\\MyFile\\MyText.text");
        InputStream inputStream = new FileInputStream(file);
        byte[] b = new byte[(int)file.length()];
        inputStream.read(b);
        String s = new String(b);
        System.out.println(s);

        inputStream.close();
    }
}

运行结果:

此外官方也提供了相对应的方法 readAllBytes() 。

代码如下:

java 复制代码
import java.io.*;

public class demo4 {
    public static void main(String[] args) throws IOException {
        File file = new File("D:\\software\\code\\2023_java\\2023_java_code\\code_24_4_21\\src\\MyFile\\MyText.text");
        InputStream inputStream = new FileInputStream(file);
        byte[] b = inputStream.readAllBytes();
        String s = new String(b);
        System.out.println(s);
    }
}

效果是跟自己实现的代码时相同的。

运行结果:

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

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

无论用官方实现的代码还是自己实现的代码,面对文件中的内容较大的时候,用该方法就很不现实了,因为磁盘肯定比内存空间大很多。读取文本内容更适合用字符流。而字节流适合坐数据的转移,如:文件复制等。

2.3 关闭流

在读取完文件数据后,应该及时关闭 FileInputStream 对象以释放资源。可以使用 close() 方法来关闭流。

以上代码已经出现过了。

java 复制代码
fis.close();

3.0 文件字节输出流(FileOutputStream)

FileOutputStream 是 Java 中用于向文件中写入字节数据的输出流类。它继承 OutputStream类,提供了一些方法来向文件中写入字节数据。

3.1 创建 FileOutputSrteam 对象

可以使用 FileOutStream 的构造函数来创建对象,需要存入要写入的文件路径作为参数。如果文件不存在,会自动创建新文件;如果文件已存在,会覆盖原有内容,也可以给构造器中再传入一个参数 true ,以支持继续追加,不会覆盖原有的内容。

代码如下:

java 复制代码
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;

public class demo5 {
    public static void main(String[] args) throws FileNotFoundException {
        OutputStream outputStream = new FileOutputStream("D:\\software\\code\\2023_java\\2023_java_code\\code_24_4_21\\src\\MyFile\\MyText.text");
        
    }
}

简单来说:创建 FileOutputStream 对象相当于创建了连接内存与文件的通道,只是该通道只能将字节流从内存流到文件中。

3.2 写入数据

FileOutputStrea 提供了 write(int b) 方法来向文件中写入一个字节数据。可以将一个整数作为参数传递,只会取低 8 位字节写入文件。

代码如下:

java 复制代码
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class demo5 {
    public static void main(String[] args) throws IOException {
        OutputStream outputStream = new FileOutputStream("D:\\software\\code\\2023_java\\2023_java_code\\code_24_4_21\\src\\MyFile\\MyText.text");
        outputStream.write(97);
        outputStream.write(98);
        outputStream.write(99);
        outputStream.write(100);
        outputStream.write(101);
        outputStream.write(102);
        outputStream.write(103);
        outputStream.write(104);
        
        outputStream.close();
    }
}

运行结果:

3.3 写入字节数组

除了单个字节的写入,FileOutputStream 还提供了 write(byte[] b) 方法来一次性写入整个字节数组中的数据。

代码如下:

java 复制代码
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class demo6 {
    public static void main(String[] args) throws IOException {
        OutputStream outputStream = new FileOutputStream("D:\\software\\code\\2023_java\\2023_java_code\\code_24_4_21\\src\\MyFile\\MyText.text");

        String str = "土豆土豆我是地瓜";
        outputStream.write(str.getBytes());

        outputStream.close();
    }
}

运行结果:

如果不想覆盖原有的内容,那么在构造 FileOutputSrteam 对象的时候,构造方法再传入一个参数 true ,代表允许追加内容。

代码如下:

java 复制代码
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class demo6 {
    public static void main(String[] args) throws IOException {
        OutputStream outputStream = new FileOutputStream("D:\\software\\code\\2023_java\\2023_java_code\\code_24_4_21\\src\\MyFile\\MyText.text",true);

        String str = "土豆土豆我是地瓜";
        outputStream.write(str.getBytes());

        outputStream.close();
    }
}

运行结果:

再次强调,是以一个字节输出或者输出字节数组到文件中。

3.3 关闭流

在读取完文件数据后,应该及时关闭 FileOutputStream 对象以释放资源。可以使用 close() 方法来关闭流。

以上代码已经出现过了。

java 复制代码
fis.close();

4.0 用字节输入流与字节输出流来实现文件复制

思路:先将要读取的文件创建通道,将文件中字节流流到内存中,再将内存中的字节流流到指定的文件中。即创建文件 ==> 读取数据 ==> 写入数据 。

代码如下:

java 复制代码
import java.io.*;

public class demo7 {
    public static void main(String[] args) throws Exception {
        copy("D:\\照片\\201651723362635996.jpg","D:\\software\\aaa.jpg");
    }
    public static void copy(String path, String obj) throws Exception {
        if (path == null || obj == null){
            return;
        }
        InputStream inputStream = new FileInputStream(path);
        OutputStream outputStream = new FileOutputStream(obj);

        byte[] b = new byte[3];
        int n;
        while ((n = inputStream.read(b)) != -1){
            outputStream.write(b,0,n);

        }

        outputStream.close();
        inputStream.close();
    }
}

运行结果:

字节流非常适合做一切文件的复制操作:

任何文件的底层都是字节,字节流复制,是一字不漏的转移完全部字节,只要复制后的文件格式一致就没问题。

5.0 释放资源的方式

在 Java 中释放资源通常是指关闭文件流、数据库连接、网络连接等资源,以避免资源泄露和浪费。以下是几种常见的释放资源的方式:

1)使用 try-with-resources:Java 7 引入了 try-with-resources 语句,可以自动关闭实现了 AutoCloseable 接口的资源。在 try-with-resources 语句中创建的资源会在代码块执行完毕后自动关闭,无需手动调用 close() 方法。

java 复制代码
try (FileInputStream fis = new FileInputStream("file.txt")) {
    // 读取文件内容
} catch (IOException e) {
    // 异常处理
}

2)手动关闭资源:如果无法使用 try-with-resources,需要手动关闭资源。在不再需要资源时,通过调用资源的 close() 方法来关闭资源。

java 复制代码
FileInputStream fis = null;
try {
    fis = new FileInputStream("file.txt");
    // 读取文件内容
} catch (IOException e) {
    // 异常处理
} finally {
    if (fis != null) {
        try {
            fis.close();
        } catch (IOException e) {
            // 异常处理
        }
    }
}

6.0 文件字符输入流(FileReader)

FileReader 是 Java 中用于读取字符数据的输入流,继承自 Reader 类。FileReader 可以用来读取文本文件中的字符数据。

6.1 创建 FileReader 对象

使用 FileReader,首先需要创建 FileReader 对象并指定要读取的文件路径或者文件对象。

代码如下:

java 复制代码
FileReader fr = new FileReader("file.txt");

6.2 读取字符

FileReader 提供了 read() 方法来读取单个字符。它会返回一个整数,表示读取的字符的 Unicode 编码。读取到文件末尾时返回 -1。

代码如下:

java 复制代码
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

public class demo8 {
    public static void main(String[] args) {
        try (Reader reader = new FileReader("D:\\software\\code\\2023_java\\2023_java_code\\code_24_4_21\\src\\MyFile\\MyText.text");){
            int n;
            while ((n = reader.read()) != -1){
                System.out.print((char) n);
            }
        }catch (IOException e){
            e.printStackTrace();
        }

    }
}

运行结果:

6.3 读取字符数组

FileReader 还提供了 read(char[] cbuf )方法来一次性读取多个字符,并将其存储到字符数组中。读取到文件末尾时返回 -1。返回值是读取的字符个数。

代码如下:

java 复制代码
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

public class demo9 {
    public static void main(String[] args) {
        try (Reader reader = new FileReader("D:\\software\\code\\2023_java\\2023_java_code\\code_24_4_21\\src\\MyFile\\MyText.text")){
            char[] chars = new char[3];
            int n;
            while ((n = reader.read(chars)) != -1){
                String s = new String(chars,0,n);
                System.out.print(s);
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

运行结果:

7.0 文件字符输出流(FileWriter)

FileWriter 是 Java 中用于写入字符数据的输出流,继承自 Writer 类。FileWriter 可以用来向文件中写入字符数据。

7.1 创建 FileWriter 对象

要使用 FileWriter ,首先需要创建 FileWriter 对象并指定要写入的文件路径或者文件对象。可以指定是否追加数据到文件末尾。

代码如下:

java 复制代码
FileWriter fw = new FileWriter("file.txt");

简答来说,创建 FileWriter 对象相当于创建连接了内存与文件的通道,只是该通道只能将字符流从内存流到文件中。

7.2 写入字符

FileWriter 提供了 write(int c) 方法来写入单个字符,以及 write(String str) 方法来写入字符串,还可以写入字符数组。

代码如下:

java 复制代码
public class demo10 {
    public static void main(String[] args) {
        try (Writer writer = new FileWriter("D:\\software\\code\\2023_java\\2023_java_code\\code_24_4_21\\src\\MyFile\\MyText.text")){
            writer.write(97);
            //换行
            writer.write("\r\n");
            writer.write("土豆土豆我是地瓜");
            //换行
            writer.write("\r\n");
            writer.write('c');

            //换行
            writer.write("\r\n");
            String s = "地瓜地瓜我是土豆";
            writer.write(s.toCharArray());

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

运行结果:

注意事项:

字符输出流写出数据后,必须刷新流,或者关闭流,写出去的数据才能生效。

写出数据时,数据会先被写入到内存缓冲区中,而不是直接写入到文件中。这是为了提高写入效率,减少频繁地访问磁盘的开销。

因此,为了确保写出去的数据能够及时生效,你可以选择调用 flush() 方法来刷新缓冲区,或者调用 close() 方法来关闭流。这样可以保证数据被写入到文件中,而不会遗失或被丢弃。

相关推荐
爱上语文31 分钟前
Springboot的三层架构
java·开发语言·spring boot·后端·spring
serve the people35 分钟前
springboot 单独新建一个文件实时写数据,当文件大于100M时按照日期时间做文件名进行归档
java·spring boot·后端
qmx_071 小时前
HTB-Jerry(tomcat war文件、msfvenom)
java·web安全·网络安全·tomcat
为风而战2 小时前
IIS+Ngnix+Tomcat 部署网站 用IIS实现反向代理
java·tomcat
技术无疆4 小时前
快速开发与维护:探索 AndroidAnnotations
android·java·android studio·android-studio·androidx·代码注入
架构文摘JGWZ7 小时前
Java 23 的12 个新特性!!
java·开发语言·学习
拾光师7 小时前
spring获取当前request
java·后端·spring
aPurpleBerry7 小时前
neo4j安装启动教程+对应的jdk配置
java·neo4j
我是苏苏8 小时前
Web开发:ABP框架2——入门级别的增删改查Demo
java·开发语言
xujinwei_gingko8 小时前
Spring IOC容器Bean对象管理-Java Config方式
java·spring