„Hallo Amigo! Heute werden wir uns noch einmal damit befassen, wie InputStream und OutputStream funktionieren. Die erste Erklärung war schon ein wenig vereinfachend. Dies sind keine Interfaces. Es handelt sich um abstrakte Klassen, und sie haben sogar einige implementierte Methoden. Sehen wir uns ihre Methoden einmal an:“

InputStream-Methoden Das tut die Methode
int read(byte[] buff);
Diese Methode liest sofort einen Block von Bytes in den Puffer (byte-Array), bis der Puffer voll ist oder bis die Quelle keine Bytes mehr zum Lesen hat.
Die Methode gibt die Anzahl der tatsächlich gelesenen Bytes zurück (die kleiner als die Länge des Arrays sein kann).
int read();
Diese Methode liest ein Byte und gibt es zurück. Das Ergebnis wird zu einem int erweitert. Wenn keine Bytes mehr zum Lesen vorhanden sind, gibt die Methode -1 zurück.
int available();
Diese Methode gibt die Anzahl der ungelesenen (verfügbaren) Bytes zurück.
void close();
Diese Methode „schließt“ den Datenstrom. Du rufst sie auf, wenn du die Arbeit mit dem Stream beendet hast.
Das Objekt führt dann die notwendigen Operationen aus, um die Datei zu schließen usw.
Zu diesem Zeitpunkt kannst du keine Daten mehr aus dem Stream lesen.

„Wir können also nicht nur einzelne Bytes lesen, sondern auch ganze Blöcke lesen?“

„Exakt.“

„Können wir auch ganze Blöcke schreiben?“

„Ja, sieh dir das an:“

OutputStream-Methoden Das tut die Methode
void write(int c);
Diese Methode schreibt ein Byte. Der Typ int wird auf ein Byte eingeschränkt. Der zusätzliche Teil wird einfach verworfen.
void write(byte[] buff);
Diese Methode schreibt einen Block von Bytes.
void write(byte[] buff, int from, int count);
Diese Methode schreibt einen Teil eines Blocks von Bytes. Sie wird in Fällen verwendet, in denen das Byte-Array möglicherweise nicht vollständig gefüllt ist.
void flush();
Wenn der Datenstrom intern noch nicht geschriebene Daten speichert, erzwingt diese Methode den Schreibvorgang.
void close();
Diese Methode „schließt“ den Datenstrom. Du rufst sie auf, wenn du die Arbeit mit dem Stream beendet hast.
Das Objekt führt dann die notwendigen Operationen aus, um die Datei zu schließen usw. Du kannst keine Daten mehr in den Stream schreiben, und flush wird automatisch aufgerufen.

„Wie würde der Code zum Kopieren von Dateien aussehen, wenn wir ganze Blöcke auf einmal lesen würden, statt einzelner Bytes?“

„Hmm. So etwa:“

Eine Datei auf die Festplatte kopieren
public static void main(String[] args) throws Exception
{
 //Erstelle einen Stream zum Lesen von Bytes aus einer Datei
 FileInputStream inputStream = new FileInputStream("c:/data.txt");
 //Erstelle einen Stream zum Schreiben von Bytes in eine Datei
 FileOutputStream outputStream = new FileOutputStream("c:/result.txt");

  byte[] buffer = new byte[1000];
 while (inputStream.available() > 0) //solange es ungelesene Bytes gibt
 {
  //Lies den nächsten Block von Bytes in buffer und speichere die tatsächliche Anzahl der gelesenen Bytes in count.
  int count = inputStream.read(buffer);
  outputStream.write(buffer, 0, count); //Block (Teil eines Blocks) in den zweiten Stream schreiben
 }

 inputStream.close(); //Beide Streams schließen Wir brauchen sie nicht mehr.
 outputStream.close();
}

„Das mit buffer verstehe ich ja, aber was hat es mit dieser count-Variable auf sich?“

„Wenn wir den letzten Datenblock aus einer Datei lesen, erhalten wir vielleicht z.B. 328 statt 1000 Bytes. Wenn wir also die Daten schreiben, müssen wir angeben, dass wir nicht den gesamten Block schreiben, sondern nur die ersten 328 Bytes.“

Wenn wir den letzten Block lesen, gibt die read-Methode die Anzahl der tatsächlich gelesenen Bytes zurück. 1000 jedes Mal, wenn wir einen Block lesen, mit Ausnahme des letzten Blocks, wo wir 328 erhalten.

Wenn wir also einen Block schreiben, geben wir an, dass nicht alle Bytes im Puffer geschrieben werden sollen, sondern nur 328 (d.h. der in der count-Variable gespeicherte Wert).

„Jetzt ist es mir klar. Danke, Ellie.“