前言:
在前面我们从IO流体系出发,分别介绍了字节流和字符流,并且详细讲解了其下沿的各种基本流,而在今天我们就要学习一下IO流中的几个高级流。
1.缓冲流
缓冲流其实就是可以让我们自己手动提供缓冲区,以此来增加读写速度,我们知道字节流是没有缓冲区的,因此创建一个缓冲区对于字节流的读写速度提高是很明显的。
缓冲区是计算机科学中常用的一个概念,用于在不同速度的系统或设备之间传输数据时解决速度不匹配的问题。它是用来临时存储数据的一段内存区域,可用于暂时保存输入数据、输出数据或处理中间数据。
缓冲区通常用于处理输入/输出(I/O)操作,例如在文件读写、网络通信、图像处理等过程中。它能够提高数据传输的效率,减少因速度不匹配而导致的延迟和性能瓶颈。
总结来说,缓冲区是一种临时存储数据的内存区域,可以解决不同速度设备之间数据传输的速度不匹配问题,提高系统的效率。它在输入/输出操作和数据处理过程中起到关键的作用。
1.字节缓冲流(BufferedInputStream和BufferedOutputStream):
BufferedInputStream:将字节输入流包装起来,提供了在内存中创建缓冲区的功能,这样可以减少磁盘IO次数。
BufferedOutputStream:将字节输出流包装起来,同样提供了缓冲区,可以减少磁盘IO次数。
示例代码:
import java.io.*;public class ByteStreamExample {public static void main(String[] args) {try {// 创建字节输入流FileInputStream fileInputStream = new FileInputStream("input.txt");// 使用字节缓冲流包装字节输入流BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);// 创建字节输出流FileOutputStream fileOutputStream = new FileOutputStream("output.txt");// 使用字节缓冲流包装字节输出流BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);// 读取文件内容并写入到目标文件int data;while ((data = bufferedInputStream.read()) != -1) {bufferedOutputStream.write(data);}// 关闭流bufferedInputStream.close();bufferedOutputStream.close();System.out.println("文件复制成功");} catch (IOException e) {e.printStackTrace();}}
}
字节缓冲流的底层默认就自带了一个长度为8192(byte)的缓冲区,这在我们一开始学习到的基本字节流中是没有的。但其实字节缓冲流本质上只是对基本字节流的一个包装,在读取数据的时候依然是基本字节流在读取数据,并不是什么新的方法。
我们可以通过调试这段代码看到缓冲区:
字符缓冲流(BufferedReader和BufferedWriter):
BufferedReader:将字符输入流包装起来,提供了在内存中创建缓冲区的功能,可以一次读取一行字符数据。
BufferedWriter:将字符输出流包装起来,同样提供了缓冲区,可以一次写入一行字符数据。
示例代码:
import java.io.*;public class CharacterStreamExample {public static void main(String[] args) {try {// 创建字符输入流FileReader fileReader = new FileReader("input.txt");// 使用字符缓冲流包装字符输入流BufferedReader bufferedReader = new BufferedReader(fileReader);// 创建字符输出流FileWriter fileWriter = new FileWriter("output.txt");// 使用字符缓冲流包装字符输出流BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);// 读取文件内容并写入到目标文件String line;while ((line = bufferedReader.readLine()) != null) {bufferedWriter.write(line);bufferedWriter.newLine();}// 关闭流bufferedReader.close();bufferedWriter.close();System.out.println("文件复制成功");} catch (IOException e) {e.printStackTrace();}}
}
虽然基本的字符流方法就自带缓冲区,但是我们缓冲字符流也提供了一些很好的方法来便捷我们的使用。
![]()
readLine在读取方法时候,一次读一整行,遇到回车换行就结束
但是他不会把回车换行读到内存中
缓冲流的优点:
1. 提高读写性能:缓冲流通过在内存中创建缓冲区,减少IO操作的次数,从而提高读写性能。它们可以一次读取或写入多个字节或字符,减少了与磁盘或网络的交互次数。
2. 减少系统开销:使用缓冲流可以减少系统调用次数和CPU的使用率。通过将数据保存在内存中的缓冲区中,在进行大规模数据读写时,可以显著降低系统开销和资源消耗。
3. 支持高效的字符处理:字符缓冲流(BufferedReader和BufferedWriter)提供了方便的方法,比如一次读取一行字符数据或一次写入一行字符数据。这对于处理文本文件非常有用,可以简化代码并提高代码的可读性。
4. 缓冲区自动管理:缓冲流会自动管理缓冲区的大小和数据的填充和清空,无需手动处理。这样可以简化代码编写,并提高代码的可维护性。
5. 可以与其他IO流结合使用:缓冲流可以与其他各种字节流或字符流结合使用,如文件流、网络流等。这使得在不同场景下进行读写操作更加灵活和高效。
2.转换流
转换流(InputStreamReader和OutputStreamWriter)是Java IO库中的字符流,用于在字节流和字符流之间进行转换。它们提供了将字节流转换为字符流的能力,以便更方便地处理文本数据。
转换流的主要作用是解决字节流和字符流之间的编码转换问题。在读取或写入文本数据时,需要将字节数据转换为字符数据或将字符数据转换为字节数据。转换流在内部实现了字符集的转换,可以根据指定的字符集将字节流转换为字符流,或将字符流转换为字节流。
转换流的构造函数接受两个参数:字节流和字符集。其中,字节流可以是字节输入流或字节输出流,而字符集可以是标准字符集名称或特定字符集名称。
主要的转换流类及其作用如下:
InputStreamReader:
作用:将字节输入流转换为字符输入流。
构造函数:
InputStreamReader(InputStream in):创建一个默认字符集的InputStreamReader对象。
InputStreamReader(InputStream in, Charset cs):创建一个使用指定字符集的InputStreamReader对象。
import java.io.*;public class InputStreamReaderExample {public static void main(String[] args) {try {FileInputStream fileInputStream = new FileInputStream("input.txt");InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "UTF-8");BufferedReader bufferedReader = new BufferedReader(inputStreamReader);String line;while ((line = bufferedReader.readLine()) != null) {System.out.println(line);}bufferedReader.close();inputStreamReader.close();} catch (IOException e) {e.printStackTrace();}}
}
OutputStreamWriter:
作用:将字节输出流转换为字符输出流。
构造函数:
OutputStreamWriter(OutputStream out):创建一个默认字符集的OutputStreamWriter对象。
OutputStreamWriter(OutputStream out, Charset cs):创建一个使用指定字符集的OutputStreamWriter对象。
示例代码:
import java.io.*;public class OutputStreamWriterExample {public static void main(String[] args) {try {FileOutputStream fileOutputStream = new FileOutputStream("output.txt");OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "UTF-8");BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);bufferedWriter.write("Hello, World!");bufferedWriter.newLine();bufferedWriter.write("你好,世界!");bufferedWriter.close();outputStreamWriter.close();} catch (IOException e) {e.printStackTrace();}}
}
通过使用转换流,我们可以在字节流和字符流之间进行方便的转换,使得处理文本数据更加简便和灵活。转换流还支持指定字符集,以便进行特定编码的读写操作。
转换流的存在主要是为了:让字节流使用字符流中的方法。
其实在以前,我们还可以利用转换流来使得读取文件的时候按照指定的字符编码读取。
3.序列化流:
序列化流是Java IO库中的一种特殊流,用于将对象转换为字节序列,以便存储到文件、数据库或通过网络进行传输。它们提供了对象的序列化和反序列化功能,使得可以方便地将对象持久化或在不同系统之间传输对象数据。
java中的序列化和反序列化是通过ObjectInputStream和ObjectOutputStream类来实现的,它们是字节流的子类,用于处理对象的读写操作。
序列化流的主要作用有以下几点:
对象的持久化:通过将对象转换为字节序列,可以将对象永久保存到磁盘上。这样即使程序结束,对象数据也可以被重新读取和使用。对象的持久化在很多场景中非常有用,比如保存用户信息、游戏进度等。
对象的传输:通过序列化流,可以将对象转换为字节序列,然后发送到网络上或通过进程间通信进行传输。接收方可以使用反序列化将字节序列转换回对象,以便获取传输的对象数据。这在分布式系统、远程方法调用等场景中非常常见。
序列化流的用法包括以下几个步骤:
对象的序列化:
创建一个输出流,如FileOutputStream或Socket.getOutputStream()。
创建一个ObjectOutputStream对象,将输出流传递给它。
使用ObjectOutputStream的writeObject()方法将对象写入输出流。该方法会将对象转换为字节序列并写入输出流。
try {FileOutputStream fileOut = new FileOutputStream("object.ser");ObjectOutputStream out = new ObjectOutputStream(fileOut);out.writeObject(object);out.close();fileOut.close();
} catch (IOException e) {e.printStackTrace();
}
对象的反序列化:
创建一个输入流,如FileInputStream或Socket.getInputStream()。
创建一个ObjectInputStream对象,将输入流传递给它。
使用ObjectInputStream的readObject()方法从输入流中读取对象。该方法会将字节序列转换回对象。
try {FileInputStream fileIn = new FileInputStream("object.ser");ObjectInputStream in = new ObjectInputStream(fileIn);Object object = in.readObject();in.close();fileIn.close();
} catch (IOException e) {e.printStackTrace();
} catch (ClassNotFoundException e) {e.printStackTrace();
}
在进行对象的序列化和反序列化时,需要注意以下事项:
要求被序列化的类必须实现Serializable接口,该接口没有任何方法,只是起到一个标识作用,表明该类可以进行序列化。
被序列化的类中的字段也需要是可序列化的,即要求它们也是实现了Serializable接口的。
序列化和反序列化的顺序必须一致,否则会导致错误。
如果我们不想给对象中的某一个成员变量序列化,那么就使用transient关键字修饰,该关键字标记的成员变量不参与序列化过程。
使用序列化的注意点:
1. 类的版本控制:当你使用序列化流将对象写入文件时,类的定义可能会发生变化。为了确保在反序列化过程中能够正确地读取对象,你需要仔细控制类的版本。你可以使用 `serialVersionUID` 字段来指定类的版本号,并在类的变化过程中进行更新。
不过我们可以手动设置让idea能对此进行提示,方便我们快捷生成serialVersionUID
2. 数据完整性:序列化流允许你将整个对象图写入文件,包括对象之间的引用。然而,这也意味着你需要确保所有相关的对象都被正确地序列化和反序列化,以保持数据的完整性。如果某个对象没有被序列化或者反序列化,那么它在反序列化后可能会变成 `null` 或者其他未初始化的值。
3. 安全性考虑:序列化流允许你将对象写入文件并从文件中读取。这可能引发安全问题,因为恶意用户可能会修改序列化的数据。为了确保安全性,你应该对序列化的数据进行验证和过滤,以防止不可预测的行为。
4. 跨语言兼容性:序列化是一种用于对象持久化和传输的通用机制。然而,不同的编程语言可能会使用不同的序列化格式和机制。如果你计划在不同的编程语言之间进行对象的序列化和反序列化,你需要确保选择一个与目标编程语言兼容的序列化方式。
5. 性能问题:序列化和反序列化过程可能会占用大量的时间和资源。如果你需要频繁地将大量对象进行序列化和反序列化操作,你可能需要考虑性能优化方面的问题,例如使用更快速的序列化库或者改变序列化策略。、
总结来说,序列化流在Java IO库中是一种特殊的流,用于对象的序列化和反序列化操作,实现了对象的持久化和传输功能。通过序列化和反序列化,我们可以方便地将对象转换为字节序列,进行持久化存储或跨系统传输