{"id":3595,"date":"2017-02-19T03:16:50","date_gmt":"2017-02-19T01:16:50","guid":{"rendered":"http:\/\/www.tutego.de\/blog\/javainsel\/?p=3595"},"modified":"2017-02-19T03:16:50","modified_gmt":"2017-02-19T01:16:50","slug":"die-klassen-outputstream-und-inputstream","status":"publish","type":"post","link":"https:\/\/www.tutego.de\/blog\/javainsel\/2017\/02\/die-klassen-outputstream-und-inputstream\/","title":{"rendered":"Die Klassen OutputStream und InputStream"},"content":{"rendered":"<h3>Die abstrakte Basisklasse OutputStream<\/h3>\n<p>Wenn wir uns den OutputStream anschauen, dann sehen wir auf den ersten Blick, dass hier alle wesentlichen Operationen rund um das Schreiben versammelt sind. Der Clou bei allen Datenstr\u00f6men ist, dass spezielle Unterklassen wissen, wie sie genau die vorgeschriebene Funktionalit\u00e4t implementieren. Das hei\u00dft, dass ein konkreter Datenstrom, der in Dateien oder in eine Netzwerkverbindung schreibt, wei\u00df, wie Bytes in Dateien oder ins Netzwerk kommen. Und an der Stelle ist Java mit seiner Plattformunabh\u00e4ngigkeit am Ende, denn auf einer so tiefen Ebene k\u00f6nnen nur native Methoden die Bytes schreiben.<\/p>\n<p>abstract\u00a0class\u00a0java.io.OutputStream<br \/>\nimplements\u00a0Closeable,\u00a0Flushable<\/p>\n<ul>\n<li>abstractvoidwrite(intb)throwsIOException<br \/>\nSchreibt ein einzelnes Byte in den Datenstrom.<\/li>\n<li>voidwrite(byte[]b)throwsIOException<br \/>\nSchreibt die Bytes aus dem Array in den Strom.<\/li>\n<li>voidwrite(byte[]b,intoff,intlen)throwsIOException<br \/>\nSchreibt Teile des Byte-Feldes, n\u00e4mlich len Byte ab der Position off, in den Ausgabestrom.<\/li>\n<li>voidclose()throwsIOException<br \/>\nSchlie\u00dft den Datenstrom. Einzige Methode aus Closeable.<\/li>\n<li>voidflush()throwsIOException<br \/>\nSchreibt noch im Puffer gehaltene Daten. Einzige Methode aus der Schnittstelle Flushable.<\/li>\n<\/ul>\n<p>Die IOException ist keine RuntimeException und muss behandelt werden.<\/p>\n<p>Beispiel:\u00a0Die Klasse ByteArrayOutputStream ist eine Unterklasse von OutputStream und speichert intern alle Daten in einem byte-Array. Schreiben wir ein paar Daten mit den drei gegeben Methoden hinein:<\/p>\n<pre>byte[] bytes = { 'O', 'N', 'A', 'L', 'D' };\n\n\/\/\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 0\u00a0\u00a0\u00a0 1\u00a0\u00a0\u00a0 2\u00a0\u00a0\u00a0 3\u00a0\u00a0\u00a0 4\n\nByteArrayOutputStream out = new ByteArrayOutputStream();\n\ntry {\n\n\u00a0 out.<strong>write<\/strong>( 'D' );\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ schreibe D\n\n\u00a0 out.<strong>write<\/strong>( bytes );\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ schreibe ONALD\n\n\u00a0 out.<strong>write<\/strong>( bytes, 1, 2 );\u00a0 \/\/ schreibe NA\n\n\u00a0 System.out.println( out.toString( StandardCharsets.ISO_8859_1.name() )\u00a0 );\n\n}\n\ncatch ( IOException e ) {\n\n\u00a0 e.printStackTrace();\n\n}<\/pre>\n<h4>\u00dcber konkrete und abstrakte Methoden *<\/h4>\n<p>Zwei Eigenschaften lassen sich an den Methoden vom OutputStream ablesen: zum einen, dass nur Bytes geschrieben werden, und zum anderen, dass nicht wirklich alle Methoden abstract sind und von Unterklassen f\u00fcr konkrete Ausgabestr\u00f6me \u00fcberschrieben werden m\u00fcssen. Nur write(int) ist abstrakt und elementar. Das ist trickreich, denn tats\u00e4chlich lassen sich die Methoden, die ein Byte-Array schreiben, auf die Methode abbilden, die ein einzelnes Byte schreibt. Wir werfen einen Blick in den Quellcode der Bibliothek:<\/p>\n<p>java\/lang\/OutputStream,java,\u00a0Ausschnitt<\/p>\n<pre>public\u00a0abstract\u00a0void\u00a0write(int\u00a0b)\u00a0throws\u00a0IOException;\n \n public\u00a0void\u00a0write(byte[]\u00a0b)\u00a0throws\u00a0IOException\u00a0{\n \u00a0\u00a0write(b,\u00a00,\u00a0b.length);\n }\n \n public\u00a0void\u00a0write(byte\u00a0b[],\u00a0int\u00a0off,\u00a0int\u00a0len)\u00a0throws\u00a0IOException\u00a0{\n \u00a0\u00a0if\u00a0(b\u00a0==\u00a0null)\u00a0{\n \u00a0\u00a0\u00a0\u00a0throw\u00a0new\u00a0NullPointerException();\n \u00a0\u00a0}\u00a0else\u00a0if\u00a0((off\u00a0&lt;\u00a00)\u00a0||\u00a0(off\u00a0&gt;\u00a0b.length)\u00a0||\u00a0(len\u00a0&lt;\u00a00)\u00a0||\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0((off\u00a0+\u00a0len)\u00a0&gt;\u00a0b.length)\u00a0||\u00a0((off\u00a0+\u00a0len)\u00a0&lt;\u00a00))\u00a0{\n \u00a0\u00a0\u00a0\u00a0throw\u00a0new\u00a0IndexOutOfBoundsException();\n \u00a0\u00a0}\u00a0else\u00a0if\u00a0(len\u00a0==\u00a00)\u00a0{\n \u00a0\u00a0\u00a0\u00a0return;\n \u00a0\u00a0}\n \u00a0\u00a0for\u00a0(int\u00a0i\u00a0=\u00a00\u00a0;\u00a0i\u00a0&lt;\u00a0len\u00a0;\u00a0i++)\u00a0{\n \u00a0\u00a0\u00a0\u00a0write(b[off\u00a0+\u00a0i]);\n \u00a0\u00a0}\n }<\/pre>\n<p>An beiden Implementierungen ist zu erkennen, dass sie die Arbeit sehr bequem an andere Methoden verschieben. Doch diese Implementierung ist nicht optimal! Stellen wir uns vor, ein Dateiausgabestrom \u00fcberschreibt nur die eine abstrakte Methode, die n\u00f6tig ist. Und nehmen wir weiterhin an, dass unser Programm nun immer ganze Byte-Felder schreibt, etwa eine 5-MiB-Datei, die im Speicher steht. Dann werden f\u00fcr jedes Byte im Byte-Array in einer Schleife alle Bytes der Reihe nach an eine vermutlich native Methode \u00fcbergeben. Wenn es so implementiert w\u00e4re, k\u00f6nnten wir die Geschwindigkeit des Mediums \u00fcberhaupt nicht nutzen, zumal jedes Dateisystem Funktionen bereitstellt, mit denen sich ganze Bl\u00f6cke \u00fcbertragen lassen. Gl\u00fcck-licherweise sieht die Implementierung nicht so aus, da wir in dem Modell vergessen haben, dass die Unterklasse zwar die abstrakte Methode implementieren muss, aber immer noch andere Methoden \u00fcberschreiben kann. Ein Blick auf die Unterklasse FileOutputStream best\u00e4tigt dies.<\/p>\n<p>Hinweis:\u00a0Ruft eine Oberklasse eine abstrakte Methode auf, die sp\u00e4tger die Unterklasse implementiert, ist das ein Entwurfsmuster mit dem Namen Schablonenmuster oder auf Englisch template pattern.<\/p>\n<p>Gleichzeitig stellt sich die Frage, wie ein OutputStream, der die Eigenschaften f\u00fcr alle erdenklichen Ausgabestr\u00f6me vorschreibt, wissen kann, wie ein spezieller Ausgabestrom etwa mit close() geschlossen wird oder seine gepufferten Bytes mit flush() schreibt \u2013 die Methoden m\u00fcssten doch auch abstrakt sein! Das wei\u00df OutputStream nat\u00fcrlich nicht, aber die Entwickler haben sich dazu entschlossen, eine leere Implementierung anzugeben. Der Vorteil besteht darin, dass Unterklassen nicht verpflichtet werden, immer die Methoden zu \u00fcberschreiben, auch wenn sie sie gar nicht nutzen wollen.<\/p>\n<h3>Ein Datenschlucker *<\/h3>\n<p>Damit wir sehen k\u00f6nnen, wie alle Unterklassen prinzipiell mit OutputStream umgehen, wollen wir eine Klasse entwerfen, die alle ihre gesendeten Daten verwirft. Die Klasse ist mit dem Unix-Device \/dev\/null vergleichbar. Die Implementierung muss lediglich die abstrakte Methode write(int) \u00fcberschreiben.<\/p>\n<pre> public\u00a0final\u00a0class\u00a0NullOutputStream\u00a0extends\u00a0java.io.OutputStream\u00a0{\n\n\u00a0\u00a0@Override\u00a0public\u00a0void\u00a0write(\u00a0int\u00a0b\u00a0)\u00a0{\u00a0\/*\u00a0Empty\u00a0*\/\u00a0}\n }<\/pre>\n<p>Da close() und flush() ohnehin schon mit einem leeren Block implementiert sind, brauchen wir sie nicht noch einmal zu \u00fcberschreiben.<\/p>\n<h3>Die abstrakte Basisklasse InputStream<\/h3>\n<p>Das Gegenst\u00fcck zu OutputStream ist InputStream; jeder bin\u00e4re Eingabestrom wird durch die abstrakte Klasse InputStream repr\u00e4sentiert. Die Konsoleneingabe System.in ist vom Typ InputStream. Die Klasse bietet mehrere readXXX(\u2026)-Methoden und ist auch ein wenig komplexer als OutputStream.<\/p>\n<p>abstract\u00a0class\u00a0java.io.InputStream<br \/>\nimplements\u00a0Closeable<\/p>\n<ul>\n<li>intavailable()throwsIOException<br \/>\nGibt die Anzahl der verf\u00fcgbaren Zeichen im Datenstrom zur\u00fcck, die sofort ohne Blockierung gelesen werden k\u00f6nnen.<\/li>\n<li>abstractintread()throwsIOException<br \/>\nLiest ein Byte aus dem Datenstrom und liefert ihn zur\u00fcck. Die R\u00fcckgabe ist -1, wenn der Datenstrom keine Daten mehr liefern kann. Der R\u00fcckgabetyp ist int, weil -1 (0xFFFFFFFF) das Ende des Datenstroms anzeigt, und ein -1 als byte (das w\u00e4re 0xFF) nicht von einem normalen Datum unterschieden werden k\u00f6nnte.<\/li>\n<li>intread(byte[]b)throwsIOException<br \/>\nLiest bis zu length Bytes aus dem Datenstrom und setzt sie in das Array b. Die tats\u00e4chliche L\u00e4nge der gelesenen Bytes wird zur\u00fcckgegeben und muss nicht b.length sein, es k\u00f6nnen auch weniger Bytes gelesen werden. In der OutputStream einfach als return read(b, 0, b.length); implementiert.<\/li>\n<li>intread(byte[]b,intoff,intlen)throwsIOException<br \/>\nLiest den Datenstrom aus und setzt die Daten in das Byte-Array b, an der Stelle off Zudem begrenzt len die maximale Anzahl der zu lesenden Bytes. Intern ruft die Methode zun\u00e4chst read() auf und wenn es zu einer Ausnahme kommt, endet auch damit unsere Methode mit einer Ausnahme. Es folgenen wiederholten Aufrufe von read(), die dann enden, wenn read() die R\u00fcckgabe -1 liefert. Falls es zu einer Ausnahme kommt, wird diese aufgefangen und nicht gemeldet.<\/li>\n<li>intreadNBytes(byte[]b,intoff,intlen)throwsIOException<br \/>\nVersucht len viele Bytes aus dem Datenstrom zu lesen und in das Byte-Array zu setzen. Im Gegensatz zu read(byte[], int, int) \u00fcbernimmt readNBytes(\u2026) mehrere Anl\u00e4ufe len viele Daten zu beziehen. Dabei greift es auf read(byte[], int, int) zur\u00fcck. Neue Methode in Java 9.<\/li>\n<li>byte[] readAllBytes() throws IOException<br \/>\nLiest alle verbleibenden Daten aus den Datenstrom und liefert ein Array mit diesen Bytes aus R\u00fcckgabe. Neue Methode in Java 9.<\/li>\n<li>long transferTo(OutputStream out) throws IOException<br \/>\nLiest alle Bytes aus dem Datenstrom aus und schreibt sie in out. Wenn es zu einer Ausnahme kommt, wird empfohlen, die Datenstr\u00f6me beide zu schlie\u00dfen. Neue Methode in Java 9.<\/li>\n<li>longskip(longn)throwsIOException<br \/>\n\u00dcberspringt eine Anzahl von Zeichen. Die R\u00fcckgabe gibt die tats\u00e4chlich gesprungenen Bytes zur\u00fcck, was nicht mit n identisch sein muss.<\/li>\n<li>booleanmarkSupported()<br \/>\nGibt einen Wahrheitswert zur\u00fcck, der besagt, ob der Datenstrom das Merken und Zur\u00fccksetzen von Positionen gestattet. Diese Markierung ist ein Zeiger, der auf bestimmte Stellen in der Eingabedatei zeigen kann.<\/li>\n<li>voidmark(intreadlimit)<br \/>\nMerkt sich eine Position im Datenstrom.<\/li>\n<li>voidreset()throwsIOException<br \/>\nSpringt wieder zu der Position zur\u00fcck, die mit mark() gesetzt wurde.<\/li>\n<li>voidclose()throwsIOException<br \/>\nSchlie\u00dft den Datenstrom. Operation aus der Schnittstelle Closeable.<\/li>\n<\/ul>\n<p>Auff\u00e4llig ist, dass bis auf mark(int) und markSupported() alle Methoden im Fehlerfall eine IOException ausl\u00f6sen.<\/p>\n<p>Hinweis:\u00a0available() liefert die Anzahl der Bytes, die ohne Blockierung gelesen werden k\u00f6nnen. (\u201eBlockieren\u201c bedeutet, dass die Methode nicht sofort zur\u00fcckkehrt, sondern erst wartet, bis neue Daten vorhanden sind.) Die R\u00fcckgabe von available() sagt nichts dar\u00fcber aus, wie viele Zeichen der InputStream insgesamt hergibt. W\u00e4hrend aber bei FileInputStream die Methode available() \u00fcblicherweise doch die Dateil\u00e4nge liefert, ist dies bei den Netzwerk-Streams im Allgemeinen nicht der Fall.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Die abstrakte Basisklasse OutputStream Wenn wir uns den OutputStream anschauen, dann sehen wir auf den ersten Blick, dass hier alle wesentlichen Operationen rund um das Schreiben versammelt sind. Der Clou bei allen Datenstr\u00f6men ist, dass spezielle Unterklassen wissen, wie sie genau die vorgeschriebene Funktionalit\u00e4t implementieren. Das hei\u00dft, dass ein konkreter Datenstrom, der in Dateien oder [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":"","_links_to":"","_links_to_target":""},"categories":[1,11,85],"tags":[],"class_list":["post-3595","post","type-post","status-publish","format-standard","hentry","category-allgemein","category-insel","category-java-9"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/3595","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/comments?post=3595"}],"version-history":[{"count":6,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/3595\/revisions"}],"predecessor-version":[{"id":3601,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/3595\/revisions\/3601"}],"wp:attachment":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/media?parent=3595"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/categories?post=3595"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/tags?post=3595"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}