何为Java流

Java 中的流(Stream) 是用于在程序中读取或写入数据的抽象概念。流可以从不同的数据源(输入流)读取数据,也可以将数据写入到不同的目标(输出流)。流提供了一种统一的方式来处理不同类型的数据,例如文件、网络数据、内存数据等。

Java IO 流

在 Java 中,流分为输入流(InputStream)输出流(OutputStream)。输入流用于从数据源读取数据,而输出流用于将数据写入到目标

IO 即 Input/Output,输入和输出。数据输入到计算机内存的过程即输入,反之输出到外部存储(比如数据库,文件,远程主机)的过程即输出。数据传输过程类似于水流,因此称为 IO 流。IO 流在 Java 中分为输入流和输出流,而根据数据的处理方式又分为字节流和字符流

如果音频文件、图片等媒体文件用字节流比较好,如果涉及到字符的话使用字符流比较好。

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

  • InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流

  • OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.IOException;

public class InputStreamReaderExample {
    public static void main(String[] args) {
        // 假设有一个字节流 InputStream
        InputStream inputStream = getClass().getResourceAsStream("/example.txt");

        try (InputStreamReader isr = new InputStreamReader(inputStream);
             BufferedReader br = new BufferedReader(isr)) {
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

字符操作

Java提供了两个抽象类来表示字符流输入输出:ReaderWriter

如果我们不知道编码类型就很容易出现乱码问题,因此I/O流提供了一个直接操作字符的接口,方便平时对字符进行流操作。
![[Character Stream.png]]

字符流

字符流(Character Stream)以字符为单位进行读取和写入操作,适用于处理文本文件、配置文件、文档等纯文本数据。字符流是由 Java 虚拟机将字节转换得到的,默认采用的是 Unicode 编码,我们可以通过构造方法自定义编码。

字符流 = 字节流 + 编码表

乱码问题

在字节流中,一个字符通常由多个字节组成,而不同的字符编码使用的字节数不同。如果使用了错误的字符编码,或者在读取和写入数据时没有正确处理字符编码的转换,就会导致读取出来的中文字符出现乱码。

![[FIS Character.png]]

由于 String 的构造方法具有解码功能,使用这种方式也可以正确读出中文

使用 new String(byte bytes[], int offset, int length) 将字节流转换为字符串时,Java 会根据 UTF-8 的规则将每 3 个字节解码为一个中文字符,从而正确地解码出中文。

尽管字节流也有办法解决乱码问题,但不够直接,于是就有了字符流

Read 字符输入流

Reader 用于从源头(通常是文件)读取数据(字符信息)到内存中,java.io.Reader 抽象类是所有字符输入流的父类,提供读取字符流的共同方法和特性。

Reader 用于读取文本, InputStream 用于读取原始字节。

常用方法
  • read(): 从输入流读取一个字符,返回读取的字符(转为 int 类型),当读取到文件末尾时,返回 -1

  • read(char[] cbuf): 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中,等价于 read(cbuf, 0, cbuf.length)

  • read(char[] cbuf, int off, int len):在read(char[] cbuf) 方法的基础上增加了 off 参数(偏移量)和 len 参数(要读取的最大字符数)。

  • skip(long n):忽略输入流中的 n 个字符 ,返回实际忽略的字符数。

  • close(): 关闭输入流并释放相关的系统资源。

常用子类
InputStreamReader

InputStreamReader 是字节流转换为字符流的桥梁,可以通过指定字符集来处理不同的编码方式。

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.IOException;

public class InputStreamReaderExample {
    public static void main(String[] args) {
        // 假设有一个字节流 InputStream
        InputStream inputStream = getClass().getResourceAsStream("/example.txt");

        try (InputStreamReader isr = new InputStreamReader(inputStream);
             BufferedReader br = new BufferedReader(isr)) {
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

示例说明:

  • 创建 InputStream:这里使用 getClass().getResourceAsStream() 方法来获取资源文件 example.txtInputStream

  • 创建 InputStreamReader:使用 inputStream 创建一个 InputStreamReader,默认使用系统默认的字符集。

  • 创建 BufferedReader:使用 InputStreamReader 创建一个 BufferedReader,以便逐行读取数据。

  • 读取数据:通过 BufferedReaderreadLine() 方法逐行读取数据,并打印出来。

FileReader

FileReader 直接从文件中读取字符的输入流。继承自 InputStreamReader 类,因此它也可以处理字节流,并将其转换为字符流。

构造方法

  1. FileReader(File file):创建一个新的 FileReader,参数为File对象。
  2. FileReader(String fileName):创建一个新的 FileReader,参数为文件名。

FileReader 实现了 AutoCloseable 接口,因此可以使用 try-with-source 语句自动关闭资源,避免了手动关闭资源的繁琐操作。

import java.io.FileReader;
import java.io.BufferedReader;
import java.io.IOException;

public class FileReaderExample {
    public static void main(String[] args) {
        String filePath = "example.txt";

        try (FileReader fr = new FileReader(filePath);
             BufferedReader br = new BufferedReader(fr)) {
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

示例说明:

  • 创建 FileReader:使用文件路径 example.txt 创建一个 FileReader

  • 创建 BufferedReader:使用 FileReader 创建一个 BufferedReader,以便逐行读取数据。

  • 读取数据:通过 BufferedReaderreadLine() 方法逐行读取数据,并打印出来。

BuffereredReader

BufferedReader 字符缓冲输入流,继承自 Reader 类。提供了缓冲功能,可以减少读取操作对底层资源文件的访问次数,提高读取效率。通常被用来逐行读取字符数据。

使用流程

  1. 创建字符输入流对象,选择合适的子类,例如 FileReader

  2. 使用构造器来指定要读取的文件或其他字符源。

  3. 使用读取方法从流中读取字符数据,例如 read()read(char[])

  4. 处理读取的字符数据,可以将其保存到变量中,或者进行其他操作。

  5. 关闭字符输入流,释放资源,可以使用 close() 方法(try-with-resources 语句可自动释放资源)来关闭流。

import java.io.FileReader;
import java.io.BufferedReader;
import java.io.IOException;

public class BufferedReaderExample {
    public static void main(String[] args) {
        String filePath = "example.txt";

        try (FileReader fr = new FileReader(filePath);
             BufferedReader br = new BufferedReader(fr)) {
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

示例说明:

  • 创建 FileReader:使用文件路径 example.txt 创建一个 FileReader

  • 创建 BufferedReader:使用 FileReader 创建一个 BufferedReader,以便逐行读取数据。

  • 读取数据:通过 BufferedReaderreadLine() 方法逐行读取数据,并打印出来。

Write 字符输出流

Writer 用于将数据(字符信息)写入到目的地(通常是文件)java.io.Writer 抽象类是所有字符输出流的父类。提供了写入字符流的共同方法和特性。

常用方法

Writer 常用方法:

  • write(int c): 写入单个字符。

  • write(char[] cbuf):写入字符数组 cbuf,等价于write(cbuf, 0, cbuf.length)

  • write(char[] cbuf, int off, int len):在write(char[] cbuf) 方法的基础上增加了 off 参数(偏移量)和 len 参数(要读取的最大字符数)。

  • write(String str):写入字符串,等价于 write(str, 0, str.length())

  • write(String str, int off, int len):在write(String str) 方法的基础上增加了 off 参数(偏移量)和 len 参数(要读取的最大字符数)。

  • append(CharSequence csq):将指定的字符序列附加到指定的 Writer 对象并返回该 Writer 对象。

  • append(char c):将指定的字符附加到指定的 Writer 对象并返回该 Writer 对象。

  • flush():刷新此输出流并强制写出所有缓冲的输出字符。

  • close():关闭输出流释放相关的系统资源。

如果不关闭资源,数据只是保存到缓冲区,并未保存到文件中。

常用子类
FileWriter

FileWriter:用于向文件中写入字符数据。通过指定文件路径和名称来创建一个用于写入文件的 FileWriter 对象,指定追加模式来续写或覆盖,类似于OutputStream

FileWriter 内置了缓冲区 ByteBuffer,所以如果不关闭输出流,就无法把字符写入到文件中。

![[FileWriter.png]]

但是关闭了流对象,就无法继续写数据了。如果我们既想写入数据,又想继续使用流,就需要 flush 方法了。

flush:刷新缓冲区,流对象可以继续使用。

close:先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。

构造方法

FileWriter 类提供了多个构造方法,用于创建不同类型的 FileWriter 对象。下面列出了 FileWriter 类最常用的构造方法及其用法:

  1. FileWriter(String fileName):创建一个将数据写入指定文件的 FileWriter 对象。若指定文件不存在,则尝试创建该文件;若文件已存在,则会将其清空。
String fileName = "example.txt";
FileWriter fileWriter = new FileWriter(fileName);
  1. FileWriter(String fileName, boolean append):创建一个将数据写入指定文件的 FileWriter 对象,append 参数用于控制是否在文件末尾追加数据。若 append 参数为 true,则在文件末尾追加数据;若 append 参数为 false,则会将文件清空。
String fileName = "example.txt";
boolean append = true;
FileWriter fileWriter = new FileWriter(fileName, append);
  1. FileWriter(File file):创建一个将数据写入指定文件的 FileWriter 对象。若指定文件不存在,则尝试创建该文件;若文件已存在,则会将其清空。
File file = new File("example.txt");
FileWriter fileWriter = new FileWriter(file);
  1. FileWriter(File file, boolean append):创建一个将数据写入指定文件的 FileWriter 对象,append 参数用于控制是否在文件末尾追加数据。若 append 参数为 true,则在文件末尾追加数据;若 append 参数为 false,则会将文件清空。
File file = new File("example.txt");
boolean append = true;
FileWriter fileWriter = new FileWriter(file, append);
  • fileName 参数表示文件名或文件路径,file 参数表示文件对象。

  • append 参数默认为 false,即默认在文件末尾覆盖数据。

  • 在创建 FileWriter 对象时,如果指定的文件或路径不存在,则会尝试创建相应的文件或目录。但是,如果没有创建权限或磁盘空间不足等原因,则会抛出 IOException 异常。

  • 在写入完数据后,应该调用 close() 方法关闭 FileWriter 对象,以释放相关资源。也可以使用 try-with-resources 语句自动关闭流

StringWriter

StringWriter:用于将字符数据写入一个字符串。它包含了一个可变的字符串缓冲区,可以将写入的字符累积到缓冲区中,最终将缓冲区中的数据作为字符串返回。

缓冲区大小是根据需要动态增长的,因此没有固定的默认大小。向 StringWriter 写入字符时,如果缓冲区的空间不足,它会自动增加缓冲区的大小以适应更多的字符数据(无论是否指定大小)。

StringWriter 类的实现是线程安全的。

构造方法

  1. StringWriter():创建一个初始化的空字符串缓冲区。
StringWriter stringWriter = new StringWriter();
  1. StringWriter(int initialSize):创建一个指定初始大小的字符串缓冲区。
int initialSize = 1024;
StringWriter stringWriter = new StringWriter(initialSize);
  • 在使用 StringWriter 类时,不需要关心底层数据的存储位置,只需要通过 write() 方法写入数据即可。

  • 写入完数据后,可以通过 toString() 方法获取缓冲区中的数据字符串。如果需要清空字符串缓冲区,可以调用 getBuffer().setLength(0) 方法。

CharArrayWriter

CharArrayWriter 用于将字符数据写入一个字符数组。它包含了一个可变的字符数组缓冲区,可以将写入的字符累积到缓冲区中,最终将缓冲区中的字符数组作为结果返回。

CharArrayWriter 的缓冲区大小是根据需要动态增长的,因此没有固定的默认大小。如果缓冲区的空间不足,它会自动增加缓冲区的大小以适应更多的字符数据。

CharArrayWriter 类的实现也是线程安全的。

构造方法

  1. CharArrayWriter():创建一个初始化的空字符数组缓冲区。
CharArrayWriter charArrayWriter = new CharArrayWriter();
  1. CharArrayWriter(int initialSize):创建一个指定初始大小的字符数组缓冲区。
int initialSize = 1024;
CharArrayWriter charArrayWriter = new CharArrayWriter(initialSize);
  • 在使用 CharArrayWriter 类时,不需要关心底层数据的存储位置,只需要通过 write() 方法写入数据即可。

  • 写入完数据后,可以通过 toCharArray() 方法获取缓冲区中的字符数组,并通过 toString() 方法将其转换为字符串。如果需要清空字符数组缓冲区,可以调用 reset() 方法。

BufferedWriter

BufferedWriter:用于向其他 Writer 对象提供缓冲功能,减少直接与底层目标进行 IO 操作的次数。它包装了其他 Writer 对象,提供了缓冲写入功能,可以一次写入大块数据,提高IO性能。

构造方法

  1. BufferedWriter(Writer writer):创建一个使用默认缓冲区大小(8192 字节)的 BufferedWriter 对象。
BufferedWriter bufferedWriter = new BufferedWriter(writer);
  1. BufferedWriter(Writer writer, int bufferSize):创建一个指定缓冲区大小的 BufferedWriter 对象。
int bufferSize = 4096;
BufferedWriter bufferedWriter = new BufferedWriter(writer, bufferSize);
  1. BufferedWriter(Writer writer, int bufferSize, int maxBufferSize):创建一个指定缓冲区初始大小和最大容量的 BufferedWriter 对象。
int bufferSize = 1024;
int maxSize = 4096;
BufferedWriter bufferedWriter = new BufferedWriter(writer, bufferSize, maxSize);
  • BufferedWriter 类是用来提高写入性能的,它会将数据先写入缓冲区,而不是直接写入目标流。当缓冲区满或发生显式调用 flush() 方法,缓冲区的内容将会真正写入到目标流中。

  • 在使用完 BufferedWriter 后,要记得调用 close() 方法关闭流,以释放资源,并确保缓冲区的内容被正确刷新到目标流中。

import java.io.FileWriter;
import java.io.BufferedWriter;
import java.io.IOException;

public class BufferedWriterExample {
    public static void main(String[] args) {
        String filePath = "example.txt";
        String content = "Hello, World!";

        try (FileWriter fw = new FileWriter(filePath);
             BufferedWriter bw = new BufferedWriter(fw)) {
            bw.write(content);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

示例解释:

  • 创建 FileWriter:使用文件路径 example.txt 创建一个 FileWriter

  • 创建 BufferedWriter:使用 FileWriter 创建一个 BufferedWriter,以便高效地写入数据

  • 写入数据:通过 BufferedWriterwrite() 方法写入字符串 content

PrintWriter

PrintWriter:用于向文本输出流或字符输出流打印格式化的字符数据。它提供了一系列便利的 print() 和 println() 方法,可以轻松地将各种类型的数据输出为字符串。

构造方法

  1. PrintWriter(OutputStream out):创建一个将文本输出到指定的 OutputStream 的 PrintWriter 对象。
OutputStream outputStream = new FileOutputStream("output.txt");
PrintWriter printWriter = new PrintWriter(outputStream);
  1. PrintWriter(OutputStream out, boolean autoFlush):创建一个将文本输出到指定的 OutputStream 的 PrintWriter 对象,并指定是否自动刷新输出流。若 autoFlush 参数为 true,则在每次调用写入操作(如 print() 或 println())后会自动刷新输出流。
OutputStream outputStream = new FileOutputStream("output.txt");
boolean autoFlush = true;
PrintWriter printWriter = new PrintWriter(outputStream, autoFlush);
  1. PrintWriter(Writer out):创建一个将文本输出到指定的 Writer 的 PrintWriter 对象。
Writer writer = new FileWriter("output.txt");
PrintWriter printWriter = new PrintWriter(writer);
  1. PrintWriter(Writer out, boolean autoFlush):创建一个将文本输出到指定的 Writer 的 PrintWriter 对象,并指定是否自动刷新输出流。
Writer writer = new FileWriter("output.txt");
boolean autoFlush = true;
PrintWriter printWriter = new PrintWriter(writer, autoFlush);

字符缓冲流

字符缓冲流的基本方法与字符流调用方式一致。字符缓冲流有特有的方法。

  • BufferedReaderString readLine(): 读一行数据,读取到最后返回 null

  • BufferedWriternewLine(): 换行,由系统定义换行符。

BufferedReader 类和 BufferedWriter 类是Java IO库中提供的两个高效的字符流类,类似于字节缓冲流,用于在读取和写入字符时提供缓冲区功能。

BufferedReader 字符缓冲输入流

BufferedReader 类是 Reader 类的子类,它包装了一个现有的 Reader 对象,并提供了缓冲功能,可以一次读取一行字符数据。

BufferedReader 类提供了:

  • read() 方法 用于读取单个字符

  • readLine() 方法用于读取一行字符数据

  • mark()reset() 方法来支持标记和复位操作。

使用 BufferedReader 可以减少底层的IO操作次数,从而提高读取字符数据的效率。特别是在读取大型文本文件时,使用 BufferedReader 可以显著提升性能。

BufferedWriter 字符缓冲输出流

BufferedWriter 类是 Writer 类的子类,它包装了一个现有的 Writer 对象,并提供了缓冲功能,可以批量写入字符数据。

BufferedWriter 类提供了:

  • write() 方法用于写入字符数据

  • newLine() 方法用于写入换行符

  • flush() 方法用于刷新缓冲区的数据。

使用 BufferedWriter 可以减少底层的IO操作次数,从而提高写入字符数据的效率。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐