Java案例如何使用字节流?

wen java案例 9

本文目录导读:

Java案例如何使用字节流?

  1. 基本概念回顾
  2. 案例一:文件复制(基础操作)
  3. 案例二:图片文件加密/解密
  4. 案例三:读取二进制文件并解析
  5. 案例四:带缓冲的复制(高性能)
  6. 性能对比与最佳实践
  7. 常见错误处理
  8. 总结与最佳实践

字节流是 Java I/O 的核心概念之一,主要用于处理二进制数据(如图片、音频、视频、压缩文件等),下面通过几个典型案例来详细说明如何使用字节流。

基本概念回顾

字节流的顶层抽象类:

  • InputStream:字节输入流
  • OutputStream:字节输出流

常用实现类:

  • FileInputStream / FileOutputStream:文件字节流
  • BufferedInputStream / BufferedOutputStream:缓冲字节流
  • DataInputStream / DataOutputStream:数据字节流
  • ObjectInputStream / ObjectOutputStream:对象字节流

案例一:文件复制(基础操作)

这个案例展示了字节流最基本的读写操作。

import java.io.*;
public class FileCopyExample {
    public static void main(String[] args) {
        // 源文件和目标文件路径
        String sourceFile = "source.jpg";
        String destFile = "dest.jpg";
        try (FileInputStream fis = new FileInputStream(sourceFile);
             FileOutputStream fos = new FileOutputStream(destFile)) {
            // 方式1:逐字节读取(效率低,不推荐)
            // int byteData;
            // while ((byteData = fis.read()) != -1) {
            //     fos.write(byteData);
            // }
            // 方式2:使用缓冲区(推荐)
            byte[] buffer = new byte[1024]; // 1KB缓冲区
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead); // 写入实际读取的字节数
            }
            System.out.println("文件复制成功!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

关键点

  • 使用 try-with-resources 自动关闭资源
  • 使用缓冲区提高效率
  • read(buffer) 返回实际读取的字节数

案例二:图片文件加密/解密

演示如何通过修改字节数据实现简单的加密功能。

import java.io.*;
public class ImageEncryptor {
    // 简单的 XOR 加密/解密
    private static final byte KEY = 0x5A; // 密钥
    public static void encryptFile(String inputPath, String outputPath) throws IOException {
        try (FileInputStream fis = new FileInputStream(inputPath);
             FileOutputStream fos = new FileOutputStream(outputPath)) {
            byte[] buffer = new byte[4096]; // 4KB缓冲区
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                // 对每个字节进行 XOR 操作
                for (int i = 0; i < bytesRead; i++) {
                    buffer[i] ^= KEY;
                }
                fos.write(buffer, 0, bytesRead);
            }
        }
    }
    public static void main(String[] args) {
        try {
            // 加密图片
            encryptFile("original.jpg", "encrypted.jpg");
            System.out.println("图片加密成功!");
            // 解密图片(同样的操作)
            encryptFile("encrypted.jpg", "decrypted.jpg");
            System.out.println("图片解密成功!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

案例三:读取二进制文件并解析

演示如何读取二进制文件格式(如 BMP 图片头信息)。

import java.io.*;
public class BMPHeaderReader {
    public static void readBMPHeader(String filePath) throws IOException {
        try (FileInputStream fis = new FileInputStream(filePath);
             BufferedInputStream bis = new BufferedInputStream(fis)) {
            // BMP文件头(共14字节)
            byte[] header = new byte[14];
            bis.read(header);
            // 验证BMP文件标识
            if (header[0] != 'B' || header[1] != 'M') {
                System.out.println("不是有效的BMP文件");
                return;
            }
            // 读取文件大小(4字节,小端序)
            int fileSize = readLittleEndianInt(header, 2);
            System.out.println("文件大小: " + fileSize + " 字节");
            // 读取位图数据偏移量(4字节)
            int dataOffset = readLittleEndianInt(header, 10);
            System.out.println("数据偏移量: " + dataOffset + " 字节");
            // 读取DIB头(通常40字节)
            byte[] dibHeader = new byte[40];
            bis.read(dibHeader);
            // 读取宽度和高度
            int width = readLittleEndianInt(dibHeader, 4);
            int height = readLittleEndianInt(dibHeader, 8);
            System.out.println("图像尺寸: " + width + " x " + height);
            // 读取位深度(2字节)
            int bitDepth = readLittleEndianShort(dibHeader, 14);
            System.out.println("位深度: " + bitDepth + " bit");
        }
    }
    // 读取小端序的4字节整数
    private static int readLittleEndianInt(byte[] data, int offset) {
        return (data[offset] & 0xFF) |
               ((data[offset + 1] & 0xFF) << 8) |
               ((data[offset + 2] & 0xFF) << 16) |
               ((data[offset + 3] & 0xFF) << 24);
    }
    // 读取小端序的2字节整数
    private static short readLittleEndianShort(byte[] data, int offset) {
        return (short)((data[offset] & 0xFF) | ((data[offset + 1] & 0xFF) << 8));
    }
    public static void main(String[] args) {
        try {
            readBMPHeader("image.bmp");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

案例四:带缓冲的复制(高性能)

使用 BufferedInputStreamBufferedOutputStream 提升性能。

import java.io.*;
public class BufferedCopyExample {
    public static void bufferedCopy(String source, String dest) throws IOException {
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(source));
             BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(dest))) {
            byte[] buffer = new byte[8192]; // 8KB缓冲区
            int bytesRead;
            while ((bytesRead = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, bytesRead);
            }
            // BufferedOutputStream 需要手动 flush
            bos.flush();
        }
    }
    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        try {
            bufferedCopy("large_file.mp4", "copy_large_file.mp4");
            long endTime = System.currentTimeMillis();
            System.out.println("拷贝完成,耗时: " + (endTime - startTime) + "ms");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

性能对比与最佳实践

各种方式的性能对比

public class PerformanceComparison {
    public static void main(String[] args) throws IOException {
        String source = "test.bin";
        long startTime, endTime;
        // 1. 无缓冲逐字节(最慢)
        startTime = System.currentTimeMillis();
        copyNoBuffer(source, "copy1.bin");
        endTime = System.currentTimeMillis();
        System.out.println("无缓冲逐字节: " + (endTime - startTime) + "ms");
        // 2. 有缓冲区(较快)
        startTime = System.currentTimeMillis();
        copyWithBuffer(source, "copy2.bin");
        endTime = System.currentTimeMillis();
        System.out.println("有缓冲区(1024): " + (endTime - startTime) + "ms");
        // 3. 缓冲流(最快)
        startTime = System.currentTimeMillis();
        copyWithBufferedStream(source, "copy3.bin");
        endTime = System.currentTimeMillis();
        System.out.println("缓冲流: " + (endTime - startTime) + "ms");
    }
    private static void copyNoBuffer(String src, String dest) throws IOException {
        try (FileInputStream fis = new FileInputStream(src);
             FileOutputStream fos = new FileOutputStream(dest)) {
            int byteData;
            while ((byteData = fis.read()) != -1) {
                fos.write(byteData);
            }
        }
    }
    private static void copyWithBuffer(String src, String dest) throws IOException {
        try (FileInputStream fis = new FileInputStream(src);
             FileOutputStream fos = new FileOutputStream(dest)) {
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
            }
        }
    }
    private static void copyWithBufferedStream(String src, String dest) throws IOException {
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(src));
             BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(dest))) {
            byte[] buffer = new byte[8192];
            int bytesRead;
            while ((bytesRead = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, bytesRead);
            }
        }
    }
}

常见错误处理

public class ErrorHandlingExample {
    public void safeFileCopy(String source, String dest) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            // 检查源文件是否存在
            File sourceFile = new File(source);
            if (!sourceFile.exists()) {
                System.err.println("源文件不存在: " + source);
                return;
            }
            // 检查目标目录是否存在
            File destFile = new File(dest);
            File parentDir = destFile.getParentFile();
            if (parentDir != null && !parentDir.exists()) {
                parentDir.mkdirs(); // 创建目录
            }
            fis = new FileInputStream(sourceFile);
            fos = new FileOutputStream(destFile);
            byte[] buffer = new byte[4096];
            int bytesRead;
            long totalBytes = 0;
            while ((bytesRead = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
                totalBytes += bytesRead;
            }
            System.out.println("成功复制 " + totalBytes + " 字节");
        } catch (FileNotFoundException e) {
            System.err.println("文件未找到: " + e.getMessage());
        } catch (IOException e) {
            System.err.println("IO错误: " + e.getMessage());
        } finally {
            // 确保资源关闭
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

总结与最佳实践

  1. 使用缓冲区:永远不要逐字节读取,使用 byte[] 缓冲区(建议 4KB-8KB)
  2. 使用缓冲流BufferedInputStream/BufferedOutputStream 能显著提升性能
  3. 及时关闭资源:使用 try-with-resources 自动关闭,或确保在 finally 中关闭
  4. 选择合适的缓冲区大小:8192 字节(8KB)是一个好的选择
  5. 注意编码问题:字节流处理二进制数据,字符流处理文本数据,不要混淆

字节流是 Java I/O 的基础,掌握好这些案例和技巧,可以处理绝大多数文件操作场景。

抱歉,评论功能暂时关闭!