Java IO相关技术小结

Java IO(输入/输出)相关技术

一、Java IO基础概念

  1. 数据流方向

    • 输入流(InputStream/Reader):从数据源(文件、网络、内存)读取数据到程序。
    • 输出流(OutputStream/Writer):从程序写入数据到目标位置。
  2. 数据类型

    • 字节流(Byte Stream):以字节为单位处理数据(InputStream/OutputStream),适用于二进制文件(图片、视频)。
    • 字符流(Character Stream):以字符为单位处理数据(Reader/Writer),自动处理字符编码(如UTF-8),适用于文本文件。
  3. 缓冲机制

    • 缓冲流(BufferedStream):通过内存缓冲区减少IO操作次数,提升性能(如BufferedInputStream)。

二、传统IO(BIO,Blocking IO)

1. 核心类库
  • 字节流

    // 文件操作示例
    try (FileInputStream fis = new FileInputStream("input.txt");
         FileOutputStream fos = new FileOutputStream("output.txt")) {
        int data;
        while ((data = fis.read()) != -1) {
            fos.write(data);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    
    • 常用类FileInputStreamFileOutputStreamByteArrayInputStreamDataInputStream
  • 字符流

    // 文本文件读写示例
    try (BufferedReader br = new BufferedReader(new FileReader("input.txt"));
         BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"))) {
        String line;
        while ((line = br.readLine()) != null) {
            bw.write(line);
            bw.newLine();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    
    • 常用类FileReaderFileWriterBufferedReaderPrintWriter
2. 装饰器模式

Java IO通过装饰器模式组合功能(如BufferedInputStream包装FileInputStream):

InputStream is = new BufferedInputStream(new FileInputStream("data.bin"));
3. 典型应用场景
  • 简单文件读写(配置文件、日志)。
  • 网络通信(Socket输入输出)。

三、NIO(New IO,JDK 1.4引入)

1. 核心特性
  • 非阻塞IO:基于通道(Channel)和缓冲区(Buffer),支持单线程处理多个连接(Selector模式)。
  • 零拷贝(Zero-copy):直接在内核空间完成数据传输,减少用户空间与内核空间的切换。
2. 核心组件
  • Buffer
    用于与Channel交互的数据容器,支持ByteBufferCharBuffer等。

    ByteBuffer buffer = ByteBuffer.allocate(1024);  // 分配堆内存缓冲区
    buffer.put("Hello".getBytes());  // 写入数据
    buffer.flip();  // 切换为读模式
    while (buffer.hasRemaining()) {
        System.out.print((char) buffer.get());
    }
    
  • Channel
    双向数据通道,支持异步读写,如:

    // 文件Channel示例
    try (FileChannel channel = new FileInputStream("input.txt").getChannel()) {
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        int bytesRead = channel.read(buffer);  // 从通道读取到缓冲区
    } catch (IOException e) {
        e.printStackTrace();
    }
    
  • Selector
    实现单线程管理多个通道的事件(连接就绪、可读、可写):

    Selector selector = Selector.open();
    channel.configureBlocking(false);  // 设置非阻塞模式
    channel.register(selector, SelectionKey.OP_READ);  // 注册读事件
    
    while (selector.select() > 0) {
        Set<SelectionKey> keys = selector.selectedKeys();
        Iterator<SelectionKey> it = keys.iterator();
        while (it.hasNext()) {
            SelectionKey key = it.next();
            if (key.isReadable()) {
                // 处理读事件
            }
            it.remove();
        }
    }
    
3. 应用场景
  • 高并发网络编程(如Netty框架基于NIO实现)。
  • 文件大文件处理(零拷贝提升性能)。

四、AIO(Asynchronous IO,JDK 7引入)

1. 核心特性
  • 异步非阻塞:基于回调或Future模式,IO操作完成后通知应用程序。
  • 真正的异步:由操作系统完成IO操作,应用程序无需等待。
2. 核心类库
  • AsynchronousFileChannel:异步文件操作。
  • AsynchronousSocketChannel:异步网络通信。
// 异步文件读取示例
AsynchronousFileChannel channel = AsynchronousFileChannel.open(
    Paths.get("data.txt"), StandardOpenOption.READ);
ByteBuffer buffer = ByteBuffer.allocate(1024);

channel.read(buffer, 0, buffer, new CompletionHandler<Integer, ByteBuffer>() {
    @Override
    public void completed(Integer result, ByteBuffer attachment) {
        System.out.println("Read " + result + " bytes");
    }
    
    @Override
    public void failed(Throwable exc, ByteBuffer attachment) {
        exc.printStackTrace();
    }
});
3. 应用场景
  • 异步网络服务(如高性能服务器)。
  • 实时数据处理(如日志收集、消息队列)。

五、IO与NIO/AIO对比

特性 BIO(传统IO) NIO(非阻塞IO) AIO(异步IO)
阻塞模式 同步阻塞 同步非阻塞 异步非阻塞
核心组件 流(Stream) 通道(Channel)、缓冲区(Buffer)、选择器(Selector) 异步通道(AsynchronousChannel)
线程模型 一个连接一个线程 一个线程处理多个连接 线程池处理回调
适用场景 连接数少且固定的场景 连接数多但短操作的场景(如Web服务器) 连接数多且长操作的场景(如文件传输)

六、典型应用场景

  1. 文件操作

    • 小文件读写:使用BufferedReader/BufferedWriter(字符流)。
    • 大文件读写:使用FileChannel+ByteBuffer(NIO零拷贝)。
  2. 网络编程

    • 低并发:传统Socket+InputStream/OutputStream
    • 高并发:NIO的Selector+SocketChannel(如Netty框架)。
    • 异步通信:AIO的AsynchronousSocketChannel(如JDK自带的AsynchronousServerSocketChannel)。
  3. 序列化与反序列化

    • 使用ObjectInputStream/ObjectOutputStream实现Java对象的序列化。

七、注意事项

  1. 资源管理

    • 使用try-with-resources语句自动关闭流,避免内存泄漏。
  2. 字符编码

    • 处理文本时显式指定编码(如new FileReader("file.txt", StandardCharsets.UTF_8)),避免乱码。
  3. 性能优化

    • 优先使用缓冲流(BufferedInputStream等)减少IO次数。
    • 大文件读写时使用NIO的FileChannel替代传统流。
  4. 异常处理

    • 捕获IOException及其子类,处理连接断开、文件不存在等异常。

八、相关框架与工具

  • Netty:基于NIO的高性能网络编程框架,广泛用于分布式系统通信。
  • Apache Commons IO:提供更简洁的IO操作工具类(如FileUtils)。
  • Google Guava:增强的IO工具类(如FilesByteStreams)。

通过合理选择IO技术(BIO/NIO/AIO)和工具类库,可显著提升Java应用的性能和可维护性。

常用类库及工具

一、文件与路径操作类库

1. java.nio.file包(JDK 7+)
  • 核心类

    • Path:表示文件或目录的路径(替代File类)。
    • Paths:工厂类,用于创建Path实例。
    • Files:工具类,提供文件操作静态方法(读写、复制、删除等)。
    // 示例:递归复制目录
    Path source = Paths.get("src");
    Path target = Paths.get("dst");
    Files.walk(source)
         .forEach(sourcePath -> {
            try {
                Path targetPath = target.resolve(source.relativize(sourcePath));
                Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
            } catch (IOException e) {
                e.printStackTrace();
            }
         });
    
2. java.io.File(传统文件操作)
  • 常用方法
    File file = new File("example.txt");
    file.exists();        // 判断文件是否存在
    file.isDirectory();   // 是否为目录
    file.listFiles();     // 获取目录下的文件列表
    file.createNewFile(); // 创建新文件
    

二、序列化与反序列化

1. 标准Java序列化
  • 核心接口

    • Serializable:标记接口,实现该接口的类可被序列化。
    • Externalizable:自定义序列化逻辑(需实现writeExternal()readExternal()方法)。
    // 序列化示例
    try (ObjectOutputStream oos = new ObjectOutputStream(
            new FileOutputStream("data.ser"))) {
        oos.writeObject(new User("Alice", 25));
    } catch (IOException e) {
        e.printStackTrace();
    }
    
    // 反序列化示例
    try (ObjectInputStream ois = new ObjectInputStream(
            new FileInputStream("data.ser"))) {
        User user = (User) ois.readObject();
    } catch (IOException | ClassNotFoundException e) {
        e.printStackTrace();
    }
    
2. 第三方序列化库
  • JSON序列化:Jackson、Gson

    // Jackson示例
    ObjectMapper mapper = new ObjectMapper();
    User user = new User("Bob", 30);
    String json = mapper.writeValueAsString(user); // 对象转JSON
    User deserializedUser = mapper.readValue(json, User.class); // JSON转对象
    
  • 高性能序列化:Kryo、Protostuff(基于Protobuf)。

三、压缩与归档

1. java.util.zip
  • 常用类

    • ZipInputStream/ZipOutputStream:读写ZIP文件。
    • GZIPInputStream/GZIPOutputStream:读写GZIP压缩文件。
    // ZIP压缩示例
    try (ZipOutputStream zos = new ZipOutputStream(
            new FileOutputStream("archive.zip"))) {
        File file = new File("data.txt");
        zos.putNextEntry(new ZipEntry(file.getName()));
        
        try (FileInputStream fis = new FileInputStream(file)) {
            byte[] buffer = new byte[1024];
            int length;
            while ((length = fis.read(buffer)) > 0) {
                zos.write(buffer, 0, length);
            }
        }
        zos.closeEntry();
    } catch (IOException e) {
        e.printStackTrace();
    }
    
2. Apache Commons Compress
  • 支持更多格式:ZIP、TAR、BZIP2、7-Zip等。
    // TAR文件操作示例
    try (TarArchiveOutputStream taos = new TarArchiveOutputStream(
            new FileOutputStream("archive.tar"))) {
        TarArchiveEntry entry = new TarArchiveEntry(new File("data.txt"));
        taos.putArchiveEntry(entry);
        
        try (FileInputStream fis = new FileInputStream("data.txt")) {
            IOUtils.copy(fis, taos);
        }
        taos.closeArchiveEntry();
    } catch (IOException e) {
        e.printStackTrace();
    }
    

四、网络编程类库

1. 标准Socket编程
  • TCP通信Socket(客户端)和ServerSocket(服务器)。

    // 服务器端示例
    try (ServerSocket serverSocket = new ServerSocket(8080)) {
        Socket clientSocket = serverSocket.accept();
        try (BufferedReader in = new BufferedReader(
                new InputStreamReader(clientSocket.getInputStream()));
             PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true)) {
            String inputLine;
            while ((inputLine = in.readLine()) != null) {
                out.println("Server received: " + inputLine);
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    
  • UDP通信DatagramSocketDatagramPacket

2. HTTP客户端
  • JDK 11+ HttpClient(替代HttpURLConnection):

    HttpClient client = HttpClient.newBuilder().build();
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://api.example.com/data"))
        .GET()
        .build();
    HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
    System.out.println(response.body());
    
  • Apache HttpClient:功能更丰富,支持连接池、代理等。

五、管道与内存流

1. 管道流(Piped Streams)
  • 实现线程间通信:
    // 发送线程
    PipedOutputStream pos = new PipedOutputStream();
    writerThread = new Thread(() -> {
        pos.write("Hello, Pipe!".getBytes());
        pos.close();
    });
    
    // 接收线程
    PipedInputStream pis = new PipedInputStream(pos);
    readerThread = new Thread(() -> {
        int data;
        while ((data = pis.read()) != -1) {
            System.out.print((char) data);
        }
        pis.close();
    });
    
2. 内存流
  • ByteArrayInputStream/ByteArrayOutputStream:在内存中读写数据。
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    baos.write("Hello, Memory!".getBytes());
    byte[] bytes = baos.toByteArray();
    
    ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
    int data;
    while ((data = bais.read()) != -1) {
        System.out.print((char) data);
    }
    

六、字符编码转换

1. java.nio.charset
  • 常用类

    • Charset:表示字符集(如UTF-8、GBK)。
    • CharsetEncoder/CharsetDecoder:实现字符与字节的转换。
    // 字符串编码转换示例
    String text = "你好,世界";
    Charset utf8 = Charset.forName("UTF-8");
    Charset gbk = Charset.forName("GBK");
    
    ByteBuffer utf8Bytes = utf8.encode(text);
    CharBuffer gbkChars = gbk.decode(utf8Bytes);
    String convertedText = gbkChars.toString();
    

七、第三方工具类库

1. Apache Commons IO
  • 常用工具类

    • FileUtils:文件操作(读取、写入、复制)。
    • IOUtils:流操作(关闭、复制、转换为字符串)。
    // 示例:读取文件内容为字符串
    String content = FileUtils.readFileToString(new File("data.txt"), StandardCharsets.UTF_8);
    
    // 示例:复制输入流到输出流
    IOUtils.copy(inputStream, outputStream);
    
2. Google Guava
  • 核心类

    • Files:文件操作(读取、写入、临时文件)。
    • ByteStreams/CharStreams:字节流/字符流操作。
    // 示例:写入字符串到文件
    Files.asCharSink(new File("output.txt"), StandardCharsets.UTF_8).write("Hello, Guava!");
    
    // 示例:读取文件所有行
    List<String> lines = Files.asCharSource(new File("input.txt"), StandardCharsets.UTF_8).readLines();
    

八、NIO.2(JDK 7+)增强功能

1. 异步文件操作
  • AsynchronousFileChannel支持回调或Future模式:
    AsynchronousFileChannel channel = AsynchronousFileChannel.open(
        Paths.get("data.txt"), StandardOpenOption.READ);
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    
    Future<Integer> result = channel.read(buffer, 0);
    while (!result.isDone()) {
        // 处理其他任务
    }
    int bytesRead = result.get();
    
2. 文件监听(WatchService)
  • 监控文件系统变化(创建、修改、删除):
    WatchService watcher = FileSystems.getDefault().newWatchService();
    Path dir = Paths.get(".");
    dir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE,
                StandardWatchEventKinds.ENTRY_MODIFY);
    
    WatchKey key;
    while ((key = watcher.take()) != null) {
        for (WatchEvent<?> event : key.pollEvents()) {
            System.out.println("Event kind:" + event.kind() + 
                               " Path : " + event.context());
        }
        key.reset();
    }
    

九、总结

Java IO体系涵盖传统BIO、NIO、AIO及多种工具类库,选择时需根据场景权衡:

  • 简单文件操作:优先使用java.nio.file.Files或Apache Commons IO。
  • 网络编程:低并发用Socket,高并发用Netty(基于NIO)。
  • 序列化:标准场景用Java序列化,高性能场景用Kryo或Protobuf。
  • 压缩:ZIP/GZIP用JDK原生类,复杂格式用Apache Commons Compress。

合理结合JDK原生类库与第三方工具,可大幅提升IO操作的效率和代码简洁性。

序列化类库

一、Java原生序列化(JDK内置)

1. 实现步骤
  1. 让类实现Serializable接口(标记接口,无需实现方法)。
  2. 使用ObjectOutputStream序列化对象
  3. 使用ObjectInputStream反序列化对象
2. 示例代码
import java.io.*;

// 1. 定义可序列化的类
class User implements Serializable {
    private static final long serialVersionUID = 1L; // 序列化版本号
    private String name;
    private int age;
    // transient字段不会被序列化
    private transient String password;

    public User(String name, int age, String password) {
        this.name = name;
        this.age = age;
        this.password = password;
    }

    // Getters and setters
    public String getName() { return name; }
    public int getAge() { return age; }
    public String getPassword() { return password; }
}

public class SerializationExample {
    public static void main(String[] args) {
        User user = new User("Alice", 30, "securePass");

        // 2. 序列化对象到文件
        try (ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("user.ser"))) {
            oos.writeObject(user);
            System.out.println("对象已序列化");
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 3. 从文件反序列化对象
        try (ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream("user.ser"))) {
            User deserializedUser = (User) ois.readObject();
            System.out.println("反序列化结果:");
            System.out.println("Name: " + deserializedUser.getName());
            System.out.println("Age: " + deserializedUser.getAge());
            System.out.println("Password: " + deserializedUser.getPassword()); // null(transient字段未被序列化)
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
3. 关键特性
  • serialVersionUID:版本控制,建议显式声明以避免反序列化时的InvalidClassException
  • transient关键字:标记不需要序列化的字段(如密码、临时数据)。
  • 自动处理引用关系:若对象包含其他对象的引用,这些对象也需实现Serializable

二、JSON序列化(Jackson/Gson)

1. Jackson示例(需添加依赖)
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.14.2</version>
</dependency>
import com.fasterxml.jackson.databind.ObjectMapper;

public class JacksonExample {
    public static void main(String[] args) throws Exception {
        User user = new User("Bob", 25, "pass123");
        ObjectMapper mapper = new ObjectMapper();

        // 1. 对象转JSON字符串
        String json = mapper.writeValueAsString(user);
        System.out.println("JSON: " + json);

        // 2. JSON字符串转对象
        User deserializedUser = mapper.readValue(json, User.class);
        System.out.println("Name: " + deserializedUser.getName());
    }
}
2. Gson示例(需添加依赖)
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.10.1</version>
</dependency>
import com.google.gson.Gson;

public class GsonExample {
    public static void main(String[] args) {
        User user = new User("Charlie", 35, "secure123");
        Gson gson = new Gson();

        // 1. 对象转JSON
        String json = gson.toJson(user);
        System.out.println("JSON: " + json);

        // 2. JSON转对象
        User deserializedUser = gson.fromJson(json, User.class);
        System.out.println("Age: " + deserializedUser.getAge());
    }
}

三、高性能序列化(Kryo)

1. 添加依赖
<dependency>
    <groupId>com.esotericsoftware</groupId>
    <artifactId>kryo</artifactId>
    <version>5.4.0</version>
</dependency>
2. 示例代码
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class KryoExample {
    public static void main(String[] args) {
        User user = new User("David", 40, "kryoPass");
        Kryo kryo = new Kryo();
        kryo.register(User.class);

        // 1. 序列化
        try (Output output = new Output(new FileOutputStream("user.kryo"))) {
            kryo.writeObject(output, user);
            System.out.println("Kryo序列化完成");
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 2. 反序列化
        try (Input input = new Input(new FileInputStream("user.kryo"))) {
            User deserializedUser = kryo.readObject(input, User.class);
            System.out.println("Name: " + deserializedUser.getName());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

四、Protobuf(Google Protocol Buffers)

1. 添加依赖
<dependency>
    <groupId>com.google.protobuf</groupId>
    <artifactId>protobuf-java</artifactId>
    <version>3.23.4</version>
</dependency>
2. 定义.proto文件
syntax = "proto3";

package example;

message User {
    string name = 1;
    int32 age = 2;
    string password = 3;
}
3. 生成Java类并使用
import com.google.protobuf.InvalidProtocolBufferException;

public class ProtobufExample {
    public static void main(String[] args) {
        // 1. 创建对象并序列化
        UserProto.User user = UserProto.User.newBuilder()
                .setName("Eve")
                .setAge(28)
                .setPassword("protoPass")
                .build();
        byte[] bytes = user.toByteArray();

        // 2. 反序列化
        try {
            UserProto.User deserializedUser = UserProto.User.parseFrom(bytes);
            System.out.println("Age: " + deserializedUser.getAge());
        } catch (InvalidProtocolBufferException e) {
            e.printStackTrace();
        }
    }
}

五、对比与选择建议

序列化方式 优点 缺点 适用场景
Java原生 无需额外依赖,支持复杂对象结构 性能较差,字节码大,兼容性差 简单内部系统,对象结构稳定
JSON(Jackson) 可读性强,跨语言支持,灵活 性能一般,字节码较大 前后端交互,REST API
Kryo 高性能,字节码小 不跨语言,需注册类 内部系统高性能场景(如缓存)
Protobuf 极致性能,跨语言,强兼容性 需要编写.proto文件,学习成本较高 分布式系统,微服务间通信

六、注意事项

  1. 版本兼容性:Java原生序列化需通过serialVersionUID控制版本。
  2. 安全性:避免反序列化不可信来源的数据(可能导致代码注入攻击)。
  3. 性能优化:对性能敏感的场景,优先选择Kryo或Protobuf。
  4. 字段控制:使用transient或Jackson的@JsonIgnore排除不需要序列化的字段。

根据场景选择合适的序列化方式,可显著提升系统的性能和可维护性。

你可能感兴趣的:(java)