Java输入输出流基础
Java 输入输出流概述
在 Java 编程中,输入输出(I/O)操作是与外部环境交互的关键手段。输入流用于从外部源(如文件、网络连接、用户输入等)读取数据,而输出流则用于将数据写入到外部目的地(同样可以是文件、网络连接等)。Java 的 I/O 流体系设计得非常灵活且强大,能够适应各种不同的 I/O 场景。
Java 的 I/O 流类库位于 java.io
包下。这个包中包含了大量的类,这些类大致可以分为字节流和字符流两大类。字节流以字节(8 位)为单位处理数据,适用于处理二进制数据,如图片、音频、视频等;字符流以字符(16 位 Unicode 字符)为单位处理数据,主要用于处理文本数据。
字节流
字节输入流 - InputStream
InputStream
是所有字节输入流的抽象基类。它定义了一系列用于读取字节数据的方法。其中,最基本的方法是 read()
,这个方法从输入流中读取一个字节的数据,并返回读取到的字节值(如果已到达流的末尾,则返回 -1)。示例代码如下:
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class ByteInputStreamExample {
public static void main(String[] args) {
try (InputStream inputStream = new FileInputStream("example.txt")) {
int data;
while ((data = inputStream.read()) != -1) {
System.out.print((char) data);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
在上述代码中,我们使用 FileInputStream
(它是 InputStream
的一个具体子类,用于从文件中读取数据)来读取一个文本文件 example.txt
的内容。while
循环通过不断调用 read()
方法读取字节,并将其转换为字符后打印到控制台。当 read()
方法返回 -1 时,表示已到达文件末尾,循环结束。
除了 read()
方法,InputStream
还提供了其他一些有用的方法。例如,read(byte[] b)
方法可以一次性读取多个字节到指定的字节数组 b
中,并返回实际读取的字节数。read(byte[] b, int off, int len)
方法则允许从字节数组的指定偏移量 off
开始,读取最多 len
个字节的数据。
字节输出流 - OutputStream
OutputStream
是所有字节输出流的抽象基类。它定义了用于将字节数据写入到输出目的地的方法。最基本的方法是 write(int b)
,该方法将指定的字节写入到输出流中。示例代码如下:
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class ByteOutputStreamExample {
public static void main(String[] args) {
String message = "Hello, World!";
try (OutputStream outputStream = new FileOutputStream("output.txt")) {
outputStream.write(message.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}
在这段代码中,我们创建了一个 FileOutputStream
(OutputStream
的具体子类,用于将数据写入文件),并使用 write(byte[] b)
方法将字符串 Hello, World!
转换为字节数组后写入到 output.txt
文件中。write(int b)
方法虽然接受一个 int
类型参数,但实际上只会写入该 int
值的低 8 位字节。
字符流
字符输入流 - Reader
Reader
是所有字符输入流的抽象基类。它以字符(16 位 Unicode 字符)为单位处理数据,提供了比字节流更方便的文本读取功能。Reader
的基本读取方法是 read()
,该方法读取一个字符并返回其整数值(如果已到达流的末尾,则返回 -1)。示例代码如下:
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class CharacterInputStreamExample {
public static void main(String[] args) {
try (Reader reader = new FileReader("example.txt")) {
int data;
while ((data = reader.read()) != -1) {
System.out.print((char) data);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
此代码使用 FileReader
(Reader
的具体子类,用于从文件中读取字符数据)读取 example.txt
文件的内容,并将每个字符打印到控制台。与字节流的 InputStream
类似,Reader
也有 read(char[] cbuf)
和 read(char[] cbuf, int off, int len)
方法,用于一次性读取多个字符到字符数组中。
字符输出流 - Writer
Writer
是所有字符输出流的抽象基类。它用于将字符数据写入到输出目的地。基本的写入方法是 write(int c)
,该方法将指定的字符写入到输出流中。示例代码如下:
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class CharacterOutputStreamExample {
public static void main(String[] args) {
String message = "你好,世界!";
try (Writer writer = new FileWriter("output.txt")) {
writer.write(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
在这段代码中,我们通过 FileWriter
(Writer
的具体子类,用于将字符数据写入文件)将包含中文字符的字符串 你好,世界!
写入到 output.txt
文件中。Writer
还提供了 write(String str)
方法,用于直接写入字符串,以及 write(char[] cbuf)
等方法用于写入字符数组。
缓冲流
缓冲字节输入流 - BufferedInputStream
BufferedInputStream
是 FilterInputStream
的子类,它为字节输入流提供了缓冲功能。通过在内存中设置一个缓冲区,减少了实际的 I/O 操作次数,从而提高了读取效率。示例代码如下:
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class BufferedByteInputStreamExample {
public static void main(String[] args) {
try (InputStream inputStream = new FileInputStream("largeFile.bin");
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream)) {
int data;
while ((data = bufferedInputStream.read()) != -1) {
// 处理数据
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
在上述代码中,我们首先创建了一个 FileInputStream
用于读取一个二进制文件 largeFile.bin
,然后将其包装在 BufferedInputStream
中。这样,当调用 bufferedInputStream.read()
方法时,它会先从缓冲区中读取数据,如果缓冲区为空,则从文件中读取一批数据填充缓冲区。
缓冲字节输出流 - BufferedOutputStream
BufferedOutputStream
是 FilterOutputStream
的子类,为字节输出流提供缓冲功能。它同样通过缓冲区减少实际的 I/O 操作次数,提高写入效率。示例代码如下:
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class BufferedByteOutputStreamExample {
public static void main(String[] args) {
byte[] data = new byte[1024];
// 填充数据
try (OutputStream outputStream = new FileOutputStream("output.bin");
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream)) {
bufferedOutputStream.write(data);
bufferedOutputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
在这段代码中,我们创建了一个 FileOutputStream
用于写入二进制文件 output.bin
,并将其包装在 BufferedOutputStream
中。调用 bufferedOutputStream.write(data)
方法时,数据会先写入缓冲区,当缓冲区满或者调用 flush()
方法时,缓冲区的数据才会被实际写入到文件中。
缓冲字符输入流 - BufferedReader
BufferedReader
是 FilterReader
的子类,为字符输入流提供缓冲功能。它除了继承自 Reader
的基本读取方法外,还提供了 readLine()
方法,用于读取一行文本。示例代码如下:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class BufferedCharacterInputStreamExample {
public static void main(String[] args) {
try (Reader reader = new FileReader("example.txt");
BufferedReader bufferedReader = new BufferedReader(reader)) {
String line;
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
在上述代码中,我们通过 BufferedReader
读取文本文件 example.txt
的内容,并逐行打印到控制台。readLine()
方法会读取到行尾(包括换行符),并返回该行的内容(不包括换行符)。如果已到达文件末尾,则返回 null
。
缓冲字符输出流 - BufferedWriter
BufferedWriter
是 FilterWriter
的子类,为字符输出流提供缓冲功能。它提供了 newLine()
方法,用于写入一个平台相关的换行符。示例代码如下:
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class BufferedCharacterOutputStreamExample {
public static void main(String[] args) {
String[] lines = {"第一行", "第二行", "第三行"};
try (Writer writer = new FileWriter("output.txt");
BufferedWriter bufferedWriter = new BufferedWriter(writer)) {
for (String line : lines) {
bufferedWriter.write(line);
bufferedWriter.newLine();
}
bufferedWriter.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
在这段代码中,我们通过 BufferedWriter
将一个字符串数组中的每一行写入到 output.txt
文件中,并使用 newLine()
方法写入换行符。同样,调用 flush()
方法可以确保缓冲区的数据被及时写入到文件中。
数据流
数据输入流 - DataInputStream
DataInputStream
是 FilterInputStream
的子类,它允许应用程序以与机器无关的方式从底层输入流中读取基本 Java 数据类型(如 int
、float
、boolean
等)。示例代码如下:
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class DataInputStreamExample {
public static void main(String[] args) {
try (InputStream inputStream = new FileInputStream("data.bin");
DataInputStream dataInputStream = new DataInputStream(inputStream)) {
int number = dataInputStream.readInt();
float fraction = dataInputStream.readFloat();
boolean flag = dataInputStream.readBoolean();
System.out.println("读取的整数: " + number);
System.out.println("读取的浮点数: " + fraction);
System.out.println("读取的布尔值: " + flag);
} catch (IOException e) {
e.printStackTrace();
}
}
}
在上述代码中,假设 data.bin
文件是由 DataOutputStream
按照特定格式写入的包含整数、浮点数和布尔值的数据文件。DataInputStream
可以按照写入的顺序正确读取这些数据,保证数据的一致性和跨平台性。
数据输出流 - DataOutputStream
DataOutputStream
是 FilterOutputStream
的子类,它允许应用程序以与机器无关的方式将基本 Java 数据类型写入到底层输出流中。示例代码如下:
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class DataOutputStreamExample {
public static void main(String[] args) {
try (OutputStream outputStream = new FileOutputStream("data.bin");
DataOutputStream dataOutputStream = new DataOutputStream(outputStream)) {
int number = 12345;
float fraction = 3.14f;
boolean flag = true;
dataOutputStream.writeInt(number);
dataOutputStream.writeFloat(fraction);
dataOutputStream.writeBoolean(flag);
} catch (IOException e) {
e.printStackTrace();
}
}
}
在这段代码中,我们创建了一个 DataOutputStream
并将整数 12345
、浮点数 3.14f
和布尔值 true
写入到 data.bin
文件中。当使用 DataInputStream
读取这个文件时,就可以按照相同的顺序和格式正确读取这些数据。
对象流
对象输入流 - ObjectInputStream
ObjectInputStream
用于从输入流中读取对象。它能够反序列化先前使用 ObjectOutputStream
写入的对象。要使用对象流进行对象的序列化和反序列化,对象的类必须实现 Serializable
接口。示例代码如下:
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class ObjectInputStreamExample {
public static void main(String[] args) {
try (FileInputStream fileInputStream = new FileInputStream("object.bin");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)) {
MySerializableObject obj = (MySerializableObject) objectInputStream.readObject();
System.out.println("反序列化后的对象: " + obj);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class MySerializableObject implements java.io.Serializable {
private String message;
public MySerializableObject(String message) {
this.message = message;
}
@Override
public String toString() {
return "MySerializableObject{" +
"message='" + message + '\'' +
'}';
}
}
在上述代码中,MySerializableObject
类实现了 Serializable
接口。ObjectInputStream
从 object.bin
文件中读取并反序列化出 MySerializableObject
对象。
对象输出流 - ObjectOutputStream
ObjectOutputStream
用于将对象写入到输出流中,即进行对象的序列化。示例代码如下:
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class ObjectOutputStreamExample {
public static void main(String[] args) {
MySerializableObject obj = new MySerializableObject("这是一个可序列化的对象");
try (FileOutputStream fileOutputStream = new FileOutputStream("object.bin");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream)) {
objectOutputStream.writeObject(obj);
} catch (IOException e) {
e.printStackTrace();
}
}
}
class MySerializableObject implements java.io.Serializable {
private String message;
public MySerializableObject(String message) {
this.message = message;
}
@Override
public String toString() {
return "MySerializableObject{" +
"message='" + message + '\'' +
'}';
}
}
在这段代码中,我们创建了一个 MySerializableObject
对象,并使用 ObjectOutputStream
将其写入到 object.bin
文件中。在对象序列化过程中,对象的状态(即对象的成员变量值)会被转换为字节序列,以便在网络传输或存储到文件中。
打印流
打印流 - PrintStream
PrintStream
是一个字节输出流,它提供了方便的格式化输出方法。System.out
就是一个 PrintStream
实例,常用于将文本输出到控制台。示例代码如下:
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
public class PrintStreamExample {
public static void main(String[] args) {
try (PrintStream printStream = new PrintStream(new FileOutputStream("output.txt"))) {
int number = 100;
double fraction = 3.14159;
printStream.printf("整数: %d, 浮点数: %.2f", number, fraction);
} catch (IOException e) {
e.printStackTrace();
}
}
}
在上述代码中,我们创建了一个 PrintStream
并将其关联到 output.txt
文件。通过 printf()
方法,我们可以按照指定的格式将整数和浮点数输出到文件中。PrintStream
还提供了 print()
和 println()
方法,用于简单的文本输出。
打印流 - PrintWriter
PrintWriter
是一个字符输出流,同样提供了方便的格式化输出方法,类似于 PrintStream
。示例代码如下:
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class PrintWriterExample {
public static void main(String[] args) {
try (PrintWriter printWriter = new PrintWriter(new FileWriter("output.txt"))) {
String name = "张三";
int age = 25;
printWriter.printf("姓名: %s, 年龄: %d", name, age);
} catch (IOException e) {
e.printStackTrace();
}
}
}
在这段代码中,我们使用 PrintWriter
将格式化后的字符串写入到 output.txt
文件中。PrintWriter
更适合处理字符数据,特别是在需要进行文本格式化输出的场景下。
转换流
字节流转字符流 - InputStreamReader
InputStreamReader
用于将字节输入流转换为字符输入流。它根据指定的字符编码将字节解码为字符。示例代码如下:
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class InputStreamReaderExample {
public static void main(String[] args) {
try (InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("example.txt"), "UTF-8")) {
int data;
while ((data = inputStreamReader.read()) != -1) {
System.out.print((char) data);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
在上述代码中,我们通过 InputStreamReader
将 FileInputStream
转换为字符输入流,并指定字符编码为 UTF - 8
。这样,在读取文件内容时,就可以按照 UTF - 8
编码正确地将字节转换为字符。
字符流转字节流 - OutputStreamWriter
OutputStreamWriter
用于将字符输出流转换为字节输出流。它根据指定的字符编码将字符编码为字节。示例代码如下:
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
public class OutputStreamWriterExample {
public static void main(String[] args) {
String message = "使用 OutputStreamWriter 写入";
try (OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream("output.txt"), "UTF-8")) {
outputStreamWriter.write(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
在这段代码中,我们通过 OutputStreamWriter
将字符数据按照 UTF - 8
编码写入到 output.txt
文件中。通过这种方式,可以方便地控制字符到字节的编码转换过程。
通过对上述各种输入输出流的学习和使用,开发者能够在 Java 编程中灵活地处理各种 I/O 场景,无论是简单的文件读写,还是复杂的网络通信、对象序列化等操作,都能找到合适的流类来完成任务。在实际应用中,需要根据具体需求选择合适的流,并注意流的正确关闭,以避免资源泄漏等问题。同时,合理使用缓冲流、数据流等高级流类,可以显著提高 I/O 操作的效率和灵活性。在处理字符数据时,要特别注意字符编码的选择,以确保数据的正确读写和跨平台兼容性。对于对象的序列化和反序列化,要确保相关类实现 Serializable
接口,并了解序列化过程中的一些细节,如 transient 关键字的使用等,以保证对象状态的正确保存和恢复。总之,深入理解 Java 的输入输出流体系是成为一名优秀 Java 开发者的重要基础之一。