ZetCode

Java Flushable 接口

最后修改时间:2025 年 4 月 16 日

java.io.Flushable 接口代表可以刷新缓冲输出到底层目标的对象。它包含一个单一的方法 flush,该方法强制将任何缓冲输出写入。

实现 Flushable 的类包括各种输出流和写入器。该接口确保数据在发生关键操作时不会滞留在缓冲区中。刷新对于网络操作和交互式应用程序尤其重要。

Flushable 接口概述

Flushable 接口很简单,只包含一个方法。它的目的是提供一种强制输出写入的标准方法。许多 I/O 类实现此接口以支持缓冲区管理。

public interface Flushable {
    void flush() throws IOException;
}

以上代码显示了完整的 Flushable 接口定义。如果操作失败,flush 方法可能会抛出 IOException。实现类必须提供此方法的具体实现。

基本 Flushable 实现

此示例演示了一个实现 Flushable 的简单自定义类。该类维护一个内部缓冲区,并在请求时刷新它。这展示了可刷新对象的基本模式。

Main.java
import java.io.Flushable;
import java.io.IOException;

public class SimpleBuffer implements Flushable {
    private StringBuilder buffer = new StringBuilder();
    
    public void write(String data) {
        buffer.append(data);
    }
    
    @Override
    public void flush() throws IOException {
        System.out.println("Flushing buffer: " + buffer.toString());
        buffer.setLength(0); // Clear the buffer
    }
    
    public static void main(String[] args) {
        SimpleBuffer buffer = new SimpleBuffer();
        
        try {
            buffer.write("Hello");
            buffer.write(" World");
            buffer.flush();
            
            buffer.write("Second");
            buffer.write(" Flush");
            buffer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例创建一个实现 FlushableSimpleBuffer 类。flush 方法输出缓冲区内容并将其清除。main 方法演示了写入数据并刷新两次。每次刷新操作都会清空缓冲区。

刷新 FileOutputStream

FileOutputStream 实现 Flushable 以确保将文件数据写入磁盘。虽然操作系统最终会写入缓冲数据,但显式刷新可以保证立即写入。这对于关键操作至关重要。

Main.java
import java.io.FileOutputStream;
import java.io.IOException;

public class Main {
    public static void main(String[] args) {
        try (FileOutputStream fos = new FileOutputStream("output.txt")) {
            // Write some data
            fos.write("Important data line 1\n".getBytes());
            fos.flush(); // Ensure first line is written
            
            // More data
            fos.write("Critical data line 2\n".getBytes());
            
            // System crash simulation
            System.out.println("Pretend system crashes here...");
            
            // Without flush, line 2 might be lost
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例展示了刷新 FileOutputStream 的重要性。第一次写入立即刷新,而第二次则没有。在崩溃情况下,第一行是安全的,但第二行可能会丢失。刷新确保了关键数据的持久性。

BufferedWriter 和刷新

BufferedWriter 实现 Flushable 以将缓冲字符写入底层写入器。缓冲区提高了性能,但延迟了写入。刷新强制立即写入所有缓冲字符。

Main.java
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class Main {
    public static void main(String[] args) {
        try (BufferedWriter writer = 
                new BufferedWriter(new FileWriter("log.txt"))) {
            
            writer.write("Starting application\n");
            writer.flush(); // Ensure startup log is written
            
            // Simulate application work
            for (int i = 0; i < 5; i++) {
                writer.write("Processing item " + i + "\n");
                Thread.sleep(100); // Simulate work
            }
            
            writer.flush(); // Force write before more work
            
            // More processing
            writer.write("Application completed\n");
            
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

此示例演示了在关键点刷新 BufferedWriter。第一次刷新确保立即写入启动消息。第二次刷新在继续之前写入所有已处理的项。如果没有这些刷新,数据可能会在缓冲区中延迟。

PrintWriter 的自动刷新

PrintWriter 可以配置为在每次写入操作后自动刷新。这对于需要立即输出的交互式应用程序很有用。构造函数的 autoFlush 参数控制此行为。

Main.java
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class Main {
    public static void main(String[] args) {
        try (PrintWriter writer = new PrintWriter(
                new FileWriter("console.log"), true)) { // autoFlush=true
            
            // These will auto-flush after each println
            writer.println("Application started");
            writer.println("Loading configuration");
            
            // Simulate user interaction
            for (int i = 0; i < 3; i++) {
                writer.println("User action " + i);
                // Without auto-flush, these might be delayed
            }
            
            writer.println("Application exiting");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例创建了一个启用自动刷新的 PrintWriter。每个 println 都会触发自动刷新,确保立即写入。这非常适合日志记录或对延迟不可接受的交互式输出。第二个构造函数参数启用此行为。

刷新 Socket 的 OutputStream

网络流通常需要显式刷新以确保及时的数据传输。Socket 的输出流实现 Flushable 以达到此目的。如果没有刷新,小消息可能会因缓冲而延迟。

Main.java
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

public class Main {
    public static void main(String[] args) {
        try (Socket socket = new Socket("localhost", 8080);
             OutputStream out = socket.getOutputStream()) {
            
            // Send initial handshake
            out.write("HELLO\n".getBytes());
            out.flush(); // Ensure handshake is sent immediately
            
            // Send data messages
            for (int i = 0; i < 3; i++) {
                String message = "DATA " + i + "\n";
                out.write(message.getBytes());
                out.flush(); // Ensure each message is sent
                Thread.sleep(500); // Simulate processing
            }
            
            // Send goodbye
            out.write("BYE\n".getBytes());
            
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

此示例演示了在每条消息之后刷新套接字的输出流。网络通信通常会缓冲数据以优化传输。刷新确保每条消息立即发送,而不是延迟。这对于基于协议的通信至关重要。

刷新 ZipOutputStream

ZipOutputStream 实现 Flushable 以确保正确写入 ZIP 条目。在创建 ZIP 文件时,刷新尤其重要,以维护条目边界和元数据完整性。

Main.java
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class Main {
    public static void main(String[] args) {
        try (ZipOutputStream zos = 
                new ZipOutputStream(new FileOutputStream("archive.zip"))) {
            
            // Add first entry
            zos.putNextEntry(new ZipEntry("file1.txt"));
            zos.write("Content for file 1".getBytes());
            zos.flush(); // Ensure entry is complete
            
            // Add second entry
            zos.putNextEntry(new ZipEntry("file2.txt"));
            zos.write("Content for file 2".getBytes());
            zos.flush(); // Ensure second entry is complete
            
            // Final flush before closing
            zos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例显示了在每个条目之后刷新 ZipOutputStream。虽然关闭流也会刷新,但显式刷新确保在创建时正确写入每个条目。这可以防止在 ZIP 文件创建期间发生错误时损坏。

来源

Java Flushable 接口文档

在本文中,我们探讨了 Flushable 接口及其实现。正确的刷新对于可靠的 I/O 操作至关重要,尤其是在使用缓冲流和关键数据时。了解何时以及如何刷新流是构建健壮 Java 应用程序的关键。

作者

我的名字是 Jan Bodnar,我是一位经验丰富的程序员,在该领域拥有多年经验。我从 2007 年开始撰写编程文章,至今已撰写了 1,400 多篇文章和八本电子书。凭借超过八年的教学经验,我致力于分享我的知识,并帮助他人掌握编程概念。

列出所有Java教程