Java I/O流详解:字节流、缓存流、字符流与编码处理、Properties及资源释放
·
1. 引言:Java I/O流的重要性
在Java编程中,输入输出(I/O)操作是程序与外部世界交互的基础。无论是读取文件、网络通信还是处理用户输入,都离不开I/O流。Java提供了丰富的I/O类库,主要包括:
- 字节流:以字节为单位进行数据传输
- 字符流:以字符为单位,支持字符编码处理
- 缓存流:提高I/O效率的缓冲机制
- Properties:配置文件处理的专用类
- 资源释放:确保系统资源正确释放
本文将结合代码示例,详细讲解这些核心概念及其在实际开发中的应用。
2. 字节流(Byte Streams)
字节流是Java I/O的基础,用于处理二进制数据。所有字节流都继承自InputStream和OutputStream抽象类。
2.1 文件字节流示例
import java.io.*;
public class ByteStreamExample {
// 使用FileInputStream和FileOutputStream读写文件
public static void copyFile(String sourcePath, String targetPath) throws IOException {
try (FileInputStream fis = new FileInputStream(sourcePath);
FileOutputStream fos = new FileOutputStream(targetPath)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
System.out.println("文件复制完成");
}
}
// 读取字节数据
public static void readBytes(String filePath) throws IOException {
try (FileInputStream fis = new FileInputStream(filePath)) {
int data;
while ((data = fis.read()) != -1) {
System.out.print((char) data); // 注意:直接转换可能乱码
}
}
}
public static void main(String[] args) {
try {
// 创建测试文件
String testContent = "Hello, Byte Stream! 字节流测试";
try (FileOutputStream fos = new FileOutputStream("test_byte.txt")) {
fos.write(testContent.getBytes("UTF-8"));
}
// 复制文件
copyFile("test_byte.txt", "test_byte_copy.txt");
// 读取文件
System.out.println("原始文件内容:");
readBytes("test_byte.txt");
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.2 字节数组流
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class ByteArrayStreamExample {
public static void byteArrayStreamDemo() throws IOException {
String text = "字节数组流测试";
byte[] data = text.getBytes("UTF-8");
// 使用ByteArrayInputStream读取字节数组
try (ByteArrayInputStream bais = new ByteArrayInputStream(data)) {
System.out.println("可读字节数: " + bais.available());
// 读取并显示内容
int byteData;
while ((byteData = bais.read()) != -1) {
System.out.print((char) byteData + " ");
}
System.out.println();
}
// 使用ByteArrayOutputStream写入字节数组
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
baos.write("第一部分 ".getBytes("UTF-8"));
baos.write("第二部分 ".getBytes("UTF-8"));
baos.write("第三部分".getBytes("UTF-8"));
byte[] result = baos.toByteArray();
System.out.println("输出结果: " + new String(result, "UTF-8"));
System.out.println("字节数组大小: " + result.length);
}
}
public static void main(String[] args) {
try {
byteArrayStreamDemo();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3. 缓存流(Buffered Streams)
缓存流通过在内存中建立缓冲区,减少实际的I/O操作次数,显著提高性能。
3.1 缓冲字节流
import java.io.*;
public class BufferedStreamExample {
// 使用BufferedInputStream和BufferedOutputStream
public static void bufferedCopy(String sourcePath, String targetPath) throws IOException {
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourcePath));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(targetPath))) {
byte[] buffer = new byte[8192]; // 8KB缓冲区
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
// 注意:BufferedOutputStream需要flush或close才能确保数据写入
bos.flush();
System.out.println("缓冲复制完成");
}
}
// 比较有缓冲和无缓冲的性能差异
public static void performanceTest(String filePath) throws IOException {
// 创建大文件用于测试
createLargeFile(filePath, 10 * 1024 * 1024); // 10MB
long startTime, endTime;
// 无缓冲复制
startTime = System.currentTimeMillis();
try (FileInputStream fis = new FileInputStream(filePath);
FileOutputStream fos = new FileOutputStream(filePath + ".nocache")) {
int data;
while ((data = fis.read()) != -1) {
fos.write(data);
}
}
endTime = System.currentTimeMillis();
System.out.println("无缓冲复制耗时: " + (endTime - startTime) + "ms");
// 有缓冲复制
startTime = System.currentTimeMillis();
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath + ".cache"))) {
int data;
while ((data = bis.read()) != -1) {
bos.write(data);
}
}
endTime = System.currentTimeMillis();
System.out.println("有缓冲复制耗时: " + (endTime - startTime) + "ms");
}
private static void createLargeFile(String filePath, int size) throws IOException {
try (FileOutputStream fos = new FileOutputStream(filePath)) {
byte[] data = new byte[1024];
for (int i = 0; i < 1024; i++) {
data[i] = (byte) (i % 256);
}
for (int i = 0; i < size / 1024; i++) {
fos.write(data);
}
}
}
public static void main(String[] args) {
try {
// 测试缓冲流
String testContent = "缓冲流测试数据\n".repeat(1000);
try (BufferedWriter bw = new BufferedWriter(new FileWriter("test_buffered.txt"))) {
bw.write(testContent);
}
bufferedCopy("test_buffered.txt", "test_buffered_copy.txt");
// 性能测试
performanceTest("large_test.dat");
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.2 缓冲字符流
import java.io.*;
public class BufferedCharacterStreamExample {
// 使用BufferedReader和BufferedWriter
public static void readAndWriteWithBuffer(String sourcePath, String targetPath) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(sourcePath));
BufferedWriter bw = new BufferedWriter(new FileWriter(targetPath))) {
String line;
int lineNumber = 1;
while ((line = br.readLine()) != null) {
// 添加行号并写入
bw.write(String.format("%04d: %s", lineNumber, line));
bw.newLine(); // 使用newLine()而不是"\n",保证平台兼容性
lineNumber++;
}
bw.flush();
System.out.println("已处理 " + (lineNumber - 1) + " 行");
}
}
// 使用mark()和reset()方法
public static void markResetDemo(String filePath) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
// 标记当前位置(缓冲区大小至少100字符)
br.mark(100);
// 读取前5行
System.out.println("前5行内容:");
for (int i = 0; i < 5; i++) {
String line = br.readLine();
if (line != null) {
System.out.println(line);
}
}
// 重置到标记位置
br.reset();
System.out.println("\n重置后再次读取前3行:");
for (int i = 0; i < 3; i++) {
String line = br.readLine();
if (line != null) {
System.out.println(line);
}
}
}
}
public static void main(String[] args) {
try {
// 创建测试文件
try (BufferedWriter bw = new BufferedWriter(new FileWriter("test_lines.txt"))) {
for (int i = 1; i <= 20; i++) {
bw.write("这是第 " + i + " 行测试数据");
bw.newLine();
}
}
// 测试缓冲字符流
readAndWriteWithBuffer("test_lines.txt", "test_lines_numbered.txt");
// 测试mark/reset
markResetDemo("test_lines.txt");
} catch (IOException e) {
e.printStackTrace();
}
}
}
4. 字符流与编码处理(Character Streams & Encoding)
字符流专门用于处理文本数据,能够正确处理字符编码问题。
4.1 字符编码基础
import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
public class EncodingExample {
// 显示所有可用字符集
public static void showAvailableCharsets() {
System.out.println("可用字符集:");
Charset.availableCharsets().forEach((name, charset) -> {
System.out.printf("%-20s: %s%n", name, charset.displayName());
});
}
// 不同编码方式读写文件
public static void writeWithDifferentEncodings() throws IOException {
String text = "中文测试 Chinese Test 🚀 特殊符号:©®™";
// UTF-8编码写入
try (OutputStreamWriter osw1 = new OutputStreamWriter(
new FileOutputStream("utf8.txt"), StandardCharsets.UTF_8)) {
osw1.write(text);
}
// GBK编码写入
try (OutputStreamWriter osw2 = new OutputStreamWriter(
new FileOutputStream("gbk.txt"), "GBK")) {
osw2.write(text);
}
// ISO-8859-1编码写入(不支持中文)
try (OutputStreamWriter osw3 = new OutputStreamWriter(
new FileOutputStream("iso.txt"), StandardCharsets.ISO_8859_1)) {
osw3.write(text);
}
System.out.println("已用不同编码写入文件");
}
// 读取不同编码的文件
public static void readWithDifferentEncodings() throws IOException {
System.out.println("\n读取UTF-8文件:");
try (InputStreamReader isr1 = new InputStreamReader(
new FileInputStream("utf8.txt"), StandardCharsets.UTF_8)) {
int ch;
while ((ch = isr1.read()) != -1) {
System.out.print((char) ch);
}
}
System.out.println("\n\n读取GBK文件:");
try (InputStreamReader isr2 = new InputStreamReader(
new FileInputStream("gbk.txt"), "GBK")) {
int ch;
while ((ch = isr2.read()) != -1) {
System.out.print((char) ch);
}
}
}
// 编码转换示例
public static void convertEncoding(String sourceFile, String sourceCharset,
String targetFile, String targetCharset) throws IOException {
try (BufferedReader br = new BufferedReader(
new InputStreamReader(new FileInputStream(sourceFile), sourceCharset));
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(targetFile), targetCharset))) {
String line;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
}
System.out.println("编码转换完成: " + sourceCharset + " → " + targetCharset);
}
}
// 检测文件编码
public static void detectFileEncoding(String filePath) throws IOException {
byte[] buffer = new byte[4096];
try (FileInputStream fis = new FileInputStream(filePath)) {
int bytesRead = fis.read(buffer);
// 简单编码检测(实际项目建议使用juniversalchardet等库)
String[] charsets = {"UTF-8", "GBK", "ISO-8859-1", "UTF-16"};
for (String charset : charsets) {
try {
String content = new String(buffer, 0, bytesRead, charset);
// 检查是否包含乱码字符
if (!content.contains("�")) {
System.out.println("可能编码: " + charset + ", 示例: " +
content.substring(0, Math.min(50, content.length())));
}
} catch (Exception e) {
// 编码不匹配,继续尝试下一个
}
}
}
}
public static void main(String[] args) {
try {
// 显示可用字符集
showAvailableCharsets();
// 测试不同编码
writeWithDifferentEncodings();
readWithDifferentEncodings();
// 编码转换
convertEncoding("utf8.txt", "UTF-8", "utf8_to_gbk.txt", "GBK");
// 编码检测
detectFileEncoding("utf8.txt");
} catch (IOException e) {
e.printStackTrace();
}
}
}
4.2 FileReader和FileWriter的编码陷阱
import java.io.*;
public class FileReaderWriterEncodingTrap {
/*
* 重要:FileReader和FileWriter使用平台默认编码
* 这可能导致跨平台编码问题!
*/
public static void demonstrateEncodingIssue() throws IOException {
String text = "中文内容 Chinese Content";
// FileWriter使用平台默认编码(可能是GBK、UTF-8等)
try (FileWriter fw = new FileWriter("default_encoding.txt")) {
fw.write(text);
}
// 明确指定UTF-8编码(推荐做法)
try (OutputStreamWriter osw = new OutputStreamWriter(
new FileOutputStream("explicit_utf8.txt"), StandardCharsets.UTF_8)) {
osw.write(text);
}
System.out.println("平台默认编码: " + Charset.defaultCharset().displayName());
System.out.println("FileWriter使用的编码: " + Charset.defaultCharset().displayName());
}
// 安全读写文本文件的工具方法
public static class SafeTextFileHandler {
// 安全写入文本(始终使用UTF-8)
public static void writeText(String filePath, String content) throws IOException {
writeText(filePath, content, StandardCharsets.UTF_8);
}
public static void writeText(String filePath, String content, Charset charset) throws IOException {
try (BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(filePath), charset))) {
bw.write(content);
}
}
// 安全读取文本(自动检测或指定编码)
public static String readText(String filePath) throws IOException {
return readText(filePath, StandardCharsets.UTF_8);
}
public static String readText(String filePath, Charset charset) throws IOException {
StringBuilder content = new StringBuilder();
try (BufferedReader br = new BufferedReader(
new InputStreamReader(new FileInputStream(filePath), charset))) {
String line;
while ((line = br.readLine()) != null) {
content.append(line).append(System.lineSeparator());
}
}
return content.toString();
}
// 带BOM的UTF-8文件处理
public static String readTextWithBOM(String filePath) throws IOException {
try (FileInputStream fis = new FileInputStream(filePath)) {
// 检查UTF-8 BOM (EF BB BF)
byte[] bom = new byte[3];
int read = fis.read(bom);
Charset charset = StandardCharsets.UTF_8;
int bomLength = 0;
if (read >= 3 && bom[0] == (byte)0xEF && bom[1] == (byte)0xBB && bom[2] == (byte)0xBF) {
bomLength = 3; // UTF-8 with BOM
}
// 重置到正确位置
try (InputStreamReader isr = new InputStreamReader(
new FileInputStream(filePath), charset)) {
isr.skip(bomLength);
StringBuilder content = new StringBuilder();
char[] buffer = new char[1024];
int charsRead;
while ((charsRead = isr.read(buffer)) != -1) {
content.append(buffer, 0, charsRead);
}
return content.toString();
}
}
}
}
public static void main(String[] args) {
try {
demonstrateEncodingIssue();
// 使用安全的文本处理方法
SafeTextFileHandler.writeText("safe_text.txt", "安全编码测试\n第二行内容");
String content = SafeTextFileHandler.readText("safe_text.txt");
System.out.println("读取的内容:\n" + content);
} catch (IOException e) {
e.printStackTrace();
}
}
}
5. Properties文件处理
Properties是Java中处理配置文件的专用类,继承自Hashtable,用于读写键值对格式的配置文件。
5.1 Properties基础用法
import java.io.*;
i
更多推荐


所有评论(0)