如果把Java IO(输入/输出)流简单地分为字节流和字符流,字节流和字符流下面又有诸多子类。
这样划分既不便于理解,还容易把各个子类之间的联系切断,有必要分的再细一些,同时还需要知道类和类之间的联系,这样才能更方便理解。
Java IO流是用于处理数据传输的机制,因为传输数据的过程类似于水流,因此称为流。
Java IO流按照数据的走向,可以分为输入流和输出流,分别用于数据的读取和写入。
Java IO流按照数据传播的方式不同,可以分为磁盘操作和网络操作。
Java IO流根据操作的角色不同,可以分为节点流和处理流。
Java IO流按照数据的传输单位,可以分为字节流和字符流。
一、字节流
字节流在Java中主要用于处理二进制数据,它们可以用于读写任何类型的数据,如图像,音频,视频等。由InputStream和OutputStream两个抽象类及其子类组成,子类由InputStream、OutputStream结尾。
InputStream:所有字节输入流的超类,定义了基本的读取字节的方法。常用子类有 FileInputStream用于从文件中读取字节流数据等。
OutputStream:所有字节输出流的超类,定义了基本的写入字节的方法。常用子类有 FileOutputStream用于向文件中写入字节流数据等。
1、示例代码
FileInputStream fis = null;
FileOutputStream fos = null;
try {
// 创建文件输入流和文件输出流
fis = new FileInputStream("source.txt");
fos = new FileOutputStream("destination.txt");
// 缓冲区数组
byte[] buffer = new byte[1024];
int bytesRead;
// 读取和写入数据
while ((bytesRead = fis.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 关闭资源
if (fis != null) {
fis.close();
}
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
上面的代码是把一个txt的内容拷贝到另一个txt文件。
1)创建输入输出流:使用 FileInputStream 和 FileOutputStream 创建文件输入和输出流,分别指向源文件 source.txt 和目标文件 destination.txt。
2)定义缓冲区:定义一个字节数组 buffer 作为缓冲区,大小为 1024 字节(1KB)。
3)读取和写入数据:使用 while 循环读取源文件的数据到缓冲区中,并将缓冲区中的数据写入目标文件中,直到文件结束。
4)关闭流:在 finally 块中关闭所有打开的流,以确保资源被正确释放,防止资源泄漏。
2、操作文件注意事项
为避免内存泄漏,需要使用try-catch-finally处理异常。
对于输入流来说,当文件不在物理磁盘上,会抛出FileNotFoundException。
对于输出流来说,File类的对象可以不存在,不存在时会自动创建。
二、字符流
字符流主要用于处理文本数据,它们按字符而非字节进行操作,因此更适合处理文本数据。由Reader和Writer两个抽象类及其子类组成,子类由Reader、WriterWriter结尾。
Reader:所有字符输入流的超类,定义了基本的读取字符的方法。常用子类:FileReader用于从文件中读取数据等。
Writer:所有字符输出流的超类,定义了基本的写入字符的方法。常用子类:FileWriter用于向文件中写入数据等。
示例:
FileReader reader = null;
FileWriter writer = null;
try {
// 创建文件读取器和文件写入器
reader = new FileReader("source.txt");
writer = new FileWriter("destination.txt");
// 缓冲区字符数组
char[] buffer = new char[1024];
int charsRead;
// 读取和写入数据
while ((charsRead = reader.read(buffer)) != -1) {
writer.write(buffer, 0, charsRead);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭资源...
}
1)创建读取器和写入器:使用 FileReader 和 FileWriter 创建文件读取和写入器,分别指向源文件 source.txt 和目标文件 destination.txt。
2)定义缓冲区:定义一个字符数组 buffer 作为缓冲区,大小为 1024 字符(根据实际情况可以调整)。
3)读取和写入数据:使用 while 循环逐块读取源文件的数据到缓冲区中,并将缓冲区中的数据写入目标文件中,直到文件结束。
4)关闭资源:...
三、缓冲流
缓冲流(Buffered Streams)是对字节流和字符流的增强,通过在读写过程中使用内存缓冲区来提高性能。
1、基于字节的缓冲流
BufferedInputStream 字节缓冲输入流,通过内存缓冲区来减少实际的 I/O 操作次数,从而提高读取性能。
BufferedOutputStream 字节缓冲输出流,通过内存缓冲区来减少实际的 I/O 操作次数,从而提高写入性能。
示例:
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
// 1、创建文件输入流和缓冲输入流
FileInputStream fis = new FileInputStream("source.txt");
bis = new BufferedInputStream(fis);
// 2、创建文件输出流和缓冲输出流
FileOutputStream fos = new FileOutputStream("destination.txt");
bos = new BufferedOutputStream(fos);
// 缓冲区数组
byte[] buffer = new byte[1024];
int bytesRead;
// 3、读取和写入数据
while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4、关闭流...
}
1)创建输入输出流:使用 FileInputStream 和 FileOutputStream 创建文件输入和输出流,分别指向源文件 source.txt 和目标文件 destination.txt。
2)包装缓冲流:用 BufferedInputStream 包装 FileInputStream,用 BufferedOutputStream 包装 FileOutputStream,提高读写效率。
3)定义缓冲区:定义一个字节数组 buffer 作为缓冲区,大小为 1024 字节(1KB)。
4)读取和写入数据:使用 while 循环读取源文件的数据到缓冲区中,并将缓冲区中的数据写入目标文件中,直到文件结束。
5)关闭流:...
2、基于字符的缓冲流
BufferedReader 字符缓冲输入流,通过内存缓冲区来减少实际的 I/O 操作次数,从而提高读取性能。
BufferedWriter 字符缓冲输出流,通过内存缓冲区来减少实际的 I/O 操作次数,从而提高写入性能。
示例:
BufferedReader reader = null;
BufferedWriter writer = null;
try {
// 创建文件读取器和缓冲读取器
FileReader fr = new FileReader("source.txt");
reader = new BufferedReader(fr);
// 创建文件写入器和缓冲写入器
FileWriter fw = new FileWriter("destination.txt");
writer = new BufferedWriter(fw);
// 缓冲区字符串
String line;
// 读取和写入数据
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.newLine(); // 写入换行符
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4、关闭流...
}
1)创建输入输出流:使用 FileReader和 FileWriter创建文件输入和输出流,分别指向源文件 source.txt 和目标文件 destination.txt。
2)包装缓冲流:用 BufferedReader包装 FileReader ,用 BufferedWriter包装 FileWriter,提高读写效率。
3)定义缓冲区:定义一个字符串line作为缓冲区。
4)读取和写入数据:使用 while 循环读取源文件的数据到缓冲区中,并将缓冲区中的数据写入目标文件中,直到文件结束。
5)关闭流:...
缓冲流的基本原理:在创建流对象时,内部会创建一个缓冲区数组(默认/缺省使用 8192个字节(8Kb)的缓冲区),通过缓冲区读写,减少系统 IO 次数,从而提高读写的效率。
4、转换流
转换流是字符流和字节流之间的桥梁,它们允许程序以字符流的形式处理字节流,同时可以指定字符集进行编码或解码。
InputStreamReader:用于将字节输入流转换为字符输入流。
OutputStreamWriter:用于将字符输出流转换为字节输出流。
示例:
InputStreamReader reader = null;
OutputStreamWriter writer = null;
try {
// 创建文件输入流和文件输出流
FileInputStream fis = new FileInputStream("source.txt");
FileOutputStream fos = new FileOutputStream("destination.txt");
// 创建字符流读取器和写入器
reader = new InputStreamReader(fis, "UTF-8");
writer = new OutputStreamWriter(fos, "UTF-8");
// 缓冲区字符数组
char[] buffer = new char[1024];
int charsRead;
// 读取和写入数据
while ((charsRead = reader.read(buffer)) != -1) {
writer.write(buffer, 0, charsRead);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4、关闭流...
}
1)创建文件输入输出流:使用 FileInputStream 和 FileOutputStream 分别创建指向源文件 source.txt 和目标文件 destination.txt 的字节流。
2)创建转换流读取器和写入器:使用 InputStreamReader 和 OutputStreamWriter 分别包装文件输入输出流,并指定字符编码为 “UTF-8”。
3)定义缓冲区:定义一个字符数组 buffer 作为缓冲区,大小为 1024 字符。
4)读取和写入数据:使用 while 循环读取源文件的数据到缓冲区中,并将缓冲区中的数据写入目标文件中,直到文件结束。
5)关闭流:...
5、数据流
数据流提供基本数据类型的读写。
DataInputStream:允许应用程序以机器无关的方式从底层输入流中读取基本Java数据类型的输入流。常用方法有readInt()、readFloat()、readUTF()等。
// 以数据输入流的方式进行读数据
DataInputStream dis = new DataInputStream(new FileInputStream("source.txt"));
dis.readInt();
dis.readDouble();
dis.readUTF();
如果读取的是文件,还需要先通过FileInputStream获取源文件,然后才能转换成数据输入流进行读数据操作。
DataOutputStream:允许应用程序以机器无关的方式将基本Java数据类型写入到底层输出流中。常用方法有writeInt()、writeFloat()、writeUTF()等。
// 以数据输出流的方式进行写数据
DataOutputStream dos = new DataOutputStream(new FileOutputStream("destination.txt"));
dos.write(100);
dos.writeInt(5000);
如果写入的是文件,还需要通过FileOutputStream获取目标文件,然后才能转换成数据输出流进行写入数据操作。
6、对象流
对象流提供对象的序列化和反序列化。
ObjectInputStream:用于从源文件中读取对象。
ObjectOutputStream:用于将对象写入目标文件。
import java.io.*;
class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + '}';
}
}
public class SerializationExample {
public static void main(String[] args) {
Person person = new Person("Alice", 30);
// 序列化
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
out.writeObject(person);
} catch (IOException e) {
e.printStackTrace();
}
// 反序列化
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("person.ser"))) {
Person deserializedPerson = (Person) in.readObject();
System.out.println("反序列化后的对象: " + deserializedPerson);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
1)创建文件输入输出流:使用 FileInputStream 和 FileOutputStream 分别创建指向源文件/目标文件 person.ser。
2)序列化:使用ObjectOutputStream对对象进行序列化并输出到文件 person.ser。
3)反序列化:使用ObjectInputStream对读取到的文件进行反序列化。
4)使用 try-with-resources 语法,确保在 try 块结束时自动关闭输入输出流,避免资源泄漏。
7. 打印流
打印流是另一种特殊的流,主要用于创建格式化的输出,而不是原始的字节。这种类型的流非常适合打印输出,或者写入文本文件。
PrintStream:为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。
PrintWriter:打印各种格式的表示形式,包括文本、字符数组、数字、日期、时间等。
String sourceFile = "source.txt"; // 请确保这个文件存在
String destinationFile = "destination.txt";
BufferedReader reader = null;
PrintWriter writer = null;
PrintStream printStream = null;
try {
// 初始化输入流
reader = new BufferedReader(new FileReader(sourceFile));
// 初始化PrintWriter输出流
writer = new PrintWriter(new FileWriter(destinationFile));
// 初始化PrintStream输出流 (用于演示目的)
printStream = new PrintStream(new FileOutputStream(destinationFile + ".ps"));
String line;
int lineNumber = 1;
// 读取源文件并写入目标文件
while ((line = reader.readLine()) != null) {
// 使用PrintWriter写入
writer.println(line);
// 使用PrintStream写入 (添加行号作为演示)
printStream.printf("Line %d: %s%n", lineNumber, line);
lineNumber++;
}
System.out.println("文件复制完成。");
} catch (FileNotFoundException e) {
System.out.println("文件未找到: " + e.getMessage());
} catch (IOException e) {
System.out.println("读写文件时发生错误: " + e.getMessage());
} finally {
// 关闭流...
}
1)创建文件输入输出流:使用 FileReader和 FileWriter分别创建指向源文件source.txt和目标文件 destination.txt。
2)包装缓冲流:用 BufferedReader 包装 FileReader提高读效率。
3)创建打印流写入器:使用 PrintStream和 PrintWriter分别创建文件输出流。
4)使用PrintWriter直接写入行内容到目标文件。
6)关闭流:...
八、最后总结
通过上面的示例,我们可以发现一个特点,有些类是直接操作数据源的,比如字节流、字符流,还有些类是间接操作数据源的,比如缓冲流、数据流、对象流和打印流。
直接从数据源或目标读写数据的,不论是文件或数组,这样的流称为节点流,又称原始流。
间接连接到数据源或目标,建立在已存在流之上的,称为处理流。
处理流又称为包装流,不能独立存在,它们必须连接到另一个流上。处理流本身不提供实际的读写能力,而是通过包装一个已存在的节点流或其他处理流来增强其功能。处理流的作用可以是数据的过滤、装饰或增强等,它们通常用于提供更高级别的功能。
把以上的流再次进行分类,常用的节点流有:
文件流:FileInputStream、FileOutputStrean、FileReader、FileWriter,直接操作文件。
字节/字符数组流:ByteArrayInputStream、ByteArrayOutputStream、CharArrayReader、CharArrayWriter,直接操作字节或字符数组。
常用的处理流:
BufferedInputStream / BufferedOutputStream、BufferedReader、BufferedWriter:提供缓冲功能,减少对底层节点流的直接访问次数,从而提升性能。
InputStreamReader / OutputStreamWriter:将字节流转换为字符流,支持字符集的指定。
DataInputStream / DataOutputStream:提供基本数据类型的读写操作。
ObjectInputStream / ObjectOutputStream:支持对象的序列化和反序列化。
PrintStream/PrintWriter:打印格式化的文本输出到目标流。
领取专属 10元无门槛券
私享最新 技术干货