目录
前言
【File文件管理及IO流(基本流)】http://t.csdnimg.cn/uG5Ff 该篇博客中,介绍了学习高级流需要的前置知识:
- 文件管理
-
基本的输入输出流:了解基本的输入输出流的概念和使用方法,包括字节流和字符流,以及如何读写文件或处理其他数据源。
Java 中的高级流指的是建立在基本字节流或字符流之上的流,它们提供了更高级别的功能以简化 I/O 操作。除缓冲流和对象流外,常见的高级流还有:数据流、打印流、压缩流、转换流等。
一、缓冲流
缓冲流(Buffered Streams)在 Java 中是一种用于提高输入输出效率的流。它们通过在内存中创建缓冲区来减少对底层资源(如文件、网络连接)的频繁访问,从而提高读写数据的性能。
常见的缓冲流包括:
BufferedInputStream(字节缓冲输入流)
BufferedOutputStream(字节缓冲输出流)
BufferedReader(字符缓冲输入流)
BufferedWriter(字符缓冲输出流)
它们构造方法的参数都是对应的基本流,例如:
public BufferedInputStream(InputStream is)
缓冲流的原理:底层自带了长度为8192的缓冲区提高性能。
缓冲中的两个独有方法(非常实用):
1.readLine() 读取一行 用于从输入流中读取一行文本数据,并返回一个字符串表示该行数据。这个方法通常用于读取文本文件中的内容,逐行读取文本信息。 2.newLine() 通用换行 作用是写入一个平台无关的行分隔符,即换行符。 使用 newLine()方法可以根据当前平台自动生成正确的换行符,而不需要手动编写特定的换行符。 通常情况下,不同操作系统的行分隔符如下: (1)Windows 系统使用回车符和换行符表示行尾,即\r\n (2)Unix 和类 Unix 系统(如 Linux)使用换行符表示行尾,即 \n。 (3)旧版 Mac OS 使用回车符表示行尾,即 \r。
为方便读取文本文件,这里用字符缓冲流进行示例(需要创建好对应的文件,路径也要正确):
import java.io.*;
public class CharacterBS { //字符缓冲流
public static void main(String[] args) throws IOException {
//读取数据(输入流)
BufferedReader br = new BufferedReader(new FileReader("File\\buffer.txt"));
//两个独有方法:readLine() 读取一行
// newLine() 通用换行
String s;
while ((s = br.readLine()) != null) {
System.out.println(s);
}
br.close();
//写数据(输出流)
BufferedWriter bw = new BufferedWriter(new FileWriter("File\\buffer2.txt"));
bw.write("缓冲流(Buffered Streams)");
bw.newLine();
bw.write("BufferedReader:提供缓冲读取文本数据的功能。");
bw.newLine();
bw.write("BufferedWriter:提供缓冲写入文本数据的功能。");
bw.newLine();
bw.close();
}
}
读取的数据:
写入的数据:
四种方式拷贝文件的用时对比
拷贝文件 四种方式拷贝文件(边读边写),并统计各自用时(这里的文件越大越好,太小体现不出差别) 1.字节流的基本流:一次读写一个字节 2.字节流的基本流:一次读写一个字节数组[8192] 3.字节缓冲流:一次读写一个字节 4.字节缓冲流:一次读写一个字节数组[8192]
import java.io.*;
public class Test1 {
public static void main(String[] args) throws IOException {
/*
拷贝文件
四种方式拷贝文件,并统计各自用时
字节流的基本流:一次读写一个字节
字节流的基本流:一次读写一个字节数组
字节缓冲流:一次读写一个字节
字节缓冲流:一次读写一个字节数组
*/
long time1 = method1();
long time2 = method2();
long time3 = method3();
long time4 = method4();
System.out.println(time1);
System.out.println(time2);
System.out.println(time3);
System.out.println(time4);
}
private static long method1() throws IOException {
long start = System.currentTimeMillis();
FileInputStream fis = new FileInputStream("File\\read.txt");
FileOutputStream fos = new FileOutputStream("File\\write.txt");
int b;
while ((b = fis.read()) != -1) {
fos.write(b);
}
fos.close();
fis.close();
long end = System.currentTimeMillis();
return end - start;
}
private static long method2() throws IOException {
long start = System.currentTimeMillis();
FileInputStream fis = new FileInputStream("File\\read.txt");
FileOutputStream fos = new FileOutputStream("File\\write.txt");
byte[] bytes = new byte[8192];
int len;
while ((len = fis.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
fos.close();
fis.close();
long end = System.currentTimeMillis();
return end - start;
}
private static long method3() throws IOException {
long start = System.currentTimeMillis();
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream("File\\read.txt"));
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("File\\write.txt"));
int b;
while ((b = bis.read()) != -1) {
bos.write(b);
}
bos.close();
bis.close();
long end = System.currentTimeMillis();
return end - start;
}
private static long method4() throws IOException {
long start = System.currentTimeMillis();
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream("File\\read.txt"));
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("File\\write.txt"));
byte[] bytes = new byte[8192];
int len;
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
bos.close();
bis.close();
long end = System.currentTimeMillis();
return end - start;
}
}
对比结果(文件太小了所以读写字节数组的差距没有体现):
- 字节流的基本流:一次读写一个字节
- 字节流的基本流:一次读写一个字节数组[8192]
- 字节缓冲流:一次读写一个字节
- 字节缓冲流:一次读写一个字节数组[8192]
二、对象流
对象流(Object Streams)也叫序列化/反序列化流,是 Java 中用于读写对象的流。对象流可以将对象以二进制形式序列化(Serialization)后写入输出流,也可以从输入流中读取二进制数据并反序列化(Deserialization)为对象。
Java 提供了两个主要的对象流类:
- ObjectOutputStream:用于将对象序列化后写入输出流。
- ObjectInputStream:用于从输入流中读取二进制数据并反序列化为对象。
同缓冲流,它们构造方法的参数也是基本流:
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("objects.bin"));
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("objects.bin"));
需要注意的是,要使对象能够被序列化,该对象所属的类必须实现 Serializable
接口。序列化过程会将对象转换成字节序列,因此对象的字段和类的结构都需要支持序列化。
对象流的使用场景包括但不限于:保存和加载对象数据(因为写入到文件后是一对看不懂的数据)、对象的网络传输等。
作用:通过对象流,我们可以方便地将对象以二进制形式写入到输出流中,并从输入流中读取并还原为对象,实现了对象的持久化和简化了数据传输。写入到文件后
细节:
要写出的对象必须实现Serializable接口(标记性接口) + serialVersionUID(版本号)
否则会抛出NotSerializableException异常
写入对象的方法:writeObject(对象);
读取对象的方法:readObject(); 返回值:Object
1. 使用对象流写入对象到本地文件
写入对象前的准备工作,自定义类:
import java.io.Serial;
import java.io.Serializable;
public class Student implements Serializable {
@Serial
private static final long serialVersionUID = -3931917465211028662L;
private String name;
private int age;
//transient: 瞬态关键字
//作用:不会把当前属性序列化到本地文件当中
private transient String address;
public Student() {
}
public Student(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
/**
* 获取
*
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
*
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
*
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
*
* @param age
*/
public void setAge(int age) {
this.age = age;
}
/**
* 获取
*
* @return address
*/
public String getAddress() {
return address;
}
/**
* 设置
*
* @param address
*/
public void setAddress(String address) {
this.address = address;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + ", address = " + address + "}";
}
}
import java.io.Serial;
import java.io.Serializable;
public class Teacher implements Serializable {
@Serial
private static final long serialVersionUID = 2983184546427358899L;
private String name;
private int age;
private String address;
public Teacher() {
}
public Teacher(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
/**
* 获取
*
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
*
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
*
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
*
* @param age
*/
public void setAge(int age) {
this.age = age;
}
/**
* 获取
*
* @return address
*/
public String getAddress() {
return address;
}
/**
* 设置
*
* @param address
*/
public void setAddress(String address) {
this.address = address;
}
public String toString() {
return "Teacher{name = " + name + ", age = " + age + ", address = " + address + "}";
}
}
写入一个对象:
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Test1 { //序列化流/对象操作输出流
public static void main(String[] args) throws IOException {
/*
需求:
利用序列化流/对象操作输出流,把一个对象写到本地文件中
细节:
要写出的对象必须实现Serializable接口(标记性接口) + serialVersionUID(版本号)
否则回抛出NotSerializableException异常
transient: 瞬态关键字
作用:不会把当前属性序列化到本地文件当中
*/
//1.创建对象
Student stu = new Student("张三", 23, "厦门");
//2.创建续流化流/对象操作输出流对象
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("File\\object.txt"));
//3.写出对象
oos.writeObject(stu);
//4.关流
oos.close();
}
}
写入后的数据:
写入多个对象:
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
public class Test_1 {
public static void main(String[] args) throws IOException {
/*
用对象流读写多个对象
需求:
将多个自定义对象序列化到文件中,但是由于对象的个数不确定,反序列流该如何读取?
*/
//序列化多个对象
//1.创建对象
Teacher t1 = new Teacher("张三", 23, "北京");
Teacher t2 = new Teacher("李四", 24, "上海");
Teacher t3 = new Teacher("王五", 25, "深圳");
//2.将对象添加到集合中
ArrayList<Teacher> list = new ArrayList<>();
list.add(t1);
list.add(t2);
list.add(t3);
//3.序列化集合
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("File\\teacher.txt"));
oos.writeObject(list);
//4.关流
oos.close();
}
}
2. 使用对象流读取对象数据
读取单个对象:
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class Test2 { //反序列化流/对象操作输入流
public static void main(String[] args) throws IOException, ClassNotFoundException {
//1.创建反序列化流/对象操作输入流
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("File\\object.txt"));
//2.读取数据
Object o = ois.readObject();
//3.打印对象
System.out.println(o);
//4.释放资源
ois.close();
}
}
运行结果:
读取多个对象:
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
public class Test_2 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//1.创建反序列化流的对象
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("File\\teacher.txt"));
//2.读取数据
ArrayList<Teacher> o = (ArrayList<Teacher>) ois.readObject();
//System.out.println(o);
for (Teacher teacher : o) {
System.out.println(teacher);
}
//3.关流
ois.close();
}
}
运行结果:
总结
当涉及到 Java I/O 操作时,缓冲流和对象流是两种非常常用的高级流。
缓冲流的功能:
- 提供了缓冲区功能,可以减少对底层数据源的直接访问次数,从而提高读写效率。
- 适用于对大量数据进行读写操作。
缓冲流的优点:
- 提高了I/O操作的性能,减少了与底层数据源的交互次数。
- 提供了逐行读取文本数据的功能,方便文本处理操作。
对象流的功能:
- 用于将对象序列化成字节流或反序列化为对象。
- 可以实现对象的持久化、网络传输和跨平台数据交换等功能。
对象流的优点:
- 方便实现对象的保存和加载,简化了对象的序列化和反序列化过程。
- 可以实现对象在不同系统之间的传输和共享。
综上所述,缓冲流适用于提高读写效率和处理大量数据,而对象流则适用于处理对象的序列化和反序列化,方便实现对象数据的持久化和传输。根据具体需求,可以选择合适的流来进行数据操作。