目录
5.3 是否我可以这么理解:如果在我对少量用户名和密码文件进行读取时使用BufferedWriter没有任何的意义?
1. 总览:
Java中I/O流大致为下图分类
在Java中,IO流是用于处理输入和输出的机制。它提供了一种统一的方式来处理各种不同类型的数据源,包括文件、网络连接、内存等。IO流分为字节流和字符流两种类型,用于处理不同类型的数据。
有一点是初学者迷惑的:
IO流中的输入和输出指的是相对于程序的输入和输出。程序向外输出内容,会向输出流里写入,虽然写入操作看似是输入,但相对于程序本身而言它是在向外输出内容。
因此,程序写的是OutputStream
,读的是InputStream
。
2. 流的分类:
按照读取的方式进行分类:
字节流、字符流
按照流的方向进行分类:
输入流、输出流
3. 字符流和字节流,他们之间的划分依据是什么?
字节流和字符流在最底层都是字节流。
字节流是以字节为单位进行读写操作的,底层使用的是字节流(InputStream和OutputStream)来进行数据的传输和处理。字节流适用于处理二进制数据,如图片、音频、视频文件等。
字符流是对字节流的封装,通过使用字符编码将字节转换为字符,并以字符为单位进行读写操作。字符流包括了Reader和Writer两个抽象类,它们底层仍然使用字节流进行实际的读写操作。在字符流的内部,会将字符转换为字节进行处理,并提供了字符编码的支持。
因此,可以说字节流是字符流的基础,字符流在字节流的基础上添加了字符编码的功能,使得处理文本数据更加方便。具体选择字节流还是字符流,取决于处理的数据类型和需求。
4. 字节流、字符流怎么选择?怎么应用?
了解了字节流和字符流的底层后,自然地针对其特性字节流和字符流由不同的使用场景。
字节流(Byte Stream)主要用于处理二进制数据,以字节为单位进行读写操作。适用于处理图片、音频、视频等非文本格式的数据。
- InputStream和OutputStream是字节流的抽象类。
- FileInputStream和FileOutputStream是用于文件操作的字节流实现类。
public static void main(String[] args) {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
// 1.创建读取的那个文件的file对象
// 2.创建读取流的对象
// 3.创建写入的文件夹 并且判断 如果不存在则创建
// 4.创建文件对象,图片文件对象。如果不存在则创建
// 5.创建写入的流
// 6.读取图片并且写入
fis = new FileInputStream("D:\\bbb\\11.jpg");
File file = new File("D:\\aaa");
if (!file.exists()) {
file.mkdirs();
}
File file1 = new File(file, "酒杯.jpg");
if (!file1.exists()) {
file.createNewFile();
}
fos = new FileOutputStream(file1);
byte[] bytes = new byte[1024];
int ii= -1;
while ((ii = fis.read(bytes)) != -1){
fos.write(bytes,0,ii);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
· read(byte[] b) --> 读取文件中的字符的个数 读取到最后返回值为 -1 ---> 从返回值做文章
· 注意! read()方法在一次使用里面最好只调用一次。一次性读取大量数据,可能会导致内存不足或者造成线程阻塞,因此推荐在一次使用中最好只调用一次read()方法。除此之外,多次调用read()方法也可能会降低I/O效率,再讲下去就到 缓冲流 部分内容了不做过多延伸。
字符流(Character Stream)则主要用于处理文本数据,以字符为单位进行读写操作。适用于处理文本数据,能够自动处理字符编码。
- Reader和Writer是字符流的抽象类。
- FileReader和FileWriter是用于文件操作的字符流实现类。
// 输出流
public static void main(String[] args) throws IOException {
File file = new File("D:\\aaa\\a.txt");
FileWriter fw = new FileWriter(file, true);
fw.write("yyds");
fw.write("\r\n");
fw.close();
}
// 输入流
public static void main(String[] args) throws IOException {
File file = new File("D:\\aaa\\a.txt");
FileReader fr = new FileReader(file);
char[] chars = new char[1024];
int i = -1;
while ((i = fr.read(chars)) != -1) {
String s = new String(chars, 0, 1);
System.out.println(s);
}
fr.close();
}
记得在使用IO流时,要正确处理异常和使用try-with-resources语句块来自动关闭流,确保资源的正确释放。
所以,除了纯文本数据文件使用字符流以外,其他文件类型都应该使用字节流方式。
5. 缓冲流:对原始字节流和字符流的功能增强
在Java中,字节缓冲流(Byte Buffered Stream)和字符缓冲流(Character Buffered Stream)是用于提供缓冲功能的输入输出流。
字节缓冲流是通过字节流来进行数据的读取和写入,它包含了两个类:BufferedInputStream和BufferedOutputStream。字节缓冲流可以提高I/O的性能,因为它们会在内存中创建一个缓冲区,将数据先存储在缓冲区中,然后再一次性地读取或写入到底层的字节流中。这样可以减少每次对底层字节流的访问次数,提高效率。
字符缓冲流则是通过字符流来进行数据的读取和写入,它也包含了两个类:BufferedReader和BufferedWriter。字符缓冲流的作用和字节缓冲流类似,不同的是它们是针对字符数据而设计的。字符缓冲流可以处理字符编码,并提供了逐行读取的功能(BufferedReader特有的方法),方便读取文本文件中的数据。
5.1 缓冲流的底层原理
字节缓冲流和字符缓冲流的底层原理都是通过创建缓冲区来减少对底层流的直接访问,提高I/O操作的效率。
当使用字节缓冲流(BufferedInputStream和BufferedOutputStream)时,底层原理是通过创建一个内部的字节数组作为缓冲区。当我们调用缓冲流的读取方法时(如read()
),它会首先检查缓冲区中是否有足够的数据可供读取,如果有,则直接从缓冲区中读取;如果没有足够的数据,则会一次性从底层的字节流(如FileInputStream)中读取一定数量的数据到缓冲区,并返回所需的数据。这样就减少了对底层字节流的实际读取次数,提高了效率。
当使用字符缓冲流(BufferedReader和BufferedWriter)时,底层原理也是通过创建一个内部的字符数组作为缓冲区。当我们调用缓冲流的读取方法时(如readLine()
),它会检查缓冲区中是否有完整的一行文本可供读取,如果有,则直接从缓冲区中读取;如果没有完整的一行文本,则会一次性从底层的字符流(如FileReader)中读取一定数量的字符数据到缓冲区,并返回所需的文本行。这样可以避免每次读取一个字符或一个字节,提高了效率。
readline()方法的返回值是一个String类型的对象,即读取到的一行文本数据。readline()方法返回的字符串不包含行尾的换行符,并且在读取到文件末尾时返回null。
因此,在使用readline()方法时,需要对返回值进行判断,以避免出现空指针异常或读取不完整的情况。
在写入数据时,缓冲流同样会将数据先写入到缓冲区中,直到缓冲区达到一定的大小或者手动进行刷新操作(如flush()
),才会将缓冲区中的数据一次性写入到底层流中。
flush()的主要作用是确保输出流中的数据被及时发送出去,而不是停留在缓冲区中等待。
需要注意的是,虽然Java的输出流在关闭时会自动执行一次flush()操作,但为了确保数据被及时写入,特别是在某些关键时刻,我们可以显式地调用flush()方法。
通过使用缓冲区,我们可以减少底层流的访问次数,从而提高I/O的效率。同时,缓冲流还提供了对数据的预加载和预读取功能,可以一次性读取或写入较大的数据块,减少了频繁地访问磁盘或网络的开销。
注意!在使用缓冲流时,要记得在读取或写入完毕后关闭底层的字节流或字符流,以确保数据被正确地刷新到目标位置并释放相关资源。
5.2 字节/字符的缓冲流怎么选择?
字节缓冲流(BufferedInputStream和BufferedOutputStream)适用的场景包括:
-
大数据量的文件读写:当需要处理大量数据时,使用字节缓冲流可以减少底层字节流的访问次数,提高读写性能。
-
网络传输:在进行网络传输时,可以使用字节缓冲流来提高数据的传输效率,减少网络传输次数,降低延迟。
-
图片、音频、视频等二进制文件的读写:字节缓冲流适用于处理二进制数据,可以提高对这类文件的读写性能。
字符缓冲流(BufferedReader和BufferedWriter)适用的场景包括:
-
文本文件的读写:字符缓冲流提供了逐行读取的功能,非常适合处理文本文件,特别是需要逐行处理文本内容时。
-
字符编码的处理:字符缓冲流能够自动处理字符编码,简化了对不同编码的文本数据的读取和写入操作。
-
日志文件的处理:当处理日志文件等大量文本文件时,使用字符缓冲流可以提高读写性能,方便逐行处理日志记录。
总结:字节缓冲流适用于处理二进制数据和大数据量的操作,而字符缓冲流则适用于处理文本文件和字符编码的操作。根据具体的业务需求和数据类型,选择适合的缓冲流可以提高程序的性能和效率。
5.3 是否我可以这么理解:如果在我对少量用户名和密码文件进行读取时使用BufferedWriter没有任何的意义?
使用 BufferedReader 对少量用户名和密码文件进行读取可能并没有显著的性能提升,但它仍然具有一些优势和好处。
BufferedReader 是 Reader 类的一个装饰者(Decorator),它提供了缓冲区的功能,可以在读取文件时减少实际的物理读取次数,从而提高读取效率。但对于少量的用户名和密码文件,其大小很可能不足以发挥缓冲区带来的优势。
然而,即使在处理少量数据的情况下,使用 BufferedReader 仍然具有以下一些好处:
方便的读取方法:BufferedReader 提供了方便的方法来读取文件中的文本行,如
readLine()
方法。这使得读取文件的代码更简洁和易读。缓冲区的自动管理:BufferedReader 会自动管理内部的字符缓冲区,无需手动操作。这样可以简化代码,并且提供更高级别的抽象。
字符编码支持:BufferedReader 可以与 InputStreamReader 一起使用,用于处理不同的字符编码。这对于处理包含特殊字符或其他字符集的用户名和密码文件是非常有用的。
虽然 BufferedReader 可能对少量的用户名和密码文件的读取性能提升不明显,但是考虑到它的便利性、灵活性和字符编码支持,使用 BufferedReader 仍然是一个良好的选择,可以提高代码的可读性和扩展性,并为未来可能的需求提供更好的支持。
6. 转换流
转换流也是I/O流的一种常用分支。它们在字节流和字符流之间起到了桥梁的作用,实现了字节数据和字符数据之间的相互转换。
转换流包括InputStreamReader(
将字节流转换为字符流)
和OutputStreamWriter(
将字符流转换为字节流)
两个类。
当需要从字节流中读取数据并以字符形式进行处理时,可以使用InputStreamReader
将字节流转换为字符流。同样地,当需要将字符流中的数据以字节形式进行输出时,可以使用OutputStreamWriter
将字符流转换为字节流。
转换流的主要作用是处理不同编码方式之间的转换。通过指定转换流的字符编码,可以在读取和写入过程中实现字符集的转换,比如将字节流按照UTF-8编码转换为字符流,或者将字符流按照GBK编码转换为字节流。
转换流是一种重要的I/O流分支,在处理字节流和字符流之间的转换时非常有用。它们提供了非常便捷的方式来处理不同编码方式的数据。
import java.io.*;
public class ConversionExample {
public static void main(String[] args) {
try {
// 创建字节输入流
FileInputStream fis = new FileInputStream("input.txt");
// 创建转换流,将字节流转换为字符流(使用默认字符集)
InputStreamReader isr = new InputStreamReader(fis);
// 创建缓冲字符输入流
BufferedReader br = new BufferedReader(isr);
// 读取文本内容
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
// 关闭流
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
在示例中,我们首先创建了一个字节输入流FileInputStream
来读取文件input.txt
的字节数据。接下来,我们使用转换流InputStreamReader
将字节流转换为字符流,此时使用的是默认字符集。然后,我们再创建一个缓冲字符输入流BufferedReader
来加快读取操作。
最后,我们使用BufferedReader
读取文件的每一行,并将其打印出来。通过转换流,我们可以将字节数据按照字符编码方式转换为字符流进行处理。
import java.io.*;
public class ConversionExample {
public static void main(String[] args) {
try {
// 创建字节输出流
FileOutputStream fos = new FileOutputStream("output.txt");
// 创建转换流,将字符流转换为字节流(使用默认字符集)
OutputStreamWriter osw = new OutputStreamWriter(fos);
// 创建缓冲字符输出流
BufferedWriter bw = new BufferedWriter(osw);
// 写入文本内容
String text = "Hello, World!";
bw.write(text);
// 关闭流
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
在示例中,我们首先创建了一个字节输出流FileOutputStream
来写入文件output.txt
的字节数据。然后,使用转换流OutputStreamWriter
将字符数据转换为字节数据,同样使用的是默认字符集。最后,我们创建一个缓冲字符输出流BufferedWriter
来加快写入操作。
通过转换流的使用,我们可以方便地在字节流和字符流之间进行转换,并进行相应的读取和写入操作。这样,即使处理的是字符数据,也可以使用字节流进行操作,或者反之。