Unsere Datenschutzrichtlinie wird in Kürze aktualisiert. Bitte sehen Sie sich die Vorschau an.

Java Grundkurs 3: Generische Programmierung, Datenströme, Datumsrechnung

Ein Binärdatei mit Byte Stream kopieren

Testen Sie unsere 2017 Kurse

10 Tage kostenlos!

Jetzt testen Alle Abonnements anzeigen
Lassen Sie sich in diesem Video zeigen, wie Sie auf der Low-Level-Ebene eine Binärdatei kopieren können.

Transkript

In diesem Video geht es nun weiter mit Ein- und Ausgabeströmen, mit denen wir auf der Low Level Ebene ja eine Datei Byte für Byte kopieren möchten. Wir machen also genau an der Stelle weiter, wo wir eine relative Pfad-Angabe für den Dateizugriff gesetzt hatten Bei dem Arbeitsverzeichnis handelt es sich normalerweise um das Verzeichnis, von dem aus die Laufzeitumgebung aufgerufen wurde. Zumindest ist das so, wenn man Java von der Konsole aus aufruft. Aber innerhalb von Eclipse ist das anders, weil Eclipse das Arbeitsverzeichnis bewusst auf dieses Projektverzeichnis hier setzt. Das macht ja auch Sinn. Daher wird meine Datei auf diese Weise hier gefunden. Zu der Klasse File muss man allerdings wissen, dass sie genauso wie die Klassen InputStream, OutputStream und auch FileInputStream von je her, also seit Java 1.0 Bestandteil der klassischen IO API gewesen ist Aber bei der Klasse File, da ist etwas Besonderes, denn die ist sagen wir mal veraltet. Denn obwohl die Klasse File nicht mit der Annotation Deprecated markiert worden ist, betrachtet man das so. Ich zeige Ihnen dieses alte Relikt nur, weil Sie es in alten Quelltexten womöglich noch vorfinden werden. Neuere Quelltext sollten hingegen mit der neuen NIO2 API programmiert werden, bei der die Klasse File von der Klasse Path ersetzt worden ist. Aber diese behandeln wir erst später, weshalb zeigen ich Ihnen noch eine Variante , bei der Sie überhaupt keine Repräsentation der Datei im Quelltext benötigen. Weil Sie den Konstruktor der Klasse FileInputStream direkt mit der Zeichenkette des Pfades aufrufen können. Und zwar kann ich die Zeichenkette java.jpg rauschneiden, aus dem Konstruktor der Klasse file und kann sie im Konstruktor von FileInputStream einfügen und dann kann ich auf diesen Ausdruck hier oben verzichten. So und auch den import den brauch ich dann nicht mehr. So das funktioniert also genauso. Dann kommen wir zum nächsten Punkt. Die Klasse FileInputStream, die löst hier eine FileNotFoundException aus. Deshalb muss ich diesen Ausdruck in einen Try Catch Block einfassen. Ich kann mir auch von Eclipse helfen lassen und mit der Maus über die Klasse wandern und dann erscheint dieses Kontextmenü und hier in diesem Kontextmenü wähle ich den Eintrag Surround with try/catch aus. Ja und die Initialisierung des OutputStream wäre dann das Nächste. Ich kann also den OutputStream auch in dem Try Block initialisieren. Und das sollde das auch tun, weil der auch eine FileNotFoundException ausloest. FileOutputStream. Hier sehen Sie, dass ich auch hier wieder mehrere Möglichkeiten habe, wie ich den Konstruktor aufrufen kann. Ich wähle auch hier wieder die Möglichkeit einen String zu übergeben. Und auch dort kann ich einen relativen oder absoluten Pfad angeben und ich wähle den relativen Pfad und schreibe hier noch rein copy.jpg. Gut das speichere ich mal ab. Und nun kann ich mich endlich um das Kopieren kümmern. Mit der Methode read der Klasse InputStream lese ich ja die Daten Byte für Byte aus und das werde ich jetzt auch mal so ausprobieren. in. und dann read und ich wähle den Konstruktor ohne Parameter und das wird mir genau einen Byte einlesen. Jetzt sehen Sie, dass mir die Methode read auch eine Exception auslöst und zwar eine IOException und das ist eine Vater Klasse der FileNotFoundException. Deshalb kann ich dort an dieser Stelle eine IOException. Also aus der FileNotFoundException eine IOException wandeln und somit hätte ich dann beide Exceptiones abgefangen. So und jetzt geht es weiter. Und zwar liefert mir die Methode read genau einen Byte. Und ein Byte kann ja nur maximal den Wert 255 haben aber dennoch handelt es sich hierbei um einen int Wert. Ich muss also eine int Variable deklarieren. Und wenn ich jetzt dieses eine Byte in den Ausgabestrom reinschreiben wollte müsste ich sagen out.write und dort in den runden Klammern als Übergabeparameter das Byte c Übergeben. Das würde genau ein Byte übertragen. Wir wollen aber alle Bytes der Datei haben, deswegen formuliere ich mal eine Endlosschleife. Also while (true) und dann hätte ich hier eine Kopie erzeugt, von java.jpg zu copy.jpg. Allerdings ist das hier noch falsch, weil nämlich die Endlosschleife nie beendet werden würde Wir müssen uns also den Rückgabewert der Methode read holen, weil uns der Rückgabewert von -1 signalisiert, dass der Eingabestrom beendet worden ist. Also muss ich die Variable c außerhalb der While Schleife deklarieren, und natürlich muss ich sie initialisieren, weil es eine lokale Variable ist. Und hier in der while Schleife lese ich dann das Byte ein und zwar immer jeweils ein neues. Und zwar so lange, bis der Wert -1 erreicht ist. So und jetzt würde unser Programm nach der Kopieraktion beendet werden. Aber etwas fehlt noch und zwar ist das der Aufruf der Methode close und zwar beim Eingabestrom, als auch beim Ausgabestrom. Denn es ist so, dass unser Java Programm mit der Methode write die Daten in die Datei schreibt, die Datei aber per Default noch nicht endgültig zur Verfügung steht Erst wenn der Datenstrom wieder geschlossen wird, kann die kopierte Datei auch tatsächlich verwendet werden. Weil der Datenstrom offenbleibt, lässt sich die Methode write nämlich gegebenenfalls mehrmals aufrufen. Der Vorteil zeigt sich in Situationen, in denen man Daten nachschieben will. Aber durch den Aufruf der Methode close wird dieser Datenstrom geschlossen. Wundern Sie sich aber nicht, wenn Sie unser kleines Programm ohne die die Methode close ablaufen lassen und es dennoch funktioniert. Das liegt daran, dass zum Ende des Java Programms die abhängigen Ressourcen automatisch abgekappt werden. Der manuelle Aufruf der Methode close könnte in vielen Fällen also überflüssig sein. Aber dies würde dennoch unsauber programmiert und wird von Oracle auch nicht empfohlen. Denn in der Regel handelt es sich beim Java Programm ja um ein großes Programm, bei dem der Einsatz eines Datenstroms ja nur eine Untereinheit bildet. Und deshalb sollte man sich an die manuelle Schließung eines Datenstroms mit Hilfe von close gewöhnen. Denn viel zu groß ist die Gefahr, dass dies in einem normalen, also größeren Java Programm vergessen wird. Und nicht nur das. In unserem Programm könnte es passieren, dass beim Schreiben der Datei ein Fehler auftritt, beispielsweise weil der Speicher voll ist. In diesem Fall würde also das Schließen von in.close und out.close an dieser Stelle eventuell nie erreicht werden. Dann würde die Ressource nicht freigegeben werden. Für unser kleines Programm ist es kein Problem. Aber wenn unsere Kopierfunktion nur ein kleines Modul innerhalb eines größeren Programms wäre, dann würde das große Programm fortgesetzt und der Ausgabestrom würde nicht geschlossen werden. Denn die Anweisung würde übersprungen werden Deshalb muss ich dafür sorgen, dass die Methode close in jedem Fall aufgerufen wird, also auch im Fehlerfall. Und hierfür kann ich sorgen, indem ich die Methode close in einen Finally Block setze. Und das gleiche mache ich auch in unserem Programmierbeispiel. Ich werde hier unten einen finally Block einbauen und dort die Methoden aufrufen. Ich schneide sie mal von hier oben raus und füge sie hier unten ein. So der Kommentar kann auch weg. Und jetzt sehen Sie, dass wir hier auch wieder eine Exception auslösen. Und dies löse ich in unserem kleinen Beispiel auf, indem ich einfach eine Close Anweisung hier oben bei dem Constructor hinsetze und auch hier, dann sind wir das Ganze umgangen, denn normalerweise müsste ich mich darum kümmern, was hiermit ist. Und hier sehen Sie jetzt noch ein weiteres Problem. in.close und out.close können wir nicht aufrufen, weil die Variablen nicht initialisiert worden sind. Ich muss also hingehen und InputStream in = null schreiben und auch hier, so und immer noch nicht sind wir fertig. Ich muss nicht jetzt auch noch darum kümmern beziehungsweise überprüfen, ob die Variablen In und Out im Finally Block immer noch auf null verweisen, denn eine nullPointException möchte ich in meinem Programm nicht belassen. Also schreibe ich dort rein, in den Finally Block, if( in != null) dann soll in.close() aufgerufen werden. Und genauso bei dem Ausgabestrom. (out != null) Auch das ist sehr wichtig So und nun sind wir mit unserem Programm fertig und können es ausprobieren. Ich habe es ausgeführt und kann über einen Refresh schauen, ob die Datei da ist. Und Sie sehen, die copy.jpg ist kopiert worden. In diesem Video habe ich das Kopierbeispiel zu Ende programmiert. Dabei habe ich Ihnen gezeigt, wie aufwendig die Programmierung mit der Low Level API ist. All das lässt sich aber mit der High Level API, die wir uns später noch anschauen werden, umgehen.

Java Grundkurs 3: Generische Programmierung, Datenströme, Datumsrechnung

Steigen Sie tiefer in die Java-Programierung ein und lernen Sie den Umgang mit generischen Typen, Lamda-Ausdrücken, DAtenströmen und mit Datums- und Zeitberechnung.

5 Std. 24 min (47 Videos)
Derzeit sind keine Feedbacks vorhanden...
 
Hersteller:
Software:
Exklusiv für Abo-Kunden
Erscheinungsdatum:09.10.2016

Dieser Online-Kurs ist als Download und als Streaming-Video verfügbar. Die gute Nachricht: Sie müssen sich nicht entscheiden - sobald Sie das Training erwerben, erhalten Sie Zugang zu beiden Optionen!

Der Download ermöglicht Ihnen die Offline-Nutzung des Trainings und bietet die Vorteile einer benutzerfreundlichen Abspielumgebung. Wenn Sie an verschiedenen Computern arbeiten, oder nicht den ganzen Kurs auf einmal herunterladen möchten, loggen Sie sich auf dieser Seite ein, um alle Videos des Trainings als Streaming-Video anzusehen.

Wir hoffen, dass Sie viel Freude und Erfolg mit diesem Video-Training haben werden. Falls Sie irgendwelche Fragen haben, zögern Sie nicht uns zu kontaktieren!