I am currently working on an English translation. If you like to help to proofread please contact me: ullenboom ät g m a i l dot c o m.

Java Videotraining Werbung

1. Exceptions

Unvorhergesehene Fehler können zu jeder Zeit auftreten. Unsere Programme müssen darauf vorbereitet sein und mit dieser Situation umgehen können. In den nächsten Aufgaben soll es darum gehen, Ausnahmen abzufangen, zu behandeln und selbst Probleme über Ausnahmen zu melden zu.

Voraussetzungen

  • Notwendigkeit für Ausnahmen verstehen

  • geprüfte und ungeprüfte Ausnahmen unterscheiden können

  • Ausnahmen mit try-catch abfangen können

  • Ausnahme-Weiterleitung mit throws kennen

  • Ausnahmen mit throw auslösen können

  • eigene Ausnahme-Klassen schreiben können

  • Ressourcen mit try-mit-Ressourcen schließen können

Verwendete Datentypen in diesem Kapitel:

1.1. Exception fangen

Geprüfte Ausnahmen müssen abgefangen oder nach oben an den Aufrufer weitergeleitet werden. Bei geprüften Ausnahmen zwingt uns der Compiler dazu, bei ungeprüften Ausnahmen gibt es diese Pflicht nicht — falls wir jedoch eine RuntimeException nicht behandeln, führt das zum Abbruch des ausführenden Threads. Daher empfiehlt es sich, auch ungeprüfte Ausnahmen immer abzufangen und zumindest zu loggen.

1.1.1. Die längste Zeile einer Datei ermitteln ⭐

Erfolgreiche Piraten müssen ein gutes Gedächtnis haben und Captain CiaoCiao möchte testen, ob alle blitzgescheit denken können. Er liest allen für einen Test eine Liste von Namen vor. Am Ende der Liste müssen alle den längsten Namen aufsagen können. Da Captain CiaoCiao aber zu sehr mit dem Vorlesen beschäftigt ist, soll eine Software am Ende den längsten Namen ausgeben.

Aufgabe:

  1. Die Datei http://tutego.de/download/family-names.txt enthält Familiennamen. Speichere die Datei lokal auf dem eigenen Dateisystem.

  2. Lege eine neue Klasse LongestLineInFile mit einer main(…​)-Methode an.

  3. Setze die Files.readAllLines(…​) in die main(…​)-Methode.

  4. Welche Ausnahme(n) muss/müssen aufgefangen werden?

  5. Was ist der längste Name (gemäß der String length()) in der Datei?

  6. Bonus: Welches sind die zwei längsten Namen in der Datei?

Eine Datei kann man in Java so einlesen:

String filename = ...
List<String> lines = Files.readAllLines( Paths.get( filename ) );

1.1.2. Ausnahmen ermitteln, Lachen am laufenden Band ⭐

Entwickler müssen im Blick haben, welche

  • Sprachkonstrukte,

  • Konstruktoren,

  • Methoden

Ausnahmen auslösen. Nur bei geprüften Ausnahmen gibt die IDE einen Hinweis, aber nicht zum Beispiel bei jedem Array-Zugriff, wo auch prinzipiell eine ArrayIndexOutOfBoundsException ausgelöst werden könnte.

Aufgabe:

  • Welche Ausnahmen müssen wir abfangen, wenn wir folgenden Block übersetzen möchten? Nutze nur die Javadoc, um das herauszufinden.

    Clip clip = AudioSystem.getClip();
    clip.open( AudioSystem.getAudioInputStream( new File("") ) );
    clip.start();
    TimeUnit.MICROSECONDS.sleep( clip.getMicrosecondLength() + 50 );
    clip.close();
  • Kann/sollte man Ausnahmen zusammenfassen?

  • Optionale Erweiterung: Finde ein paar Lacher-Dateien im InternetInternet (unter https:// soundbible.com/tags-laugh.html gibt es zum Beispiel freie WAV-Dateien). Speichere die WAV-Dateien lokal. Spiele in einer Endlosschleife zufällige Lacher hintereinander ab.

1.1.3. String-Array in int-Array konvertieren und nachsichtig bei Nichtzahlen sein ⭐

Die Methode Integer.parseInt(String) konvertiert einen String in eine Ganzzahl vom Typ int und löst eine NumberFormatException aus, wenn keine Konvertierung möglich ist, etwa bei Integer.parseInt("0x10") oder Integer.parseInt(null). Die Java-Bibliothek bietet keine Methode zur Konvertierung eines String-Arrays mit Zahlen in ein int-Array.

Aufgabe:

  • Schreibe eine neue Methode static int[] parseInts(String... numbers), die alle gegebenen Strings in Ganzzahlen konvertiert.

  • Die Anzahl der übergebenen Strings bestimmt die Größe des Rückgabe-Arrays.

  • Wenn ein String im Array an einer Stelle nicht konvertiert werden kann, kommt an die Stelle eine 0. null als Argument in der Übergabe ist erlaubt und führt zu 0.

  • Der Aufruf von parseInts() ohne Argumente ist in Ordnung, aber parseInts(null) muss zu einer Ausnahme führen.

Beispiel:

String[] strings = { "1", "234", "333" };
int[] ints1 = parseInts( strings );                                // [1, 234, 333]
int[] ints2 = parseInts( "1", "234", "333" );                      // [1, 234, 333]
int[] ints3 = parseInts( "1", "ll234", "3", null, "99" );          // [1, 0, 3, 0, 99]
int[] ints4 = parseInts( "Person", "Woman", "Man", "Camera, TV" ); // [0, 0, 0, 0, 0]

1.2. Eigene Ausnahmen auslösen

Ausnahmen haben ihren Ursprung in:

  • falschen Nutzung gewisser Sprachkonstrukte, wie z. B. der Ganzzahldivision durch 0, der Dereferenzierung über eine null-Referenz, einer unmöglichen Typanpassung bei Objekten, fehlerhaftem Array-Zugriff usw.

  • explizit erzeugten Ausnahmen durch das Schlüsselwort throw.

Hinter throw steht eine Referenz auf ein Ausnahmeobjekt. Dieses Objekt wird im Regelfall mit new neu aufgebaut. Die Typen stammen entweder aus Bibliotheken, wie z. B. IOException, es können aber auch eigene Ausnahmeklassen sein, die von Throwable abgeleitet werden müssen, im Regelfall aber Unterklassen von Exception sind.

1.3. Eigene Ausnahmeklassen schreiben

Die Java SE bietet eine große Anzahl von Ausnahmetypen, doch sie sind in der Regel technologieabhängig, wenn es zum Beispiel eine Zeitüberschreitung im Netzwerk gibt oder der SQL-Befehl falsch ist. Das ist für Low-level-Funktionalität in Ordnung, doch Software ist in Schichten aufgebaut, und in der äußersten Schicht geht es vielmehr um die Konsequenz diese Low-Level-Events: Es gab eine IOException → Konfiguration konnte nicht geladen werden; es gab eine SQLException → Kundendaten konnten nicht aktualisiert werden usw. Diese Ausnahmen werden durch neue semantische Exception-Klassen modelliert.

1.3.1. »Watt ist unmöglich« mit eigener Ausnahme anzeigen ⭐

Elektrogeräte ohne Leistungsaufnahme gibt es nicht, genau wie negative Wattwerte.

Aufgabe:

  • Erstelle eine eigene Ausnahmeklasse IllegalWattException. Leite die Klasse von RuntimeException ab.

  • Die Ausnahme soll immer dann ausgelöst werden, wenn bei setWatt(watt) die Wattzahl kleiner oder gleich Null ist.

  • Teste das Auftreten der Ausnahme durch Auffangen.

1.4. try-mit-Ressourcen

Eine wichtige Regel ist: Wer etwas aufmacht, macht es auch wieder zu. Schnell wird das vergessen, und dann gibt es nicht geschlossene Ressourcen, die zu Problemen führen können. Dazu zählen Datenverlust und Speicherprobleme. Damit Entwickler Ressourcen möglichst einfach schließen können, gibt es eine besondere Schnittstelle AutoCloseable und ein Sprachkonstrukt, die das Schließen kurz und einfach machen. Das spart Codezeilen ein und hilft, Fehler zu vermeiden, wenn zum Beispiel auch beim Schließen selbst wieder Ausnahmen ausgelöst werden.

1.4.1. Aktuelles Datum in Datei schreiben ⭐

Ein java.io.PrintWriter ist eine einfache Klasse zum Schreiben von Textdokumenten und kann auch direkt in Dateien schreiben.

Aufgabe:

  • Studiere die Javadoc von PrintWriter.

  • Finde heraus, wie man einen PrintWriter mit einer Ausgabedatei verbindet. Die Zeichenkodierung soll die der Plattform sein.

  • Schließe den PrintWriter korrekt mit try-mit-Ressourcen.

  • Das Java-Programm soll die String-Repräsentation von LocalDateTime.now() in die Textdatei schreiben.

1.4.2. Noten einlesen und in eine neue ABC-Datei schreiben ⭐⭐

Der berühmte Komponist Amadeus van Barsch bespricht am Telefon seine neusten Werke. Captain CiaoCiao ist ein großer Fan vom Komponisten und besorgt sich heimlich einen Audiomitschnitt, der als transkribierte Textdatei geliefert wird. Sie enthält alle Noten untereinander, wie:

C
D
C

Die Aufgabe gliedert sich in zwei Teile.

Aufgabenteil A, aus Datei lesen:

  • java.util.Scanner ist eine einfache Klasse zum Lesen und Verarbeiten von Textressourcen. Studiere in der Javadoc die Konstruktoren.

  • Im Konstruktor Scanner lassen sich diverse Quellen angeben, unter anderem Path; öffne einen Scanner, und übergib im Konstruktor über Paths.get("file.txt") eine Datei. Schließe den Scanner korrekt mit try-mit-Ressourcen.

  • Die Methoden hasNextLine() und nextLine() sind von besonderem Interesse. Lies eine Textdatei zeilenweise ein, und gib alle Zeilen auf der Konsole aus. Wenn zum Beispiel die Eingabedatei die Zeilen

    C,
    d
    d'

    enthält, ist die Ausgabe auf dem Bildschirm

    C, d d'
  • Erweitere das Programm so, dass nur die Zeilen mit Inhalt berücksichtigt werden. Wenn die Datei zum Beispiel eine Leerzeile enthält oder eine Zeile mit ausschließlich Weißraum, also Leerzeichen oder Tabulatorzeichen, wird sie nicht ausgegeben. Beispiel:

    C,
    
    
    d

    führt zu

    C, d
  • Nur gültige Noten dürfen erkannt werden, das sind

    C, D, E, F, G, A, B, C D E F G A B c d e f g a b c' d' e' f' g' a' b'

    Bedenke, dass das Komma an den Großbuchstaben kein Trenner, sondern ein Teil der Notenangabe ist, wie auch das Hochkomma an den Kleinbuchstaben der letzten Oktave. Barsch verwendet die internationale Schreibweise, schreibt also b statt h.

Captain CiaoCiao möchte sich die neue Komposition anhören und auf einem Notenblatt sehen. Hier bietet sich die Notation ABC an; eine Datei mit Noten von C, bis b' sieht so aus:

M:C
L:1/4
K:C
C, D, E, F, G, A, B, C D E F G A B c d e f g a b c' d' e' f' g' a' b'

Unter https://www.abcjs.net/abcjs-editor.html kann man die ABC-Datei anzeigen und sogar vorspielen lassen.

Image 290520 033932.511
Abbildung 1. Notenanzeige von https://www.abcjs.net/abcjs-editor.html

Die Grundidee vom Algorithmus ist die folgende: wir gehen mithilfe der Klasse Scanner zeilenweise durch die Datei und prüfen, ob der Inhalt der Zeile, die Note, in einer Datenstruktur vorkommt, die wir zur Validierung einsetzen. Handelt es sich um eine gültige Note, schreiben wir sie in das gewünschte Ausgabeformat.

Das Programm zum Einlesen der Noten soll ergänzt werden um einen Schreibanteil.

Aufgabenteil B, in Datei schreiben:

  • Öffne mit dem PrintWriter eine zweite Datei zum Schreiben; im Konstruktor kann man einen Dateinamen direkt übergeben. Achtung: Wähle einen anderen Dateinamen als die Quelldatei, sonst wird die Datei überschrieben!

  • Lies die Datei aus dem ersten Aufgabenteil weiterhin ein, aber schreibe die Ausgabe nicht mehr auf die Konsole, sondern in die neue Datei, sodass eine gültige ABC-Datei entsteht. Der PrintWriter bietet die von System.out bekannten Methoden print(String) und println(String).

  • Hinweis: Beide Ressourcen können (und sollten auch) in einem gemeinsamen try-mit-Ressourcen stehen.