1. 引言:Java I/O流的重要性

在Java编程中,输入输出(I/O)操作是程序与外部世界交互的基础。无论是读取文件、网络通信还是处理用户输入,都离不开I/O流。Java提供了丰富的I/O类库,主要包括:

  • 字节流:以字节为单位进行数据传输
  • 字符流:以字符为单位,支持字符编码处理
  • 缓存流:提高I/O效率的缓冲机制
  • Properties:配置文件处理的专用类
  • 资源释放:确保系统资源正确释放

本文将结合代码示例,详细讲解这些核心概念及其在实际开发中的应用。

2. 字节流(Byte Streams)

字节流是Java I/O的基础,用于处理二进制数据。所有字节流都继承自InputStreamOutputStream抽象类。

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

更多推荐