From 33613a85afc4b1481367fbe92a17ee59c240250b Mon Sep 17 00:00:00 2001 From: Sven Eisenhauer Date: Fri, 10 Nov 2023 15:11:48 +0100 Subject: add new repo --- .../hjp5/html/k100044.html | 661 +++++++++++++++++++++ 1 file changed, 661 insertions(+) create mode 100644 Master/Reference Architectures and Patterns/hjp5/html/k100044.html (limited to 'Master/Reference Architectures and Patterns/hjp5/html/k100044.html') diff --git a/Master/Reference Architectures and Patterns/hjp5/html/k100044.html b/Master/Reference Architectures and Patterns/hjp5/html/k100044.html new file mode 100644 index 0000000..dd0c017 --- /dev/null +++ b/Master/Reference Architectures and Patterns/hjp5/html/k100044.html @@ -0,0 +1,661 @@ + + + +Handbuch der Java-Programmierung, 5. Auflage + + + + + + + + + +
 Titel  + Inhalt  + Suchen  + Index  + DOC  +Handbuch der Java-Programmierung, 5. Auflage +
 <<  +  <   +  >   + >>  + API  +Kapitel 6 - Anweisungen +
+
+ + + + +

6.4 Sonstige Anweisungen

+
+ +
+ + + + +

6.4.1 Die assert-Anweisung

+

+ + + + + + + + + +
+ +

+Die Ausführungen in diesem Abschnitt können beim ersten +Lesen übersprungen werden. Bei der assert-Anweisung +handelt es sich um ein weiterführendes Konzept, das seit dem +JDK 1.4 zur Verfügung steht. Neben einem grundlegenden Verständnis +von Klassen und Methoden (ab Kapitel 7) +setzt es insbesondere Kenntnisse über das Auslösen und Behandeln +von Ausnahmen voraus, die erst in Kapitel 12 +vermittelt werden. Aus systematischen Gründen soll das Thema +jedoch an dieser Stelle behandelt werden.

+ + + + +
 Hinweis 
+
+ + + + +

Syntax

+

+ + + + +
+ +
+assert ausdruck1 [ : ausdruck2 ] ;
+
+
+
+ + + + +

Bedeutung

+

+ + + + + + + + + + + +
+ +

+Seit dem JDK 1.4 gibt es in Java eine assert-Anweisung. +Diese, ursprünglich schon für die erste JDK-Version vorgesehene, +dann aber aus Zeitgründen zurückgestellte Anweisung wird +C- oder C++-Programmierern wohlbekannt sein. Sie dient dazu, an einer +bestimmten Stelle im Programmablauf einen logischen Ausdruck zu platzieren, +von dem der Programmierer annimmt, dass er stets wahr ist. +Ist das der Fall, fährt das Programm mit der nächsten Anweisung +fort, andernfalls wird eine Ausnahme des Typs AssertionError +ausgelöst. Ein solcher Ausdruck wird auch als Invariante +bezeichnet, denn er bleibt unverändert true, +solange das Programm korrekt funktioniert.

+ + + + +
 JDK1.1-6.0 
+
+ +

+Assertions, wie assert-Anweisungen +vereinfachend genannt werden, dienen also dazu, bestimmte Annahmen +über den Zustand des Programms zu verifizieren und sicherzustellen, +dass diese eingehalten werden. Soll im Programm beispielsweise überprüft +werden, ob eine Variable x nicht-negativ +ist, könnte dazu die folgende assert-Anweisung +verwendet werden: + +

+assert x >= 0;
+
+ + +

+Das Programm überprüft die Bedingung und fährt fort, +wenn sie erfüllt ist. Andernfalls wird eine Ausnahme ausgelöst. +Natürlich hätte derselbe Test auch mit Hilfe einer einfachen +if-Anweisung +ausgeführt werden können. Die assert-Anweisung +hat ihr gegenüber jedoch einige Vorteile: +

+ +

+Der im Syntaxdiagramm angegebene ausdruck1 +muss immer vom Typ boolean +sein, andernfalls gibt es einen Compilerfehler. Fehlt ausdruck2 +(er darf von beliebigem Typ sein), wird im Falle des Nichterfülltseins +der Bedingung ein AssertionError +mit einer leeren Fehlermeldung erzeugt. Wurde ausdruck2 +dagegen angegeben, wird er im Fehlerfall an den Konstruktor von AssertionError +übergeben und dient als Meldungstext für das Fehlerobjekt. + + + + +

An- und Abschalten der Assertions

+ +

+Aus Kompatibilität zu älteren JDK-Versionen sind Assertions +sowohl im Compiler als auch im Interpreter standardmäßig +deaktiviert. Um einen Quellcode zu übersetzen, der Assertions +enthält, kann dem Java-Compiler ab der Version 1.4 die Option +-source 1.4 +(ab der J2SE 5.0 auch -source 1.5) +übergeben werden. Wird dies nicht gemacht, gibt es eine Fehlermeldung, +nach der »assert« ein Schlüsselwort ist und nicht mehr +als Bezeichner verwendet werden darf. Ältere Compilerversionen +melden einen Syntaxfehler. Wir wollen uns ein einfaches Beispielprogramm +ansehen: + + +

+ + + + + +
+ +
+001 /* AssertionTest.java */
+002 
+003 public class AssertionTest
+004 {
+005   public static void main(String[] args)
+006   {
+007     assert args.length >= 2; 
+008     int i1 = Integer.parseInt(args[0]);
+009     int i2 = Integer.parseInt(args[1]);
+010     assert i2 != 0 : "Teilen durch 0 nicht moeglich"; 
+011     System.out.println(i1 + "/" + i2 + "=" + (i1/i2));
+012   }
+013 }
+
+
+AssertionTest.java
+ +Listing 6.9: Anwendung von Assertions

+ +

+Das Beispielprogramm verwendet zwei Assertions, um sicherzustellen, +dass mindestens zwei Kommandozeilenargumente übergeben werden +und dass das zweite von ihnen nicht 0 ist. Es kann mit folgendem Kommando +übersetzt werden, wenn der Compiler mindestens die Version 1.4 +hat: + +

+javac -source 1.4 AssertionTest.java
+
+ + +

+Um das Programm laufen zu lassen, kennt der Java-Interpreter ab der +Version 1.4 die Kommandozeilenoptionen -enableassertions +und -disableassertions, +die mit -ea +bzw. -da +abgekürzt werden können. Ihre Syntax lautet: +

+ + + + +
+ +
+java [ -enableassertions | -ea  ] [:PaketName... | :KlassenName ]
+
+java [ -disableassertions | -da  ] [:PaketName... | :KlassenName ]
+
+
+
+ +

+Werden die Optionen ohne nachfolgenden Paket- oder Klassennamen angegeben, +werden die Assertions für alle Klassen mit Ausnahme der Systemklassen +java.* an- bzw. ausgeschaltet. +Wird, durch einen Doppelpunkt getrennt, ein Klassenname angegeben, +gilt die jeweilige Option nur für diese Klasse. Wird ein Paketname +angegeben (von einem Klassennamen durch drei angehängte Punkte +zu unterscheiden), erstreckt sich die Option auf alle Klassen innerhalb +des angegebenen Pakets. Die Optionen können mehrfach angegeben +werden, sie werden der Reihe nach ausgewertet. Wird keine dieser Optionen +angegeben, sind die Assertions deaktiviert. + +

+Soll unser Beispielprogramm mit aktivierten Assertions ausgeführt +werden, kann also folgendes Kommando verwendet werden: + +

+java -ea AssertionTest
+
+ + +

+In diesem Fall gibt es sofort eine Fehlermeldung, denn die erste assert-Anweisung +ist nicht erfüllt. Rufen wir das Programm mit zwei Zahlen als +Argumente auf, wird erwartungsgemäß deren Quotient berechnet: + +

+java -ea AssertionTest 100 25
+
+ + +

+Die Ausgabe lautet: + +

+100/25=4
+
+ + +

+Wenn das zweite Argument dagegen 0 ist, gibt es eine Fehlermeldung, +weil die zweite Assertion nicht erfüllt ist. Auch in diesem Fall +steigt das Programm mit einem AssertionError +aus, der zusätzlich die Fehlermeldung »Teilen durch 0 nicht +moeglich« enthält, die wir nach dem Doppelpunkt in Zeile 010 +angegeben haben: + +

+Exception in thread "main" java.lang.AssertionError:
+  Teilen durch 0 nicht moeglich
+...
+
+ + +

+Wird das Programm mit deaktivierten Assertions aufgerufen, verhält +es sich, als wären die Zeilen 007 +und 010 gar nicht +vorhanden. In diesem Fall gibt es die üblichen Laufzeitfehler, +die bei einem Zugriff auf ein nicht vorhandenes Array-Element oder +die Division durch 0 entstehen. + + + + +

Anwendungen

+ +

+Genau genommen war das vorige Programm kein wirklich gutes Beispiel +für die Anwendung von Assertions. Das Überprüfen der +an ein Programm übergebenen Kommandozeilenparameter sollte nämlich +besser einer IllegalArgumentException +überlassen werden: + + +

+ + + + +
+ +
+001 public class Listing0610
+002 {
+003   public static void main(String[] args)
+004   {
+005     if (args.length < 2) {
+006       throw new IllegalArgumentException();
+007     }
+008     ...
+009   }
+010 }
+
+
+ +Listing 6.10: Verwendung einer IllegalArgumentException

+ +

+Genau wie bei der Übergabe eines Arguments an eine öffentliche +Methode sollte es nicht einfach möglich sein, deren Überprüfung +zur Laufzeit abzuschalten. Da ein Programm bei der Übergabe von +Werten an öffentliche Schnittstellen keinerlei Annahmen darüber +machen kann, ob diese Werte korrekt sind, sollte die Überprüfung +dieser Werte immer aktiv sein. Die Verwendung von Assertions empfiehlt +sich also in diesem Fall nicht. Weitere Beispiele für derartige +öffentliche Schnittstellen sind etwa die Daten, die über +eine grafische Benutzerschnittstelle in ein Programm gelangen oder +die aus Dateien oder über Netzwerkverbindungen eingelesen werden. +In all diesen Fällen sollten nicht-abschaltbare Fehlerprüfungen +anstelle von Assertions verwendet werden. + +

+Der Einsatz von Assertions ist dagegen immer dann sinnvoll, wenn Daten +aus unbekannten Quellen bereits verifiziert sind. Wenn also das Nichterfülltsein +einer Assertion einen Programmfehler anzeigt und nicht fehlerhafte +Eingabedaten. Beispiele sind: +

+

+ + + + + + + + + +
+ +

+Sowohl Pre- als auch Postconditions wurden mit der Programmiersprache +Eiffel, die Bertrand Meyer +in seinem Buch »Object-oriented Software Construction« 1988 +vorgestellt hat, einer breiteren Öffentlichkeit bekannt. Das +im JDK 1.4 implementierte Assertion-Konzept entspricht allerdings +nicht in allen Aspekten Meyer's ausgefeiltem »Programming by +Contract«. Dort gibt es beispielsweise explizite Schlüsselwörter +für Pre- und Postconditions, und es ist möglich, in Postconditions +auf den »alten« Wert eines Parameters zuzugreifen (also +auf den, den er beim Eintritt in die Methode hatte).

+ + + + +
 Hinweis 
+
+ +

+Dennoch stellen Assertions ein wichtiges Hilfsmittel dar, um Programme +zuverlässiger und besser lesbar zu machen. Das folgende Programm +zeigt verschiedene Anwendungen von Assertions am Beispiel einer einfachen +Klasse zur Speicherung von Listen von Ganzzahlen: + + +

+ + + + + +
+ +
+001 public class SimpleIntList
+002 {
+003   private int[] data;
+004   private int   len;
+005 
+006   public SimpleIntList(int size)
+007   {
+008     this.data = new int[size];
+009     this.len  = 0;
+010   }
+011 
+012   public void add(int value)
+013   {
+014     //Precondition als RuntimeException
+015     if (full()) { 
+016       throw new RuntimeException("Liste voll");
+017     }
+018     //Implementierung
+019     data[len++] = value;
+020     //Postcondition
+021     assert !empty(); 
+022   }
+023 
+024   public void bubblesort()
+025   {
+026     if (!empty()) {
+027       int cnt = 0;
+028       while (true) {
+029         //Schleifeninvariante
+030         assert cnt++ < len: "Zu viele Iterationen"; 
+031         //Implementierung...
+032         boolean sorted = true;
+033         for (int i = 1; i < len; ++i) {
+034           if (sortTwoElements(i - 1, i)) {
+035             sorted = false;
+036           }
+037         }
+038         if (sorted) {
+039           break;
+040         }
+041       }
+042     }
+043   }
+044 
+045   public boolean empty()
+046   {
+047     return len <= 0;
+048   }
+049 
+050   public boolean full()
+051   {
+052     return len >= data.length;
+053   }
+054 
+055   private boolean sortTwoElements(int pos1, int pos2)
+056   {
+057     //Private Preconditions
+058     assert (pos1 >= 0 && pos1 < len); 
+059     assert (pos2 >= 0 && pos2 < len); 
+060     //Implementierung...
+061     boolean ret = false;
+062     if (data[pos1] > data[pos2]) {
+063       int tmp = data[pos1];
+064       data[pos1] = data[pos2];
+065       data[pos2] = tmp;
+066       ret = true;
+067     }
+068     //Postcondition
+069     assert data[pos1] <= data[pos2] : "Sortierfehler"; 
+070     return ret;
+071   }
+072 }
+
+
+SimpleIntList.java
+ +Listing 6.11: Anwendung von Assertions

+ +

+Precondition-Assertions sind in den Zeilen 058 +und 059 zu sehen. Sie stellen +sicher, dass die Methode mit gültigen Array-Indizes aufgerufen +wird. Eine Precondition, die als RuntimeException +implementiert wurde, findet sich in Zeile 015 +und prüft, ob die Liste vor dem Einfügen eines weiteren +Elements bereits voll ist. Während die Postcondition in Zeile 021 +eher formaler Natur ist, überprüft jene in Zeile 069, +ob das Sortieren der Elemente tatsächlich erfolgreich war. Die +Schleifeninvariante in Zeile 030 +stellt sicher, dass der Bubblesort nicht zu viele Arraydurchläufe +macht, was ein Indiz dafür wäre, dass er in eine Endlosschleife +geraten wäre. + + + + +

Nebeneffekte

+

+ + + + + + + + + + + +
+ +

+Normalerweise sollten die Ausdrücke in Assertions keine Nebeneffekte +enthalten. Also keinen Code, der Variablen verändert oder den +Status des Programms auf andere Weise modifiziert. Der Grund dafür +ist, dass diese Nebeneffekte bei abgeschalteten Assertions natürlich +nicht ausgeführt werden. Das Programm würde sich also anders +verhalten, als wenn die Assertions angeschaltet wären. Dadurch +können sehr schwer zu findende Fehler entstehen. Generell gilt, +dass die Funktionalität des Programms nicht davon abhängen +sollte, ob die Assertions an- oder abgeschaltet sind.

+ + + + +
 Warnung 
+
+ +

+Allerdings kann es berechtigte Ausnahmen geben. Ein Gegenbeispiel +liefert etwa das vorige Listing in Zeile 030. +Hier wird innerhalb der Assertion die Zählervariable cnt +inkrementiert, also eine Variable verändert. Das ist allerdings +unkritisch, denn diese wird auch nur innerhalb der Assertion benötigt, +spielt ansonsten im Programm aber keine Rolle. Variablen, die auch +an anderen Stellen im Programm verwendet werden, sollten allerdings +nicht innerhalb von Assertions verändert werden. + + + + +

Kompatibilität

+ +

+Wie schon erwähnt, gibt es Assertions erst seit der Version 1.4 +des JDK. Ihre Verwendung wirft leider einige Kompatibilitätsprobleme +auf: +

+ +

+Was folgt daraus? Assertions lassen sich nur dann sinnvoll einsetzen, +wenn der Entwickler mindestens einen JDK-1.4.Compiler besitzt und +davon ausgehen kann, dass auf allen Zielsystemen, für die er +entwickelt, ein mindestens 1.4-kompatibles Laufzeitsystem vorhanden +ist. Letzteres ist mitunter nicht unbedingt der Fall. Der Reiz von +Java liegt ja gerade in seiner Plattformunabhängigkeit, und bis +auf allen wichtigen Zielsystemen ein JDK-1.4-Laufzeitsystem zur Verfügung +stehen wird und auch installiert ist, könnte es noch einige Zeit +dauern. Wegen der zur Installation eines neueren JDKs oft erforderlichen +Betriebssystempatches auf UNIX- oder Host-Systemen sind noch immer +viele derartige Systeme mit älteren JDKs ausgestattet. + +

+Fazit: Assertions sind ein ausgezeichnetes Mittel, um Code +lesbarer und robuster zu gestalten. Während der Debugphase helfen +sie bei der Fehlersuche. Sofern keine Kompatibilität zu älteren +JDK-Versionen erforderlich ist, sollten sie daher unbedingt verwendet +werden. Kann dagegen nicht sichergestellt werden, dass die Zielsysteme +mindestens das JDK 1.4 unterstützen, können Assertions nicht +verwendet werden. +


+ + + +
 Titel  + Inhalt  + Suchen  + Index  + DOC  +Handbuch der Java-Programmierung, 5. Auflage, Addison +Wesley, Version 5.0.1 +
 <<  +  <   +  >   + >>  + API  +© 1998, 2007 Guido Krüger & Thomas +Stark, http://www.javabuch.de +
+ + + -- cgit v1.2.3