Java 字节流与字符流
字节流和字符流是 Java IO 的核心。理解它们的使用是进行文件读写的基础。本章将详细介绍 Java 中的字节流和字符流。
InputStream / OutputStream
InputStream(字节输入流)
**InputStream**是所有字节输入流的抽象基类。
核心方法:
// 读取一个字节
int read() throws IOException;
// 读取到字节数组
int read(byte[] b) throws IOException;
int read(byte[] b, int off, int len) throws IOException;
// 跳过字节
long skip(long n) throws IOException;
// 关闭流
void close() throws IOException;
// 标记和重置(如果支持)
void mark(int readlimit);
void reset();
boolean markSupported();
常用子类:
FileInputStream:文件输入流ByteArrayInputStream:字节数组输入流BufferedInputStream:缓冲输入流DataInputStream:数据输入流
OutputStream(字节输出流)
**OutputStream**是所有字节输出流的抽象基类。
核心方法:
// 写入一个字节
void write(int b) throws IOException;
// 写入字节数组
void write(byte[] b) throws IOException;
void write(byte[] b, int off, int len) throws IOException;
// 刷新缓冲区
void flush() throws IOException;
// 关闭流
void close() throws IOException;
常用子类:
FileOutputStream:文件输出流ByteArrayOutputStream:字节数组输出流BufferedOutputStream:缓冲输出流DataOutputStream:数据输出流
Reader / Writer
Reader(字符输入流)
**Reader**是所有字符输入流的抽象基类。
核心方法:
// 读取一个字符
int read() throws IOException;
// 读取到字符数组
int read(char[] cbuf) throws IOException;
int read(char[] cbuf, int off, int len) throws IOException;
// 跳过字符
long skip(long n) throws IOException;
// 关闭流
void close() throws IOException;
常用子类:
FileReader:文件字符输入流CharArrayReader:字符数组输入流BufferedReader:缓冲字符输入流InputStreamReader:字节流转字符流
Writer(字符输出流)
**Writer**是所有字符输出流的抽象基类。
核心方法:
// 写入一个字符
void write(int c) throws IOException;
// 写入字符数组
void write(char[] cbuf) throws IOException;
void write(char[] cbuf, int off, int len) throws IOException;
// 写入字符串
void write(String str) throws IOException;
void write(String str, int off, int len) throws IOException;
// 追加
Writer append(CharSequence csq) throws IOException;
// 刷新缓冲区
void flush() throws IOException;
// 关闭流
void close() throws IOException;
常用子类:
FileWriter:文件字符输出流CharArrayWriter:字符数组输出流BufferedWriter:缓冲字符输出流OutputStreamWriter:字符流转字节流
常用子类(FileInputStream, FileOutputStream, FileReader, FileWriter)
FileInputStream
**FileInputStream**用于从文件读取字节。
import java.io.FileInputStream;
import java.io.IOException;
// 读取文件
try (FileInputStream fis = new FileInputStream("file.txt")) {
int data;
while ((data = fis.read()) != -1) {
System.out.print((char) data);
}
}
// 使用缓冲区读取(推荐)
try (FileInputStream fis = new FileInputStream("file.txt")) {
byte[] buffer = new byte[1024];
int length;
while ((length = fis.read(buffer)) != -1) {
System.out.write(buffer, 0, length);
}
}
FileOutputStream
**FileOutputStream**用于向文件写入字节。
import java.io.FileOutputStream;
import java.io.IOException;
// 写入文件
try (FileOutputStream fos = new FileOutputStream("file.txt")) {
fos.write(65); // 写入字节 'A'
fos.write("Hello".getBytes());
}
// 追加模式
try (FileOutputStream fos = new FileOutputStream("file.txt", true)) {
fos.write(" World".getBytes());
}
FileReader
**FileReader**用于从文件读取字符。
import java.io.FileReader;
import java.io.IOException;
// 读取文件
try (FileReader fr = new FileReader("file.txt")) {
int data;
while ((data = fr.read()) != -1) {
System.out.print((char) data);
}
}
// 使用缓冲区读取(推荐)
try (FileReader fr = new FileReader("file.txt")) {
char[] buffer = new char[1024];
int length;
while ((length = fr.read(buffer)) != -1) {
System.out.print(new String(buffer, 0, length));
}
}
FileWriter
**FileWriter**用于向文件写入字符。
import java.io.FileWriter;
import java.io.IOException;
// 写入文件
try (FileWriter fw = new FileWriter("file.txt")) {
fw.write("Hello");
fw.write(" World");
fw.flush(); // 刷新缓冲区
}
// 追加模式
try (FileWriter fw = new FileWriter("file.txt", true)) {
fw.write(" Appended");
}
示例:文本文件读写
示例 1:使用字节流读写文本
import java.io.*;
public class ByteStreamExample {
// 写入文本文件
public static void writeTextFile(String filename, String content) throws IOException {
try (FileOutputStream fos = new FileOutputStream(filename)) {
fos.write(content.getBytes("UTF-8"));
}
}
// 读取文本文件
public static String readTextFile(String filename) throws IOException {
try (FileInputStream fis = new FileInputStream(filename)) {
byte[] buffer = new byte[1024];
StringBuilder sb = new StringBuilder();
int length;
while ((length = fis.read(buffer)) != -1) {
sb.append(new String(buffer, 0, length, "UTF-8"));
}
return sb.toString();
}
}
public static void main(String[] args) throws IOException {
writeTextFile("test.txt", "Hello, 世界!");
String content = readTextFile("test.txt");
System.out.println(content);
}
}
示例 2:使用字符流读写文本(推荐)
import java.io.*;
public class CharStreamExample {
// 写入文本文件
public static void writeTextFile(String filename, String content) throws IOException {
try (FileWriter fw = new FileWriter(filename)) {
fw.write(content);
}
}
// 读取文本文件
public static String readTextFile(String filename) throws IOException {
try (FileReader fr = new FileReader(filename)) {
char[] buffer = new char[1024];
StringBuilder sb = new StringBuilder();
int length;
while ((length = fr.read(buffer)) != -1) {
sb.append(buffer, 0, length);
}
return sb.toString();
}
}
public static void main(String[] args) throws IOException {
writeTextFile("test.txt", "Hello, 世界!");
String content = readTextFile("test.txt");
System.out.println(content);
}
}
示例 3:按行读取文件
import java.io.*;
public class LineReaderExample {
// 使用 BufferedReader 按行读取
public static void readLines(String filename) throws IOException {
try (FileReader fr = new FileReader(filename);
BufferedReader br = new BufferedReader(fr)) {
String line;
int lineNumber = 1;
while ((line = br.readLine()) != null) {
System.out.println(lineNumber + ": " + line);
lineNumber++;
}
}
}
// 使用 Files.readAllLines(Java 7+)
public static void readLinesNew(String filename) throws IOException {
List<String> lines = Files.readAllLines(Paths.get(filename));
for (int i = 0; i < lines.size(); i++) {
System.out.println((i + 1) + ": " + lines.get(i));
}
}
}
示例 4:复制文件
import java.io.*;
public class FileCopyExample {
// 使用字节流复制(适合任何文件)
public static void copyFileByteStream(String source, String dest) throws IOException {
try (FileInputStream fis = new FileInputStream(source);
FileOutputStream fos = new FileOutputStream(dest)) {
byte[] buffer = new byte[8192]; // 8KB 缓冲区
int length;
while ((length = fis.read(buffer)) != -1) {
fos.write(buffer, 0, length);
}
}
}
// 使用字符流复制(只适合文本文件)
public static void copyFileCharStream(String source, String dest) throws IOException {
try (FileReader fr = new FileReader(source);
FileWriter fw = new FileWriter(dest)) {
char[] buffer = new char[8192];
int length;
while ((length = fr.read(buffer)) != -1) {
fw.write(buffer, 0, length);
}
}
}
public static void main(String[] args) throws IOException {
// 复制二进制文件使用字节流
copyFileByteStream("image.jpg", "copy.jpg");
// 复制文本文件可以使用字符流
copyFileCharStream("text.txt", "copy.txt");
}
}
示例 5:处理编码
import java.io.*;
public class EncodingExample {
// 指定编码读取
public static String readWithEncoding(String filename, String encoding) throws IOException {
try (FileInputStream fis = new FileInputStream(filename);
InputStreamReader isr = new InputStreamReader(fis, encoding);
BufferedReader br = new BufferedReader(isr)) {
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
sb.append(line).append("\n");
}
return sb.toString();
}
}
// 指定编码写入
public static void writeWithEncoding(String filename, String content, String encoding) throws IOException {
try (FileOutputStream fos = new FileOutputStream(filename);
OutputStreamWriter osw = new OutputStreamWriter(fos, encoding);
BufferedWriter bw = new BufferedWriter(osw)) {
bw.write(content);
}
}
public static void main(String[] args) throws IOException {
// 使用 UTF-8 编码
writeWithEncoding("test.txt", "Hello, 世界!", "UTF-8");
String content = readWithEncoding("test.txt", "UTF-8");
System.out.println(content);
}
}
try-with-resources
使用 try-with-resources 自动关闭流(Java 7+):
// ✅ 推荐:自动关闭
try (FileInputStream fis = new FileInputStream("file.txt")) {
// 使用流
} // 自动关闭
// ❌ 不推荐:手动关闭
FileInputStream fis = null;
try {
fis = new FileInputStream("file.txt");
// 使用流
} finally {
if (fis != null) {
fis.close();
}
}
流的最佳实践
1. 使用缓冲区
// ✅ 推荐:使用缓冲区
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("file.txt"))) {
// 性能更好
}
// ❌ 不推荐:不使用缓冲区
// try (FileInputStream fis = new FileInputStream("file.txt")) {
// // 性能较差
// }
2. 使用 try-with-resources
// ✅ 推荐:自动关闭
try (FileReader fr = new FileReader("file.txt")) {
// 使用流
}
3. 选择合适的流类型
// 文本文件:使用字符流
FileReader fr = new FileReader("text.txt");
// 二进制文件:使用字节流
FileInputStream fis = new FileInputStream("image.jpg");
4. 处理异常
try (FileInputStream fis = new FileInputStream("file.txt")) {
// 使用流
} catch (FileNotFoundException e) {
System.err.println("文件未找到:" + e.getMessage());
} catch (IOException e) {
System.err.println("IO 错误:" + e.getMessage());
}
小结
Java 字节流与字符流要点:
- InputStream/OutputStream:字节流,处理二进制数据
- Reader/Writer:字符流,处理文本数据
- FileInputStream/FileOutputStream:文件字节流
- FileReader/FileWriter:文件字符流
- 使用建议:文本用字符流,二进制用字节流
关键要点:
- 字节流以字节为单位,字符流以字符为单位
- 文本文件使用字符流,二进制文件使用字节流
- 使用缓冲区提高性能
- 使用 try-with-resources 自动关闭流
- 注意字符编码问题
理解了字节流和字符流,你就能进行基本的文件读写操作。在下一章,我们将学习 Java 的缓冲流与对象流。