何为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提供了两个抽象类来表示字符流输入输出:Reader
和 Writer
。
如果我们不知道编码类型就很容易出现乱码问题,因此I/O流提供了一个直接操作字符的接口,方便平时对字符进行流操作。
字符流
字符流(Character Stream)以字符为单位进行读取和写入操作,适用于处理文本文件、配置文件、文档等纯文本数据。字符流是由 Java 虚拟机将字节转换得到的,默认采用的是 Unicode
编码,我们可以通过构造方法自定义编码。
字符流 = 字节流 + 编码表
乱码问题
在字节流中,一个字符通常由多个字节组成,而不同的字符编码使用的字节数不同。如果使用了错误的字符编码,或者在读取和写入数据时没有正确处理字符编码的转换,就会导致读取出来的中文字符出现乱码。
由于 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.txt
的InputStream
。 -
创建
InputStreamReader
:使用inputStream
创建一个InputStreamReader
,默认使用系统默认的字符集。 -
创建
BufferedReader
:使用InputStreamReader
创建一个BufferedReader
,以便逐行读取数据。 -
读取数据:通过
BufferedReader
的readLine()
方法逐行读取数据,并打印出来。
FileReader
FileReader
直接从文件中读取字符的输入流。继承自 InputStreamReader
类,因此它也可以处理字节流,并将其转换为字符流。
构造方法
FileReader(File file)
:创建一个新的FileReader
,参数为File对象。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
,以便逐行读取数据。 -
读取数据:通过
BufferedReader
的readLine()
方法逐行读取数据,并打印出来。
BuffereredReader
BufferedReader
字符缓冲输入流,继承自 Reader
类。提供了缓冲功能,可以减少读取操作对底层资源文件的访问次数,提高读取效率。通常被用来逐行读取字符数据。
使用流程
-
创建字符输入流对象,选择合适的子类,例如
FileReader
。 -
使用构造器来指定要读取的文件或其他字符源。
-
使用读取方法从流中读取字符数据,例如
read()
或read(char[])
。 -
处理读取的字符数据,可以将其保存到变量中,或者进行其他操作。
-
关闭字符输入流,释放资源,可以使用
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
,以便逐行读取数据。 -
读取数据:通过
BufferedReader
的readLine()
方法逐行读取数据,并打印出来。
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
,所以如果不关闭输出流,就无法把字符写入到文件中。
但是关闭了流对象,就无法继续写数据了。如果我们既想写入数据,又想继续使用流,就需要 flush
方法了。
flush
:刷新缓冲区,流对象可以继续使用。
close
:先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。
构造方法:
FileWriter
类提供了多个构造方法,用于创建不同类型的 FileWriter
对象。下面列出了 FileWriter
类最常用的构造方法及其用法:
FileWriter(String fileName)
:创建一个将数据写入指定文件的 FileWriter 对象。若指定文件不存在,则尝试创建该文件;若文件已存在,则会将其清空。
String fileName = "example.txt";
FileWriter fileWriter = new FileWriter(fileName);
FileWriter(String fileName, boolean append)
:创建一个将数据写入指定文件的 FileWriter 对象,append 参数用于控制是否在文件末尾追加数据。若 append 参数为 true,则在文件末尾追加数据;若 append 参数为 false,则会将文件清空。
String fileName = "example.txt";
boolean append = true;
FileWriter fileWriter = new FileWriter(fileName, append);
FileWriter(File file)
:创建一个将数据写入指定文件的 FileWriter 对象。若指定文件不存在,则尝试创建该文件;若文件已存在,则会将其清空。
File file = new File("example.txt");
FileWriter fileWriter = new FileWriter(file);
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
类的实现是线程安全的。
构造方法
StringWriter()
:创建一个初始化的空字符串缓冲区。
StringWriter stringWriter = new StringWriter();
StringWriter(int initialSize)
:创建一个指定初始大小的字符串缓冲区。
int initialSize = 1024;
StringWriter stringWriter = new StringWriter(initialSize);
-
在使用
StringWriter
类时,不需要关心底层数据的存储位置,只需要通过write()
方法写入数据即可。 -
写入完数据后,可以通过
toString()
方法获取缓冲区中的数据字符串。如果需要清空字符串缓冲区,可以调用getBuffer().setLength(0)
方法。
CharArrayWriter
CharArrayWriter
用于将字符数据写入一个字符数组。它包含了一个可变的字符数组缓冲区,可以将写入的字符累积到缓冲区中,最终将缓冲区中的字符数组作为结果返回。
CharArrayWriter
的缓冲区大小是根据需要动态增长的,因此没有固定的默认大小。如果缓冲区的空间不足,它会自动增加缓冲区的大小以适应更多的字符数据。
CharArrayWriter
类的实现也是线程安全的。
构造方法
CharArrayWriter()
:创建一个初始化的空字符数组缓冲区。
CharArrayWriter charArrayWriter = new CharArrayWriter();
CharArrayWriter(int initialSize)
:创建一个指定初始大小的字符数组缓冲区。
int initialSize = 1024;
CharArrayWriter charArrayWriter = new CharArrayWriter(initialSize);
-
在使用
CharArrayWriter
类时,不需要关心底层数据的存储位置,只需要通过write()
方法写入数据即可。 -
写入完数据后,可以通过
toCharArray()
方法获取缓冲区中的字符数组,并通过toString()
方法将其转换为字符串。如果需要清空字符数组缓冲区,可以调用reset()
方法。
BufferedWriter
BufferedWriter
:用于向其他 Writer 对象提供缓冲功能,减少直接与底层目标进行 IO 操作的次数。它包装了其他 Writer 对象,提供了缓冲写入功能,可以一次写入大块数据,提高IO性能。
构造方法
BufferedWriter(Writer writer)
:创建一个使用默认缓冲区大小(8192 字节)的 BufferedWriter 对象。
BufferedWriter bufferedWriter = new BufferedWriter(writer);
BufferedWriter(Writer writer, int bufferSize)
:创建一个指定缓冲区大小的 BufferedWriter 对象。
int bufferSize = 4096;
BufferedWriter bufferedWriter = new BufferedWriter(writer, bufferSize);
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
,以便高效地写入数据 -
写入数据:通过
BufferedWriter
的write()
方法写入字符串content
PrintWriter
PrintWriter
:用于向文本输出流或字符输出流打印格式化的字符数据。它提供了一系列便利的 print() 和 println() 方法,可以轻松地将各种类型的数据输出为字符串。
构造方法
PrintWriter(OutputStream out)
:创建一个将文本输出到指定的 OutputStream 的 PrintWriter 对象。
OutputStream outputStream = new FileOutputStream("output.txt");
PrintWriter printWriter = new PrintWriter(outputStream);
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);
PrintWriter(Writer out)
:创建一个将文本输出到指定的 Writer 的 PrintWriter 对象。
Writer writer = new FileWriter("output.txt");
PrintWriter printWriter = new PrintWriter(writer);
PrintWriter(Writer out, boolean autoFlush)
:创建一个将文本输出到指定的 Writer 的 PrintWriter 对象,并指定是否自动刷新输出流。
Writer writer = new FileWriter("output.txt");
boolean autoFlush = true;
PrintWriter printWriter = new PrintWriter(writer, autoFlush);
字符缓冲流
字符缓冲流的基本方法与字符流调用方式一致。字符缓冲流有特有的方法。
-
BufferedReader
:String readLine()
: 读一行数据,读取到最后返回 null -
BufferedWriter
:newLine()
: 换行,由系统定义换行符。
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操作次数,从而提高写入字符数据的效率。