HOWTO · Java
Gepufferte Java-I/O-Ausgabe mit `flush()` weitergeben
Erfahre, wie `flush()` gepufferte Java-I/O-Ausgabe an die nächste Schicht weitergibt.
Auf dieser Seite
Java-Entwickler verwenden flush(), um gepufferte Ausgaben an die nächste Ebene weiterzugeben, wenn sie verfügbar sein müssen, bevor das Ausgabeobjekt geschlossen wird. Die Methode gehört zu Java I/O (Writer und OutputStream), nicht zur Java Stream API für die Verarbeitung von Sequenzen. Der Aufruf garantiert weder, dass eine Datei dauerhaft auf dem Datenträger gespeichert ist, noch, dass ein entfernter Server die Bytes empfangen oder verarbeitet hat.
Was flush() in Java I/O bewirkt
Ausgabeklassen können Schreibvorgänge im Arbeitsspeicher sammeln, um Aufrufe des zugrunde liegenden Ziels zu reduzieren. Mit flush() wird das Ausgabeobjekt aufgefordert, seine ausstehenden Daten weiterzugeben. Bei einem BufferedOutputStream werden beispielsweise die noch ausstehenden Bytes des Wrappers an den umschlossenen Stream weitergereicht. Eine nachgelagerte Ebene kann unabhängig davon puffern; deshalb leert das Flushen eines Objekts nicht unbedingt jeden Puffer in der Kette. flush() gibt ausstehende Daten weiter und macht den belegten Platz im Puffer der betreffenden Ebene wiederverwendbar. Der Puffer selbst wird dadurch nicht freigegeben; auch die Zustellung an nachgelagerte Ebenen oder eine dauerhafte Speicherung ist damit nicht garantiert. flush() allein gewährleistet außerdem weder die Datenintegrität noch den Schutz vor beschädigter Ausgabe. Wenn Integrität wichtig ist, verwenden Sie die für Ihre Anwendung erforderlichen Prüfungen, etwa die Validierung einer Prüfsumme oder des Nachrichtenformats.
Bei der Zeichenausgabe führen Writer den entsprechenden Vorgang aus. Ein Writer kann Zeichen beim Weitergeben an einen untergeordneten Stream in Bytes codieren. Das genaue Pufferungsverhalten hängt von den Objekten in der Kette ab. flush() ist sinnvoll, wenn das Programm die Ausgabe jetzt weitergeben soll, statt auf einen späteren Schreibvorgang oder das Schließen zu warten.
Wann flush() aufgerufen werden sollte
Rufen Sie flush() an einem sinnvollen Übergabepunkt auf: etwa nachdem Sie eine Eingabeaufforderung ausgegeben haben und bevor Sie auf Benutzereingaben warten, nachdem Sie einen Protokolleintrag geschrieben haben, den ein anderer Prozess zeitnah lesen soll, oder nachdem Sie eine vollständige Nachricht über einen gepufferten Socket-Writer gesendet haben. Schreibt ein Programm viele kleine Teile in eine Datei oder ein anderes auf Durchsatz ausgelegtes Ziel, kann das Flushen nach jedem Teil die Vorteile der Pufferung zunichtemachen und zusätzlichen Aufwand verursachen. Wenn eine Ausgabe nicht schon zwischendurch sichtbar sein muss, überlassen Sie sie der normalen Pufferung und dem Schließen der Ressource.
flush() mit einem Writer verwenden
Dieses eigenständige Java-Beispiel schreibt über einen BufferedWriter und einen PrintWriter in einen StringWriter. Der ausgegebene Wert ist der Text, der nach dem expliziten Flush im Ziel verfügbar ist.
Führen Sie dieses Beispiel aus:
Der Code und die angezeigten Bezeichnungen oder Ausgaben in diesem Abschnitt bleiben auf Englisch.
import java.io.BufferedWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
public class FlushWriterExample {
public static void main(String[] args) throws java.io.IOException {
StringWriter destination = new StringWriter();
try (PrintWriter writer = new PrintWriter(new BufferedWriter(destination))) {
writer.println("status=ready");
writer.flush();
System.out.print(destination.toString());
}
}
}
status=ready
Der BufferedWriter macht die Pufferebene sichtbar. Das Flushen des äußeren PrintWriter gibt seine Zeichen über diesen Writer an den StringWriter weiter. Anschließend zeigt die Ausgabe des Ziels die entstandene Zeile. Der StringWriter dient als speicherinternes Ziel für eine kompakte Demonstration; bei einem Datei- oder Socket-Writer verhalten sich nachgelagerte Ebenen anders.
flush() mit einem BufferedOutputStream verwenden
Bei der Byteausgabe flushen Sie den Puffer-Wrapper, wenn seine ausstehenden Bytes an den umschlossenen Stream weitergegeben werden sollen. Dieses eigenständige Beispiel verwendet ein speicherinternes Byte-Ziel und benötigt daher weder eine Datei noch einen Dienst.
Der Code und die angezeigten Bezeichnungen oder Ausgaben in diesem Abschnitt bleiben auf Englisch.
import java.io.ByteArrayOutputStream;
import java.io.BufferedOutputStream;
import java.nio.charset.StandardCharsets;
public class BufferedFlushExample {
public static void main(String[] args) throws java.io.IOException {
ByteArrayOutputStream destination = new ByteArrayOutputStream();
try (BufferedOutputStream output = new BufferedOutputStream(destination)) {
output.write("batch-42".getBytes(StandardCharsets.UTF_8));
output.flush();
System.out.println(new String(destination.toByteArray(), StandardCharsets.UTF_8));
}
}
}
batch-42
Hier gibt der Wrapper die codierten Bytes bei flush() an den speicherinternen Stream weiter. Bei einem Dateistream werden die Bytes an die Dateiebene des Betriebssystems übergeben; dadurch wird nicht zugesichert, dass sie bereits dauerhaft auf dem Speichermedium gespeichert sind. Für dauerhaft gespeicherte Dateidaten verwenden Sie die passende Datei-API und die für Ihre Anwendung geeigneten Optionen zur Sicherung der Dauerhaftigkeit.
flush() bei der Netzwerkausgabe verwenden
Wenn Sie eine Anfrage oder Nachricht über einen gepufferten Socket-Writer schreiben und die Gegenstelle darauf wartet, rufen Sie nach der vollständigen Nachricht flush() auf. Zum Beispiel übergibt out.println("PING"); out.flush(); die Zeile vom Writer an den Ausgabepfad des Sockets. Das beweist nicht, dass die Bytes die Gegenstelle erreicht haben, dort gelesen oder verarbeitet wurden. Benötigt die Anwendung eine Bestätigung, verwenden Sie eine Protokollantwort oder eine Empfangsbestätigung und behandeln Sie Socketfehler und Zeitüberschreitungen im umgebenden Code. Ein Netzwerkbeispiel setzt eine verbundene Gegenstelle voraus; der kurze Ausdruck dient daher nur der Veranschaulichung und ist kein eigenständig ausführbares Programm.
Wie flush() mit dem Schließen eines Streams zusammenhängt
Beim Schließen eines Ausgabestreams oder Writers wird dieser in der Regel auch geflusht. Verwenden Sie Try-with-resources, damit die Ressource auch dann geschlossen wird, wenn die weitere Verarbeitung vorzeitig endet. Ein explizites Flushen vor dem Schließen ist unnötig, wenn direkt danach geschlossen wird und kein zwischenzeitlicher Leser die Daten benötigt. Es ist sinnvoll, wenn das Programm die Ausgabe sichtbar machen muss, während die Ressource geöffnet bleibt. Weder flush() noch das Schließen schützt vor sämtlichen Anwendungs- oder Systemfehlern. Behandeln Sie beides daher nicht als Ersatz für die von Ihrer Aufgabe benötigten Garantien zur dauerhaften Speicherung oder Empfangsbestätigung.