494 Die Verwaltung wird um eine Objektvariable semester erweitert in der das aktuell betrachtete Semester drinsteht. 495 Der genau Prozess des Ladens und Speichern wird später genauer analysiert. Hier soll zunächst das Prinzip mit einer Realisierungsmöglichkeit erklärt werden. Parallel dazu wird der Umgang mit Exceptions beschrieben. Hinweis: Es werden hier 8-Bit-Versionen der Streams betrachtet (gibt ähnliche Klassen für Uni-Code, 16 Bit). 496 Das Schließen von Dateien ist ein sehr zentraler Schritt, da ein Vergessen dieser Aktion oftmals nicht direkt zu einem Fehler führt. Man spricht von einem schleichenden Fehler, der oft erst später gespürt wird, wenn eine Applikation deutlich langsamer läuft oder sich das Betriebssystem plötzlich weigert, eine bestimmte Aktionen, die sonst immer möglich sind, auszuführen. 497 Der Dateiname ist beliebig, die Endung aber sinnvoll, da eine syntaktische korrekte XML-Datei erzeugt wird. Die genaue Kenntnis von XML ist hier nicht notwendig, aber leicht erlernbar und sollte irgendwann im Studium genauer verstanden werden. 498 499 500 Die Idee der mehrfach geschachtelten Konstruktoren wird später erklärt, wichtig ist hier nur, dass ein Stream zu einer Datei geöffnet wird und so Informationen in die Datei geschrieben werden. 501 Anmerkung: Das Exception Handling wurde in Java 7 etwas vereinfacht. Da es hier um die Grundlagen geht, die in der präsentierten Form in anderen Programmiersprachen sehr ähnlich sind, wird die klassische Variante vorgestellt. Diese Variante ist natürlich auch in Java 7 nutzbar. 502 503 504 Die Zeilen "normalesProgramm" stehen für einfache Programmzeilen, die keine try-catch-Blöcke enthalten. Es sind beliebig viele catch-Blöcke möglich. Der finally-Block kann weggelassen werden, wenn er existiert, wird er immer garantiert ausgeführt. Der angedeutete Fall2 wird später behandelt, falls es keinen finally-Block gibt, wird die Exception direkt an die aufrufende Methode weitergegeben. 505 Durch die verwendete Klasse XMLEncoder werden alle mit writeObject(.) geschriebenen Objekte im XML-Format abgespeichert. Dies ist möglich, da die Bean-Eigenschaften genutzt werden, bei denen durch get- und set- ein einfaches Lesen und Schreiben möglich ist. XML ist eine sogenannte Auszeichnungssprache mit einfachen syntaktischen Regeln. Basierend auf diesen Regeln kann man eine Teilmenge von XMLDokumenten definieren, die zur Verwaltung bestimmter Daten genutzt werden können. XML ist dabei recht einfach maschinenlesbar, jede ordentliche Programmiersprache bietet Bibliotheken zur XML-Bearbeitung, und kann auch von Menschen gelesen und notfalls geschrieben werden, was häufig in Konfigurationsdateien genutzt wird. Das Thema XML wird in späteren Vorlesungen des Studiums vertieft. 506 Der Hintergrund des Problems ist es, dass BlueJ selbst als Java-Programm die Ausführung der Java-Programme steuert. Bei dieser Steuerung ist leider nicht sichergestellt, dass alle benutzten Klassen auch zur Nutzung mit XMLDecoder zur Verfügung stehen. Dies ist eine der wenigen Programmzeilen, die man nur hinschreiben, aber noch nicht verstehen muss. Wird das Java-Programm außerhalb von BlueJ genutzt, ist die Ergänzung überflüssig, schadet aber bei den vorgestellten Programmen auch nicht. 507 508 Man kann auch vollständig auf die Klasse File hier verzichten. Sie ermöglicht aber eine genauere Analyse des Fehlerfalls und kann deshalb hilfreich sein. 509 510 511 Die Warnung bezieht sich nur auf das Casten der Liste, da der genaue Typ der Liste, d. h. welche Objekte in ihr gespeichert sind, nicht explizit mit abgespeichert wird. (Längere Geschichte, da erst mit Java 5 die Möglichkeit entstand, den Typen der Listenelemente anzugeben) 512 513 514 Man beachte, dass bei diesem Beispiel die zu file gehörige Datei eventuell offen bleiben kann, da z. B. bei einer Exception in file.readObject() die Zeile file.close() nicht ausgeführt wird. Dieses Problem wird wenige Folien später gelöst. 515 516 Dies ist die Variante, die immer genutzt werden soll, wenn mit kritischen Ressourcen wie Dateien gearbeitet wird. Die Variable wird vor dem try deklariert, im try-Block genutzt und im finally-Block garantiert geschlossen. 517 Der Testfall zeigt, dass die Ausgangssituation mit zwei IMI- und MID-Studenten abgespeichert wird, danach zwei Studiengänge gelöscht und dann die gespeicherten Daten wieder eingelesen werden. Da keine Exception erwartet wird, steht im catch-Block ein "assertTrue(false)", was dazu führt, dass, wenn diese Anweisung erreicht wird, immer ein Fehler erzeugt wird. Generell gilt bei JUnit, dass nicht behandelte Exceptions wie Fehler behandelt werden und so zu einem erfolglosen Testfall führen. 518 Wieder wurde die Programmstelle, von der man erwartet, dass sie nicht erreicht wird, mit assertTrue(false) gekennzeichnet. Alternativ könnte hier auch fail() stehen, was auch nur bedeutet, dass die eigentlich nicht zu erreichende Programmzeile doch erreicht wurde. 519 Die Zeilen "normalesProgramm" stehen für einfache Programmzeilen, die keine try-catch-Blöcke enthalten. Tritt in machWas eine Exception vom Typ ExTyp2 auf, wird diese an die aufrufende Methode weitergeleitet. Da die Exception hier nicht gefangen wird, muss sie in der throws-Deklaration von rufAuf stehen. 520 521 522 523 Natürlich kann man auch andere Konstruktoren nutzen, Objektvariablen definieren und Methoden ergänzen. Dies kann z. B. Sinn machen, wenn man die Fehlerbehandlung weiter konkretisieren möchte. 524 525 526 Das Bild zeigt nur einen recht kleinen Ausschnitt aller bereits existierenden Klassen zur Fehlerbehandlung. Es fällt auf, dass es die noch allgemeinere Klasse Throwable als Grundlage gibt, wobei es üblich ist, eigene Exceptions von der Klasse Exception erben zu lassen und gegebenenfalls einen eigenen Exception-Vererbungsbaum aufzubauen. Alle von Error erbenden Klassen stehen für gravierende Fehler, die man in der eigenen Software nicht behandeln sollte, und teilweise gar nicht behandeln kann. Da jede Programmausführung Speicher benötigt, ist es unklar, ob auf "OutOfMemory" reagiert werden kann. Auch die von RuntimeException erbenden Klassen stehen meist für größere Fehler, können aber gegebenenfalls wie eine IllegalArgumentException abgefangen werden. Für die bisher genannten Klassen gilt, dass sie nicht in einer throws-Liste stehen müssen, da in Java keine Behandlung des Fehlers erwartet wird. Die Klassen können aber trotzdem in throws-Listen stehen und mit catchBlöcken bearbeitet werden. Die "normalen" Exceptions müssen immer mit try-catch-Blöcken behandelt werden. 527 528 529 530 531 532 533 534 Bei den Klassenvariablen darf kein "this" genutzt werden, da diese Variablen nicht zu this, also dem jeweiligen Objekt, gehören. Man erkennt daran auch, dass die nur bei Namensgleichheit notwendige Markierung von Objektvariablen mit "this." das Lesen von Programmen bei konsequenter Nutzung von this eindeutig erleichtern kann. Sieht man jetzt eine Variable ohne this, muss man nur prüfen, ob es sich um eine lokale Variable oder einen Parameter handelt und, wenn nicht, muss es eine Klassenvariable sein, insofern die Coding-Guidelines eingehalten wurden. Der Klassenname vor einer Klassenvariablen kann weggelassen werden, erhöht aber die Lesbarkeit. 535 Beim Zugriff von außen muss der Klassenname vor der Klassenmethode stehen. 536 537 538 539 540 Man erkennt, dass Klassenvariablen vererbt werden, genauer auch Eigenschaft der erbenden Klasse, also nicht der Objekte werden. Man kann nur Objekt-Methoden überschreiben, da nur hierfür dynamische Polymorphie sinnvoll einsetzbar ist. Man kann allerdings Klassenmethoden mit der gleichen Signatur anlegen, die dann zur jeweiligen Klasse gehören und auch in denen aufgerufen werden können. Das Beispiel zeigt die Möglichkeit Klassenvariablen ohne die Klasse davor zu nutzen, was die Lesbarkeit deutlich verringert. Leider ist es in Java sogar möglich, klav als Klassenvariable von K2 anzusprechen, was nach obiger Vererbungsregel natürlich formal korrekt ist, aber in einer praktischen Nutzung zu absolut unlesbaren Programmen führen würde. 541 542 System.out.println() ist also aus objektorientierter Sicht ein extrem merkwürdiger Befehl, weshalb er bisher in "ordentlichen" Objekten der Klasse EinUndAusgabe gekapselt wurde. Bei System.out handelt es sich bei out um eine nach außen sichbare (public) Klassenvariable der Klasse System. In out selbst steht ein ganz "normales" Objekt, das eine Methode println() anbietet. Der Methode println(.) können beliebige Objekte übergeben werden, für die dann die toString()-Methode aufgerufen wird. Das "ln" am Ende deutet ein "Linefeed" an und führt dazu, dass ein Zeilenumbruch nach der Ausgabe durchgeführt wird. Es gibt die ähnliche Methode print, die die gleiche Ausgabe macht, aber am Ende auf einen Zeilenumbruch verzichtet. 543 Ein typisches Beispiel bei dem Objektmethoden keinen Sinn machen, ist die Klasse EinUndAusgabe. Hier wird ausschließlich ein Objekt erstellt, damit man die Methoden ausführen kann. Es gibt keine echten Objektvariablen, die sich irgendeine Zustandsinformation merken. 544 Vereinfachend kann man sich an dieser Stelle noch double durch Double und long durch Log ersetzt vorstellen. Die Klasse Long kann auch ganze Zahlen aufnehmen, kann aber wesentlich größere Zahlen als Integer darstellen, braucht dafür aber mehr Speicherplatz. 545 546 547 548 549 550 551 Die Konstanten wurden durch Aufrufe von Klassenmethoden ersetzt. 552 553 554 555 556 557