„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
{
 //Create a stream to read bytes from a file
 FileInputStream inputStream = new FileInputStream("c:/data.txt");
 //Create a stream to write bytes to a file
 FileOutputStream outputStream = new FileOutputStream("c:/result.txt");

  byte[] buffer = new byte[1000];
 while (inputStream.available() > 0) //as long as there are unread bytes
 {
  //Read the next block of bytes into buffer, and store the actual number of bytes read in count.
  int count = inputStream.read(buffer);
  outputStream.write(buffer, 0, count); //Write a block (part of a block) to the second stream
 }

 inputStream.close(); //Close both streams. We don't need them any more.
 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.“