12.01.2015 Aufrufe

Informatik B Objekt-orientierte Programmierung in Java

Informatik B Objekt-orientierte Programmierung in Java

Informatik B Objekt-orientierte Programmierung in Java

MEHR ANZEIGEN
WENIGER ANZEIGEN

Erfolgreiche ePaper selbst erstellen

Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.

<strong>Informatik</strong> B<br />

<strong>Objekt</strong>-<strong>orientierte</strong> <strong>Programmierung</strong><br />

<strong>in</strong> <strong>Java</strong><br />

Sommersemester 2003<br />

Fachbereich Mathematik/<strong>Informatik</strong>, Universität Osnabrück<br />

Ute Schmid (Vorlesung)<br />

Elmar Ludwig (Übung)<br />

und Tutoren<br />

Voraussetzungen: (Vorlesung <strong>Informatik</strong> A “Algorithmen”)<br />

Grundkenntnisse <strong>in</strong> der (prozeduralen) <strong>Programmierung</strong> mit <strong>Java</strong><br />

Grundkenntnisse <strong>in</strong> Algorithmenentwicklung<br />

http://www.vorlesungen.uos.de/<strong>in</strong>formatik/b03/


Ute Schmid<br />

Fachgebiet <strong>Informatik</strong><br />

Fachbereich 6, Mathematik/<strong>Informatik</strong><br />

Universität Osnabrück<br />

Albrechtstrasse 28, D-49076 Osnabrück<br />

Raum 31/318<br />

Tel.: 0541/969-2558<br />

schmid@<strong>in</strong>formatik.uni-osnabrueck.de<br />

http://www.<strong>in</strong>f.uos.de/schmid/<br />

Dies ist e<strong>in</strong>e überarbeitete Version der Skripts vom SS 2001 und SS 2002. Vielen Dank an<br />

Elmar Ludwig für kritische Durchsicht, Korrekturvorschläge, Anregungen und Diskussionen.


Literatur<br />

<strong>Objekt</strong>-<strong>orientierte</strong> <strong>Programmierung</strong>/Spezielle Aspekte<br />

Zum Thema ‘<strong>Objekt</strong>-Orientierte <strong>Programmierung</strong>’:<br />

Timothy Budd, An Introduction to Object-Oriented Programm<strong>in</strong>g, 2nd Edition, 1997,<br />

Addison-Wesley.<br />

Zum Thema ‘Design Patterns’ (und etwas UML):<br />

James W. Cooper, <strong>Java</strong> Design Patterns. Tutorial, 1994, Addison-Wesley.<br />

Matthias Felleisen, Daniel P. Friedman, A Little <strong>Java</strong>, A Few Patterns, 1998, MIT Press.<br />

Zum Thema ‘Abstrakte Datentypen’:<br />

Timothy Budd, Classic Data Structures <strong>in</strong> <strong>Java</strong>, 2001, Addison-Wesley.<br />

Zum Thema ‘Nebenläufigkeit’:<br />

Doug Lea, Concurrent Programm<strong>in</strong>g <strong>in</strong> <strong>Java</strong>. Design Pr<strong>in</strong>ciples and Patterns, 2nd<br />

Edition, 1999, Sun Books.<br />

Lehrbuch, das den Stoff von ‘Algorithmen’ abdeckt mit Programmen <strong>in</strong> Pseudo-<strong>Java</strong> (eher<br />

prozeduraler Code):<br />

Sara Baase, Allen van Gelder, Computer Algorithms – Introduction to Design and<br />

Analysis, 3rd Edition, 2000, Addison-Wesley.<br />

Überblick über verschiedene Programmierkonzepte:<br />

Ravi Sethi, Programm<strong>in</strong>g Languages: Concepts & Constructs, 2nd Edition, 1997,<br />

Addison-Wesley.<br />

<strong>Java</strong><br />

D. Flanagan, <strong>Java</strong> <strong>in</strong> a Nutshell, 3rd Edition, 1999, O’Reilly.<br />

D. Flanagan, <strong>Java</strong> Examples <strong>in</strong> a Nutshell, 2nd Edition, 2000, O’Reilly.<br />

Bruce Eckel, Th<strong>in</strong>k<strong>in</strong>g <strong>in</strong> <strong>Java</strong>, 2nd Edition, 1998, Prentice-Hall.<br />

Mary Campione, Kathy Walrath, The <strong>Java</strong> Tutorial, Object-Oriented Programm<strong>in</strong>g for the<br />

Internet, 2nd Edition, 1997, Addison-Wesley. (auch <strong>in</strong> deutscher Übersetzung erhältlich)<br />

Ken Arnold, James Gosl<strong>in</strong>g, The <strong>Java</strong> Programm<strong>in</strong>g Language, 3rd Edition, Addison-Wesley.<br />

(auch <strong>in</strong> deutscher Übersetzung erhältlich)


Das Skript bezieht sich auf folgende weitere Quellen:<br />

J. R. Anderson, Kognitive Psychologie, 3. Auflage, 2001, Spektrum. (Hierarchisches<br />

semantisches Gedächtnis)<br />

R. G. Herrtwich und G. Hommel, Nebenläufige Programme, 2. Auflage, Spr<strong>in</strong>ger, 1994.<br />

Atsushi Igarashi, Benjam<strong>in</strong> Pierce and Philip Wadler, Featherweight <strong>Java</strong>: A M<strong>in</strong>imal Core<br />

Calculus for <strong>Java</strong> and GJ. In Proc. of ACM Conference on Object-Oriented Program<strong>in</strong>g,<br />

Systems, Languages, and Applications (OOPSLA), ACM SIGPLAN Notices, 34(10), pp./<br />

132-146, 1999.<br />

Atsushi Igarashi and Benjam<strong>in</strong> C. Pierce. On Inner Classes. Accepted for publication <strong>in</strong><br />

Information and Computation as of November 24, 2000.<br />

P. Pepper, Grundlagen der <strong>Informatik</strong>, 2. Auflage, Oldenbourg, 1995.<br />

A. T. Schre<strong>in</strong>er, Vorlesungsskript Programm<strong>in</strong>g with <strong>Java</strong> 2, Universität Osnabrück und<br />

Rochester Institute of Technology. http://www.cs.rit.edu/˜ats/java-2000-1/<br />

Sperschneider und Hammer, 1996, Theoretische <strong>Informatik</strong>. E<strong>in</strong>e problem<strong>orientierte</strong><br />

E<strong>in</strong>führung. Spr<strong>in</strong>ger.<br />

Oliver Vornberger und Olaf Müller, Skript <strong>Informatik</strong> A.


Inhaltsverzeichnis<br />

1 E<strong>in</strong>führung: Programmier-Paradigmen und -Sprachen 1<br />

1.1 Entwicklung von Paradigmen und Sprachen . . . . . . . . . . . . . . . . . . . . . 1<br />

1.1.1 Überblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1<br />

1.1.2 Entwicklung von Hochsprachen . . . . . . . . . . . . . . . . . . . . . . . . 1<br />

1.1.3 Motivation: Kenntnis mehrere Paradigmen . . . . . . . . . . . . . . . . . . 3<br />

1.2 Prozeduren und Parameterübergabe-Mechanismen . . . . . . . . . . . . . . . . 4<br />

1.2.1 Programmstruktur bei imperativen Sprachen . . . . . . . . . . . . . . . . 4<br />

1.2.2 Prozeduren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4<br />

1.2.3 Parameterübergabe-Methoden . . . . . . . . . . . . . . . . . . . . . . . . 6<br />

1.2.4 Call-by-Value versus Call-by-Reference . . . . . . . . . . . . . . . . . . . 7<br />

1.2.5 Call-by-Value versus Call-by-Name . . . . . . . . . . . . . . . . . . . . . . 7<br />

1.2.6 Call-by-value <strong>in</strong> <strong>Java</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8<br />

1.3 Sprach-Implementierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10<br />

1.4 Syntaktische Struktur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12<br />

1.4.1 Formale Sprachen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12<br />

1.4.2 BNF und EBNF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13<br />

1.5 Semantik und Pragmatik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15<br />

2 <strong>Java</strong> und <strong>Objekt</strong>-Orientierung 17<br />

2.1 Grundkonzepte der <strong>Objekt</strong>-Orientierung . . . . . . . . . . . . . . . . . . . . . . . 17<br />

2.1.1 Eigenschaften <strong>Objekt</strong>-Orientierter Sprachen . . . . . . . . . . . . . . . . . 17<br />

2.1.2 Pr<strong>in</strong>zipien der <strong>Objekt</strong>-Orientierten <strong>Programmierung</strong> . . . . . . . . . . . . 17<br />

2.2 Die Sprache <strong>Java</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18<br />

2.2.1 Entstehungsgeschichte . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18<br />

2.2.2 <strong>Java</strong> Buzzwords . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19<br />

2.2.3 Sprache, Virtuelle Masch<strong>in</strong>e, Plattform . . . . . . . . . . . . . . . . . . . . 19<br />

2.3 Das 8-Damen Problem Imperativ und <strong>Objekt</strong>-Orientiert . . . . . . . . . . . . . . . 21<br />

2.3.1 Problemstellung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21<br />

2.3.2 Identifikation von Schlagstellungen . . . . . . . . . . . . . . . . . . . . . . 22<br />

2.3.3 Imperative Lösung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23<br />

2.3.4 <strong>Objekt</strong>-Orientierte Lösung . . . . . . . . . . . . . . . . . . . . . . . . . . . 24<br />

3 Klassen und ihre Komponenten 28<br />

3.1 Klassen <strong>in</strong> <strong>Java</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28<br />

3.2 Members: Felder, Methoden, Klassen . . . . . . . . . . . . . . . . . . . . . . . . 28<br />

3.3 Beispielcode ‘Circle’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29<br />

3.4 Klassen- und Instanz-Komponenten . . . . . . . . . . . . . . . . . . . . . . . . . 29<br />

3.4.1 Klassen-Felder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29<br />

3.4.2 Klassen-Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30<br />

3.4.3 Instanz-Felder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30<br />

3.4.4 Erzeugung und Initialisierung von <strong>Objekt</strong>en . . . . . . . . . . . . . . . . . 31<br />

3.4.5 Instanz-Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31<br />

3.4.6 Funktionsweise von Instanz-Methoden: ‘this’ . . . . . . . . . . . . . . . . 32<br />

3.4.7 Instanz- oder Klassen-Methode . . . . . . . . . . . . . . . . . . . . . . . 32<br />

3.5 Referenztypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33<br />

3.5.1 Kopieren von <strong>Objekt</strong>en (und Arrays) . . . . . . . . . . . . . . . . . . . . . 34


3.5.2 Gleichheit von <strong>Objekt</strong>en (und Arrays) . . . . . . . . . . . . . . . . . . . . . 35<br />

3.6 Wrapper-Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37<br />

3.7 Dokumentation mit ‘javadoc’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37<br />

3.8 Packages und Namespace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40<br />

3.8.1 <strong>Java</strong> API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40<br />

3.8.2 Packages und Namespaces <strong>in</strong> <strong>Java</strong> . . . . . . . . . . . . . . . . . . . . . 41<br />

3.8.3 Namens-Kollisionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42<br />

3.8.4 Verhaltensänderungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43<br />

4 Konstruktoren und Vererbung 45<br />

4.1 Konstruktoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45<br />

4.1.1 Def<strong>in</strong>ition von Konstruktoren . . . . . . . . . . . . . . . . . . . . . . . . . 45<br />

4.1.2 Def<strong>in</strong>ition mehrerer Konstruktoren . . . . . . . . . . . . . . . . . . . . . . 46<br />

4.2 Defaults und Initialisierung für Felder . . . . . . . . . . . . . . . . . . . . . . . . . 46<br />

4.2.1 Defaults . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46<br />

4.2.2 Initialisierung von Instanz-Feldern: Konstruktoren . . . . . . . . . . . . . . 47<br />

4.2.3 Initialisierung von Klassen-Feldern: Initialisierungs-Blöcke . . . . . . . . . 48<br />

4.3 Zerstören und F<strong>in</strong>alisieren von <strong>Objekt</strong>en . . . . . . . . . . . . . . . . . . . . . . . 49<br />

4.3.1 Garbage Collection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49<br />

4.3.2 Anmerkung: F<strong>in</strong>alization . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49<br />

4.4 Unterklassen und Vererbung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50<br />

4.4.1 Exkurs: Hierarchische Semantische Netze . . . . . . . . . . . . . . . . . . 50<br />

4.4.2 Erweiterung von ‘Circle’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52<br />

4.4.3 Erweiterung e<strong>in</strong>er Klasse . . . . . . . . . . . . . . . . . . . . . . . . . . . 52<br />

4.5 Kapslung und Zugriffskontrolle . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53<br />

4.5.1 Zugriffs-Kontrolle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54<br />

4.5.2 Vier Ebenen von Zugriffsrechten . . . . . . . . . . . . . . . . . . . . . . . 54<br />

4.5.3 Zugriffs-Kontrolle und Vererbung . . . . . . . . . . . . . . . . . . . . . . . 55<br />

4.5.4 Daumenregeln für Sichtbarkeits-Modifikatoren . . . . . . . . . . . . . . . 56<br />

4.5.5 Daten-Zugriffs-Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . 56<br />

5 Klassenabhängigkeiten 58<br />

5.1 Klassenhierarchie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58<br />

5.1.1 F<strong>in</strong>ale Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58<br />

5.1.2 Die Klasse ‘Object’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58<br />

5.1.3 Klasse ‘Str<strong>in</strong>g’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58<br />

5.1.4 Hierarchische Klassenstruktur . . . . . . . . . . . . . . . . . . . . . . . . 59<br />

5.2 Ergänzung: Konstruktoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61<br />

5.2.1 Unterklassen-Konstruktoren . . . . . . . . . . . . . . . . . . . . . . . . . . 61<br />

5.2.2 Default-Konstruktoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61<br />

5.2.3 Konstruktor-Verkettung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62<br />

5.3 Vererbung: Shadow<strong>in</strong>g und Overrid<strong>in</strong>g . . . . . . . . . . . . . . . . . . . . . . . . 62<br />

5.3.1 Verdecken von Feldern der Oberklasse . . . . . . . . . . . . . . . . . . . 62<br />

5.3.2 Shadow<strong>in</strong>g von Klassen-Feldern . . . . . . . . . . . . . . . . . . . . . . . 63<br />

5.3.3 Überschreiben von Instanz-Methoden der Oberklasse . . . . . . . . . . . 64<br />

5.3.4 Überschreiben vs. Verdecken . . . . . . . . . . . . . . . . . . . . . . . . . 64<br />

5.3.5 Dynamisches ‘Method Lookup’ . . . . . . . . . . . . . . . . . . . . . . . . 65<br />

5.3.6 ‘F<strong>in</strong>al’ Methoden und Statisches ‘Method Lookup’ . . . . . . . . . . . . . 65


5.3.7 Aufruf e<strong>in</strong>er überschriebenen Methode . . . . . . . . . . . . . . . . . . . . 66<br />

5.4 Overload<strong>in</strong>g und Polymorphismus . . . . . . . . . . . . . . . . . . . . . . . . . . 66<br />

5.4.1 Operator-Overload<strong>in</strong>g . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66<br />

5.4.2 Operator-Overload<strong>in</strong>g <strong>in</strong> <strong>Java</strong> . . . . . . . . . . . . . . . . . . . . . . . . . 67<br />

5.4.3 Method-Overload<strong>in</strong>g <strong>in</strong> <strong>Java</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . 68<br />

5.4.4 Polymorphismus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69<br />

5.4.5 Cast<strong>in</strong>g und Polymorphismus . . . . . . . . . . . . . . . . . . . . . . . . . 69<br />

5.4.6 Cast<strong>in</strong>g vs. Parameterisierte Klassen . . . . . . . . . . . . . . . . . . . . 70<br />

6 Exceptions 72<br />

6.1 Fehler und Ausnahmen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72<br />

6.2 Vorteile von Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73<br />

6.2.1 Separierung von Code und Fehlerbehandlung . . . . . . . . . . . . . . . 73<br />

6.2.2 Propagierung von Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . 74<br />

6.3 Exception Handl<strong>in</strong>g – ‘try’, ‘catch’, ‘f<strong>in</strong>ally’ . . . . . . . . . . . . . . . . . . . . . . 75<br />

6.4 Spezifikation von Exceptions – ‘throws’ . . . . . . . . . . . . . . . . . . . . . . . . 79<br />

6.5 Vererbung und ‘throws’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79<br />

6.5.1 Gruppierung von Fehler-Typen . . . . . . . . . . . . . . . . . . . . . . . . 80<br />

6.6 Def<strong>in</strong>ition eigener Exception-Klassen und Auslösen von Exceptions . . . . . . . 81<br />

6.7 Exkurs: UML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82<br />

6.7.1 Klassendiagramme <strong>in</strong> UML . . . . . . . . . . . . . . . . . . . . . . . . . . 82<br />

6.7.2 Klassen-/ Unterklassenbeziehungen <strong>in</strong> UML . . . . . . . . . . . . . . . . 83<br />

6.7.3 Assoziationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84<br />

6.7.4 Kommentare und Annotierung <strong>in</strong> UML . . . . . . . . . . . . . . . . . . . . 85<br />

6.7.5 UML-Tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85<br />

6.8 Exkurs: Design Patterns – Factory Pattern . . . . . . . . . . . . . . . . . . . . . . 86<br />

7 Input/Output 90<br />

7.1 E<strong>in</strong>-/Ausgabe-Ströme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90<br />

7.1.1 Klassenstruktur <strong>in</strong> ‘java.io’ . . . . . . . . . . . . . . . . . . . . . . . . . . . 90<br />

7.1.2 Character und Byte Ströme . . . . . . . . . . . . . . . . . . . . . . . . . . 91<br />

7.1.3 Wichtige Reader- und Writer-Klassen . . . . . . . . . . . . . . . . . . . . 93<br />

7.2 Datei-Ströme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94<br />

7.3 Puffern von Daten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95<br />

7.4 Filter-Ströme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96<br />

7.5 Standard-E<strong>in</strong>- und Ausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98<br />

7.6 IO-Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99<br />

7.7 RandomAccess . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99<br />

7.8 Weitere Aspekte von I/O . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100<br />

7.8.1 Tokenizer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100<br />

7.8.2 Serializable, Externalizable . . . . . . . . . . . . . . . . . . . . . . . . . . 101<br />

7.8.3 Pipe-Ströme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101<br />

8 Vererbung und Typsicherheit 102<br />

8.1 Formale Modelle für Programmiersprachen . . . . . . . . . . . . . . . . . . . . . 102<br />

8.2 Featherweight <strong>Java</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103<br />

8.2.1 Programmbeispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104<br />

8.2.2 Syntax für FJ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105


8.2.3 Subtyp<strong>in</strong>g . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105<br />

8.2.4 Hilfsfunktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106<br />

8.3 Typisierung und Reduktion <strong>in</strong> FJ . . . . . . . . . . . . . . . . . . . . . . . . . . . 108<br />

8.3.1 Typisierungsregeln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108<br />

8.3.2 Reduktionsregeln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108<br />

8.3.3 Veranschaulichung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110<br />

9 Abstrakte Klassen und Interfaces 113<br />

9.1 Abstrakte Klassen und Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . 113<br />

9.2 Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114<br />

9.2.1 Implementation e<strong>in</strong>es Interfaces . . . . . . . . . . . . . . . . . . . . . . . 115<br />

9.2.2 Interfaces und Konstanten . . . . . . . . . . . . . . . . . . . . . . . . . . . 116<br />

9.2.3 Benutzung von Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . 116<br />

9.2.4 Interface vs. Abstrakte Klasse . . . . . . . . . . . . . . . . . . . . . . . . . 117<br />

9.2.5 Implementation mehrerer Interfaces und Erweitern von Interfaces . . . . . 118<br />

9.2.6 Marker-Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120<br />

9.3 Das Enumeration-Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120<br />

10 Innere Klassen 123<br />

10.1 Member Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123<br />

10.1.1 Anschauliches Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123<br />

10.1.2 Beispiel ‘Enumerator’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124<br />

10.1.3 Eigenschaften von Member-Klassen . . . . . . . . . . . . . . . . . . . . . 125<br />

10.1.4 Implementation von Member-Klassen . . . . . . . . . . . . . . . . . . . . 126<br />

10.1.5 Member-Klassen und Vererbung . . . . . . . . . . . . . . . . . . . . . . . 127<br />

10.2 Static Member Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128<br />

10.2.1 Anschauliches Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128<br />

10.2.2 Eigenschaften von Static Member Classes . . . . . . . . . . . . . . . . . 129<br />

10.2.3 Implementation von statischen Member-Klassen . . . . . . . . . . . . . . 130<br />

10.3 Lokale Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130<br />

10.3.1 Anschauliches Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130<br />

10.3.2 Beispiel: ‘Enumerator’ als lokale Klasse . . . . . . . . . . . . . . . . . . . 131<br />

10.3.3 Eigenschaften Lokaler Klassen . . . . . . . . . . . . . . . . . . . . . . . . 132<br />

10.3.4 Geltungsbereich Lokaler Klassen . . . . . . . . . . . . . . . . . . . . . . . 133<br />

10.4 Anonyme Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135<br />

10.4.1 Beispiel: ‘Enumerator’ als anonyme Klasse . . . . . . . . . . . . . . . . . 135<br />

10.4.2 Eigenschaften von Anonymen Klassen . . . . . . . . . . . . . . . . . . . . 135<br />

10.4.3 Implementation von Lokalen und Anonymen Klassen . . . . . . . . . . . . 136<br />

10.4.4 Adapter-Klassen als Anonyme Klassen . . . . . . . . . . . . . . . . . . . 136<br />

10.4.5 Anwendung und Konventionen . . . . . . . . . . . . . . . . . . . . . . . . 137<br />

10.5 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137<br />

10.6 Beispiel-Code ‘Enumeration’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138<br />

10.7 Adapter-Patterns und <strong>Java</strong> Adapter-Klassen . . . . . . . . . . . . . . . . . . . . . 140<br />

10.8 Innere Klassen und Lexical Closures . . . . . . . . . . . . . . . . . . . . . . . . . 141<br />

10.8.1 Code as Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142<br />

10.8.2 Adder-Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142


11 Abstrakte Datentypen und Collections 143<br />

11.1 Abstrakte Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143<br />

11.1.1 Grundlagen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143<br />

11.1.2 Funktionalitäten von Collections . . . . . . . . . . . . . . . . . . . . . . . 143<br />

11.2 Implementation von Collections . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144<br />

11.3 Implementation mit Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145<br />

11.3.1 MyCollection/Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145<br />

11.3.2 Erläuterungen zu ‘MyCollection’ . . . . . . . . . . . . . . . . . . . . . . . 147<br />

11.3.3 Test-Protokoll für ‘MyCollection’ . . . . . . . . . . . . . . . . . . . . . . . . 147<br />

11.3.4 Bag/Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148<br />

11.3.5 Erläuterungen zu ‘Bag’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149<br />

11.3.6 Set/Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149<br />

11.3.7 Erläuterungen zu Set . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150<br />

11.3.8 Test-Protokoll für ‘Set’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151<br />

11.3.9 EquivalenceSet/Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151<br />

11.3.10Erläuterungen zu ‘EquivalenceSet’ . . . . . . . . . . . . . . . . . . . . . . 152<br />

11.3.11Test-Protokoll für ‘EquivalenceSet’ . . . . . . . . . . . . . . . . . . . . . . 152<br />

11.4 Implementation mit Offener Hash-Tabelle . . . . . . . . . . . . . . . . . . . . . . 153<br />

11.4.1 Array versus Hash-Tabelle . . . . . . . . . . . . . . . . . . . . . . . . . . . 153<br />

11.4.2 MyCollection/Hash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153<br />

11.4.3 Erläuterungen zu ‘MyCollection’ . . . . . . . . . . . . . . . . . . . . . . . 156<br />

11.4.4 Bag/Hash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156<br />

11.4.5 Set/Hash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157<br />

11.5 ADT-Test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157<br />

11.5.1 Anforderungen an ‘Test’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157<br />

11.5.2 Test/ADT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158<br />

11.5.3 Erläuterungen zu ‘Test’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160<br />

11.6 Implementation mit Liste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161<br />

11.6.1 Dynamische Datenstrukturen . . . . . . . . . . . . . . . . . . . . . . . . . 161<br />

11.6.2 Bag/List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163<br />

11.6.3 Erläuterungen zu ‘Bag’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165<br />

11.6.4 Unterklasse ‘Set’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166<br />

11.6.5 Unterklasse ‘EquivalenceSet’ . . . . . . . . . . . . . . . . . . . . . . . . . 166<br />

11.7 Implementation mit Suchbaum . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167<br />

11.7.1 Suchbäume . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167<br />

11.7.2 Set/Tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168<br />

11.7.3 Erläuterungen zu ‘Set’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172<br />

11.8 Visitor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174<br />

11.8.1 Konzept e<strong>in</strong>es Visitor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174<br />

11.8.2 ‘Visitor’, ‘Visitable’/ADT . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175<br />

11.8.3 Suchbaum mit Visitor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176<br />

11.9 <strong>Java</strong> Collection Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176<br />

11.9.1 Grundstruktur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176<br />

11.9.2 Illustration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177


12 Reflections 180<br />

12.1 Methoden des Reflection-API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180<br />

12.2 Die Klassen ‘Class’, ‘Method’, ‘Field’ und ‘Constructor’ . . . . . . . . . . . . . . . 180<br />

12.3 Inspektion von Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181<br />

12.3.1 Abruf e<strong>in</strong>es ‘Class’ <strong>Objekt</strong>s . . . . . . . . . . . . . . . . . . . . . . . . . . 181<br />

12.3.2 Abruf des Klassen-Namens e<strong>in</strong>es <strong>Objekt</strong>s . . . . . . . . . . . . . . . . . . 182<br />

12.3.3 Abruf von Klassen-Modifikatoren . . . . . . . . . . . . . . . . . . . . . . . 183<br />

12.3.4 Abruf von Oberklassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183<br />

12.3.5 Abruf des implementierten Interfaces e<strong>in</strong>er Klasse . . . . . . . . . . . . . 184<br />

12.3.6 Interface oder Klasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184<br />

12.3.7 Abruf von Klassen-Feldern . . . . . . . . . . . . . . . . . . . . . . . . . . 185<br />

12.3.8 Abruf von Klassen-Konstruktoren . . . . . . . . . . . . . . . . . . . . . . . 186<br />

12.3.9 Abruf von Methoden-Information . . . . . . . . . . . . . . . . . . . . . . . 186<br />

12.4 Manipulation von <strong>Objekt</strong>en zur Laufzeit . . . . . . . . . . . . . . . . . . . . . . . 188<br />

12.4.1 Dynamische Erzeugung von <strong>Objekt</strong>en . . . . . . . . . . . . . . . . . . . . 188<br />

12.4.2 Exceptions beim dynamischen Erzeugen von <strong>Objekt</strong>en . . . . . . . . . . 188<br />

12.4.3 Dynamische Erzeugung mit Konstruktor-Argumenten . . . . . . . . . . . 189<br />

12.4.4 Abruf und Belegung von Feldern . . . . . . . . . . . . . . . . . . . . . . . 190<br />

12.4.5 Method Invocation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190<br />

12.5 Bemerkungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191<br />

13 Multi-Thread<strong>in</strong>g – Grundlagen der Nebenläufigkeit 193<br />

13.1 Sequentialität, Determ<strong>in</strong>ismus, Determ<strong>in</strong>iertheit . . . . . . . . . . . . . . . . . . . 193<br />

13.1.1 Nebenläufigkeit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193<br />

13.1.2 Nicht-Determ<strong>in</strong>ismus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194<br />

13.1.3 Nicht-Determ<strong>in</strong>iertheit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194<br />

13.1.4 Verzahnung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195<br />

13.2 Nebenläufigkeit <strong>in</strong> <strong>Java</strong>: Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . 195<br />

13.2.1 Def<strong>in</strong>ition von Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196<br />

13.2.2 Die Klasse Thread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196<br />

13.2.3 E<strong>in</strong>faches Beispiel: ‘ThreadDemo’ . . . . . . . . . . . . . . . . . . . . . . 197<br />

13.2.4 Erläuterungen zu ‘ThreadDemo’ . . . . . . . . . . . . . . . . . . . . . . . 199<br />

13.2.5 Zustände von Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200<br />

13.3 Kooperierende und Konkurrierende Prozesse . . . . . . . . . . . . . . . . . . . . 200<br />

13.3.1 Kooperierende Prozesse . . . . . . . . . . . . . . . . . . . . . . . . . . . 200<br />

13.3.2 Konkurrierende Prozesse . . . . . . . . . . . . . . . . . . . . . . . . . . . 201<br />

13.4 Synchronisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203<br />

13.5 Monitore <strong>in</strong> <strong>Java</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203<br />

13.5.1 Synchronized . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203<br />

13.5.2 Funktion von ‘synchronized’ . . . . . . . . . . . . . . . . . . . . . . . . . . 205<br />

13.5.3 Warten auf Ereignisse mit Monitoren . . . . . . . . . . . . . . . . . . . . . 205<br />

13.6 Beispiel: Produzent/Konsument . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206<br />

14 Multi-Thread<strong>in</strong>g: Semaphoren und Deadlocks 209<br />

14.1 Semaphoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209<br />

14.1.1 Konzept . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209<br />

14.1.2 Klasse ‘Semaphore’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209<br />

14.1.3 E<strong>in</strong>seitige und Mehrseitige Synchronisation . . . . . . . . . . . . . . . . . 210


14.1.4 Erzeuger-/Verbraucher-Problem mit Semaphoren . . . . . . . . . . . . . . 210<br />

14.2 Conditional Critical Regions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212<br />

14.2.1 Monitore, CCRs, Semaphoren . . . . . . . . . . . . . . . . . . . . . . . . 213<br />

14.3 Deadlocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213<br />

14.3.1 Lösung mit Semaphoren . . . . . . . . . . . . . . . . . . . . . . . . . . . 213<br />

14.3.2 D<strong>in</strong><strong>in</strong>g Philosophers – Lösung mit globaler Kontrolle . . . . . . . . . . . . 214<br />

14.3.3 D<strong>in</strong><strong>in</strong>g Philosophers – Bed<strong>in</strong>gter Zugriff auf Gabel . . . . . . . . . . . . . 217<br />

14.3.4 Deadlocks durch falsche Anordnung . . . . . . . . . . . . . . . . . . . . . 219<br />

14.4 Threads: Ergänzungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220<br />

14.5 Pipe-Ströme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221<br />

15 Reguläre Ausdrücke und Pattern-Match<strong>in</strong>g 225<br />

15.1 Str<strong>in</strong>g Pattern-Match<strong>in</strong>g . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225<br />

15.1.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225<br />

15.1.2 Straightforward Lösung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225<br />

15.1.3 Str<strong>in</strong>g-Match<strong>in</strong>g mit endlichen Automaten . . . . . . . . . . . . . . . . . . 225<br />

15.1.4 Der Knuth-Morris-Pratt (KPM) Algorithmus . . . . . . . . . . . . . . . . . 227<br />

15.1.5 Pattern-Match<strong>in</strong>g mit Regulären Ausdrücken . . . . . . . . . . . . . . . . 229<br />

15.2 <strong>Java</strong> 1.4 ‘regex’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230<br />

15.2.1 Konstruktion regulärer Ausdrücke . . . . . . . . . . . . . . . . . . . . . . . 230<br />

15.2.2 Die Pattern-Klasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231<br />

15.2.3 Die Matcher-Klasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231<br />

15.2.4 Beispiel: Suche und Ersetze . . . . . . . . . . . . . . . . . . . . . . . . . 231<br />

16 Assertions 234<br />

16.1 Zusicherungskalkül . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234<br />

16.2 Die ‘assert’-Anweisung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235<br />

17 Ausblick: GUIs und Event Handl<strong>in</strong>g 236<br />

17.1 <strong>Java</strong> Foundation Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236<br />

17.2 Sw<strong>in</strong>g-Komponenten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236<br />

17.2.1 Erstes Beispiel ‘HelloWorldSw<strong>in</strong>g’ . . . . . . . . . . . . . . . . . . . . . . 236<br />

17.2.2 Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238<br />

17.2.3 Conta<strong>in</strong>er . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238<br />

17.2.4 Layout Management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240<br />

17.2.5 Anmerkungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241<br />

17.3 Event-Handl<strong>in</strong>g . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242<br />

17.3.1 Event-<strong>Objekt</strong>e . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242<br />

17.3.2 Event Listener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242<br />

17.3.3 Event Handl<strong>in</strong>g mit Inneren Klassen . . . . . . . . . . . . . . . . . . . . . 243<br />

17.4 Applets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245<br />

17.4.1 Unterschiede zwischen Applets und Applications . . . . . . . . . . . . . . 245<br />

17.4.2 Schreiben von Applets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245<br />

17.4.3 Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246<br />

17.5 GUIs und Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247<br />

17.6 Beans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248


18 Ausblick: Verteilte Systeme 249<br />

18.1 Netzwerk-Anwendungen <strong>in</strong> <strong>Java</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . 249<br />

18.2 Grundlagen für Kommunikation im Netz . . . . . . . . . . . . . . . . . . . . . . . 249<br />

18.2.1 Open System Interconncetion (OSI) Model . . . . . . . . . . . . . . . . . 249<br />

18.2.2 TCP und UDP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250<br />

18.2.3 Ports . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251<br />

18.2.4 Network<strong>in</strong>g Klassen <strong>in</strong> <strong>Java</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . 251<br />

18.3 Die Klasse ‘URL’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251<br />

18.3.1 Was ist e<strong>in</strong>e URL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251<br />

18.3.2 Nutzen der URL Klasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252<br />

18.3.3 Beispiel: URLConnection . . . . . . . . . . . . . . . . . . . . . . . . . . . 253<br />

18.4 Sockets für Client/Server Kommunikation . . . . . . . . . . . . . . . . . . . . . . 254<br />

18.4.1 Grundidee der Client/Server Kommunikation . . . . . . . . . . . . . . . . 254<br />

18.4.2 Sockets <strong>in</strong> <strong>Java</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255<br />

18.4.3 Beispiel ‘KnockKnockServer’ . . . . . . . . . . . . . . . . . . . . . . . . . 255<br />

18.5 Sicherheit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258<br />

19 Andere <strong>Objekt</strong>-Orientierte Sprachen 259<br />

19.1 Das 8-Damen Problem Revisited . . . . . . . . . . . . . . . . . . . . . . . . . . . 259<br />

19.2 Lösung <strong>in</strong> Smalltalk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259<br />

19.3 Lösung <strong>in</strong> Objective-C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260<br />

19.4 Lösung <strong>in</strong> C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261


¡<br />

<strong>Informatik</strong> B SS 03 1<br />

1 E<strong>in</strong>führung: Programmier-Paradigmen und -Sprachen<br />

1.1 Entwicklung von Paradigmen und Sprachen<br />

1.1.1 Überblick<br />

Zur Zeit existieren vier Programmier-Paradigmen:<br />

imperativ, funktional, logisch, objekt-orientiert.<br />

<strong>Objekt</strong>-Orientierung ist das jüngste Paradigma (80er Jahre).<br />

Jedem Paradigma lassen sich konkrete Programmiersprachen zuordnen.<br />

<strong>Java</strong> ist e<strong>in</strong>e relativ junge Sprache, die das objekt-<strong>orientierte</strong> Paradigma<br />

unterstützt (1995).<br />

Imperative/Prozedurale Sprachen (Fortran, Pascal, Modula, C): Zuweisung von<br />

Werten an Variablen durch Befehle, Zustandstransformation.<br />

Logische Sprachen (Prolog): Programm als Menge von logischen Klauseln,<br />

Interpretation: Beweis der Anfrage bei gegebenem Programm.<br />

Funktionale Sprachen (Lisp, ML): Programm als Menge (typisierter) Funktionen,<br />

nur call by value ke<strong>in</strong>e Seiteneffekte.<br />

<strong>Objekt</strong>-<strong>orientierte</strong> Sprachen (C++, Smalltalk, <strong>Java</strong>): Programm als Menge von<br />

<strong>Objekt</strong>en, die mite<strong>in</strong>ander <strong>in</strong>teragieren.<br />

Is a programm<strong>in</strong>g language a tool for <strong>in</strong>struct<strong>in</strong>g mach<strong>in</strong>es A means for<br />

communicat<strong>in</strong>g between programmers A vehicle for express<strong>in</strong>g high-level<br />

designs A notation for algorithms A way of express<strong>in</strong>g relationships<br />

between concepts A tool for experimentation A means for controll<strong>in</strong>g<br />

computerized devices My conclusion is that a general-purpose<br />

programm<strong>in</strong>g language must be all of those to serve its diverse set of<br />

users. The only th<strong>in</strong>g a language cannot be – and survive – is be a mere<br />

collection of “neat” features. (Stroustrup, 1994)<br />

1.1.2 Entwicklung von Hochsprachen<br />

Die Entwicklung von Hochsprachen wird <strong>in</strong> Abbildung 1 skizziert:<br />

Am Beg<strong>in</strong>n der Entwicklung von Computern wurde mit Masch<strong>in</strong>ensprachen<br />

(“erste Generation”) und Assembler-Sprachen (“zweite Generation”)<br />

programmiert.<br />

Hochsprachen (“dritte Generation”; seit Ende der 50er Jahre) brachten e<strong>in</strong>en<br />

wesentlichen Fortschritt: Lesbarkeit, Fehlerprüfung, Masch<strong>in</strong>enunabhängigkeit,<br />

Bibliotheken.<br />

strukturierte <strong>Programmierung</strong> (Dijkstra): Zerlegung e<strong>in</strong>es Programms<br />

(Prozeduren, Abstrakte Datentypen, Module)


2 <strong>Informatik</strong> B SS 03<br />

1957<br />

1958<br />

1959<br />

1960<br />

1961<br />

1962<br />

1963<br />

1964<br />

1965<br />

1966<br />

1967<br />

1968<br />

1969<br />

1970<br />

1971<br />

1972<br />

1973<br />

1974<br />

1975<br />

1976<br />

1977<br />

1978<br />

1979<br />

1980<br />

1981<br />

1982<br />

1983<br />

1984<br />

1985<br />

1986<br />

1987<br />

1988<br />

1989<br />

1990<br />

1991<br />

1992<br />

1993<br />

1994<br />

1995<br />

Fortran<br />

Fortran−77<br />

Fortran−90<br />

Algol−58<br />

Algol−60<br />

Algol−68<br />

Pascal<br />

Modula−2<br />

Modula−3<br />

Cobol<br />

C<br />

Ada−83<br />

Ada−95<br />

Simula I<br />

Simula−67<br />

C++<br />

Eiffel<br />

Smalltalk<br />

Objective C<br />

<strong>Java</strong><br />

CLOS<br />

Lisp<br />

ML<br />

Scheme<br />

Common Lisp<br />

Standard−ML<br />

Haskell<br />

Prolog<br />

IMPERATIV OBJEKTORIENTIERT FUNKTIONAL LOGISCH<br />

Abbildung 1: Entwicklung von höheren Programmiersprachen<br />

Hochsprachen werden entweder von e<strong>in</strong>em Compiler <strong>in</strong> Masch<strong>in</strong>encode<br />

übersetzt oder über e<strong>in</strong>en Interpreter ausgeführt. Dazwischen stehen<br />

just-<strong>in</strong>-time Compiler, bei denen Teile des Programms erst während der<br />

Ausführung übersetzt werden.<br />

(“vierte Generation”: anwendungsspezifische Sprachen wie SQL, Mathematica)<br />

Funktionale und logische Sprachen werden geme<strong>in</strong>sam auch als deklarative<br />

Sprachen bezeichnet (“fünfte Generation”, KI-Sprachen).<br />

E<strong>in</strong>e Programmiersprache ist e<strong>in</strong>e formale Sprache mit Syntax (Grammatik, die<br />

beschreibt, wie die Symbole der Sprache komb<strong>in</strong>iert werden dürfen), Semantik<br />

(Bedeutung der Sprachkonstrukte, ihr Laufzeitverhalten), und Pragmatik<br />

(Verwendung von Sprachmerkmalen; Sprachimplementation)<br />

Sprachen s<strong>in</strong>d sich mehr oder weniger ähnlich. Sprachen <strong>in</strong>nerhalb desselben<br />

Programmierparadigmas basieren auf ähnlichen Grundstrukturen.<br />

Natürliche Sprachen – Deutsch, Englisch, Hopi-Indianisch, Japanisch,<br />

Esperanto – fallen <strong>in</strong> verschiedene Sprachfamilien. Indogermanische Sprachen<br />

basieren auf anderen Konzepten als <strong>in</strong>dianische Sprachen und damit s<strong>in</strong>d<br />

andere Vorstellungen von der Welt verbunden (z. B. Zeit als Pfeil versus Zyklus).


<strong>Informatik</strong> B SS 03 3<br />

1.1.3 Motivation: Kenntnis mehrere Paradigmen<br />

Die Grenzen me<strong>in</strong>er Sprache s<strong>in</strong>d die Grenzen me<strong>in</strong>er Welt. (L.<br />

Wittgenste<strong>in</strong>)<br />

Gründe, warum man mehrere Paradigmen/Sprachen kennen sollte:<br />

Größeres Repertoire an Ausdrucksmöglichkeiten<br />

– Sprache beschränkt nicht nur was wir formulieren, sondern auch, was wir<br />

denken können (Whorf, Language, Thought, and Reality, 1956)<br />

– Die Sprache, <strong>in</strong> der Programme entwickelt werden, beschränkt die<br />

möglichen Kontrollstrukturen, Datenstrukturen und Abstraktionen, die<br />

verwendet werden können. Damit ist auch die Bandbreite der realisierbaren<br />

Algorithmen beschränkt.<br />

– Zum Teil können Konzepte e<strong>in</strong>er Sprachklasse <strong>in</strong> e<strong>in</strong>er anderen simuliert<br />

werden. Aber: Es ist immer besser, die Konzepte der Sprache, <strong>in</strong> der man<br />

arbeitet, voll auszunutzen, als nicht gut unterstützte Konzepte zu simulieren<br />

(weniger elegant, weniger effizient).<br />

H<strong>in</strong>tergrund für geeignete Sprachwahl<br />

– Die Kenntnis e<strong>in</strong>er größeren Menge von Sprachen aus verschiedenen<br />

Paradigmen erlaubt es, für e<strong>in</strong> gegebenes Problem die geignetste Sprache<br />

zu wählen.<br />

Bessere Voraussetzung um neue Sprachen zu lernen<br />

– Programmierer mit wenig formaler Ausbildung lernen häufig nur e<strong>in</strong> oder<br />

zwei Sprachen. Ist die Ausbildung zudem an der Syntax der Sprache und<br />

nicht an den Konzepten orientiert, ist es nur schwer möglich, sich schnell <strong>in</strong><br />

neue Sprachen e<strong>in</strong>zuarbeiten.<br />

– Kenntnis des Vokabulars und der Konzepte von Programmiersprachen ist<br />

Voraussetzung, um Sprachdokumentationen verstehen zu können.<br />

Verständnis von Implementations-Details<br />

– Wenn man etwas darüber weiss, wie e<strong>in</strong>e Sprache entworfen und<br />

implementiert ist, kann man die Sprache oft <strong>in</strong>telligenter, ihrem Design<br />

entsprechend, nutzen. Man wird e<strong>in</strong> besserer Programmierer, wenn man<br />

versteht, warum bestimmte Konstrukte <strong>in</strong> e<strong>in</strong>er Sprache realisiert wurden<br />

und was die Konsequenzen davon s<strong>in</strong>d.<br />

– Verständnis darüber, wie Programme ausgeführt werden, kann die<br />

Fehlervermeidung, -erkennung und -beseitigung erleichtern und hilft oft,<br />

Programme effizienter zu realisieren.


¡<br />

4 <strong>Informatik</strong> B SS 03<br />

Voraussetzung für Sprachentwicklung<br />

– Die Entwicklung neuer Allzweck-Programmiersprachen kommt nicht allzu<br />

häufig vor, und es arbeitet nur e<strong>in</strong>e relativ kle<strong>in</strong>e Gruppe von <strong>Informatik</strong>ern<br />

<strong>in</strong> der Sprachentwicklung.<br />

– Aber: Viele Programmierer werden ab und zu kle<strong>in</strong>e Sprachen für spezielle<br />

Anwendungen entwerfen.<br />

Allgeme<strong>in</strong>e Entwicklung von Programmiersprachen<br />

– Nicht immer ist die populärste Sprache die beste Sprache. Wenn<br />

diejenigen, die sich für die Verwendung e<strong>in</strong>er Sprache entscheiden, besser<br />

<strong>in</strong>formiert s<strong>in</strong>d, können sich gute Sprachen vielleicht besser/schneller<br />

durchsetzen.<br />

1.2 Prozeduren und Parameterübergabe-Mechanismen<br />

1.2.1 Programmstruktur bei imperativen Sprachen<br />

In objekt-<strong>orientierte</strong>n Sprachen s<strong>in</strong>d Klassen/<strong>Objekt</strong>e die kle<strong>in</strong>sten autonomen<br />

Bauste<strong>in</strong>e.<br />

In imperativen Sprachen gliedert sich e<strong>in</strong> Programm dagegen <strong>in</strong><br />

– e<strong>in</strong>en Deklarationsblock (Typdef<strong>in</strong>itionen, globale Variablen und<br />

Konstanten),<br />

– e<strong>in</strong>e Menge von Prozeduren und<br />

– e<strong>in</strong> Hauptprogramm.<br />

In Abbildung 2 ist e<strong>in</strong> Modula-2 Programm abgedruckt.<br />

1.2.2 Prozeduren<br />

Prozeduren dienen der Strukturierung des Programms. Wie Methoden <strong>in</strong> <strong>Java</strong><br />

s<strong>in</strong>d Prozeduren benannt ( Rekursion als Kontrollstruktur möglich).<br />

Prozeduren bestehen aus e<strong>in</strong>em Kopf und e<strong>in</strong>em Rumpf.<br />

Der Kopf besteht aus e<strong>in</strong>em Schlüsselwort (z.B. PROCEDURE), e<strong>in</strong>em<br />

benutzerdef<strong>in</strong>ierten, möglichst vielsagenden Namen (z.B. ReadMaxIndex),<br />

e<strong>in</strong>er Folge von (0 bis beliebig vielen) formalen Parametern, und möglicherweise<br />

e<strong>in</strong>em Ergebnistyp (Funktion). (vgl. <strong>Java</strong> Methoden mit Rückgabetyp bzw.<br />

void).<br />

Wie bei <strong>Java</strong>-Methoden können im Rumpf lokale Variablen und Konstanten<br />

deklariert werden. Die Berechnungsvorschrift wird als Folge von Anweisungen<br />

angegeben.


<strong>Informatik</strong> B SS 03 5<br />

MODULE ggt;<br />

FROM InOut IMPORT ReadCard, WriteCard, WriteStr<strong>in</strong>g, WriteLn;<br />

PROCEDURE ReadMaxIndex(VAR max<strong>in</strong>dex : CARDINAL) : BOOLEAN;<br />

BEGIN<br />

WriteStr<strong>in</strong>g("Gib e<strong>in</strong>e ganze, positive Zahl e<strong>in</strong> : ");<br />

ReadCard(max<strong>in</strong>dex);<br />

RETURN (max<strong>in</strong>dex > 0) (* TRUE wenn max<strong>in</strong>dex > 0 *)<br />

END ReadMaxIndex;<br />

PROCEDURE ggT(arg1, arg2 : CARDINAL) : CARDINAL;<br />

VAR x, y, z : CARDINAL; (* Hilfsvariablen zur ggT-Bestimmung *)<br />

BEGIN<br />

x:=arg1; (* ggT(arg1, arg2) bestimmen *)<br />

y:=arg2;<br />

WHILE y#0 DO<br />

z:=x MOD y;<br />

x:=y;<br />

y:=z<br />

END;<br />

RETURN x<br />

END ggT;<br />

PROCEDURE WriteGGTs(max<strong>in</strong>dex : CARDINAL);<br />

VAR l<strong>in</strong>e : CARDINAL; (* Zeilen<strong>in</strong>dex *)<br />

column : CARDINAL; (* Spalten<strong>in</strong>dex *)<br />

BEGIN<br />

WriteStr<strong>in</strong>g(" "); (* Tabelle *)<br />

FOR column:=1 TO max<strong>in</strong>dex DO WriteCard(column, 3) END;<br />

WriteLn;<br />

WriteLn;<br />

FOR l<strong>in</strong>e:=1 TO max<strong>in</strong>dex DO<br />

WriteCard(l<strong>in</strong>e, 3);<br />

FOR column:=1 TO max<strong>in</strong>dex DO<br />

WriteCard(ggT(l<strong>in</strong>e, column), 3)<br />

END;<br />

WriteLn<br />

END<br />

END WriteGGTs;<br />

VAR max<strong>in</strong>dex : CARDINAL; (* Zeilenanzahl=Spaltenanzahl der Tabelle *)<br />

BEGIN<br />

IF ReadMaxIndex(max<strong>in</strong>dex) THEN WriteGGTs(max<strong>in</strong>dex)<br />

ELSE<br />

WriteStr<strong>in</strong>g("falsche E<strong>in</strong>gabe");<br />

WriteLn<br />

END<br />

END ggt.<br />

Abbildung 2: Modula-2 Programm zur Berechnung des ggT


6 <strong>Informatik</strong> B SS 03<br />

1.2.3 Parameterübergabe-Methoden<br />

Formale Parameter werden im Kopf e<strong>in</strong>er Prozedur angegeben. Im Rumpf<br />

tauchen sie <strong>in</strong> Anweisungen auf.<br />

Formale Parameter s<strong>in</strong>d Platzhalter für konkrete Parameter, die beim<br />

Prozeduraufruf übergeben werden.<br />

Parameterübergabe me<strong>in</strong>t das Match<strong>in</strong>g der konkreten mit den formalen<br />

Parametern.<br />

Es gibt verschiedene Parameterübergabe-Methoden:<br />

– Call-by-value: Argumente werden zunächst ausgewertet. Die resultierenden<br />

Werte werden dann übergeben. Zuweisungen an die Parameter <strong>in</strong>nerhalb<br />

der Prozedur haben ke<strong>in</strong>e Auswirkung auf die im aufrufenden Kontext<br />

vorhandenen Variablen. (Man spricht auch von strict evaluation.)<br />

Beim Aufruf ggT(l<strong>in</strong>e, columnn) <strong>in</strong> der Prozedur WriteGGTs werden<br />

zunächst l<strong>in</strong>e und column durch die aktuellen Werte dieser Variablen<br />

ersetzt. Mit diesen Werten werden dann die Parameter von ggT – arg1,<br />

arg2 – <strong>in</strong>itialisiert.<br />

– Call-by-reference: Die Referenz (Adresse) der Argument-Variablen wird an<br />

die Prozedur übergeben. Ausführung der Prozedur kann Seiteneffekte auf<br />

die Werte der Argumente im aufrufenden Kontext haben.<br />

Im Modula-2-Programm <strong>in</strong> Abb. 2 ist max<strong>in</strong>dex e<strong>in</strong> call-by-reference<br />

Parameter. max<strong>in</strong>dex wird <strong>in</strong> der Prozedur ReadMaxIndex belegt. Wird<br />

die Prodzedur WriteGGTs aufgerufen, so hat max<strong>in</strong>dex den <strong>in</strong><br />

ReadMaxIndex ermitteltend Wert.<br />

– Call-by-name: Argumentausdrücke werden unausgewertet (als Referenz<br />

auf Code, der den Wert des Arguments liefert, zusammen mit e<strong>in</strong>er<br />

Umgebung für die Werte von freien Variablen) übergeben. (lazy evaluation)<br />

Um Mehrfachauswertung zu vermeiden, wird üblicherweise mit<br />

Graphreduktionsmethoden gearbeitet (call-by-need).<br />

Call-by-name wurde im Rahmen von Algol-60 entwickelt und wird heute vor<br />

allem bei funktionalen Sprachen verwendet. Vorteile: Term<strong>in</strong>ation bei<br />

unendlichen Datenstrukturen (lazy lists) und Rekursion.


<strong>Informatik</strong> B SS 03 7<br />

1.2.4 Call-by-Value versus Call-by-Reference<br />

MODULE ParaPass;<br />

FROM InOut IMPORT WriteCard, WriteLn;<br />

PROCEDURE P(x : CARDINAL; y : CARDINAL; VAR z : CARDINAL);<br />

VAR help : CARDINAL;<br />

BEGIN<br />

help := x;<br />

x := 15;<br />

z := 2 * x + help;<br />

y := 0<br />

END P;<br />

VAR a, b, c : CARDINAL;<br />

BEGIN<br />

a := 1;<br />

b := 2;<br />

c := 3;<br />

P(a, a+b, c); (* call by value: Ausdruecke moeglich *)<br />

(* call by reference: muss Variable se<strong>in</strong> *)<br />

WriteCard(a); (* immer noch 1 *)<br />

WriteCard(b); (* immer noch 2 *)<br />

WriteCard(c) (* 31 *)<br />

END ParaPass.<br />

Abbildung 3: Call-by-Value und Call-by-Reference <strong>in</strong> Modula<br />

1.2.5 Call-by-Value versus Call-by-Name<br />

(Beispiel <strong>in</strong> ML)<br />

fun sqr(x) : <strong>in</strong>t = x*x; (* uses its argument twice *)<br />

fun zero(x :<strong>in</strong>t) = 0; (* ignores its argument *)<br />

¡<br />

Auswertung von zero(sqr(sqr(sqr(2)))) mit call-by-value:<br />

zero(sqr(sqr(sqr(2)))) zero(sqr(sqr(2 2)))<br />

zero(sqr(sqr(4)))<br />

...<br />

zero(256)<br />

0<br />

Auswertung von zero(sqr(sqr(sqr(2)))) mit call-by-name:<br />

direkt Rückgabe von 0.


¦¡£¢¤¦¡£¢¤¦¡£¢¤¦¡£¢¥¤§¦¨<br />

¦¦¡©¢¥¤¦¡£¢¤¦¡£¢¥¤§¦¨<br />

8 <strong>Informatik</strong> B SS 03<br />

Aber (ohne Graphreduktion) resultiert unnötige Mehrfachberechnung:<br />

sqr(sqr(sqr(2))) ¡£¢¥¤§¦¨¡©¢¥¤¦¡£¢¤¦¡£¢¥¤§¦¨<br />

...<br />

¦¡£¢¤¦¡£¢¤¦¡£¢¥¤§¦¨<br />

¦¦¡£¢¤¦¡£¢¥¤§¦¨<br />

1.2.6 Call-by-value <strong>in</strong> <strong>Java</strong><br />

In Sprachen wie Modula ! und existieren sowohl call-by-value als auch<br />

call-by-reference als Parameterübergabe-Mechanismen.<br />

Call-by-reference macht auch dann S<strong>in</strong>n, wenn die Parameterwerte nicht<br />

<strong>in</strong>nnerhalb der Prozedur geändert werden sollen: Speicherersparnis und<br />

Effizienz (z.B. für die Übergabe von Arrays entfällt Kopieren)<br />

In <strong>Java</strong> existiert call-by-value als e<strong>in</strong>ziger Parameterübergabe-Mechanismus.<br />

Meist liest man Aussagen wie “primitive types are passed by value, but objects<br />

are passed by reference”, die Verwirrung stiften können.<br />

Auch wenn <strong>Objekt</strong>e natürlich als Referenzen übergeben werden, ist der<br />

Parameterübergabe-Mechanismus call-by-value!<br />

Das Programm PassByValue “missbraucht” die Klasse Button aus<br />

java.awt etwas: genutzt wird nur das Feld label.<br />

// Copyright (C) Andrew D. Mackie, 1999.<br />

// http://www.javama<strong>in</strong>.com/<br />

import java.awt.*;<br />

public class PassByValue {<br />

//Demonstrates that <strong>Java</strong> parameters are always passed by value<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args){<br />

System.out.pr<strong>in</strong>tln("In ma<strong>in</strong>");<br />

//the reference to an object is passed by value<br />

Button b = new Button("AAA");<br />

System.out.pr<strong>in</strong>tln("The value of b’s label is " + b.getLabel());<br />

methodX(b);<br />

System.out.pr<strong>in</strong>tln("Back <strong>in</strong> ma<strong>in</strong>");<br />

System.out.pr<strong>in</strong>tln("The value of b’s label is " + b.getLabel());<br />

System.out.pr<strong>in</strong>tln("");<br />

//primitives are passed by value as well<br />

<strong>in</strong>t i = 5;<br />

System.out.pr<strong>in</strong>tln("The value of i is " + i);<br />

methodZ(i);<br />

System.out.pr<strong>in</strong>tln("Back <strong>in</strong> ma<strong>in</strong>");


<strong>Informatik</strong> B SS 03 9<br />

}<br />

System.out.pr<strong>in</strong>tln("The value of i is " + i);<br />

System.exit(0);<br />

//the reference to an object is passed by value<br />

public static void methodX(Button y){<br />

System.out.pr<strong>in</strong>tln("In methodX");<br />

System.out.pr<strong>in</strong>tln("The value of y’s label is " + y.getLabel());<br />

//update the button object that both y and b refer to<br />

y.setLabel("BBB");<br />

System.out.pr<strong>in</strong>tln("The value of y’s label is " + y.getLabel());<br />

//make y reference a different object - doesn’t affect variable b<br />

y = new Button("CCC");<br />

System.out.pr<strong>in</strong>tln("The value of y’s label is " + y.getLabel());<br />

//updat<strong>in</strong>g button that y now references<br />

//has no affect on button referenced by b<br />

y.setLabel("DDD");<br />

System.out.pr<strong>in</strong>tln("The value of y’s label is " + y.getLabel());<br />

}<br />

}<br />

//primitives are passed by value as well<br />

public static void methodZ(<strong>in</strong>t j){<br />

System.out.pr<strong>in</strong>tln("In methodZ");<br />

System.out.pr<strong>in</strong>tln("The value of j is " + j);<br />

//change value of j - doesn’t affect variable i with<strong>in</strong> ma<strong>in</strong><br />

j = 6;<br />

System.out.pr<strong>in</strong>tln("The value of j is " + j);<br />

}<br />

Ausgabe:<br />

In ma<strong>in</strong><br />

The value of b’s label is AAA<br />

In methodX<br />

The value of y’s label is AAA<br />

The value of y’s label is BBB<br />

The value of y’s label is CCC<br />

The value of y’s label is DDD<br />

Back <strong>in</strong> ma<strong>in</strong><br />

The value of b’s label is BBB<br />

The value of i is 5<br />

In methodZ<br />

The value of j is 5<br />

The value of j is 6<br />

Back <strong>in</strong> ma<strong>in</strong><br />

The value of i is 5


10 <strong>Informatik</strong> B SS 03<br />

b<br />

Button<br />

Object<br />

b<br />

Button<br />

Object<br />

y<br />

y<br />

Another<br />

Button<br />

Object<br />

Abbildung 4: Call-by-Value <strong>in</strong> <strong>Java</strong> (siehe Klasse PassByValue)<br />

1.3 Sprach-Implementierung<br />

E<strong>in</strong> Programm (Code, Quellcode) wird geschrieben, um Aufgaben automatisch<br />

von e<strong>in</strong>em Rechner ausführen zu lassen.<br />

Üblicherweise wird e<strong>in</strong> Programm gestartet, erhält e<strong>in</strong>e E<strong>in</strong>gabe, führt<br />

Berechnungen <strong>in</strong> Abhängigkeit von dieser E<strong>in</strong>gabe durch, liefert e<strong>in</strong>e Ausgabe<br />

und wird beendet. (Programmausführung)<br />

Die Laufzeit des Programms me<strong>in</strong>t die Zeit vom Aufruf bis zur Term<strong>in</strong>ation; die<br />

Compilezeit me<strong>in</strong>t die Zeit, während das Programm <strong>in</strong> Masch<strong>in</strong>en- oder<br />

Zwischencode überführt wird.<br />

In anderem Zusammenhang me<strong>in</strong>t Laufzeit auch die Zeitdauer, die zur<br />

Ausführung e<strong>in</strong>es Programms benötigt wird: Diese ist abhängig von der<br />

Masch<strong>in</strong>e und <strong>in</strong>sbesondere von der Aufwandsklasse der zugrundeliegenden<br />

Algorithmen. (Die Komplexität von Problemen, die gelöst werden sollen, bed<strong>in</strong>gt<br />

die untere Schranke des Aufwands von Algorithmen. Thema <strong>in</strong> der Vorlesung<br />

“Theoretische <strong>Informatik</strong>”)<br />

E<strong>in</strong> Compiler übersetzt e<strong>in</strong> Programm <strong>in</strong> e<strong>in</strong>e Ausgabesprache (häufig<br />

Masch<strong>in</strong>encode). Beispiele für Compilersprachen s<strong>in</strong>d C und Pascal. (siehe<br />

Abb. 5)<br />

E<strong>in</strong> Interpreter ist e<strong>in</strong>e Masch<strong>in</strong>e auf höherer Ebene, die das Programm direkt<br />

ausführt. Beispiele für Interpretersprachen s<strong>in</strong>d Common Lisp und Prolog.<br />

(siehe Abb. 5)<br />

Alternativ gibt es hybride Implementationssysteme: Das Programm wird<br />

zunächst <strong>in</strong> e<strong>in</strong>en Zwischencode (Bytecode) übersetzt, der auf beliebige<br />

Masch<strong>in</strong>en portierbar ist, für die e<strong>in</strong> entsprechender Bytecode-Interpreter und<br />

dazugehöriges Laufzeitsystem existiert. Beispielsweise erzeugt der


<strong>Informatik</strong> B SS 03 11<br />

Quellprogramm<br />

Quellprogramm<br />

Quellprogramm<br />

Compiler<br />

Lexikalische Analyse<br />

Lexikalische Analyse<br />

ÜBERSETZZEIT<br />

Symbol−<br />

tabelle<br />

Syntaxanalyse<br />

Zwischencode−Erzeugung<br />

Semantische Analyse<br />

Syntaxanalyse<br />

Zwischencode−Erzeugung<br />

(Optimierung)<br />

Codeerzeugung<br />

LAUFZEIT<br />

E<strong>in</strong>gabe<br />

Masch<strong>in</strong>encode<br />

Masch<strong>in</strong>e<br />

E<strong>in</strong>gabe<br />

Interpreter<br />

E<strong>in</strong>gabe<br />

Zwischencode<br />

Interpreter<br />

Ausgabe<br />

Ausgabe<br />

Ausgabe<br />

Abbildung 5: Implementations-Systeme: Compiler, Interpreter, Hybrid<br />

<strong>Java</strong>-Compiler Bytecode, der von der <strong>Java</strong> Virtual Mach<strong>in</strong>e (rechnerspezifisch)<br />

ausgeführt wird. (siehe Abb. 5)<br />

Arbeitsschritte e<strong>in</strong>es Compilers:<br />

– Lexikalische Analyse: Zerlegung des Programmtextes <strong>in</strong> lexikalische<br />

E<strong>in</strong>heiten (Reservierte Worte, Operatoren, Identifier; Kommentare werden<br />

ignoriert)<br />

– Syntaktische Analyse (Parsierung): Erzeugung e<strong>in</strong>es Parse-Baums, der die<br />

syntaktische Struktur des Programms repräsentiert.<br />

– Semantische Analyse: üblicherweise Typisierung<br />

Zwischencode-Erzeugung (wenn ohne Optimierungsoption, z. B.<br />

Assembler)<br />

– Optimierung (optional): Methoden der Programmtransformation, partielle<br />

Evaluation (Ersetzen von Ausdrücken durch ihr Ergebnis)<br />

– Codegenerierung: äquivalentes Masch<strong>in</strong>encode-Programm<br />

– Symboltabelle: Datenbasis für die Compilierung, enthält <strong>in</strong>sbesondere<br />

Typen und Eigenschaften der benutzergenerierten Namen im Programm.<br />

Vor der Ausführung müssen noch die entsprechenden Programme des<br />

Betriebssystems und eventuell Bibliotheksfunktionen mit dem<br />

Masch<strong>in</strong>enprogramm verbunden werden (l<strong>in</strong>k<strong>in</strong>g, E<strong>in</strong>fügen der entsprechenden<br />

Adressen <strong>in</strong> das Masch<strong>in</strong>encode-Programm).<br />

Ausführung: In fetch-execute-Zyklen: Jede Masch<strong>in</strong>en<strong>in</strong>struktion muss vom<br />

Speicher <strong>in</strong> den Prozessor übertragen werden und Ergebnisse/Verweise


£<br />

<br />

<br />

@<br />

C<br />

<br />

¡<br />

¡<br />

¡<br />

¡<br />

¡<br />

¡<br />

¡<br />

¡<br />

¡<br />

12 <strong>Informatik</strong> B SS 03<br />

müssen <strong>in</strong> den Speicher zurückgeschrieben werden. (von-Neumann<br />

Flaschenhals)<br />

1.4 Syntaktische Struktur<br />

1.4.1 Formale Sprachen<br />

Die Syntax e<strong>in</strong>er Sprache legt fest, wie e<strong>in</strong>zelne Symbole zu größeren<br />

Strukturen zusammengefügt werden dürfen.<br />

Die Syntax e<strong>in</strong>er Sprache wird – für natürliche wie für formale Sprachen – durch<br />

e<strong>in</strong>e Grammatik beschrieben: ¢¡ ¦¤£¦¥¨§©¥¥ mit<br />

– £ als Menge der Nonterm<strong>in</strong>al-Symbole<br />

– § als Menge der Term<strong>in</strong>alsymbole<br />

– als Menge der Grammatikregeln (Produktionen) und<br />

– £ als Startsymbol.<br />

Beispiel (e<strong>in</strong>facher Ausschnitt des Englischen)<br />

¡ ¦¥£¥¥¥£¥! "¥#¨$%&¥')(&¥*"+-,.¥0/1+-2 ¡#&¥#(&¡3¥04)( ¤65§¡6 "¥¥ mit :<br />

£<br />

7<br />

<br />

£7<br />

8£<br />

:9;£¦<br />

¨$%<br />

¡#<br />

')(&


Abbildung 6: Ableitungsbaum für die e<strong>in</strong>fache reguläre Sprache ¦ EFG GHE <br />

¡<br />

¡<br />

¡<br />

¡<br />

¡<br />

¡<br />

<strong>Informatik</strong> B SS 03 13<br />

S<br />

0A<br />

1B<br />

01S<br />

01 10<br />

10S<br />

010A 011B 100A 101B<br />

0101S 0101 0110 0110S 1001 1001S 1010 1010S<br />

Die Sprache ist regulär und ¦ EFG G#E<br />

kann durch den regulären Ausdruck<br />

dargestellt werden. bezeichnet , bezeichnet die positive Hülle<br />

¡ <br />

(Menge aller Konkatenationen von ¢ Elementen aus ohne das ¢¤£ leere Wort),<br />

bezeichnet die Kleene’sche Hülle.)<br />

¢ ¢¦¥<br />

¢<br />

Reguläre Sprachen s<strong>in</strong>d die e<strong>in</strong>fachsten Sprachen <strong>in</strong> der sogenannten Chomsky<br />

Hierarchie. Sie können durch endliche Automaten erkannt werden.<br />

Programmiersprachen s<strong>in</strong>d im Wesentlichenn kontextfreie Sprachen.<br />

Kontextfreie Sprachen folgen <strong>in</strong> der Chomsky-Hierarchie auf die regulären.<br />

Während Grammatikregeln für reguläre Sprachen l<strong>in</strong>ks- bzw. rechts-l<strong>in</strong>ear s<strong>in</strong>d<br />

(J bzw. ( ), s<strong>in</strong>d bei kontextfreien Sprachen beliebige<br />

(<br />

Komb<strong>in</strong>ationen aus Term<strong>in</strong>al- und Nonterm<strong>in</strong>alsymbolen erlaubt.<br />

¨§J E3 G&¥<br />

Beispiel:<br />

von genauso G vielen -en.<br />

EFG beschreibt die Sprache E©FG© , also 1E -en gefolgt<br />

Die Kenntnis formaler Sprachen ist wichtig für die Implementierung von<br />

Programmiersprachen. Das Sprachdesign (die Grammatik) bed<strong>in</strong>gt wie<br />

aufwendig die Parsierung von Programmen (siehe Phasen der Compilierung) ist!<br />

Formale Sprachen s<strong>in</strong>d Gegenstand der Vorlesung “Theoretische <strong>Informatik</strong>”.<br />

1.4.2 BNF und EBNF<br />

Die Syntax von Programmiersprachen wird üblicherweise <strong>in</strong> Backus-Naur Form<br />

(BNF) oder erweiterter Backus-Naur Form (EBNF) dargestellt.<br />

BNF und EBNF s<strong>in</strong>d zu kontextfreien Grammatiken äquivalent.<br />

BNF und EBNF s<strong>in</strong>d Meta-Sprachen zur Beschreibung von Sprachen.<br />

Produktionsregeln haben die Form Kopf ::= Körper.<br />

Nichtterm<strong>in</strong>ale werden <strong>in</strong> spitzen Klammern dargestellt, dadurch können auch<br />

ganze Wörter statt nur e<strong>in</strong>zelne Symbole als Nonterm<strong>in</strong>ale verwendet werden.<br />

Alternative rechte Seiten werden durch e<strong>in</strong>en senkrechten Strich getrennt<br />

dargestellt.


14 <strong>Informatik</strong> B SS 03<br />

class_declaration<br />

::=<br />

{ modifier } "class" identifier<br />

[ "extends" class_name ]<br />

[ "implements" <strong>in</strong>terface_name { "," <strong>in</strong>terface_name } ]<br />

"{" { field_declaration } "}"<br />

for_statement<br />

::=<br />

"for" "(" ( variable_declaration | ( expression ";" ) | ";" )<br />

[ expression ] ";"<br />

[ expression ] ";"<br />

")" statement<br />

Abbildung 7: Klassen-Def<strong>in</strong>ition und For-Statement von <strong>Java</strong> <strong>in</strong> EBNF<br />

Runde Klammern regeln Zusammenfassung und Vorrang. Alternativen haben<br />

den ger<strong>in</strong>gsten Vorrang.<br />

In der EBNF s<strong>in</strong>d zusätzlich folgende Abkürzungen e<strong>in</strong>geführt: eckige Klammern<br />

([ ]) umschliessen Symbolfolgen, die null oder e<strong>in</strong>mal auftreten dürfen;<br />

geschweifte Klammern (7 ) umschliessen Symbolfolgen, die beliebig oft (auch<br />

null-mal) vorkommen dürfen.<br />

Anschaulich stellt man BNF durch Syntaxdiagramme dar.<br />

<strong>Java</strong> <strong>in</strong> EBNF f<strong>in</strong>det sich unter: http:<br />

//cui.unige.ch/db-research/Enseignement/analyse<strong>in</strong>fo/JAVA/<br />

Beispiele s<strong>in</strong>d <strong>in</strong> Abb. 7 angegeben. (Hier werden Term<strong>in</strong>alsymbole <strong>in</strong><br />

Anführungszeichen angegeben und Nonterm<strong>in</strong>ale ersche<strong>in</strong>en ohne spitze<br />

Klammern.)<br />

Die e<strong>in</strong>deutige und vollständige Beschreibung der Syntax e<strong>in</strong>er


¡<br />

<strong>Informatik</strong> B SS 03 15<br />

S<br />

S<br />

if<br />

E1<br />

then<br />

S<br />

if<br />

E1<br />

then<br />

S<br />

else<br />

S2<br />

if<br />

E2 then S1<br />

else<br />

S2<br />

if<br />

E2 then S1<br />

Abbildung 8: ‘Dangl<strong>in</strong>ge-else’ Une<strong>in</strong>deutigkeit<br />

Programmiersprache ist wichtig für den Programmierer, um syntaktisch korrekte<br />

Programme zu schreiben, und Grundlage für den Parser.<br />

E<strong>in</strong> Parser ist e<strong>in</strong> Akzeptor für e<strong>in</strong>en Programmtext. E<strong>in</strong> syntaktisch korrektes<br />

Programm wird e<strong>in</strong> e<strong>in</strong>en Syntaxbaum überführt. E<strong>in</strong> guter Parser gibt<br />

<strong>in</strong>formative Fehlermeldungen, falls das Programm nicht der Grammatik der<br />

Programmiersprache genügt.<br />

Programmiersprachen sollten syntaktisch e<strong>in</strong>deutig se<strong>in</strong>. Das heisst, jede Folge<br />

von Symbolen entspricht genau e<strong>in</strong>em Parse-Baum.<br />

Wenn Une<strong>in</strong>deutigkeiten existieren, werden sie durch Konventionen aufgelöst.<br />

Beispiel: “dangl<strong>in</strong>g-else ambiguity”<br />

§<br />

¨§<br />

¡ ¨$% ©¡ ¡# ¢¡<br />

¢¡¦§ ¡¤£¦¥<br />

entspricht den Alternativen:<br />

¨§<br />

¢¡¦§ § ¡¤£¦¥<br />

¡ ¨$% ©¡<br />

§<br />

¨§<br />

¡ ¨$% ©¡ ¡- ¢¡<br />

¢¡¦§ ¡¤£¦¥<br />

Zu welchem if gehört das else<br />

if E1 then if E2 then S1 else S2<br />

(Parse-Bäume siehe Abb. 8)<br />

Typische Auflösung: Matche else mit dem am nächsten stehenden noch<br />

ungematchten if. Die Kenntnis entsprechender Konventionen s<strong>in</strong>d<br />

Voraussetzung für die Vermeidung von Programmierfehlern!<br />

1.5 Semantik und Pragmatik<br />

Die formale Beschreibung der Syntax e<strong>in</strong>er Programmiersprache ist relativ leicht<br />

nachvollziehbar.<br />

Üblicherweise verb<strong>in</strong>den wir durch die Wahl der Schlüsselworte bereits e<strong>in</strong>e<br />

(<strong>in</strong>tuitive und nicht immer zutreffende) Bedeutung mit den Sprachkonstrukten.<br />

Aber: Dem Parser ist es völlig egal, ob im Programm “for” oder “bla” steht,<br />

solange er die entsprechende Regel <strong>in</strong> der Grammatik f<strong>in</strong>den kann!<br />

Es ist wünschenswert, dass die Semantik e<strong>in</strong>er Sprache genauso e<strong>in</strong>deutig und<br />

präzise beschrieben wird wie die Syntax.


¡<br />

16 <strong>Informatik</strong> B SS 03<br />

Nur für wenige Sprachen ist jedoch e<strong>in</strong>e formale Semantik angegeben (z.B. für<br />

die funktionale Sprache ML). Meistens (z.B. bei <strong>Java</strong>) wird die Bedeutung der<br />

Sprachkonstrukte <strong>in</strong>formell natürlichsprachig beschrieben.<br />

Für voll durchformalisierte Sprachen ist es möglich, Korrektheitsbeweise<br />

wenigstens zum Teil automatisch durchzuführen (Spezifikationssprachen wie ).<br />

Verschiedene Möglichkeiten, die Semantik e<strong>in</strong>er Sprache formal anzugeben:<br />

(siehe Vorlesung “Theoretische <strong>Informatik</strong>”, Thema Algebraische Spezifikation)<br />

– Operationale Semantik: Regeln, nach denen Ausdrücke ausgewertet<br />

werden (Reduktionssemantik).<br />

– Denotationale Semantik: Interpretation der Sprachkonstrukte <strong>in</strong> e<strong>in</strong>e<br />

“bekannte” Sprache (z.B. Algebra).<br />

– Axiomatische Semantik: Angabe von Gesetzen und Regeln (Hoare Kalkül).<br />

Später wird für e<strong>in</strong>en kle<strong>in</strong>en Ausschnitt von <strong>Java</strong> (“Featherweight <strong>Java</strong>”) die<br />

formale Semantik dargestellt. Beweis von Typsicherheit<br />

Die Pragmatik e<strong>in</strong>er Sprache beschreibt, wie die Konstrukte e<strong>in</strong>er Sprache<br />

s<strong>in</strong>nvoll e<strong>in</strong>gesetzt werden.<br />

(Entscheidung, ob e<strong>in</strong>e pre-check-loop (while) oder e<strong>in</strong>e post-check-loop<br />

(do-while) verwendet werden soll; Entscheidung zwischen Schleife und<br />

rekursiver Lösung.)<br />

Um das, was e<strong>in</strong>e Sprache bietet, optimal auszunutzen, ist genaue Kenntnis des<br />

realisierten Paradigmas sowie der Implementierung der Sprache wichtig!


¡<br />

¡<br />

<strong>Informatik</strong> B SS 03 17<br />

2 <strong>Java</strong> und <strong>Objekt</strong>-Orientierung<br />

2.1 Grundkonzepte der <strong>Objekt</strong>-Orientierung<br />

2.1.1 Eigenschaften <strong>Objekt</strong>-Orientierter Sprachen<br />

(nach Alan Kay, e<strong>in</strong>em der Smalltalk-Entwickler)<br />

Alles ist e<strong>in</strong> <strong>Objekt</strong>: <strong>Objekt</strong>e s<strong>in</strong>d “mächtige Daten”. Sie halten Daten und<br />

können Operationen auf ihren eigenen Daten ausführen. Der “Datentyp” e<strong>in</strong>es<br />

<strong>Objekt</strong>s ist die Klasse mit der es erzeugt wurde.<br />

Beispiel: <strong>Objekt</strong> circle vom Typ Circle kann se<strong>in</strong>en Flächen<strong>in</strong>halt<br />

berechnen.<br />

Anmerkung: In <strong>Java</strong> ist im Gegensatz zu Smalltalk nicht alles e<strong>in</strong> <strong>Objekt</strong><br />

(primitive Datentypen).<br />

E<strong>in</strong> Programm ist e<strong>in</strong>e Menge <strong>in</strong>teragierender <strong>Objekt</strong>e: E<strong>in</strong> <strong>Objekt</strong> kann<br />

Botschaften an e<strong>in</strong> anderes <strong>Objekt</strong> schicken, z.B. e<strong>in</strong>e Aufforderung, e<strong>in</strong>e<br />

bestimmte Methode auszuführen.<br />

<strong>Objekt</strong>e können zu komplexen <strong>Objekt</strong>en komb<strong>in</strong>iert werden: Komplexe <strong>Objekt</strong>e<br />

können aus e<strong>in</strong>fachen Bauste<strong>in</strong>en aufgebaut werden, <strong>in</strong>dem <strong>Objekt</strong>e<br />

Referenzen auf andere <strong>Objekt</strong>e enthalten.<br />

Alle <strong>Objekt</strong>e e<strong>in</strong>es Typs können dieselben Botschaften erhalten: Dies wird vor<br />

allem im H<strong>in</strong>blick auf Klassenhierarchien <strong>in</strong>teressant. Jedes circle-<strong>Objekt</strong><br />

kann alle Methoden ausführen, die <strong>in</strong> der Circle Klasse def<strong>in</strong>iert s<strong>in</strong>d. Ist<br />

Circle e<strong>in</strong> spezieller Shape, so können auch (nicht-überschriebene)<br />

Shape-Methoden ausgeführt werden.<br />

2.1.2 Pr<strong>in</strong>zipien der <strong>Objekt</strong>-Orientierten <strong>Programmierung</strong><br />

Zwei wesentliche Pr<strong>in</strong>zipien der objekt-<strong>orientierte</strong>n <strong>Programmierung</strong> s<strong>in</strong>d<br />

Kapslung (Encapsulation) und<br />

Wiederverwendbarkeit (reuse).<br />

Zur Kapslung gehört die Organisation von Daten und Methoden <strong>in</strong> Klassen<br />

sowie das Verstecken von Implementierungsdetails (<strong>in</strong>formation hid<strong>in</strong>g).<br />

Kapslung ist <strong>in</strong> mehrfacher H<strong>in</strong>sicht guter Programmierstil:<br />

– Das Problem wird <strong>in</strong> kle<strong>in</strong>ere, unabhängige Komponenten gegliedert und ist<br />

damit leichter zu überschauen, leichter zu testen und weniger fehleranfällig.<br />

– Das Verstecken von Implementationsdetails erlaubt e<strong>in</strong>e bessere<br />

Austausch von Code. Beispielsweise ist es notwendig zu wissen, dass die<br />

e<strong>in</strong>e Klasse Tree e<strong>in</strong>e Methode <strong>in</strong>sert anbietet, aber nicht, wie <strong>in</strong>sert<br />

genau realisiert ist (z.B. wie gewährleistet wird, dass der Baum<br />

ausgewogen ist).


¡<br />

¡<br />

18 <strong>Informatik</strong> B SS 03<br />

– Schränkt man zudem die Zugriffsrechte für Felder e<strong>in</strong> (z.B. private Felder<br />

mit public Zugriffsmethoden) kann man unerwünscht Seiteneffekte<br />

vermeiden, wie etwa, dass e<strong>in</strong> zu e<strong>in</strong>er anderen Klasse gehöriges <strong>Objekt</strong><br />

e<strong>in</strong> Feld unkontrolliert ändert und möglicherweise Inkonsistenzen erzeugt.<br />

– Neben Klassen dienen Pakete zur Kapslung von Information<br />

(Zugriffsrechte). Pakete dienen <strong>in</strong>sbesondere der Strukturierung.<br />

Wiederverwendbarkeit ist möglich, weil Klassen unabhängig von konkreten<br />

Daten def<strong>in</strong>ierbar s<strong>in</strong>d. Aussderdem können bereits def<strong>in</strong>ierte Klassen durch<br />

Vererbung für spezielle Bedürfnisse angepasst werden. Generell sollte Code so<br />

geschrieben und dokumentiert werden, dass die Funktionalität der Klasse<br />

transparent ist und so Wiederverwendbarkeit (<strong>in</strong> eigenen Projekten oder<br />

Nutzung durch andere) erleichtert wird.<br />

<strong>Java</strong> bietet e<strong>in</strong>e Menge vordef<strong>in</strong>ierter Klassen an (<strong>Java</strong> APIs). Es ist guter Stil,<br />

diese (gut getesteten, bewährten) Klassen zu verwenden.<br />

Es ist wichtig, sich e<strong>in</strong>en Überblick über die <strong>Java</strong> APIs zu verschaffen:<br />

http://www-lehre.<strong>in</strong>f.uos.de/manuals/jdk1.4/docs/<br />

2.2 Die Sprache <strong>Java</strong><br />

2.2.1 Entstehungsgeschichte<br />

Start 1990 bei Sun, Gruppe von James Gosl<strong>in</strong>g<br />

Angelehnt an C++, Elemente aus Smalltalk (Bytecode, Garbage Collection) –<br />

via Objective C objekt-orientiert.<br />

Bytecode Compilierung und Garbage Collection s<strong>in</strong>d Konzepte, die ursprünglich<br />

im Rahmen von Lisp entwickelt wurden. (Guy Steele, der aus dem Bereich<br />

Common Lisp bekannt ist, ist bei Sun mitverantwortlich für die Entwicklung der<br />

<strong>Java</strong> Sprachspezifikation!)<br />

Ziel: Entwicklung e<strong>in</strong>er Hochsprache für hybride Systeme im<br />

Consumer-Electronic Bereich: Steuerung von Waschmasch<strong>in</strong>en,<br />

Telefonanlagen, ... ( ursprünglicher Name “Oak”)<br />

Boom des WWW 1993<br />

E<strong>in</strong>satz für Internet-Anwendungen<br />

<strong>Java</strong>-Applets: kle<strong>in</strong>e Programme, die <strong>in</strong> HTML-Seiten e<strong>in</strong>gebunden werden<br />

können (Sun Demo Web-Browser Hot<strong>Java</strong> <strong>in</strong> den 90-ern)<br />

Durchbruch 1995: Netscape Navigator 2.0 mit <strong>in</strong>tegrierter <strong>Java</strong> Virtual Mach<strong>in</strong>e<br />

<strong>Java</strong>-Versionen, siehe Tabelle 1.<br />

Anmerkung: <strong>Java</strong> 1 (<strong>Java</strong> 1.0 und 1.1), <strong>Java</strong> 2 (ab <strong>Java</strong> 1.2) bezeichnen<br />

Versionen mit vielen neuen Features; <strong>Java</strong> 1.1, 1.2, 1.3, 1.4 bezeichnet<br />

Versionen mit e<strong>in</strong>igen neuen Features; von <strong>Java</strong> 1.2 zu 1.3 vor allem<br />

Geschw<strong>in</strong>digkeitsverbesserungen; <strong>Java</strong> 1.1.3 und andere dreistellige Nummern<br />

bezeichnen <strong>in</strong> kle<strong>in</strong>eren Details verschiedene Implementierungen e<strong>in</strong>er Version<br />

(“m<strong>in</strong>or releases”).


¡<br />

¡<br />

<strong>Informatik</strong> B SS 03 19<br />

Version # Klassen # Pakete<br />

Tabelle 1: <strong>Java</strong> Versionen<br />

Ersch. Anmerkungen<br />

1.0 212 8 Jan. 1996<br />

1.1 504 23 Feb. 1997<br />

1.2 1520 59 Dez. 1998 auch “<strong>Java</strong> 2, Release 1.2”<br />

1.3 1840 76 Mai 2000 auch ”<strong>Java</strong> 2, Release 1.3, Standard Edition”<br />

1.4 2977 135 Feb. 2002 (1.4.0)<br />

2.2.2 <strong>Java</strong> Buzzwords<br />

Portabel: JVM (virtuelle Masch<strong>in</strong>e ist bewährtes Pr<strong>in</strong>zip); alle Datentypen s<strong>in</strong>d<br />

unabhängig von der Implementierung festgelegt (Standards)<br />

<strong>Objekt</strong>-Orientiert: Kapslung <strong>in</strong> Klassen, Vererbung<br />

Multithreaded: Zerlegung <strong>in</strong> e<strong>in</strong>zelne “Prozesse” (Threads), die unabhängig<br />

vone<strong>in</strong>ander ablaufen können<br />

Verteilt: Remote Method Invocation (RMI), Netzwerk-<strong>Programmierung</strong> basierend auf<br />

Client-Server Architekturen<br />

Robust: ke<strong>in</strong> explizites Arbeiten mit Zeigern, Speicherverwaltung wird von <strong>Java</strong><br />

gehandhabt (Garbage Collection)<br />

Sicher: Plattform erlaubt Laden von unbekanntem Code, der <strong>in</strong> e<strong>in</strong>er sicheren<br />

Umgebung abläuft (ke<strong>in</strong> Lesen und Schreiben von Festplatte, ...)<br />

Dynamisch, Erweiterbar: Organisation des Programmcodes <strong>in</strong> Klassen, <strong>in</strong><br />

verschiedenen Dateien gespeichert, load when needed<br />

Internationalisierung: 16-bit Unicode (statt ASCII)<br />

2.2.3 Sprache, Virtuelle Masch<strong>in</strong>e, Plattform<br />

<strong>Java</strong>, die Programmiersprache:<br />

– Sprache, <strong>in</strong> der <strong>Java</strong> Programme geschrieben werden.<br />

– Compilation <strong>in</strong> Byte-Code mit javac Date<strong>in</strong>amen (<strong>in</strong>klusive Suffix,<br />

.java)<br />

– portable Masch<strong>in</strong>ensprache<br />

– Ausgabe: *.class Datei(en)<br />

– Das Kommando javac erlaubt verschiedene Optionen, wie:<br />

-classpath path: Liste von Verzeichnissen, <strong>in</strong> denen nach weiteren<br />

im Quellcode benutzten Klassen gesucht wird. (default: aktuelles<br />

Verzeichnis)<br />

-d directory: Verzeichnis, <strong>in</strong> dem die erzeugten class-Dateien<br />

abgelegt werden . (default: Verzeichnis der *.java Dateien)


¡<br />

¡<br />

¡<br />

20 <strong>Informatik</strong> B SS 03<br />

-verbose: Ausgaben des Compilers<br />

– In e<strong>in</strong>er Datei *.java können mehrere top-level Klassen def<strong>in</strong>iert werden.<br />

Maximal e<strong>in</strong>e davon darf public se<strong>in</strong>.<br />

Für jede der Klassen wird bei Übersetzung e<strong>in</strong>e eigene *.class Datei<br />

erzeugt.<br />

<strong>Java</strong> Virtual Mach<strong>in</strong>e (JVM):<br />

– Kann <strong>in</strong> Hardware oder Software realisiert se<strong>in</strong> (üblicherw. <strong>in</strong> Software)<br />

– Interpretation und Ausführung des Byte-Codes; JVM für Solaris, Microsoft<br />

W<strong>in</strong>dows, L<strong>in</strong>ux, ...<br />

– Interpretersprachen s<strong>in</strong>d meist langsam, übliche Technik just-<strong>in</strong>-time<br />

Compilierung, <strong>Java</strong>: Byte-Code wird <strong>in</strong> die Masch<strong>in</strong>ensprache der<br />

gegebenen Plattform übersetzt (gute Ausführungsgeschw<strong>in</strong>digkeit für<br />

Code, der mehrfach ausgeführt wird)<br />

– Ausführung des Bytecodes Klassenname.class mit<br />

java Klassenname .<br />

– Die entsprechende Klasse muss e<strong>in</strong>e Methode ma<strong>in</strong>() mit folgender<br />

Signatur enthalten: public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args).<br />

Diese Methode ist der E<strong>in</strong>stiegspunkt <strong>in</strong>s Programm: hier beg<strong>in</strong>nt der<br />

Interpreter die Ausführung. Das Programm läuft so lange, bis die ma<strong>in</strong><br />

Methode (und evtl. erzeugte Threads) verlassen werden.<br />

– Das Kommando java erlaubt verschiedene Optionen, wie:<br />

-classpath path: Liste von Verzeichnissen und JAR Dateien, <strong>in</strong><br />

denen gesucht wird, wenn e<strong>in</strong>e Klasse geladen wird. (Kurzform: -cp)<br />

-Dpropertyname=value: Setzt e<strong>in</strong>e Property mit Namen<br />

propertyname auf den Wert value. Im <strong>Java</strong>-Programm können<br />

Properties dann abgefragt werden.<br />

H<strong>in</strong>ter dem Klassennamen können Daten angegeben werden, die an<br />

Str<strong>in</strong>g[] args weitergegeben werden.<br />

<strong>Java</strong> Plattform:<br />

– Vordef<strong>in</strong>ierte Menge von <strong>Java</strong> Klassen, die auf jeder <strong>Java</strong> Installation<br />

vorhanden s<strong>in</strong>d und von allen <strong>Java</strong> Programmen genutzt werden können<br />

– Klassen s<strong>in</strong>d <strong>in</strong> Paketen organisiert (Input/Output, Graphik,<br />

Netzwerkfunktionen, ...)<br />

– Auch <strong>Java</strong> Runtime Environment oder <strong>Java</strong> APIs (Application Programm<strong>in</strong>g<br />

Interface) genannt.


¡<br />

¥<br />

¡<br />

©¨<br />

5§¨-5¨ ¦<br />

<strong>Informatik</strong> B SS 03 21<br />

2.3 Das 8-Damen Problem Imperativ und <strong>Objekt</strong>-Orientiert<br />

2.3.1 Problemstellung<br />

Problem: Plaziere 8 Damen auf e<strong>in</strong>em (8 8) Schachbrett so, dass ke<strong>in</strong>e Dame<br />

e<strong>in</strong>e andere schlagen kann.<br />

E<strong>in</strong>e Dame kann e<strong>in</strong>e Figur schlagen, die <strong>in</strong> derselben Reihe oder derselben<br />

Spalte oder derselben Diagonale ist.<br />

Lösung(en) für das 4-Damen Problem:<br />

4<br />

4<br />

3<br />

3<br />

2<br />

1<br />

2<br />

1<br />

1 2 3 4<br />

1 2 3 4<br />

Allgeme<strong>in</strong>: ( Problem Damen auf Brett)<br />

<br />

¢¡<br />

Ke<strong>in</strong>e <br />

Lösung für .<br />

Standard-Beispiel für generate and test Algorithmen (backtrack<strong>in</strong>g)<br />

Aufwand:<br />

– naiv: 8 aus ¤£ 64 (B<strong>in</strong>omialkoeffizient über )<br />

<br />

5§¦<br />

für ¦¡ : 4.426.165.368<br />

– <strong>in</strong> jeder Spalte nur e<strong>in</strong>e © Dame:<br />

¦¡ für : 16.777.216<br />

– <strong>in</strong> ke<strong>in</strong>e Spalte die gleiche Position: ©¨<br />

für ¦¡ : 40320<br />

Problem mit exponentieller Komplexität!<br />

Es kann ke<strong>in</strong>en vollständigen Algorithmus geben, der das Problem effizient löst.<br />

Verwendung von heuristischen Suchverfahren bzw.<br />

Constra<strong>in</strong>t-Erfüllungstechniken (Künstliche Intelligenz).<br />

Komplexität von Problemen wird <strong>in</strong> der Vorlesung Theoretische <strong>Informatik</strong><br />

behandelt<br />

Aktuelle Literatur zum -Damen Problem:<br />

http://www.liacs.nl/˜kosters/nqueens.html


22 <strong>Informatik</strong> B SS 03<br />

4<br />

aufw.<br />

3<br />

2<br />

1<br />

1 2 3 4<br />

abw.<br />

testRow testColumn colDif upRowDif downRowDif<br />

testRow - row row - testRow<br />

1 1 -2 -2 2<br />

1 2 -1 -2 2<br />

1 3 0 -2 2<br />

1 4 1 -2 2<br />

. . . . . . . . . . . . . . .<br />

4 1 -2 1 -1<br />

4 2 -1 1 -1<br />

4 3 0 1 -1<br />

4 4 1 1 -1<br />

Abbildung 9: Bedrohte Diagonalen<br />

Sequenz der Anzahl von Lösungen:<br />

1,0,0,2,10,4,40,92,352,724,2680,14200,73712,365596,2279184,<br />

14772512,95815104,666090624,4968057848,39029188884,<br />

314666222712,2691008701644,24233937684440<br />

e<strong>in</strong>e kompakte Formel wurde bisher nicht gefunden (siehe<br />

http://mathworld.wolfram.com/QueensProblem.html)<br />

2.3.2 Identifikation von Schlagstellungen<br />

Wenn <strong>in</strong> jede Spalte genau e<strong>in</strong>e Dame gesetzt wird, dann kann e<strong>in</strong>e Dame mit<br />

Position (row, column) folgende Stellungen bedrohen:<br />

– die Zeile row<br />

– die Aufwärtsdiagonale (testColumn column) ¡ (testRow - row)<br />

– die Abwärtsdiagonale (testColumn column) ¡ (row testRow)<br />

Erläuterung: Diagonalen haben e<strong>in</strong>e Steigung G von (aufwärts) G bzw.<br />

(abwärts). Das heisst, Spaltendifferenz (Waagerechte) und Zeilendifferenz<br />

(Senkrechte) s<strong>in</strong>d identisch.<br />

Beispiel für row ¡ 3 und column ¡ 3 <strong>in</strong> Abb. 9<br />

¡<br />

¡<br />

Bedrohung auf Diagonalen existiert also, wenn:<br />

row (testColumn column) testrow oder<br />

row (testColumn column) testrow


¡<br />

<strong>Informatik</strong> B SS 03 23<br />

Da beidesmal die Spaltendifferenz benötigt wird, kann diese auch vorab<br />

berechnet werden.<br />

siehe Methode predecCanAttack()<br />

Die von e<strong>in</strong>er Dame bedrohten Positionen können global <strong>in</strong> entsprechende<br />

boolesche Arrays e<strong>in</strong>getragen werden. Alternativ zum E<strong>in</strong>tragen und<br />

Durchmustern der Arrays können die bedrohten Stellungen auch jeweils für jede<br />

Dame geprüft werden.<br />

2.3.3 Imperative Lösung<br />

Im folgenden geben wir Programme zum F<strong>in</strong>den e<strong>in</strong>er gültigen Lösung an.<br />

(Alternativ: F<strong>in</strong>den aller gültigen Lösungen )<br />

Die Anzahl von Queens (MAX) wird als Property über die<br />

gesetzt. Default ist 8.<br />

Option von java<br />

Globale Kontrolle: In der ma<strong>in</strong>() Methode wird e<strong>in</strong> Array queens[]<br />

spaltenweise mit e<strong>in</strong>er legalen Zeilenposition für die aktuelle Dame belegt.<br />

Existiert für e<strong>in</strong>e Dame ke<strong>in</strong>e legale Position wird die bisherige Lösung<br />

zurückgenommen (die Vorgängerdame, evtl. deren Vorgänger, etc.)<br />

backtrack<strong>in</strong>g<br />

public class QueensIM {<br />

f<strong>in</strong>al static <strong>in</strong>t MAX = Integer.getInteger("MAX", 8).<strong>in</strong>tValue();<br />

// Current row for queens at columns 0..MAX-1<br />

f<strong>in</strong>al static <strong>in</strong>t[] queens = new <strong>in</strong>t[MAX]; // <strong>in</strong>itialized with 0s<br />

// CAUTION: The "real" column is (array <strong>in</strong>dex) + 1!<br />

// predecCanAttack() depends on the real chessboard positions<br />

// therefore we def<strong>in</strong>e an array shift and an advance function<br />

static <strong>in</strong>t queens(<strong>in</strong>t i) {<br />

return queens[i-1];<br />

}<br />

static void advance(<strong>in</strong>t i) {<br />

queens[i-1]++;<br />

}<br />

// Test whether a queen (with position row, column)<br />

// or any of its predecessors can attack another position<br />

static boolean predecCanAttack(<strong>in</strong>t row, <strong>in</strong>t column,<br />

<strong>in</strong>t testRow, <strong>in</strong>t testColumn) {<br />

// Case 1: current Queen can attack<br />

<strong>in</strong>t columnDifference = testColumn - column;<br />

if ((row == testRow) ||<br />

// same row<br />

(row + columnDifference == testRow) || // same up-diagonal<br />

(row - columnDifference == testRow)) // same down-diagonal<br />

return true;


24 <strong>Informatik</strong> B SS 03<br />

}<br />

// Case 2: can neighbor Queen attack<br />

if (column > 1) // there are queens to the left<br />

return predecCanAttack(queens(column-1), column-1,<br />

testRow, testColumn);<br />

// Case 3: Position cannot attack, is ok<br />

return false;<br />

// Pr<strong>in</strong>ts the position of all queens via loop through the array<br />

static void pr<strong>in</strong>tSolution() {<br />

for (<strong>in</strong>t i = 1; i 1)<br />

while ( queens(i) MAX) {<br />

queens[i-1] = 0; // reset queen, CAUTION: us<strong>in</strong>g the array <strong>in</strong>dex<br />

i--;<br />

// backtrack<br />

} else i++;<br />

} // end while (i 0)<br />

pr<strong>in</strong>tSolution();<br />

}<br />

2.3.4 <strong>Objekt</strong>-Orientierte Lösung<br />

Statt e<strong>in</strong>er Klasse mit der Kontrolle <strong>in</strong> der ma<strong>in</strong>() Methode wird nun e<strong>in</strong>e<br />

Klasse Queen def<strong>in</strong>iert: Jede Dame kennt ihre eigene Position und kann sich<br />

selbst verschieben.<br />

Dazu existiert e<strong>in</strong>e Dame, die ihre direkte Nachbar<strong>in</strong> kennt, als Unterklasse von<br />

Queen. Sie kann prüfen, ob sie mit dieser <strong>in</strong> Konflikt steht, sowie diese<br />

“anschubsen”.


<strong>Informatik</strong> B SS 03 25<br />

public class QueensOO {<br />

// Number of Queens set as property<br />

public f<strong>in</strong>al static <strong>in</strong>t MAX = Integer.getInteger("MAX", 8).<strong>in</strong>tValue();<br />

// Loops from first to last Queen<br />

// and tries to generate a solution via backtrack<strong>in</strong>g.<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args) {<br />

// The current Queen.<br />

Queen lastQueen = new Queen(1);<br />

}<br />

}<br />

for (<strong>in</strong>t i = 2; i


26 <strong>Informatik</strong> B SS 03<br />

}<br />

<strong>in</strong>t columnDifference = queen.column - column;<br />

return (row == queen.row) ||<br />

(row + columnDifference == queen.row) ||<br />

(row - columnDifference == queen.row);<br />

// same row<br />

// same up-diagonal<br />

// same down-diagonal<br />

}<br />

// Pr<strong>in</strong>ts the position of the current Queen<br />

public void pr<strong>in</strong>tSolution() {<br />

System.out.pr<strong>in</strong>tln(column + " : " + row);<br />

}<br />

class NeighborQueen extends Queen {<br />

// data fields<br />

// immediate left neighbor of a Queen<br />

protected f<strong>in</strong>al Queen neighbor;<br />

// Initializes a new NeighborQueen object<br />

public NeighborQueen (<strong>in</strong>t c, Queen n) {<br />

super(c);<br />

neighbor = n;<br />

}<br />

// Tries to advance the Queen to a conflict free position.<br />

public boolean f<strong>in</strong>dSolution() {<br />

while (neighbor.predecCanAttack(this))<br />

if (! advance()) return false;<br />

return true;<br />

}<br />

// Advance one row if possible (realizes the backtrack<strong>in</strong>g)<br />

protected boolean advance() {<br />

return super.advance() ||<br />

// test if neighbor advance and f<strong>in</strong>dSolution is ok:<br />

neighbor.advance() && neighbor.f<strong>in</strong>dSolution();<br />

}<br />

// Test whether this Queen<br />

// or any of its predecessors can attack another Queen<br />

protected boolean predecCanAttack(Queen queen) {<br />

// can current Queen or neighbor Queen attack<br />

return super.predecCanAttack(queen) || neighbor.predecCanAttack(queen);<br />

}<br />

}<br />

// Pr<strong>in</strong>ts the position of the current Queen and all predecessors<br />

public void pr<strong>in</strong>tSolution() {<br />

neighbor.pr<strong>in</strong>tSolution();<br />

super.pr<strong>in</strong>tSolution();<br />

}


<strong>Informatik</strong> B SS 03 27<br />

1<br />

Queen<br />

#row: <strong>in</strong>t<br />

#column: <strong>in</strong>t<br />

+f<strong>in</strong>dSolution(): boolean<br />

#advance(): boolean<br />

#predecCanAttack(queen:Queen): boolean<br />

+pr<strong>in</strong>tSolution()<br />

creates<br />

creates<br />

QueensOO<br />

+MAX: <strong>in</strong>t<br />

+ma<strong>in</strong>()<br />

left neighbor<br />

NeighborQueen<br />

#neighbor: Queen<br />

Abbildung 10: Klassenstruktur von QueensOO


¡<br />

28 <strong>Informatik</strong> B SS 03<br />

3 Klassen und ihre Komponenten<br />

3.1 Klassen <strong>in</strong> <strong>Java</strong><br />

<strong>Java</strong> als objekt-<strong>orientierte</strong> Sprache<br />

Klassen s<strong>in</strong>d die fundamentale Struktur.<br />

Jedes <strong>Java</strong> Programm ist als Klasse def<strong>in</strong>iert; alle <strong>Java</strong> Programme benutzen<br />

<strong>Objekt</strong>e.<br />

E<strong>in</strong>e Klasse ist die kle<strong>in</strong>ste E<strong>in</strong>heit an <strong>Java</strong>-Code, die für sich alle<strong>in</strong> stehen kann<br />

und vom <strong>Java</strong>-Compiler und -Interpreter erkannt wird<br />

Jede Klasse def<strong>in</strong>iert e<strong>in</strong>en neuen Datentyp!<br />

primitiver Datentyp – Wert (<strong>in</strong>t 42)<br />

Klasse – <strong>Objekt</strong> (Circle ‘Kreis mit Radius 2’)<br />

Klasse: Sammlung von Attributen (typisierte Platzhalter für Daten) und von<br />

Programmcode (gespeichert <strong>in</strong> benannten Methoden, die auf diesen Daten<br />

arbeiten)<br />

Klassendef<strong>in</strong>ition: siehe Abb. 7 für die vollständige Spezifikation. Im e<strong>in</strong>fachsten<br />

Fall: class Name¡ Members¡<br />

Konvention: Erster Buchstabe des Klassennamens wird gross geschrieben.<br />

Die erste Zeile der Klassendef<strong>in</strong>ition repräsentiert die “Klassen-Signatur” (vgl.<br />

erste Zeile e<strong>in</strong>er Methodendef<strong>in</strong>ition)<br />

Der <strong>Java</strong>-Interpreter lädt Klassen dynamisch (dynamic load<strong>in</strong>g): Wenn das erste<br />

Mal e<strong>in</strong> <strong>Objekt</strong> dieser Klasse erzeugt bzw. e<strong>in</strong>e statische Komponente benötigt<br />

wird. Wie der <strong>Java</strong>-Interpreter die Klassen f<strong>in</strong>det, wird im Abschnitt “Packages<br />

und <strong>Java</strong> Namespace” dargestellt.<br />

3.2 Members: Felder, Methoden, Klassen<br />

Member (Komponente): Felder, Methoden und (seit <strong>Java</strong> 1.1) weitere (<strong>in</strong>nere)<br />

Klassen<br />

E<strong>in</strong>e Klasse, die nur Felder und ke<strong>in</strong>e Methoden def<strong>in</strong>iert, ist lediglich e<strong>in</strong>e<br />

Sammlung von Daten, ähnlich e<strong>in</strong>em Record (Modula).<br />

Zentral für objekt-<strong>orientierte</strong> <strong>Programmierung</strong> ist, dass Nachrichten (Methoden)<br />

an Empfängerobjekte (Receiver) gesendet werden, die diese dann ausführen<br />

(a.f(): a ist e<strong>in</strong> <strong>Objekt</strong>, das die Nachricht “erledige f()” empfängt).<br />

zwei Typen von Members:<br />

– class members: als static deklariert, assoziiert mit Klasse selbst<br />

– <strong>in</strong>stance members: assoziiert mit <strong>in</strong>dividuellen Instanzen (<strong>Objekt</strong>en!) der<br />

Klasse


<strong>Informatik</strong> B SS 03 29<br />

3.3 Beispielcode ‘Circle’<br />

public class Circle {<br />

// A class field<br />

public static f<strong>in</strong>al double PI = 3.14159; // A useful constant<br />

// A class method: just compute a value based on the arguments<br />

public static double radiansToDegrees(double rads) {<br />

return rads * 180/PI;<br />

}<br />

// An <strong>in</strong>stance field<br />

public double r; // The radius of the circle<br />

// Two <strong>in</strong>stance methods: they operate on the <strong>in</strong>stance fields<br />

// of an object<br />

public double area() { // Compute the area of the circle<br />

return PI * r * r;<br />

}<br />

}<br />

public double circumference() { // Compute the circumference<br />

return 2 * PI * r;<br />

// of the circle<br />

}<br />

3.4 Klassen- und Instanz-Komponenten<br />

3.4.1 Klassen-Felder<br />

public static f<strong>in</strong>al double PI = 3.14159;<br />

assoziiert mit Klasse selbst durch static modifier: Klassen-Feld (static field)<br />

Es existiert nur e<strong>in</strong>e e<strong>in</strong>zige Kopie e<strong>in</strong>es statischen Feldes!<br />

Feld vom Typ double mit Namen PI und zugewiesenem Wert 3.14159<br />

f<strong>in</strong>al modifier: Wert des Feldes ändert sich nach erster Zuweisung nicht mehr<br />

(Konstante)<br />

public modifier: “jeder” kann das Feld benutzen (visibility modifier)<br />

lokale Variable/Konstante: <strong>in</strong>nerhalb e<strong>in</strong>er Methode oder e<strong>in</strong>es Blocks<br />

Felder: Komponenten e<strong>in</strong>er Klasse!<br />

ähnlich e<strong>in</strong>er globalen Variable <strong>in</strong> e<strong>in</strong>em Programm<br />

Innerhalb der Klasse Circle kann PI mit se<strong>in</strong>em e<strong>in</strong>fachen Namen referenziert<br />

werden;<br />

ausserhalb: Circle.PI (e<strong>in</strong>deutige Spezifikation: “das PI, das <strong>in</strong> Klasse Circle<br />

def<strong>in</strong>iert ist”)


30 <strong>Informatik</strong> B SS 03<br />

3.4.2 Klassen-Methoden<br />

public static double radiansToDegrees(double rads) return rads *<br />

180/PI;¡<br />

assoziiert mit Klasse selbst durch static modifier: Klassen-Methode (static<br />

method)<br />

Aufruf (<strong>in</strong>vocation) von ausserhalb der Klasse:<br />

Circle.radiansToDegrees(2.0)<br />

oft guter Stil Klassennamen auch <strong>in</strong>nerhalb der Klasse mitanzugeben, um klar<br />

zu machen, dass e<strong>in</strong>e Klassen-Methode benutzt wird<br />

Klassen-Methoden s<strong>in</strong>d “globale Methoden” (<strong>in</strong> anderen Programmiersprachen<br />

s<strong>in</strong>d alle Prozeduren/Funktionen global), “imperativer Programmierstil” kann <strong>in</strong><br />

<strong>Java</strong> realisiert werden, wenn nur Klassen-Methoden benutzt werden.<br />

radiansToDegree() ist e<strong>in</strong>e “utility”-Methode, die <strong>in</strong> Circle def<strong>in</strong>iert ist, weil<br />

sie beim Arbeiten mit Kreisen nützlich se<strong>in</strong> kann.<br />

benutzt das Klassen-Feld PI<br />

Klassen-Methoden können alle anderen Klassen-Komponenten der eigenen<br />

Klasse (oder auch anderer Klassen) benutzen.<br />

Klassen-Methoden können nicht direkt (mit this) auf Instanz-Felder oder<br />

-methoden zugreifen! (weil die Klassen-Methoden nicht mit e<strong>in</strong>er Instanz<br />

assoziiert s<strong>in</strong>d)<br />

3.4.3 Instanz-Felder<br />

public double r;<br />

Jedes Feld, das nicht static deklariert ist, ist e<strong>in</strong> Instanz-Feld.<br />

Instanz-Felder s<strong>in</strong>d mit den <strong>Objekt</strong>en der Klasse assoziiert.<br />

Jedes <strong>Objekt</strong> der Klasse Circle hat se<strong>in</strong>e eigene Kopie des Felds r. Jeder<br />

Kreis hat se<strong>in</strong>en eigenen Radius.<br />

Innerhalb von Klassen werden Instanz-Felder durch ihren Namen referenziert. In<br />

Code ausserhalb wird der Name über e<strong>in</strong>e Referenz zu dem <strong>Objekt</strong>, das das<br />

Feld enthält, angegeben.<br />

Instanz-Felder s<strong>in</strong>d der Kern des objekt-<strong>orientierte</strong>n Programmierens: Mit<br />

Instanz-Feldern wird e<strong>in</strong> <strong>Objekt</strong> (über se<strong>in</strong>e Eigenschaften) def<strong>in</strong>iert. Die Werte<br />

dieser Felder machen <strong>Objekt</strong>e zu unterschiedlichen Identitäten.<br />

Circle c = new Circle(); // Create a new Circle object<br />

// store it <strong>in</strong> variable c<br />

c.r = 2.0;<br />

// Assign a value to its <strong>in</strong>stance field r<br />

Circle d = new Circle(); // Create a different Circle object<br />

d.r = c.r * 2;<br />

// Make this one twice as big


<strong>Informatik</strong> B SS 03 31<br />

Circle<br />

PI<br />

r<br />

+ radiansToDegrees()<br />

+ area()<br />

+ circumference()<br />

circle1: Circle<br />

circle2: Circle<br />

r=1 r=42<br />

Abbildung 11: Jedes <strong>Objekt</strong> hat se<strong>in</strong>e eigenen Instanzkomponenten<br />

3.4.4 Erzeugung und Initialisierung von <strong>Objekt</strong>en<br />

Erzeugung von <strong>Objekt</strong>en mit new<br />

Circle c = new Circle();<br />

Konstruktor¡<br />

E<strong>in</strong> <strong>Objekt</strong> mit Namen name vom Typ des Konstruktors (Klasse oder<br />

Unterklasse der Klasse) wird erzeugt und durch Aufruf e<strong>in</strong>es Konstruktors für<br />

diese Klasse <strong>in</strong>itialisiert.<br />

Konstruktoren können selbst def<strong>in</strong>iert werden. (Kapitel ‘Konstruktoren und<br />

Vererbung’)<br />

Wenn e<strong>in</strong>e Klasse ke<strong>in</strong>en eigenen Konstruktor def<strong>in</strong>iert, dann bekommt sie<br />

automatisch e<strong>in</strong>en Default-Konstruktor ohne Parameter.<br />

3.4.5 Instanz-Methoden<br />

public double circumference() return 2 * PI * r;¡<br />

Jede Methode, die nicht static deklariert ist, ist e<strong>in</strong>e Instanz-Methode.<br />

Instanz-Methoden arbeiten auf e<strong>in</strong>er Instanz e<strong>in</strong>er Klasse (auf e<strong>in</strong>em <strong>Objekt</strong>)<br />

und nicht auf der Klasse selbst.<br />

Instanz-Methoden s<strong>in</strong>d der zweite wesentliche Kern der <strong>Objekt</strong>orientierung.<br />

Um e<strong>in</strong>e Instanz-Methode ausserhalb der Klasse, <strong>in</strong> der sie def<strong>in</strong>iert ist, zu<br />

verwenden, muss zunächst e<strong>in</strong> entsprechendes <strong>Objekt</strong> erzeugt werden.<br />

<strong>Objekt</strong>e – nicht Funktionen – stehen im Mittelpunkt!<br />

(a = area(c) vs. a = c.area())<br />

An die Methode area() muss ke<strong>in</strong> Parameter übergeben werden, weil alle<br />

Informationen über das <strong>Objekt</strong> implizit <strong>in</strong> der Instanz c vorhanden s<strong>in</strong>d.


32 <strong>Informatik</strong> B SS 03<br />

Instanz-Methoden können auf Klassen- und Instanz-Members zugreifen.<br />

In circumference() ist r mit dem Wert aus der gerade betrachteten Instanz<br />

belegt.<br />

Circle c = new Circle(); // Create a new Circle object;<br />

// store it <strong>in</strong> variable c<br />

c.r = 2.0;<br />

// Assign a value to its <strong>in</strong>stance field r<br />

double a = c.area(); // Invoke an <strong>in</strong>stance method of the object<br />

3.4.6 Funktionsweise von Instanz-Methoden: ‘this’<br />

a = c.area();<br />

area() ist sche<strong>in</strong>bar parameterlos, aber die Methode hat e<strong>in</strong>en impliziten<br />

Parameter this.<br />

this hat als Wert die Referenz auf das <strong>Objekt</strong>, über das die Methode<br />

aufgerufen wurde.<br />

this muss häufig nicht explizit angegeben werden: Wenn e<strong>in</strong>e Methode auf<br />

Komponenten zugreift, wird – wenn nichts anderes gesagt wird – angenommen,<br />

dass die Komponenten des aktuellen <strong>Objekt</strong>s geme<strong>in</strong>t s<strong>in</strong>d.<br />

this kann/sollte explizit angegeben werden, wenn man klar machen will, dass<br />

die Methode auf ihre eigenen Komponenten zugreift.<br />

public double area(){<br />

return Circle.PI * this.r * this.r;<br />

}<br />

this wird explizit benötigt, wenn Parameter oder lokale Variablen e<strong>in</strong>er<br />

Methode denselben Namen haben wie Felder. Der Name wird zunächst auf<br />

lokale Grössen bezogen!<br />

public void setRadius(double r) {<br />

this.r = r; //Assign the argument r to the field this.r<br />

}<br />

r = r ist s<strong>in</strong>nlos: weist Parameter r se<strong>in</strong>en eigenen Wert zu.<br />

Klassen-Methoden können das this Schlüsselwort nicht benutzen! (weil sie<br />

nicht mit e<strong>in</strong>er Instanz, sondern mit der Klasse assoziiert s<strong>in</strong>d)<br />

3.4.7 Instanz- oder Klassen-Methode<br />

Instanz-Methoden s<strong>in</strong>d zentral für objekt-<strong>orientierte</strong>n Programmierstil.<br />

Dennoch kann es s<strong>in</strong>nvoll se<strong>in</strong>, Klassen-Methoden zu def<strong>in</strong>ieren.


¡<br />

¡<br />

¡<br />

<strong>Informatik</strong> B SS 03 33<br />

Design-Entscheidung: Wenn häufig die Fläche e<strong>in</strong>es Kreises berechnet werden<br />

soll, aber es ansonsten nicht notwendig ist, dafür extra e<strong>in</strong> <strong>Objekt</strong> der Klasse<br />

Circle zu erzeugen, sollte area() als Klassen-Methode def<strong>in</strong>iert werden.<br />

Die Methode kann zweimal def<strong>in</strong>iert se<strong>in</strong> (hier: e<strong>in</strong>mal als Klassen-, e<strong>in</strong>mal als<br />

Instanz-Methode). Unterscheidung: verschiedene Signaturen!<br />

public static double area(double r){ return PI * r * r;}<br />

Design-Entscheidung: Wie soll e<strong>in</strong>e Methode realisiert werden, die den<br />

grösseren von zwei Kreisen zurückliefert<br />

// Compare the implicit ‘this’ circle to the ‘that’ circle passed<br />

// explicitly as an argument and return the bigger one.<br />

public Circle bigger(Circle that) {<br />

if(this.r > that.r) return this;<br />

else return that;<br />

}<br />

...<br />

Circle biggest = c.bigger(d);<br />

// Instance method:<br />

// alternatively d.bigger(c)<br />

// Compare circle a to circle b and return the one with the larger radius<br />

public static Circle bigger(Circle a, Circle b) {<br />

if(a.r > b.r) return a;<br />

else return b;<br />

}<br />

...<br />

Circle biggest = Circle.bigger(x,y); // Static method<br />

3.5 Referenztypen<br />

Klassen (und Arrays) s<strong>in</strong>d Referenztypen. <strong>Objekt</strong>e haben e<strong>in</strong>en bestimmten Typ:<br />

– Circle c = new Circle();<br />

– <strong>in</strong>t[] a = 1,2,3,4,5 ;<br />

– Str<strong>in</strong>g s = "Hello";<br />

Array und Str<strong>in</strong>g haben speziellen Status:<br />

c ist e<strong>in</strong> <strong>Objekt</strong> vom Typ Circle<br />

a ist e<strong>in</strong> Array-<strong>Objekt</strong><br />

s ist e<strong>in</strong> Str<strong>in</strong>g-<strong>Objekt</strong><br />

– In der Object-Hierarchie f<strong>in</strong>det sich ke<strong>in</strong>e Unterklasse Array: Es gibt<br />

potentiell unendlich viele Arrays (Dimensionalität, Typ der Elemente). Wie<br />

bei der Def<strong>in</strong>ition und Erzeugung anderer <strong>Objekt</strong>e steht l<strong>in</strong>ks der Typ des<br />

Arrays (Dimensionalität und Typ der Elemente), aber nicht die Länge des<br />

Arrays! (siehe Vorlesung <strong>Informatik</strong> A)


¡<br />

34 <strong>Informatik</strong> B SS 03<br />

<strong>in</strong>t x = 42;<br />

<strong>in</strong>t y = x;<br />

Circle c = new Circle();<br />

c.r = 2.0;<br />

Circle d = c;<br />

x<br />

y<br />

1124<br />

1125<br />

42<br />

42<br />

c<br />

d<br />

2411<br />

2412<br />

4711<br />

4711<br />

4711<br />

Object<br />

r=2.0<br />

Abbildung 12: Kopieren von Werten/Referenzen<br />

– Für Str<strong>in</strong>g kann e<strong>in</strong> <strong>Objekt</strong> direkt <strong>in</strong>itialisiert werden: Text vom Typ Str<strong>in</strong>g<br />

(Zeichen <strong>in</strong> doppelten Anführungszeichen) kann direkt an e<strong>in</strong>e Variable<br />

vom Typ Str<strong>in</strong>g zugewiesen werden.<br />

Primitive Datentypen: feste, bei Deklaration bekannte Grösse (z. B. <strong>in</strong>t<br />

bekommt 32 Bit)<br />

Klassen und Arrays: s<strong>in</strong>d zusammengesetzte Typen (composite types), für die<br />

ke<strong>in</strong>e Standardgrösse angegeben werden kann und die häufig mehr<br />

Speicherplatz benötigen Manipulation by reference<br />

Referenz: fester Wert (Speicheradresse), der auf das <strong>Objekt</strong> verweist<br />

Zuweisung e<strong>in</strong>es <strong>Objekt</strong>s an e<strong>in</strong>e Variable: Variable hält Referenz auf dieses<br />

<strong>Objekt</strong>; Übergabe e<strong>in</strong>es <strong>Objekt</strong>s als Parameter: Methode erhält die Referenz,<br />

über die das <strong>Objekt</strong> manipuliert werden kann.<br />

null Referenz: Referenz auf “Nichts”, Abwesenheit e<strong>in</strong>er Referenz; Wert null<br />

kann an jede Variable für e<strong>in</strong>en Referenztyp zugewiesen werden.<br />

<strong>Java</strong> erlaubt ke<strong>in</strong>e explizite Manipulation von Referenzen.<br />

Wichtiger Unterschied zwischen primitiven Datentypen und Referenztypen:<br />

Kopieren von Werten und Prüfung von Gleichheit.<br />

3.5.1 Kopieren von <strong>Objekt</strong>en (und Arrays)<br />

Für die <strong>in</strong>t-Variablen x und y existieren zwei Kopien des 32-bit Integers 42.<br />

Variable d enthält e<strong>in</strong>e Kopie der Referenz, die <strong>in</strong> Variable c steht. Man spricht<br />

hier auch von alias<strong>in</strong>g.<br />

Achtung: Wenn zwei Referenzen a und b auf dasselbe <strong>Objekt</strong> zeigen, kann e<strong>in</strong><br />

Methodenaufruf (z.B a.f()) an das e<strong>in</strong>e <strong>Objekt</strong> den Wert e<strong>in</strong>es Feldes ändern<br />

(“call<strong>in</strong>g a method for its side effects”). Das Feld ist dann beim <strong>Objekt</strong> geändert.<br />

Sowohl a.feld als auch b.feld liefern den aktuellen Wert!<br />

E<strong>in</strong>e Kopie des Circle <strong>Objekt</strong>s <strong>in</strong> der <strong>Java</strong> VM, aber zwei Kopien der Referenz<br />

auf dieses <strong>Objekt</strong>!


¡<br />

¡<br />

<strong>Informatik</strong> B SS 03 35<br />

System.out.pr<strong>in</strong>tln(c.r); // Pr<strong>in</strong>t out radius of c: 2.0<br />

d.r = 4.0;<br />

// Change radius of d<br />

System.out.pr<strong>in</strong>tln(c.r); // Pr<strong>in</strong>t out radius of c aga<strong>in</strong>: 4.0<br />

Kopieren des <strong>Objekt</strong>es selbst: clone()<br />

<strong>Objekt</strong>e müssen zu e<strong>in</strong>er Klasse gehören, die als Cloneable deklariert ist<br />

(Implementieren des Cloneable-Interfaces und der clone()-Methode)<br />

Arrays s<strong>in</strong>d immer Cloneable<br />

clone() erzeugt e<strong>in</strong>e flache (“shallow”) Kopie: alle primitiven Werte und<br />

Referenzen werden kopiert. Das heisst, Referenzen werden nicht ge-cloned!<br />

rekursives Kopieren muss explizit implementiert werden.<br />

Cast<strong>in</strong>g ist notwendig (clone() liefert Object)<br />

<strong>in</strong>t[] data = {1,2,3,4,5};<br />

// An array<br />

<strong>in</strong>t[] copy = (<strong>in</strong>t[]) data.clone(); // A copy of the array<br />

Flache Kopie:<br />

<strong>in</strong>t[][] data = {{1,2,3},{4,5}}; // Array of 2 refs<br />

<strong>in</strong>t[][] copy = (<strong>in</strong>t[][]) data.clone(); // Copy<br />

copy[1] = new <strong>in</strong>t[]{7,8,9}; // this does not change data[1]<br />

copy[0][0] = 99; // this changes data[0][0] too!<br />

Tiefe Kopie:<br />

<strong>in</strong>t[][] data = {{1,2,3},{4,5}}; // Array of 2 refs<br />

<strong>in</strong>t[][] copy = new <strong>in</strong>t[data.length][]; // new array to hold copied arrays<br />

for (<strong>in</strong>t i = 0; i < data.length; i++)<br />

copy[i] = (<strong>in</strong>t[]) data[i].clone();<br />

3.5.2 Gleichheit von <strong>Objekt</strong>en (und Arrays)<br />

Bei primitiven Werten prüft der == Operator, ob sie denselben Wert haben (bei<br />

ganzzahligen Werten: gleiche Bits).<br />

Bei Referenztypen prüft ==, ob zwei Referenzen auf dasselbe <strong>Objekt</strong> verweisen,<br />

aber nicht, ob zwei <strong>Objekt</strong>e denselben Inhalt haben!<br />

Prüfung der Gleichheit von Inhalten von <strong>Objekt</strong>en (nicht bei Arrays): equals()<br />

Methode ist <strong>in</strong> Object def<strong>in</strong>iert (default ist ==) Methode <strong>in</strong> eigener Klasse<br />

entsprechend überschreiben (z.B. <strong>in</strong> Str<strong>in</strong>g gemacht)<br />

Anmerkung: zur Unterscheidung “equals” für Gleichheit von Referenzen und<br />

“equivalent” für Gleichheit von Inhalten verschiedener <strong>Objekt</strong>e. Die Benennung<br />

der Methode equals ist etwas ungünstig.<br />

bei Arrays: java.util.Arrays.equals() (ab <strong>Java</strong> 1.2)


36 <strong>Informatik</strong> B SS 03<br />

data<br />

1 2 3 4 5<br />

<strong>in</strong>t[ ] data = {1,2,3,4,5}<br />

data<br />

1 2 3<br />

4 5<br />

<strong>in</strong>t[ ][ ] data = {{1,2,3}, {4,5}}<br />

data<br />

copy<br />

1 2 3<br />

4 5<br />

<strong>in</strong>t[][] copy = (<strong>in</strong>t[][]) data.clone();<br />

data<br />

0<br />

1 2 3<br />

copy[0][]0]<br />

copy<br />

1<br />

4 5<br />

0<br />

1<br />

7 8 9<br />

copy[1] = new <strong>in</strong>t[] {7,8,9};<br />

Abbildung 13: Arrays s<strong>in</strong>d Referenztypen


<strong>Informatik</strong> B SS 03 37<br />

// A class method for Circle replac<strong>in</strong>g the <strong>in</strong>stance method equals() <strong>in</strong> Object<br />

public static boolean equals (Circle c, Circle d) {<br />

return c.r == d.r;<br />

}<br />

Circle c = Circle();<br />

c.r = 2.0;<br />

Circle d = Circle();<br />

d.r = 2.0;<br />

if (c == d) System.out.pr<strong>in</strong>tln("equal"); // but c and d are not equal<br />

if (Circle.equals(c,d)) System.out.pr<strong>in</strong>tln("equivalent");<br />

// c and d are equivalent<br />

3.6 Wrapper-Klassen<br />

Problem: Array ist e<strong>in</strong> Conta<strong>in</strong>er für primitive Typen wie beliebige <strong>Objekt</strong>e.<br />

Manchmal möchte man primitive Typen vielleicht <strong>in</strong> anderen Datenstrukturen<br />

speichern (Vector aus den <strong>Java</strong> Collection Classes <strong>in</strong> java.util). Diese<br />

Klassen s<strong>in</strong>d für Object def<strong>in</strong>iert, aber nicht für primitive Typen.<br />

Lösung: E<strong>in</strong>packen von primitiven Typen <strong>in</strong> korrespondierende Klassen<br />

(Wrapper) – Boolean, Byte, Short, Integer, Long, Character, Float, Double<br />

Dort werden Konstanten und nützliche (statische) Methoden def<strong>in</strong>iert.<br />

// java.lang.Integer<br />

public Integer(Str<strong>in</strong>g s) throws NumberFormatException;<br />

public Integer(<strong>in</strong>t value);<br />

public static <strong>in</strong>t parseInt(Str<strong>in</strong>g s) throws NumberFormatException;<br />

public <strong>in</strong>t compareTo(Integer anotherInteger);<br />

Str<strong>in</strong>g s = "-42";<br />

<strong>in</strong>t i = Integer.parseInt(s); // class method<br />

Integer j = new Integer(-50); // create new Integer Object<br />

<strong>in</strong>t t = j.compareTo(new Integer(i)); // <strong>in</strong>stance method<br />

// >0 if j>i,


38 <strong>Informatik</strong> B SS 03<br />

* e<strong>in</strong>leitend ebenfalls mit e<strong>in</strong>em Stern<br />

*/<br />

<strong>in</strong>t i = 0; // hier ist e<strong>in</strong> Zeilenkommentar<br />

Zwischen /* und */ e<strong>in</strong>geschlossener Text wird vom Compiler ignoriert. Der<br />

Doppel-Slash markiert alles dah<strong>in</strong>ter bis zum Zeilenende als Kommentar.<br />

E<strong>in</strong>e dritte Möglichkeit, Kommentare zu schreiben, hat die Form<br />

/** von javadoc extrahierbarer Kommentar */ .<br />

Programm javadoc erzeugt API Dokumentation im HTML-Format.<br />

javadoc [options] packages | sourcefiles | @lists<br />

(Angegeben werden müssen entweder Pakete oder Quelldateien oder Namen<br />

von Dateien mit Paket- oder Date<strong>in</strong>amen.)<br />

Optionen, z.B:<br />

-author Information h<strong>in</strong>ter @author wird <strong>in</strong> die Dokumentation e<strong>in</strong>getragen<br />

-private alle Klassen und Komponenten werden <strong>in</strong> der Dokumentation<br />

berücksichtigt. (meist nicht s<strong>in</strong>nvoll, <strong>in</strong>formation hid<strong>in</strong>g)<br />

-classpath Pfad für Klassen- und Sourcefiles<br />

-header Text, der oben <strong>in</strong> jeder doc-Datei ersche<strong>in</strong>t<br />

Mit javadoc *.java wird e<strong>in</strong>e Dokumentation für alle Klassen im aktuellen<br />

Verzeichnis erstellt.<br />

javadoc benutzt den <strong>Java</strong>-Compiler und <strong>in</strong>tegriert alle<br />

Dokumentationskommentare.<br />

Dokumentationskommentare können HTML-Tags enthalten, z.B.<br />

Classname. Allerd<strong>in</strong>gs sollten ke<strong>in</strong>e Tags verwendet werden.<br />

(besser den speziellen {@l<strong>in</strong>k} Tag).<br />

Weitere Schlüsselwörter:<br />

@author @version @see<br />

empfehlenswert für Methoden: @param und @return.<br />

Unter anderem wird e<strong>in</strong>e Datei <strong>in</strong>dex.html angelegt, <strong>in</strong> der e<strong>in</strong>e Übersicht<br />

über alle Klassen gegeben wird.<br />

siehe zum Beispiel http://www.vorlesungen.uos.de/<strong>in</strong>formatik/<br />

b02/code/shapes/doc/<strong>in</strong>dex.html.<br />

/**<br />

* Simple example class for demonstrat<strong>in</strong>g<br />

* class (static) components and<br />

* <strong>in</strong>stance components.<br />

* see <strong>Java</strong> <strong>in</strong> a Nutshell, 3rd Edition, chap. 3<br />

*<br />

* @author Ute Schmid<br />

* @version Vorlesung InfoB SS 02


<strong>Informatik</strong> B SS 03 39<br />

*/<br />

public class Circle {<br />

/** A class field for the constant PI */<br />

public static f<strong>in</strong>al double PI = 3.14159;<br />

/** A class method: compute degrees from radians<br />

* @param rads the radians <strong>in</strong> double<br />

* @return rads * 180/PI<br />

*/<br />

public static double radiansToDegrees(double rads) {<br />

return rads * 180/PI;<br />

}<br />

/** A class method which computes the area of a circle with<br />

* given radius. (also exists as <strong>in</strong>stance method)<br />

* @param nr radius of a circle<br />

* @return PI * nr * nr<br />

*/<br />

public static double area(double nr) {<br />

return PI * nr * nr;<br />

}<br />

/** An <strong>in</strong>stance field represent<strong>in</strong>g the radius of the circle */<br />

public double r;<br />

/** An <strong>in</strong>stance method which returns the area of the circle object<br />

* @return PI * r * r<br />

*/<br />

public double area() { // Compute the area of the circle<br />

return PI * r * r; // also Circle.Pi, this.r<br />

}<br />

/** An <strong>in</strong>stance method which returns the circumference of the circle object<br />

* @return 2 * PI * r<br />

*/<br />

public double circumference() {<br />

return 2 * PI * r;<br />

}<br />

/** An <strong>in</strong>stance method which returns the bigger of the current and<br />

* another Circle<br />

* @param that a Circle object<br />

* @return this if the current circle has a larger radius,<br />

* that otherwise<br />

*/<br />

public Circle bigger(Circle that) {<br />

if (this.r > that.r) return this;<br />

else return that;


40 <strong>Informatik</strong> B SS 03<br />

}<br />

}<br />

3.8 Packages und Namespace<br />

Paket: Organisation von Klassen; Konzept, um e<strong>in</strong>deutige Klassennnamen zu<br />

garantieren; Achtung: Pakete s<strong>in</strong>d nicht mit Modulen zu verwechseln<br />

(Diskussion im Kapitel ‘ADTs’)<br />

Namespace: Menge von e<strong>in</strong>zigartigen Namen.<br />

Die Organisation von Klassen <strong>in</strong> Pakete wird vor allem beim Erstellen grösserer<br />

Programme <strong>in</strong>teressant (Strukturierung und Kapslung); Thema<br />

Software-Eng<strong>in</strong>eer<strong>in</strong>g (Vorlesung <strong>Informatik</strong> C)<br />

Die von <strong>Java</strong> zur Verfügung gestellten Klassen s<strong>in</strong>d <strong>in</strong> Paketen organisiert. Um<br />

diese Klassen <strong>in</strong> eigenen Def<strong>in</strong>itionen zu nutzen, wird e<strong>in</strong> grundlegendes<br />

Verständnis von Paketen und Namensräumen benötigt.<br />

Sichtbarkeitsmodifikatoren s<strong>in</strong>d mit dem Paket-Konzept von <strong>Java</strong> verknüpft.<br />

Dieses Thema wird im Kapitel ‘Konstruktoren und Vererbung’ besprochen.<br />

3.8.1 <strong>Java</strong> API<br />

Das API (Application Programm<strong>in</strong>g Interface) wird als Teil des JRE (<strong>Java</strong><br />

Runtime Environment) zur Verfügung gestellt und enthält alle Pakete.<br />

z.B. /usr/lib/jdk1.3/jre/lib/rt.jar<br />

*.jar ist e<strong>in</strong>e spezielle Art der Archivierung (vgl. *.zip und *.tar). Der<br />

Inhalt e<strong>in</strong>er name.jar Datei kann mit jar tvf name.jar angezeigt werden.<br />

Auf alle (zugreifbaren) Komponenten von Klassen kann mit dem<br />

voll-qualifizierten Klassennamen zugegriffen werden.<br />

Beispiel System.out.pr<strong>in</strong>tln("Hello World!");<br />

Durch e<strong>in</strong>e import Anweisung kann mit dem e<strong>in</strong>fachen Klassennamen<br />

zugegriffen werden.<br />

z.B. import java.io.*;: alle Klassen des I/O-Pakets, import<br />

java.io.InputStream;: Klasse InputStream des I/O-Pakets.<br />

Warnung: undifferenzierter Import mit .* birgt die Gefahr von ungewollten<br />

Kollisionen!<br />

java.lang (core API) wird automatisch importiert.<br />

System.out.pr<strong>in</strong>tln("Hello World!");<br />

System ist e<strong>in</strong>e Klasse (genauer java.lang.System).<br />

out ist e<strong>in</strong> Klassen-Feld von System (vom Typ java.io.Pr<strong>in</strong>tstream).<br />

System.out verweist auf e<strong>in</strong> <strong>Objekt</strong>.


<strong>Informatik</strong> B SS 03 41<br />

0 Wed Oct 25 05:30:36 CEST 2000 java/lang/<br />

1428 Wed Oct 25 05:30:34 CEST 2000 java/lang/Object.class<br />

11661 Wed Oct 25 05:30:34 CEST 2000 java/lang/Str<strong>in</strong>g.class<br />

155 Wed Oct 25 05:30:34 CEST 2000 java/lang/Comparable.class<br />

7193 Wed Oct 25 05:30:34 CEST 2000 java/lang/Class.class<br />

325 Wed Oct 25 05:30:34 CEST 2000 java/lang/CloneNotSupportedException.class<br />

291 Wed Oct 25 05:30:34 CEST 2000 java/lang/Exception.class<br />

1559 Wed Oct 25 05:30:34 CEST 2000 java/lang/Throwable.class<br />

...<br />

113 Wed Oct 25 05:30:36 CEST 2000 java/io/Serializable.class<br />

293 Wed Oct 25 05:30:36 CEST 2000 java/io/IOException.class<br />

4064 Wed Oct 25 05:30:36 CEST 2000 java/io/ObjectStreamField.class<br />

1401 Wed Oct 25 05:30:36 CEST 2000 java/io/InputStream.class<br />

4463 Wed Oct 25 05:30:36 CEST 2000 java/io/Pr<strong>in</strong>tStream.class<br />

880 Wed Oct 25 05:30:36 CEST 2000 java/io/FilterOutputStream.class<br />

740 Wed Oct 25 05:30:36 CEST 2000 java/io/OutputStream.class<br />

4398 Wed Oct 25 05:30:36 CEST 2000 java/io/Pr<strong>in</strong>tWriter.class<br />

...<br />

Abbildung 14: Ausschnitt aus ’rt.jar’<br />

Das <strong>Objekt</strong> hat e<strong>in</strong>e Instanz-Methode pr<strong>in</strong>tln().<br />

Man kann es nicht oft genug sagen:<br />

– E<strong>in</strong>es der wichtigen Konzepte der <strong>Objekt</strong>-Orientierung ist reuse, also die<br />

Nutzung bereits vorhandener (getesteter) Klassen bei der Entwicklung von<br />

neuen Klassen.<br />

– Es ist empfehlenswert, sich e<strong>in</strong>en Überblick über die vom API zur<br />

Verfügung gestellten Klassen zu verschaffen, und diese vordef<strong>in</strong>ierten<br />

Klassen auch zu verwenden!<br />

– Beispielsweise muss man sich e<strong>in</strong>en Stack nicht selber schreiben, sondern<br />

kann die entsprechende Klasse aus java.util verwenden.<br />

– Am besten orientiert man sich über die “<strong>Java</strong> 2 Platform API Specification”<br />

(z.B. http://www-lehre.<strong>in</strong>f.uos.de/manuals/jdk1.4/docs/<br />

api/overview-summary.html)<br />

3.8.2 Packages und Namespaces <strong>in</strong> <strong>Java</strong><br />

Jede Klasse hat e<strong>in</strong>en e<strong>in</strong>fachen Namen, z.B. Circle oder InputStream,<br />

sowie e<strong>in</strong>en voll-qualifizierten Namen, z.B. java.io.InputStream.<br />

Indem man als erstes die Anweisung name¡ package ; <strong>in</strong> e<strong>in</strong>er Datei<br />

schreibt, bestimmt man, dass alle Klassen <strong>in</strong> dieser Datei zum name¡<br />

Paket<br />

gehören.


42 <strong>Informatik</strong> B SS 03<br />

Beispielsweise gehört die Klasse InputStream zum Paket java.io.<br />

E<strong>in</strong>e Datei, <strong>in</strong> der e<strong>in</strong>e Klasse def<strong>in</strong>iert wird, kann also vor der Klassendef<strong>in</strong>ition<br />

noch e<strong>in</strong>e Paket-Def<strong>in</strong>ition (als aller erstes, ausser Kommentar) sowie beliebig<br />

viele Import-Anweisungen enthalten:<br />

package mypackage;<br />

import java.io.InputStream;<br />

import java.util.Stack;<br />

public class MyClass {<br />

// def<strong>in</strong>ition<br />

}<br />

Gibt man für e<strong>in</strong>e Datei ke<strong>in</strong>e Paket-Anweisung an, so gehören alle dort<br />

def<strong>in</strong>ierten Klassen zu e<strong>in</strong>em default (unbenannten) Paket.<br />

Im Allgeme<strong>in</strong>en bestehen Pakete aus mehreren Klassen.<br />

Die Klassen e<strong>in</strong>es Pakets stehen <strong>in</strong> e<strong>in</strong>em geme<strong>in</strong>samen Verzeichnis.<br />

Das Verzeichnis muss denselben Namen wie das Paket haben.<br />

Vorteile:<br />

– Mehr Übersicht und Ordnung <strong>in</strong> den Dateien.<br />

– E<strong>in</strong>zigartige, voll-qualifizierte Klassennamen!<br />

Durch <strong>Java</strong>-Konvention: Der erste Teil des Paket-Namens ist der<br />

umgedrehte Internet-Doma<strong>in</strong>-Name des Nutzers, der die Klasse erzeugt.<br />

Wenn e<strong>in</strong> <strong>Java</strong>-Programm läuft und e<strong>in</strong>e Klasse (*.class) geladen werden soll<br />

(dynamic load<strong>in</strong>g), wird der Paket-Name <strong>in</strong> e<strong>in</strong>en Verzeichnisnamen (Pfad)<br />

aufgelöst.<br />

Um die entsprechenden absoluten Pfade zu f<strong>in</strong>den, schaut der <strong>Java</strong>-Interpreter<br />

<strong>in</strong> der Variable CLASSPATH nach und beg<strong>in</strong>nt <strong>in</strong> den dort gegebenen<br />

Verzeichnissen mit der Suche.<br />

– Diese Variable wird üblicherweise über das Betriebssystem gesetzt.<br />

– Es ist nützlich, eigene Makefiles zu schreiben, <strong>in</strong> denen die konkret<br />

benötigten Klassenpfade angegeben s<strong>in</strong>d (Übung!)<br />

3.8.3 Namens-Kollisionen<br />

Was passiert, wenn zwei Pakete importiert werden, die Klassen mit demselben<br />

Namen enthalten<br />

import java.util.*; // enthaelt Vector<br />

import mycollection.*; // enthaelt Vector


<strong>Informatik</strong> B SS 03 43<br />

Zunächst: nur potentielle Kollision.<br />

Der Compiler beschwert sich nicht, solange die Klasse Vektor nicht genutzt wird.<br />

Erst wenn versucht wird, e<strong>in</strong> <strong>Objekt</strong> der Klasse Vector zu erzeugen, meldet der<br />

Compiler das Problem.<br />

Lösung: java.util.Vector v = new java.util.Vector();<br />

kennzeichnet e<strong>in</strong>deutig, welche Klasse geme<strong>in</strong>t ist.<br />

Anmerkung: Der Compiler ersetzt alle Klassennamen durch ihre<br />

voll-qualifizierten Namen.<br />

3.8.4 Verhaltensänderungen<br />

Achtung: Wenn via import Klassen genutzt werden, so kann das eigene<br />

Programm se<strong>in</strong> Verhalten verändern, wenn die Def<strong>in</strong>itionen e<strong>in</strong>er importierten<br />

Klasse geändert wurden.<br />

Wenn die importierten Klassen nicht selbstgeschrieben s<strong>in</strong>d, kann dies, wenn<br />

es dumm läuft, unbeabsichtigt se<strong>in</strong> und zu Fehlern <strong>in</strong> der eigenen Klasse führen!<br />

Guter Stil ist es, dass solche Klassen und Komponenten, die öffentlich zur<br />

Verfügung gestellt werden, e<strong>in</strong>e klar def<strong>in</strong>ierte und dokumentierte Schnittstelle<br />

(Art und Anzahl der Parameter, Rückgabewert) haben und dass die<br />

Funktionalität/Semantik der öffentlich zur Verfügung gestellten Klassen nicht<br />

(bzw. nicht ohne alle Nutzer zu <strong>in</strong>formieren) geändert wird.<br />

Thema: Software-Eng<strong>in</strong>eer<strong>in</strong>g<br />

Verfolgen, welche Klassen für e<strong>in</strong> Programm geladen werden mit<br />

java -verbose CircleTest (kle<strong>in</strong>er Ausschnitt <strong>in</strong> Abb. 15)


44 <strong>Informatik</strong> B SS 03<br />

$ java -verbose CircleTest<br />

[Opened /usr/lib/jdk1.3.1/jre/lib/rt.jar]<br />

[Loaded java.lang.Object from /usr/lib/jdk1.3.1/jre/lib/rt.jar]<br />

[Loaded java.io.Serializable from /usr/lib/jdk1.3.1/jre/lib/rt.jar]<br />

[Loaded java.lang.Comparable from /usr/lib/jdk1.3.1/jre/lib/rt.jar]<br />

[Loaded java.lang.Str<strong>in</strong>g from /usr/lib/jdk1.3.1/jre/lib/rt.jar]<br />

[Loaded java.lang.Class from /usr/lib/jdk1.3.1/jre/lib/rt.jar]<br />

...<br />

[Loaded java.util.Dictionary from /usr/lib/jdk1.3.1/jre/lib/rt.jar]<br />

[Loaded java.util.Map from /usr/lib/jdk1.3.1/jre/lib/rt.jar]<br />

[Loaded java.util.Hashtable from /usr/lib/jdk1.3.1/jre/lib/rt.jar]<br />

[Loaded java.util.Properties from /usr/lib/jdk1.3.1/jre/lib/rt.jar]<br />

...<br />

[Loaded java.io.ObjectStreamField from /usr/lib/jdk1.3.1/jre/lib/rt.jar]<br />

...<br />

[Loaded java.security.AccessController from /usr/lib/jdk1.3.1/jre/lib/rt.jar]<br />

...<br />

[Loaded sun.io.ByteToCharConverter from /usr/lib/jdk1.3.1/jre/lib/rt.jar]<br />

[Loaded sun.io.Converters from /usr/lib/jdk1.3.1/jre/lib/rt.jar]<br />

...<br />

[Loaded sun.misc.Launcher from /usr/lib/jdk1.3.1/jre/lib/rt.jar]<br />

[Loaded java.net.URLStreamHandlerFactory from /usr/lib/jdk1.3.1/jre/lib/rt.jar]<br />

[Loaded sun.misc.Launcher$Factory from /usr/lib/jdk1.3.1/jre/lib/rt.jar]<br />

...<br />

[Loaded com.sun.rsajca.Provider from /usr/lib/jdk1.3.1/jre/lib/sunrsasign.jar]<br />

...<br />

[Loaded CircleTest]<br />

[Loaded Circle]<br />

[Loaded java.lang.Float<strong>in</strong>gDecimal from /usr/lib/jdk1.3.1/jre/lib/rt.jar]<br />

[Loaded java.lang.Math from /usr/lib/jdk1.3.1/jre/lib/rt.jar]<br />

Circle c Radius: 2.0<br />

Circle d Radius: 4.0<br />

Radius of bigger of c and d: 4.0<br />

Circle c Radius: 6.0<br />

[Loaded java.lang.Shutdown$Lock from /usr/lib/jdk1.3.1/jre/lib/rt.jar]<br />

Abbildung 15: Der <strong>Java</strong>-Interpreter lädt die core API-Klassen, weitere Klassen werden dynamisch<br />

dazugeladen


<strong>Informatik</strong> B SS 03 45<br />

4 Konstruktoren und Vererbung<br />

4.1 Konstruktoren<br />

Im Allgeme<strong>in</strong>en s<strong>in</strong>d Konstruktoren spezielle Funktionen zur Erzeugung von<br />

<strong>Objekt</strong>en.<br />

In der funktionalen <strong>Programmierung</strong> und <strong>in</strong> der Typtheorie ist e<strong>in</strong> Konstruktor e<strong>in</strong><br />

Symbol, mit dem e<strong>in</strong> <strong>Objekt</strong> e<strong>in</strong>es algebraischen Datentyps erzeugt wird.<br />

– Beispiel <strong>in</strong> ML:<br />

datatype ’a list = nil | :: of ’a * ’a list<br />

– Ausdrücke über Konstruktoren werden nicht reduziert (durch Ergebnis der<br />

Funktionsanwendung ersetzt); sie s<strong>in</strong>d bereits <strong>in</strong> Normalform.<br />

– Beispiele für ML-Listen mit Typvariable ’a als Integer:<br />

nil, 1::nil, 2::(1::nil)<br />

– Algebraische Datentypen heissen abstrakte Datentypen, wenn sie ihre<br />

Konstruktoren nicht “nach aussen” weitergeben. <strong>Objekt</strong>e e<strong>in</strong>es abstrakten<br />

Datentyps können nur mit speziellen Funktionen, die für diesen ADT (im<br />

selben “Modul”) def<strong>in</strong>iert wurden, manipuliert werden.<br />

In der objekt-orientieren <strong>Programmierung</strong> wurde der Begriff “Konstruktor” mit<br />

C++ e<strong>in</strong>geführt:<br />

– E<strong>in</strong> Konstruktor wird von e<strong>in</strong>er Klasse zur Verfügung gestellt und dient der<br />

Initialisierung e<strong>in</strong>es <strong>Objekt</strong>s.<br />

– In C++ und <strong>Java</strong> haben Konstruktoren denselben Namen wie die Klasse.<br />

– Da e<strong>in</strong> neues <strong>Objekt</strong> fast immer über e<strong>in</strong>en Konstruktoraufruf erzeugt wird,<br />

kann die Initialisierung nicht vergessen werden.<br />

– Als Gegenstück könnnen Destruktoren def<strong>in</strong>iert werden, die die vom<br />

<strong>Objekt</strong>e gehaltenen Ressourcen freigeben.<br />

– <strong>Java</strong> hat das Konstruktor-Konzept übernommen. Destruktoren s<strong>in</strong>d kaum<br />

notwendig, da <strong>Java</strong> mit Garbage Collector arbeitet.<br />

4.1.1 Def<strong>in</strong>ition von Konstruktoren<br />

Wird für e<strong>in</strong>e Klasse ke<strong>in</strong> Konstruktor def<strong>in</strong>iert, so wird e<strong>in</strong> Default Konstruktor<br />

(auch “no-arg constructor”) angelegt.<br />

Der Default Konstruktor hat denselben Namen wie die Klasse, ke<strong>in</strong>e Argumente<br />

und ke<strong>in</strong>en Rückgabetyp (auch nicht void).<br />

Alle Konstruktoren haben implizit e<strong>in</strong>e Referenz zum neu erzeugten <strong>Objekt</strong><br />

(this) als Argument.


46 <strong>Informatik</strong> B SS 03<br />

Im Konstruktor-Körper werden die Initialisierungen des this-<strong>Objekt</strong>s<br />

vorgenommen.<br />

Beispiel: Radius e<strong>in</strong>es Circle-<strong>Objekt</strong>s kann bei der Erzeugung <strong>in</strong>itialisiert werden<br />

public Circle(double r) { this.r = r; }<br />

...<br />

Circle c = new Circle(2.0);<br />

Die Variable ' ist vom Typ Circle. Sie wird mit e<strong>in</strong>em neuen Circle-<strong>Objekt</strong><br />

mit Radius 2 belegt (genauer mit dem Verweis auf dieses <strong>Objekt</strong>).<br />

4.1.2 Def<strong>in</strong>ition mehrerer Konstruktoren<br />

Möglichkeit, <strong>Objekt</strong> auf verschiedene Art zu <strong>in</strong>itialisieren.<br />

Beispiel: Initialisierung e<strong>in</strong>es Circle-<strong>Objekt</strong>s mit spezifischem Radius oder mit<br />

Default-Wert.<br />

public Circle() { r = 1.0; }<br />

public Circle(double r) { this.r = r; }<br />

Wie bei Methoden gilt overload<strong>in</strong>g: gleicher Name aber unterschiedliche<br />

Signaturen (Anzahl und Typ der Argumente). (Overload<strong>in</strong>g wird im Kapitel<br />

‘Klassenabhängigkeiten’ besprochen)<br />

E<strong>in</strong> Konstruktor kann andere Konstruktoren aufrufen:<br />

this() als Konstruktoraufruf; welcher Konstruktor aktiviert wird, hängt wieder<br />

von Anzahl und Typ der Argumente ab.<br />

Verwendung von this() ist e<strong>in</strong>e gute Strategie, wenn die Konstruktoren Teile<br />

der Initialisierung geme<strong>in</strong>sam haben<br />

this() darf nur als erste Anweisung <strong>in</strong> e<strong>in</strong>em Konstruktor vorkommen. Grund:<br />

automatischer Aufruf der Konstruktoren der Oberklasse (Kapitel<br />

‘Klassenabhängigkeiten’)<br />

// This is the basic constructor: <strong>in</strong>itialize the radius<br />

public Circle(double r) { this.r = r; }<br />

// This constructor uses this() to <strong>in</strong>voke the constructor above<br />

public Circle() { this(1.0); }<br />

4.2 Defaults und Initialisierung für Felder<br />

4.2.1 Defaults<br />

Lokale Variablen (<strong>in</strong>nerhalb von Methoden def<strong>in</strong>iert) haben ke<strong>in</strong>e Default-Werte.<br />

Werden lokale Variablen nicht vor ihrer Verwendung <strong>in</strong>itialisiert, liefert der<br />

<strong>Java</strong>-Compiler e<strong>in</strong>e Fehlermeldlung (der C-Compiler nur e<strong>in</strong>e Warnung).


<strong>Informatik</strong> B SS 03 47<br />

Tabelle 2: Default-Werte für Felder<br />

Typ<br />

Default<br />

boolean<br />

false<br />

char ‘ u0000’<br />

byte, short, <strong>in</strong>t, long 0<br />

float, double 0.0<br />

reference<br />

null<br />

(Klassen- und Instanz-) Felder s<strong>in</strong>d automatisch mit Default-Werten <strong>in</strong>itialisiert.<br />

Übliche Deklaration mit Zuweisung e<strong>in</strong>es <strong>in</strong>itialen Wertes ist ebenfalls möglich.<br />

public static f<strong>in</strong>al double PI = 3.14159;<br />

public double r = 1.0;<br />

4.2.2 Initialisierung von Instanz-Feldern: Konstruktoren<br />

Der <strong>Java</strong> Compiler erzeugt Initialisierungscode für Instanz-Felder und fügt sie <strong>in</strong><br />

den Konstruktor (oder die Konstruktoren) der Klasse e<strong>in</strong>.<br />

Reihenfolge: die im Programm angegebene (Nutzung von bereits <strong>in</strong>itialisierten<br />

Feldern bei der Initialisierung weiterer Felder möglich).<br />

Wenn e<strong>in</strong> Konstruktor mit this() Anweisung beg<strong>in</strong>nt, dann wird <strong>in</strong> diesen<br />

Konstruktor die Initialisierung nicht e<strong>in</strong>gefügt, sondern <strong>in</strong> denjenigen<br />

Konstruktor, der durch this() aktiviert wird.<br />

ab <strong>Java</strong> 1.1: Initialisierungsblöcke für <br />

Instanz-Felder: ... , die an<br />

beliebiger Stelle (an der Komponenten stehen können) <strong>in</strong> die Klasse e<strong>in</strong>gefügt<br />

werden können; üblich: direkt nach Feld; benutzt vor allem für anonyme <strong>in</strong>nere<br />

Klassen (kommt später)<br />

public class TestClass {<br />

public <strong>in</strong>t len = 10;<br />

public <strong>in</strong>t[] table = new <strong>in</strong>t[len];<br />

}<br />

public TestClass(){<br />

for (<strong>in</strong>t i = 0; i < len; i++) table[i] = i;<br />

}<br />

Ist äquivalent zu<br />

public class TestClass {<br />

public <strong>in</strong>t len;


¡<br />

48 <strong>Informatik</strong> B SS 03<br />

public <strong>in</strong>t[] table;<br />

}<br />

public TestClass() {<br />

len = 10;<br />

table = new <strong>in</strong>t[len];<br />

for (<strong>in</strong>t i = 0; i < len; i++) table[i] = i;<br />

}<br />

4.2.3 Initialisierung von Klassen-Feldern: Initialisierungs-Blöcke<br />

Klassen-Felder existieren auch dann, wenn ke<strong>in</strong> <strong>Objekt</strong> erzeugt wird.<br />

Initialisierung vor Konstruktoraufruf notwendig.<br />

<strong>Java</strong> Compiler erzeugt automatisch e<strong>in</strong>e Klassen-Initialisierungs-Methode für<br />

jede Klasse (<strong>in</strong>terne, versteckte cl<strong>in</strong>it¡ Methode<br />

), <strong>in</strong> der alle<br />

Klassen-Felder <strong>in</strong>itialisiert werden. Diese Methode wird genau e<strong>in</strong>mal<br />

ausgewertet, nämlich wenn die Klasse das erstemal benutzt (geladen) wird.<br />

Initialisierung wieder <strong>in</strong> der im Programm angegebenen Reihenfolge.<br />

Explizite Initialisierung von Klassen-Feldern mit static <strong>in</strong>itializer Block:<br />

static ... , der an jeder Stelle der Klasse stehen kann, wo<br />

Komponenten stehen können.<br />

Es kann mehrere solche Initialisierungs-Blöcke geben.<br />

Initialisierungs-Blöcke werden vom Compiler <strong>in</strong> die<br />

Klassen-Initialisierungs-Methode <strong>in</strong>tegriert.<br />

Statische Initialisierung ist wie e<strong>in</strong>e Klassen-Methode, also ke<strong>in</strong>e Verwendung<br />

von this möglich, ke<strong>in</strong>e Nutzung von Instanz-Komponenten möglich.<br />

// We can draw the outl<strong>in</strong>e of a circle us<strong>in</strong>g trigonometric functions<br />

// Trigonometry is slow, though, so we precompute a bunch of values<br />

public class TrigCircle {<br />

// Here are our static lookup tables and their own simple <strong>in</strong>itializers<br />

private static f<strong>in</strong>al <strong>in</strong>t NUMPTS = 500;<br />

private static double s<strong>in</strong>es[] = new double[NUMPTS];<br />

private static double cos<strong>in</strong>es[] = new double[NUMPTS];<br />

// Here’s a static <strong>in</strong>itializer that fills <strong>in</strong> the arrays<br />

static {<br />

double x = 0.0;<br />

double delta_x = (Circle.PI/2)/(NUMPTS-1);<br />

for (<strong>in</strong>t i = 0; i < NUMPTS; i++, x += delta_x) {<br />

s<strong>in</strong>es[i] = Math.s<strong>in</strong>(x);<br />

cos<strong>in</strong>es[i] = Math.cos(x);<br />

} } }


<strong>Informatik</strong> B SS 03 49<br />

4.3 Zerstören und F<strong>in</strong>alisieren von <strong>Objekt</strong>en<br />

4.3.1 Garbage Collection<br />

Mit new werden neue <strong>Objekt</strong>e erzeugt (und dynamisch, zur Laufzeit auf dem<br />

heap abgelegt)<br />

Anmerkung: Werte primitiver Datentypen (bei lokalen Variablen) werden<br />

dagegen statisch, auf dem Stack abgelegt, dessen Grösse bereits zur<br />

Compilezeit bestimmt wird.<br />

Wenn e<strong>in</strong> <strong>Objekt</strong> nicht länger benutzt wird, wird der Speicherplatz automatisch<br />

freigegeben (garbage collection, alte Technik, ursprünglich <strong>in</strong> Lisp entwickelt)<br />

Der <strong>Java</strong> Interpreter weiss, welche <strong>Objekt</strong>e und Arrays er angelegt (allocated)<br />

hat, und kann ermitteln, welche <strong>Objekt</strong>e und lokale Variablen auf andere<br />

<strong>Objekt</strong>e verweisen. Wenn ke<strong>in</strong> Verweis auf e<strong>in</strong> <strong>Objekt</strong> existiert, kann es zerstört<br />

werden; dito für nicht mehr referenzierte Verweis-Zyklen.<br />

Der Garbage Collector läuft immer im H<strong>in</strong>tergrund als low priority thread<br />

(Multi-Thread<strong>in</strong>g wird später behandelt); wird im Normalfall immer aktiv, wenn<br />

nichts Wichtiges passiert (z.B. beim Warten auf Input), ausser: wenn kaum noch<br />

freier Speicher vorhanden ist.<br />

Garbage Collection kann nie so effizient se<strong>in</strong> wie gute selbstgeschriebene<br />

Speicherverwaltung (free(), delete); aber es verh<strong>in</strong>dert Fehler (z.B. memory<br />

leaks) und erlaubt schnellere Entwicklung von Code.<br />

Memory leaks können <strong>in</strong> <strong>Java</strong> so gut wie nicht passieren (Ausnahmen: lokale<br />

Variablen <strong>in</strong> Methoden, die lange Ausführungszeiten haben und dabei nicht auf<br />

diese Variablen zugreifen; <strong>Objekt</strong>e mit Referenzen <strong>in</strong> Hash-Tabellen werden erst<br />

zerstört, wenn die Hash-Tabelle selbst zerstört wird).<br />

4.3.2 Anmerkung: F<strong>in</strong>alization<br />

Freigabe von bestimmten Resourcen, die e<strong>in</strong> <strong>Objekt</strong> benutzt, wird nicht vom<br />

Garbage Collector erledigt (z.B. temporäre Dateien löschen)<br />

F<strong>in</strong>alizer ist Instanz-Methode, Gegenstück zu Konstruktor (“Destruktor”); wird<br />

vom Garbage Collector aufgerufen; ke<strong>in</strong>e Argumente, ke<strong>in</strong> Rückgabewert<br />

Es darf nur e<strong>in</strong>en F<strong>in</strong>alizer pro Klasse geben.<br />

protected void f<strong>in</strong>alize()<br />

Selbstgeschriebene Klassen benötigen selten explizite F<strong>in</strong>alizer (Ausnahme<br />

native f<strong>in</strong>alize für Schnittstellen zu Code, der nicht unter Kontrolle des<br />

Garbage Collectors ist)


50 <strong>Informatik</strong> B SS 03<br />

Animal<br />

has sk<strong>in</strong><br />

can move around<br />

eats<br />

breathes<br />

Bird<br />

has w<strong>in</strong>gs<br />

can fly<br />

has feathers<br />

Fish<br />

has f<strong>in</strong>s<br />

can swim<br />

has gills<br />

Canary<br />

can s<strong>in</strong>g<br />

is yellow<br />

Ostrich<br />

has th<strong>in</strong> long legs<br />

is tall<br />

can’t fly<br />

Shark<br />

can bite<br />

is dangerous<br />

Salmon<br />

is p<strong>in</strong>k<br />

is edible<br />

swims upriver<br />

to lay eggs<br />

Abbildung 16: Illustration e<strong>in</strong>es hierarchischen semantischen Netwerks<br />

4.4 Unterklassen und Vererbung<br />

4.4.1 Exkurs: Hierarchische Semantische Netze<br />

Der Teachable Language Comprehender (TLC) von Coll<strong>in</strong>s und Quillian (1969)<br />

ist e<strong>in</strong> frühes (implementiertes) kognitives Modell zum semantischen<br />

Gedächtnis. (siehe Abb. 16)<br />

Psychologische Experimente: Antwortzeiten bei Entscheidungsfragen<br />

“A canary eats” dauert länger als “A canary is yellow”<br />

Idee: Wissen ist <strong>in</strong> hierarchischem Netz mit Vererbung organisiert (“kognitive<br />

Ökonomie”) Antworten dauern um so länger, je weiter man “nach oben” (zu<br />

Oberklassen) suchen muss, um e<strong>in</strong>e Eigenschaft zu verifizieren.<br />

“Flache” logische Repräsentation:<br />

Klassen und <strong>Objekt</strong>e werden mehrfach repräsentiert!<br />

Verifikation über logische Inferenzregeln (Transitivität)<br />

“natürlicher”: Jede Klasse existiert genau e<strong>in</strong>mal,<br />

Ober-/Unterklassen-Beziehungen werden für jede Klasse angegeben.


<strong>Informatik</strong> B SS 03 51<br />

/* Fakten */<br />

isa(canary, bird).<br />

isa(ostrich, bird).<br />

isa(bird, animal).<br />

isa(shark, fish).<br />

isa(salmon, fish).<br />

isa(fish, animal).<br />

has(sk<strong>in</strong>, animal).<br />

does(eat, animal).<br />

...<br />

/* Inferenzregeln */<br />

is_a(A,B) :- isa(A,B). /* R1: direkter Fall isa */<br />

is_a(A,C) :- isa(A,B), is_a(B,C). /* R2: Transitivitaet von isa */<br />

/* analog fuer has, does, ... */<br />

Abbildung 17: Flache Prolog-Realisierung des TLC<br />

class Animal {<br />

boolean hasSk<strong>in</strong> = true;<br />

boolean canEat = true;<br />

}<br />

class Bird extends Animal {<br />

boolean hasW<strong>in</strong>gs = true;<br />

boolean canFly = true; // default, gilt nicht fuer alle Voegel<br />

}<br />

class Ostrich extends Bird {<br />

Ostrich() { canFly = false; }<br />

}<br />

Abbildung 18: Hierarchie und Vererbung s<strong>in</strong>d natürliche Konzepte <strong>in</strong> der OO-<strong>Programmierung</strong>


¡<br />

52 <strong>Informatik</strong> B SS 03<br />

4.4.2 Erweiterung von ‘Circle’<br />

Spezielle Kreise: haben Radius und Position <strong>in</strong> der Ebene<br />

PlaneCircle als Unterklasse von Circle<br />

Funktionale Erweiterung von Klassen durch Unterklassenbildung ist zentral für<br />

objekt-<strong>orientierte</strong> <strong>Programmierung</strong><br />

class Name¡ extends SName¡ ...<br />

Felder und Methoden der Oberklasse werden automatisch vererbt,<br />

Konstruktoren nicht! (In anderen Sprachen werden s<strong>in</strong>nvollerweise auch die<br />

Konstruktoren vererbt. Dafür kann es dann ke<strong>in</strong>e automatisch generierten<br />

Default-Konstruktoren geben.)<br />

Unterklassen-Konstruktor kann Konstruktor der Oberklasse durch super()<br />

aufrufen (analog zu this())<br />

public class PlaneCircle extends Circle {<br />

// We autmatically <strong>in</strong>herit the fields and methods of Circle,<br />

// so we only have to put the new stuff here.<br />

// New <strong>in</strong>stance fields that store the center po<strong>in</strong>t of the circle<br />

public double cx, cy;<br />

// A new constructor method to <strong>in</strong>itialize the new fields<br />

// It uses a special syntax to <strong>in</strong>voke the Circle() constructor<br />

public PlaneCircle(double r, double x, double y) {<br />

super(r); // Invoke constructor of the superclass<br />

this.cx = x; // Initialize <strong>in</strong>stance fields<br />

this.cy = y; // us<strong>in</strong>g ‘this’ is not necessary here<br />

}<br />

// The area() and circumference() methods are <strong>in</strong>herited from Circle<br />

// A new <strong>in</strong>stance method that checks whether a po<strong>in</strong>t is <strong>in</strong>side the circle<br />

// Note that it uses the <strong>in</strong>herited <strong>in</strong>stance field r<br />

public boolean isInside(double x, double y) {<br />

double dx = x - cx, dy = y - cy; // Distance from center<br />

}<br />

}<br />

double distance = Math.sqrt(dx*dx + dy*dy); // Pythagorean theorem<br />

return (distance < r);<br />

// Returns true or false<br />

4.4.3 Erweiterung e<strong>in</strong>er Klasse<br />

Vererbung von Feldern (z.B. r)<br />

Vererbung von Methoden:<br />

Methoden der Oberklasse können für <strong>Objekt</strong>e der Unterklasse genutzt werden.<br />

Zusätzliche Felder und Methoden können für die Unterklasse def<strong>in</strong>iert werden.


<strong>Informatik</strong> B SS 03 53<br />

Circle<br />

PI<br />

r<br />

+ radiansToDegrees<br />

+ area<br />

+ circumference<br />

PlaneCircle<br />

cx<br />

cy<br />

+ isInside<br />

Abbildung 19: E<strong>in</strong> Circle und se<strong>in</strong>e Unterklasse<br />

Jedes <strong>Objekt</strong> der Unterklasse besitzt alle Instanz-Felder und -Methoden der<br />

Oberklasse.<br />

Typkonversion zwischen Unter- und Oberklassen:<br />

von Unterklasse zu Oberklasse (upcast<strong>in</strong>g), <strong>Objekt</strong> wird allgeme<strong>in</strong>er (verliert<br />

Zugriff auf spezielle Felder und Methoden) ohne Cast<strong>in</strong>g;<br />

von Oberklasse zu Unterklasse (downcast<strong>in</strong>g): Cast<strong>in</strong>g notwendig (und Prüfung<br />

zur Laufzeit durch die VM)<br />

(vergleiche widen<strong>in</strong>g und narrow<strong>in</strong>g bei primitiven Datentypen)<br />

PlaneCircle pc = new PlaneCircle(2.0, 5.0, 5.0);<br />

double ratio = pc.circumference() / pc.area();<br />

Circle c = pc; // no access to position<strong>in</strong>g<br />

PlaneCircle pc2 = (PlaneCircle) c;<br />

boolean orig<strong>in</strong><strong>in</strong>side = ((PlaneCircle) c).isInside(0.0, 0.0);<br />

4.5 Kapslung und Zugriffskontrolle<br />

Klassen als Sammlung von Daten und Methoden.<br />

Wichtige objekt-<strong>orientierte</strong> Technik: Information-Hid<strong>in</strong>g, Encapsulation<br />

(E<strong>in</strong>kapslung):<br />

Daten nur über Methoden zugänglich machen;<br />

Daten und <strong>in</strong>terne (private) Methoden s<strong>in</strong>d sicher <strong>in</strong> der Klasse e<strong>in</strong>geschlossen<br />

und können nur von vertrauenswürdigen Nutzern (also über ordentlich def<strong>in</strong>ierte<br />

öffentliche Methoden der Klasse) benutzt werden.


54 <strong>Informatik</strong> B SS 03<br />

Schutz der Klasse gegen absichtliche oder unabsichtliche E<strong>in</strong>griffe.<br />

z.B. konsistente Belegung von vone<strong>in</strong>ander abhängigen Felder (Sicherung über<br />

sorgfältig def<strong>in</strong>ierte Methoden)<br />

Verstecken <strong>in</strong>terner Implementations-Details. Ändern der Implementation, ohne<br />

dass genutzter Code dieser Klasse betroffen ist. (vgl. Theorie der Modularen<br />

<strong>Programmierung</strong>)<br />

Öffentlich sichtbare Felder und zu viele Methoden machen API unübersichtlich<br />

und schwer zu verstehen.<br />

4.5.1 Zugriffs-Kontrolle<br />

(access control)<br />

Paket-Zugriff: Nicht Teil von <strong>Java</strong> selbst (Lesbarkeit von Dateien,<br />

Verzeichnissen)<br />

Paket: üblicherweise <strong>in</strong> e<strong>in</strong>em Verzeichnis; wenn ke<strong>in</strong> explizites Paket<br />

angegeben wird, wird e<strong>in</strong> unbenanntes Default-Paket generiert.<br />

Klassen-Zugriff: Default ist, dass top-level Klassen (etwas anderes kennen wir<br />

noch nicht) paket-weit zugreifbar s<strong>in</strong>d. public deklarierte Klassen s<strong>in</strong>d überall<br />

(wo das Paket zugreifbar ist) zugreifbar.<br />

Zugriff auf Komponenten e<strong>in</strong>er Klasse: Komponenten s<strong>in</strong>d <strong>in</strong> jedem Fall <strong>in</strong> der<br />

Klasse selbst zugreifbar; Default: paket-weiter Zugriff;<br />

Alle Klassen, die zum selben Paket gehören, dürfen zugreifen. Bei<br />

unbenanntem Paket typischerweise alle Klassen im selben Verzeichnis<br />

(implementationsabhängig).<br />

Zugriffsmodifikatoren: public, protected, private für Felder und<br />

Methoden.<br />

4.5.2 Vier Ebenen von Zugriffsrechten<br />

Für Klassen-Komponenten:<br />

public: von überall (wo Paket zugreifbar ist) zugreifbar<br />

protected: paket-weit und aus allen Unterklassen (egal, <strong>in</strong> welchem Paket sie<br />

def<strong>in</strong>iert s<strong>in</strong>d) zugreifbar<br />

default: paket-weit zugreifbar (wenn ke<strong>in</strong> Modifikator angegeben)<br />

private: nur <strong>in</strong> der Klasse selbst zugreifbar


<strong>Informatik</strong> B SS 03 55<br />

Tabelle 3: Zugriffsmodifkatoren<br />

Accessible to public protected ‘package’ private<br />

Def<strong>in</strong><strong>in</strong>g class yes yes yes yes<br />

Class <strong>in</strong> same package yes yes yes no<br />

Subclass <strong>in</strong> different package yes yes no no<br />

Non-subclass <strong>in</strong> different package yes no no no<br />

4.5.3 Zugriffs-Kontrolle und Vererbung<br />

Unterklasse erbt alle Instanz-Felder und -Methoden der Oberklasse. Manche<br />

Komponenten s<strong>in</strong>d aufgrund der E<strong>in</strong>schränkung der Sichtbarkeit nicht<br />

zugreifbar.<br />

Wenn Ober- und Unterklasse im selben Paket: Zugriff auf alle nicht-privaten<br />

Felder und Methoden.<br />

Wenn Ober- und Unterklasse <strong>in</strong> verschiedenen Paketen: Zugriff auf alle public<br />

und protected Felder und Methoden.<br />

private Komponenten und Konstruktoren können nie ausserhalb der Klasse<br />

zugegriffen werden.<br />

In Tabelle 3 ist e<strong>in</strong>e Übersicht über die Zugriffsmodifikatoren <strong>in</strong> <strong>Java</strong> angegeben.<br />

Die Semantik ist leicht verschiedenen zu anderen objekt-orientieren Sprachen<br />

(C++). H<strong>in</strong>ter der e<strong>in</strong>fachen Struktur der Tabelle s<strong>in</strong>d subtile Probleme versteckt<br />

(z.B. wie verhält sich protected, wenn Klasse A und Unterklasse B <strong>in</strong><br />

verschiedenen Paketen stehen und Klasse B <strong>in</strong> Klasse A genutzt wird Wie<br />

spielen Cast<strong>in</strong>g und Zugriffsmodifikatoren zusammen)<br />

Beispiel:<br />

Circle mit protected r.<br />

PlaneCircle (ist Unterklasse) <strong>in</strong> anderem Paket.<br />

Methode <strong>in</strong> PlaneCircle:<br />

public boolean isBigger (Circle c) {<br />

return (this.r > c.r);<br />

}<br />

Compiler-Fehler: Zugriff auf this.r ist erlaubt, da das Feld r von der Unterklasse<br />

PlaneCircle geerbt wird.<br />

Aber: Zugriff auf c.r ist <strong>in</strong> PlaneCircle nicht erlaubt!<br />

Wäre erlaubt, wenn<br />

PlaneCircle c anstelle von Circle c, oder wenn<br />

Circle und PlaneCircle im selben Paket def<strong>in</strong>iert wären. (“<strong>Java</strong>-Feature”)


56 <strong>Informatik</strong> B SS 03<br />

In Tabelle 3 fehlt die Information über das Zusammenspiel der Rechte mit dem<br />

Typ der Referenz. (siehe Übung)<br />

In der Regel darf man auf protected/private Komponenten nur über e<strong>in</strong>e<br />

Referenz der Klasse zugreifen, <strong>in</strong> der der Zugriff erfolgt.<br />

4.5.4 Daumenregeln für Sichtbarkeits-Modifikatoren<br />

Benutze public nur für Methoden und Konstanten, die den öffentlichen Teil des<br />

API der Klasse darstellen sollen.<br />

Kapsle Felder: als privat deklarieren und Zugriff über public Methoden<br />

Benutze protected für Komponenten, die bei der Erzeugung von Unterklassen<br />

notwendig se<strong>in</strong> könnten.<br />

Achtung: Änderung von protected Komponenten kann im Code der Klasse<br />

Inkonsistenzen erzeugen.<br />

Benutze default Sichtbarkeit für Komponenten, die <strong>in</strong>terne Implementation<br />

realisieren und von Klassen im selben Paket genutzt werden sollen.<br />

Nutzung der package Anweisung, um mite<strong>in</strong>ander kooperierende Klassen <strong>in</strong><br />

e<strong>in</strong> Paket zu bündeln.<br />

Sonst benutze private.<br />

Besser zunächst möglichst restriktive Rechte vergeben und erst wenn nötig lockern.<br />

4.5.5 Daten-Zugriffs-Methoden<br />

package myshapes; // Specify a package for the class<br />

public class Circle { // The class is still public<br />

// This is a generally useful constant, so we keep it public<br />

public static f<strong>in</strong>al double PI = 3.14159;<br />

protected double r; // Radius is hidden, but visible to subclasses<br />

// A method to enforce the restriction of the radius<br />

// This is an implementation detail that may be of<br />

// <strong>in</strong>terest to subclasses<br />

protected void checkRadius(double radius) {<br />

if (radius < 0.0)<br />

throw new IllegalArgumentException("radius may not be negative.");<br />

}<br />

// The constructor method<br />

public Circle(double r) {<br />

checkRadius(r);<br />

this.r = r;<br />

}


<strong>Informatik</strong> B SS 03 57<br />

// Public data accessor methods<br />

public double getRadius() { return r; }<br />

public void setRadius(double r) {<br />

checkRadius(r);<br />

this.r = r;<br />

}<br />

}<br />

// Methods to operate on the <strong>in</strong>stance field<br />

public double area() { return PI * r * r; }<br />

public double circumference() { return 2 * PI * r; }<br />

Klasse ist e<strong>in</strong>em Paket zugeordnet (Verzeichnisname gleich dem Paketnamen).<br />

Methoden s<strong>in</strong>d um Fehlerprüfung erweitert (explizite checkXX() Methoden).<br />

Zugriff auf Werte von Instanz-Feldern erfolgt über Methoden (setXX(),<br />

getXX()).<br />

Felder, auf die mit set- und get-Methoden zugegriffen wird, werden auch<br />

Properties genannt. Man spricht von “Getter”- und “Setter”-Methoden.


¡<br />

58 <strong>Informatik</strong> B SS 03<br />

5 Klassenabhängigkeiten<br />

5.1 Klassenhierarchie<br />

5.1.1 F<strong>in</strong>ale Klassen<br />

Als f<strong>in</strong>al deklarierte Klassen können nicht erweitert werden.<br />

z.B. java.lang.System<br />

Verh<strong>in</strong>dern ungewünschter Erweiterungen<br />

(Compiler kann e<strong>in</strong>ige Optimierungen vornehmen, siehe später)<br />

5.1.2 Die Klasse ‘Object’<br />

Jede Klasse, die def<strong>in</strong>iert wird, hat e<strong>in</strong>e Oberklasse.<br />

Wenn ke<strong>in</strong>e Oberklasse (spezifiziert nach extends) angegeben wird, dann wird<br />

als Oberklasse java.lang.Object vergeben.<br />

Object ist e<strong>in</strong>e Klasse mit speziellem Status: e<strong>in</strong>zige Klasse, die ke<strong>in</strong>e<br />

Superklasse hat; alle <strong>Java</strong> Klassen erben die Methoden von Object.<br />

Vorteil e<strong>in</strong>er solchen “s<strong>in</strong>gle rooted hierarchy”:<br />

– Bei jedem <strong>Objekt</strong> ist garantiert, dass es vom Typ Object ist. Damit können<br />

Klassen/Methoden def<strong>in</strong>iert werden, die auf allen <strong>Objekt</strong>en arbeiten (siehe<br />

Collection Klassen) und bestimmte Methoden können für alle <strong>Objekt</strong>e<br />

angewendet werden (z.B. toStr<strong>in</strong>g()).<br />

– Für alle <strong>Objekt</strong>e ist via Object garantiert, dass sie s<strong>in</strong>nvoll mit dem<br />

Laufzeit-System <strong>in</strong>teragieren (z.B. Garbage Collection)<br />

5.1.3 Klasse ‘Str<strong>in</strong>g’<br />

Spezieller Status (wie Array; mit spezieller Syntax).<br />

Text <strong>in</strong> doppelten Anführungszeichen kann direkt an e<strong>in</strong>e Variable vom Typ<br />

Str<strong>in</strong>g zugewiesen werden.<br />

Escape-Sequenzen können verwendet werden.<br />

Str<strong>in</strong>gs im Quelltext dürfen nicht über Zeilen umgebrochen werden. (Lösung:<br />

Aufteilen und konkatenieren; Konkatenation konstanter Str<strong>in</strong>gs bei Compilation,<br />

nicht zur Laufzeit)<br />

Str<strong>in</strong>g name = "David";<br />

System.out.pr<strong>in</strong>tln("Hello\t" + name);<br />

Str<strong>in</strong>g s = "This is a test of the" +<br />

"emergency broadcast system";


¡<br />

<strong>Informatik</strong> B SS 03 59<br />

5.1.4 Hierarchische Klassenstruktur<br />

In <strong>Java</strong> kann e<strong>in</strong>e Klasse nur genau e<strong>in</strong>e andere Klasse als Oberklasse haben.<br />

Im Gegensatz zu C++ gibt es ke<strong>in</strong>e Mehrfachvererbung<br />

Klassenbaum (nicht Klassenverband)!<br />

Datenstruktur:<br />

Problem bei Mehrfachvererbung: Existiert dieselbe Komponente <strong>in</strong> mehr als<br />

e<strong>in</strong>er Klasse, von der geerbt wird, muss geregelt werden, welche dieser<br />

Komponenten <strong>in</strong> die Unterklasse übernommen wird!<br />

Beispiel: java.lang.Object ist die Wurzel des Baums. Number stammt von<br />

Object ab; Integer von Number.<br />

In Abb. 20 (Quelle: Charles L. Perk<strong>in</strong>s, siehe auch http:<br />

//www.cs.rit.edu/˜ats/java/html/skript/A_Classes.htmld/<br />

zeigen die durchgezogenen L<strong>in</strong>ien Klassenabhängigkeiten, die gestrichelten<br />

L<strong>in</strong>ien zeigen die Implementierung von Schnittstellen (Kapitel ‘Abstrakte Klassen<br />

und Interfaces’).<br />

– Boxen mit runden Ecken: Interfaces (Kapitel ‘Abstrakte Klassen und<br />

Interfaces’)<br />

– weisse Boxen: Klassen<br />

– hellgraue Boxen: F<strong>in</strong>al<br />

– dunkelgraue Boxen: abstrakte Klassen (Kapitel ‘Abstrakte Klassen und<br />

Interfaces’)


60 <strong>Informatik</strong> B SS 03<br />

Boolean<br />

java.lang<br />

©1995-7, Charles L. Perk<strong>in</strong>s<br />

http://rendezvous.com/java<br />

Cloneable<br />

Character<br />

Class<br />

ClassLoader<br />

FDBigInt<br />

local to package<br />

Serializable<br />

java.io-objects<br />

Compiler<br />

Math<br />

Number<br />

Process<br />

Byte<br />

Double<br />

Float<br />

Integer<br />

Long<br />

Object<br />

Runtime<br />

SecurityManager<br />

Short<br />

Float<strong>in</strong>gDecimal<br />

local to package<br />

Str<strong>in</strong>g<br />

Str<strong>in</strong>gBuffer<br />

NullSecurityManager<br />

local to package<br />

System<br />

Runnable<br />

Thread<br />

ThreadGroup<br />

Throwable<br />

Void<br />

Error<br />

java.lang-errors<br />

Exception<br />

java.lang-exceptions<br />

ThreadDeath<br />

Abbildung 20: Hierarchische Organisation von ‘java.lang’


¡<br />

<strong>Informatik</strong> B SS 03 61<br />

5.2 Ergänzung: Konstruktoren<br />

5.2.1 Unterklassen-Konstruktoren<br />

PlaneCircle extends Circle<br />

// A new constructor method to <strong>in</strong>itialize the new fields<br />

public PlaneCircle(double r, double x, double y) {<br />

super(r); // Invoke constructor of the superclass<br />

this.cx = x; // Initialize <strong>in</strong>stance fields<br />

this.cy = y;<br />

}<br />

super(): Aufruf e<strong>in</strong>es Oberklassen-Konstruktors<br />

(analog zu this(): Aufruf e<strong>in</strong>es Klassen-Konstruktors)<br />

super() kann nur <strong>in</strong>nerhalb e<strong>in</strong>er Konstruktor-Methode benutzt werden<br />

Aufruf des Oberklassen-Konstruktors muss an erster Stelle <strong>in</strong> e<strong>in</strong>em Konstruktor<br />

stehen (sogar vor der Deklaration lokaler Variablen)<br />

Argumente, die an super() übergeben werden, müssen mit der Signatur e<strong>in</strong>es<br />

Oberklassen-Konstruktors übere<strong>in</strong>stimmen. Der entsprechende Konstruktor wird<br />

dann ausgewählt.<br />

5.2.2 Default-Konstruktoren<br />

<strong>Java</strong> garantiert, dass immer, wenn e<strong>in</strong>e Instanz e<strong>in</strong>er Klasse erzeugt wird, die<br />

(bzw. e<strong>in</strong>e) Konstruktormethode der Klasse aufgerufen wird.<br />

Wenn die Klasse e<strong>in</strong>e Unterklasse ist, ist ebenfalls garantiert, dass der<br />

Konstruktor der Oberklasse aufgerufen wird.<br />

<strong>Java</strong> muss sicherstellen, dass jede Konstruktormethode e<strong>in</strong>e<br />

Konstruktormethode der Oberklasse aufruft.<br />

Wenn ke<strong>in</strong> expliziert Aufruf angegeben ist, wird der Aufruf des Default<br />

Konstruktors super() e<strong>in</strong>gefügt.<br />

Achtung: Wenn die Oberklasse ke<strong>in</strong>en null-stelligen Konstruktor anbietet, erfolgt<br />

e<strong>in</strong> Compiler-Fehler!<br />

Wenn e<strong>in</strong>e Klasse gar ke<strong>in</strong>en Konstruktor def<strong>in</strong>iert, wird e<strong>in</strong> null-stelliger<br />

Default-Konstruktor erzeugt:<br />

public Klassen erhalten public Konstruktoren; alle anderen Klassen erhalten<br />

den default Konstruktor ohne Sichtbarkeits-Modifikator.<br />

Um zu verh<strong>in</strong>dern, dass e<strong>in</strong> public Konstruktor e<strong>in</strong>gefügt wird, sollte<br />

m<strong>in</strong>destens e<strong>in</strong> nicht-public Konstruktor def<strong>in</strong>iert werden.<br />

Für Klassen, die nicht <strong>in</strong>stantiiert werden sollen, sollte e<strong>in</strong> private Konstruktor<br />

def<strong>in</strong>iert werden (kann nicht von ausserhalb der Klasse aufgerufen werden,<br />

verh<strong>in</strong>dert automatische E<strong>in</strong>führung e<strong>in</strong>es default-Konstruktors.)<br />

(später: abstrakte Klassen)


¡<br />

¡<br />

62 <strong>Informatik</strong> B SS 03<br />

5.2.3 Konstruktor-Verkettung<br />

(constructor cha<strong>in</strong><strong>in</strong>g)<br />

Wenn e<strong>in</strong>e neue Instanz e<strong>in</strong>es <strong>Objekt</strong>s zur ¡ Klasse erzeugt wird, wird der<br />

entsprechende ¢ ¦¨ Konstruktor aufgerufen. Dieser ruft explizit oder implizt den<br />

Konstruktor der unmittelbaren £ ¥¤§¦<br />

Oberklasse auf usw., solange bis der<br />

Konstruktor der Klasse Object aufgerufen wird.<br />

Die Ausführung ist <strong>in</strong> umgekehrter Reihenfolge zum Aufruf, also zuerst wird<br />

Object() ausgeführt.<br />

Wann immer der Körper e<strong>in</strong>es Konstruktors ausgeführt wird, ist<br />

sichergestellt, dass alle Felder der Oberklasse bereits <strong>in</strong>itialisiert s<strong>in</strong>d!<br />

Anmerkung f<strong>in</strong>alizer cha<strong>in</strong><strong>in</strong>g: f<strong>in</strong>alize() Methoden werden nicht<br />

automatisch verkettet. super.f<strong>in</strong>alize()<br />

5.3 Vererbung: Shadow<strong>in</strong>g und Overrid<strong>in</strong>g<br />

5.3.1 Verdecken von Feldern der Oberklasse<br />

(Shadow<strong>in</strong>g)<br />

E<strong>in</strong> etwas konstruiertes Beispiel:<br />

Weiteres Instanz-Feld <strong>in</strong> Klasse PlaneCircle, das die Distanz zwischen dem<br />

Kreismittelpunkt und dem Ursprung (0, 0) angibt, das r genannt wird, also<br />

dieselbe Bezeichnung hat, wie das Feld r (für Radius) <strong>in</strong> der Oberklasse<br />

Circle.<br />

class PlaneCircle extends Circle {<br />

public double r;<br />

...<br />

PlaneCircle(...) { // Pythagorean Theorem<br />

this.r = Math.sqrt(cx * cx + cy * cy);<br />

} }<br />

Zugriff auf Felder der aktuellen Klasse und der Oberklasse:<br />

r<br />

this.r<br />

super.r<br />

// Feld der aktuellen Klasse<br />

// dito<br />

// Feld der Oberklasse<br />

Alternativ: Cast des <strong>Objekt</strong>s zur entsprechenden Oberklasse.<br />

((Circle) this).r<br />

Klammerung beachten: erst Cast, dann Zugriff auf Feld!


<strong>Informatik</strong> B SS 03 63<br />

Cast<strong>in</strong>g ist vor allem hilfreich, wenn auf e<strong>in</strong> Feld referenziert werden soll, das<br />

nicht <strong>in</strong> der direkten Oberklasse def<strong>in</strong>iert ist.<br />

Beispiel: class C extends B, class B extends A und alle drei Klassen<br />

haben e<strong>in</strong> Feld x; Innerhalb von C:<br />

x<br />

this.x<br />

super.x<br />

((B)this).x<br />

((A)this).x<br />

super.super.x<br />

// Field x <strong>in</strong> class C<br />

// dito<br />

// Field x <strong>in</strong> class B<br />

// dito<br />

// Field x <strong>in</strong> class A<br />

// Illegal Syntax<br />

Zugriff auf x e<strong>in</strong>er Instanz von C<br />

C c = new C();<br />

c.x<br />

((B)c).x<br />

((A)c).x<br />

// Field x of class C<br />

// Field x of class B<br />

// Field x of class A<br />

(Häufig s<strong>in</strong>nvoller: andere Variablennamen verwenden.)<br />

5.3.2 Shadow<strong>in</strong>g von Klassen-Feldern<br />

Klassen-Felder können ebenfalls überdeckt werden<br />

Beispiel: exakteres PI <strong>in</strong> PlaneCircle<br />

public static f<strong>in</strong>al double PI = 3.14159265358979323846;<br />

Innerhalb von PlaneCircle:<br />

PI<br />

PlaneCircle.PI<br />

super.PI<br />

Circle.PI<br />

// Field PI <strong>in</strong> class PlaneCircle<br />

// dito<br />

// Field PI <strong>in</strong> class Circle<br />

// dito<br />

Achtung: Innerhalb der Methoden area() und circumference(), die <strong>in</strong><br />

Circle def<strong>in</strong>iert s<strong>in</strong>d, wird immer auf Circle.PI referenziert, auch wenn<br />

diese Methoden <strong>in</strong>nerhalb von PlaneCircle oder von e<strong>in</strong>em<br />

PlaneCircle-<strong>Objekt</strong> benutzt werden!


64 <strong>Informatik</strong> B SS 03<br />

5.3.3 Überschreiben von Instanz-Methoden der Oberklasse<br />

(Overrid<strong>in</strong>g)<br />

Wenn <strong>in</strong> e<strong>in</strong>er Klasse e<strong>in</strong>e Instanz-Methode def<strong>in</strong>iert wird, die dieselbe Signatur<br />

(Name, Parameter) hat wie e<strong>in</strong>e Methode der Oberklasse, wird die Methode der<br />

Oberklasse überschrieben:<br />

Wenn die Methode bei e<strong>in</strong>em <strong>Objekt</strong> der Klasse aufgerufen wird, dann wird die<br />

neue Methodendef<strong>in</strong>ition aktiviert.<br />

Achtung: Bei gleicher Signatur darf ke<strong>in</strong> anderer Rückgabetyp verwendet<br />

werden, Sichtbarkeitsmodifikatoren dürfen nur gelockert werden (sonst<br />

Compiler-Fehler).<br />

Überschreiben von Methoden ist e<strong>in</strong>e wichtige Technik der objekt-<strong>orientierte</strong>n<br />

<strong>Programmierung</strong>.<br />

Klassen-Methoden werden nicht überschrieben sondern nur überdeckt.<br />

. methode¡ (), OberKlasse¡ . methode¡ () haben <strong>in</strong> jedem<br />

Klasse¡<br />

Fall verschiedene Namen<br />

Etwas konstruierte Erweiterung des Circle-Beispiels: Ellipse als Unterklasse<br />

von Circle mit neuer Def<strong>in</strong>ition von area() und circumference().<br />

Es ist wichtig, dass für e<strong>in</strong> <strong>Objekt</strong> der Klasse Ellipse immer die neuen<br />

Methoden zur Berechnung verwendet werden!<br />

5.3.4 Überschreiben vs. Verdecken<br />

class A {<br />

// Def<strong>in</strong>e a class named A<br />

<strong>in</strong>t i = 1;<br />

// An <strong>in</strong>stance field<br />

<strong>in</strong>t f() { return i; }<br />

// An <strong>in</strong>stance method<br />

static char g() { return ’A’; } // A class method<br />

}<br />

class B extends A {<br />

// Def<strong>in</strong>e a subclass of A<br />

<strong>in</strong>t i = 2;<br />

// Shadows field i <strong>in</strong> class A<br />

<strong>in</strong>t f() { return -i; }<br />

// Overrides <strong>in</strong>stance method f <strong>in</strong> A<br />

static char g() {return ’B’; } // Shadows class method g <strong>in</strong> A<br />

}<br />

public class OverrideTest {<br />

public static void ma<strong>in</strong> (Str<strong>in</strong>g[] args) {<br />

B b = new B();<br />

// Creates new object of type B<br />

System.out.pr<strong>in</strong>tln(b.i); // Refers to B.i; pr<strong>in</strong>ts 2<br />

System.out.pr<strong>in</strong>tln(b.f()); // Refers to B.f(); pr<strong>in</strong>ts -2<br />

System.out.pr<strong>in</strong>tln(b.g()); // Refers to B.g(); pr<strong>in</strong>ts B<br />

System.out.pr<strong>in</strong>tln(B.g()); // This is a better way to <strong>in</strong>voke B.g()<br />

A a = (A)b;<br />

// Casts b to a reference to class A<br />

System.out.pr<strong>in</strong>tln(a.i); // Now refers to A.i; pr<strong>in</strong>ts 1


¡<br />

<strong>Informatik</strong> B SS 03 65<br />

} }<br />

System.out.pr<strong>in</strong>tln(a.f()); // Still refers to B.f(); pr<strong>in</strong>ts -2<br />

System.out.pr<strong>in</strong>tln(a.g()); // Refers to A.g(); pr<strong>in</strong>ts A<br />

System.out.pr<strong>in</strong>tln(A.g()); // This is a better way to <strong>in</strong>voke A.g()<br />

Unterschied zwischen Überschreiben von Instanz-Methoden und Überlagern<br />

von Feldern (und Klassen-Methoden) macht S<strong>in</strong>n:<br />

Für e<strong>in</strong> <strong>Objekt</strong> sollen auf jeden Fall se<strong>in</strong>e spezifischen Methoden angewendet<br />

werden.<br />

(Beispiel: Array von Circle-<strong>Objekt</strong>en, von denen manche Circle- und<br />

manche Ellipse-<strong>Objekt</strong>e s<strong>in</strong>d. Methoden zur Berechnung von Flächen<strong>in</strong>halt<br />

und Umfang sollen auf jeden Fall die der spezifischen Klasse se<strong>in</strong>!)<br />

5.3.5 Dynamisches ‘Method Lookup’<br />

Woher weiss der Compiler, ob er die Methode zur Oberklasse A oder zur<br />

Unterklasse B aufrufen soll, wenn beispielsweise e<strong>in</strong> Array von A <strong>Objekt</strong>en<br />

def<strong>in</strong>iert wurde, <strong>in</strong> dem manche <strong>Objekt</strong>e zur Klasse B gehören können<br />

Kann er nicht wissen: Code, der dynamisches Method Lookup zur Laufzeit<br />

benutzt: Interpreter prüft Typ e<strong>in</strong>es <strong>Objekt</strong>es und ruft die entsprechende<br />

Methode auf.<br />

auch als Virtual Method Invocation bezeichnet (<strong>in</strong> C++)<br />

5.3.6 ‘F<strong>in</strong>al’ Methoden und Statisches ‘Method Lookup’<br />

Schneller, wenn ke<strong>in</strong> dynamic method lookup zur Laufzeit benötigt wird.<br />

Wenn e<strong>in</strong>e Methode mit dem f<strong>in</strong>al Modifikator deklariert ist, heisst das, dass<br />

die Methode nicht von e<strong>in</strong>er Unterklassen-Methode überschrieben werden darf.<br />

Der Compiler weiss bereits, welche Version der Methode geme<strong>in</strong>t ist, und<br />

dynamisches Lookup ist damit unnötig.<br />

Für bestimmte Methoden kann das <strong>Java</strong>-Laufzeitsystem dynamic method<br />

lookup vermeiden:<br />

– Alle Methoden e<strong>in</strong>er f<strong>in</strong>al deklarierten Klasse s<strong>in</strong>d f<strong>in</strong>al: also ist<br />

bekannt, für welche Klasse der Aufruf erfolgt.<br />

– Alle private Methoden können generell nur <strong>in</strong> der Klasse selbst<br />

aufgerufen werden: damit ist ebenfalls bekannnt, für welche Klasse der<br />

Aufruf erfolgt. private Methoden s<strong>in</strong>d implizit f<strong>in</strong>al und können nicht<br />

überschrieben werden.<br />

– static (Klassen)-Methoden werden generell nicht überschrieben<br />

(sondern überdeckt).


66 <strong>Informatik</strong> B SS 03<br />

5.3.7 Aufruf e<strong>in</strong>er überschriebenen Methode<br />

Aufruf überschriebener Methoden ist syntaktisch ähnlich zu Zugriff auf<br />

überdeckte Felder: super. methode¡ ()<br />

Aufruf e<strong>in</strong>er überschriebenen Methode kann nicht mit Cast<strong>in</strong>g<br />

(((A)this).f()) realisiert werden!<br />

Modifizierte Form von dynamischem Method Lookup bei super: Gehe zur<br />

direkten Oberklasse derjenigen Klasse, <strong>in</strong>nerhalb derer super aufgerufen wird.<br />

Wenn die Methode dort def<strong>in</strong>iert ist, verwende sie, ansonsten gehe zur direkten<br />

Oberklasse dieser Klasse etc.<br />

super spricht die Methode an, die unmittelbar überschrieben wurde.<br />

super bezieht sich immer auf die unmittelbare Oberklasse der Klasse, <strong>in</strong> der<br />

der Aufruf steht.<br />

Beispiel: super.f() <strong>in</strong> OverrideTest bezieht sich auf die Klasse Object!<br />

Überdeckte Klassen-Methoden können ebenfalls durch super angesprochen<br />

werden. (Hier erfolgt generell ke<strong>in</strong> dynamic lookup.)<br />

class A {<br />

// Def<strong>in</strong>e a class named A<br />

<strong>in</strong>t i = 1;<br />

// An <strong>in</strong>stance field<br />

<strong>in</strong>t f() { return i; }<br />

// An <strong>in</strong>stance method<br />

static char g() { return ’A’; } // A class method<br />

}<br />

class B extends A {<br />

// Def<strong>in</strong>e a subclass of A<br />

<strong>in</strong>t i;<br />

// Shadows field i <strong>in</strong> class A<br />

<strong>in</strong>t f() {<br />

// Overrides <strong>in</strong>stance method f <strong>in</strong> A<br />

i = super.i + 1;<br />

// It can retrieve A.i like this<br />

return super.f() + i;<br />

// It can <strong>in</strong>voke A.f() like this<br />

}}<br />

5.4 Overload<strong>in</strong>g und Polymorphismus<br />

5.4.1 Operator-Overload<strong>in</strong>g<br />

Nicht zu verwechseln: Überschreiben und Überladen!<br />

Überladen (operator overload<strong>in</strong>g): Verwendung desselben Symbols, um<br />

Operatoren mit verschiedenen Signaturen zu bezeichnen.<br />

Beispiel: monadisches und diadisches -; + für Integers, Floats, Str<strong>in</strong>gs<br />

Overload<strong>in</strong>g wird auch als “ad hoc Polymorphismus” bezeichnet.<br />

<strong>Java</strong> erlaubt dem Benutzer ke<strong>in</strong> Überladen von primitiven Operatoren, aber<br />

Überladen von Methoden (und Konstruktoren).<br />

Vorteil von Overload<strong>in</strong>g: Bedeutung e<strong>in</strong>es Symbols im Kontext spart die<br />

E<strong>in</strong>führung zusätzlicher Symbole. Beispiel: Def<strong>in</strong>ition von für komplexe<br />

Zahlen.<br />

Vergleiche natürliche Sprache (das vermisste Buch f<strong>in</strong>den, se<strong>in</strong> Glück f<strong>in</strong>den).


¡<br />

<strong>Informatik</strong> B SS 03 67<br />

Nachteil von Overload<strong>in</strong>g: Es ist nicht mehr ohneweiteres nachvollziehbar, was<br />

e<strong>in</strong> Operator wirklich tut (unklare Semantik), wenn primitive Operatoren vom<br />

Benutzer überladen werden dürfen (Kritik an C++).<br />

Beispiel: a <br />

b<br />

– Intuitiv, wenn a und b vom gleichen primitiven numerischen Typ s<strong>in</strong>d:<br />

<strong>in</strong>t <strong>in</strong>t ¡ <strong>in</strong>t, float float ¡ float.<br />

– Verschiedene Optionen für char char: liefert den Str<strong>in</strong>g aus den<br />

beiden Zeichen, liefert die Summe der Ordnungszahl der Zeichen, ...<br />

– Häufig s<strong>in</strong>nvoll: wenn verschiedene numerische Typen beteiligt s<strong>in</strong>d, wird<br />

zunächst auf den “größeren” Typ ge-castet: <strong>in</strong>t float ¡ float.<br />

– Was tun bei char short (<strong>in</strong> <strong>Java</strong> haben beide 16 Bit)<br />

– Eventuell Verlust der Kommutativität: Vorrang des ersten Arguments, also<br />

könnte char short e<strong>in</strong>en anderen Ergebnistyp liefern als short<br />

char.<br />

– Was ist die Intuition für HashMap HashMap oder Stack Vector<br />

Zulässigkeit, E<strong>in</strong>schränkungen und Techniken für Overload<strong>in</strong>g s<strong>in</strong>d e<strong>in</strong>e wichtige<br />

Frage für das Design e<strong>in</strong>er Programmiersprache!<br />

5.4.2 Operator-Overload<strong>in</strong>g <strong>in</strong> <strong>Java</strong><br />

Arithmetische ( Operatoren , , ,<br />

überladen.<br />

¡<br />

, ¡ ) und Vergleichsoperatoren <strong>in</strong> <strong>Java</strong> s<strong>in</strong>d<br />

Arithmetische Operatoren und Vergleichsoperatoren s<strong>in</strong>d für numerische Typen<br />

def<strong>in</strong>iert (alle primitiven Typen ausser boolean, siehe auch die entsprechenden<br />

Wrapper-Klassen <strong>in</strong> Abb. 20)<br />

Es ist zulässig, dass e<strong>in</strong> Operator Argumente verschiedenen Typs mite<strong>in</strong>ander<br />

verknüpft. Dabei erfolgt e<strong>in</strong> implizites Cast<strong>in</strong>g zu dem größeren Typ, m<strong>in</strong>destens<br />

zu <strong>in</strong>t (widen<strong>in</strong>g, siehe Tab. 4).<br />

Rückgabetyp bei arithmetischen Operatoren:<br />

double wenn m<strong>in</strong>destens e<strong>in</strong> Argument double,<br />

float wenn m<strong>in</strong>destens e<strong>in</strong> Argument float,<br />

long wenn m<strong>in</strong>destens e<strong>in</strong> Argument long,<br />

<strong>in</strong>t sonst (auch, wenn beide Argumente byte, short oder char s<strong>in</strong>d).<br />

Dabei werden zuerst die impliziten Casts auf den Operanden durchgeführt und<br />

dann der Operator angewendet.<br />

Der Operator + (und +=) ist zusätzlich für Str<strong>in</strong>g-<strong>Objekt</strong>e def<strong>in</strong>iert.<br />

Wenn m<strong>in</strong>destens e<strong>in</strong>es der Argumente Str<strong>in</strong>g ist, wird das andere Argument<br />

zu Str<strong>in</strong>g konvertiert.


68 <strong>Informatik</strong> B SS 03<br />

Tabelle 4: Typumwandlung <strong>in</strong> <strong>Java</strong><br />

Von Nach<br />

boolean byte short char <strong>in</strong>t long float double<br />

boolean - N N N N N N N<br />

byte N - Y C Y Y Y Y<br />

short N C - C Y Y Y Y<br />

char N C C - Y Y Y Y<br />

<strong>in</strong>t N C C C - Y Y* Y<br />

long N C C C C - Y* Y*<br />

float N C C C C C - Y<br />

double N C C C C C C -<br />

N(o), Y(es), explicit C(ast<strong>in</strong>g), Y*(yes, automatic widen<strong>in</strong>g, möglicher Verlust)<br />

Klammern s<strong>in</strong>d häufig notwendig:<br />

System.out.pr<strong>in</strong>tln("Total: " + 3 + 4); // Total: 34 nicht 7<br />

(Klammern (3 + 4): erst Addition auf Zahlen)<br />

Für Str<strong>in</strong>g Object wird Object <strong>in</strong> Str<strong>in</strong>g umgewandelt, <strong>in</strong>dem die<br />

toStr<strong>in</strong>g()-Methode des <strong>Objekt</strong>s angewendet wird.<br />

Achtung: Die für Object def<strong>in</strong>ierte toStr<strong>in</strong>g() Methode liefert die<br />

Referenz-Adresse des <strong>Objekt</strong>s als Str<strong>in</strong>g. Falls andere Information gewünscht<br />

wird, muss diese Methode überschrieben werden.<br />

5.4.3 Method-Overload<strong>in</strong>g <strong>in</strong> <strong>Java</strong><br />

In <strong>Java</strong> musste Method-Overload<strong>in</strong>g zugelassen werden, da<br />

Konstruktoren-Namen durch den Klassennamen bestimmt werden<br />

(automatische Erzeugung von default-Konstruktoren), und da es möglich se<strong>in</strong><br />

sollte, mehr als e<strong>in</strong>en Konstruktor für e<strong>in</strong>e Klasse zu def<strong>in</strong>ieren.<br />

Überladene Methoden müssen sich e<strong>in</strong>deutig durch ihre Signatur<br />

unterscheiden: Anzahl, Reihenfolge und Typ der Argumente.<br />

E<strong>in</strong>e blosse Unterscheidung durch verschiedene Rückgabe-Typen ist<br />

unzulässig:<br />

void f() {}<br />

<strong>in</strong>t f() {}<br />

Könnte nur klappen, wenn der Compiler e<strong>in</strong>deutig aus dem Kontext bestimmen<br />

kann, welche Methode geme<strong>in</strong>t ist, z.B. <strong>in</strong>t x = f();<br />

Auch bei Methoden mit unterschiedlichen Signaturen kann es Probleme geben,<br />

die “geme<strong>in</strong>te” Methode e<strong>in</strong>deutig zu identifizieren. In diesem Fall liefert der<br />

Compiler e<strong>in</strong>e Fehlermeldung. (siehe Abb. 21)


¡<br />

<strong>Informatik</strong> B SS 03 69<br />

public class Overload<strong>in</strong>g {<br />

public static Str<strong>in</strong>g f(Str<strong>in</strong>g s, Object o) {<br />

return s + o;<br />

}<br />

public static Object f(Object o, Str<strong>in</strong>g s) {<br />

return (Object) (o + s);<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args) {<br />

System.out.pr<strong>in</strong>tln(f("Die Zahl ist ", (Object)"17"));<br />

System.out.pr<strong>in</strong>tln(f((Object)"17", " ist e<strong>in</strong>e Zahl"));<br />

// System.out.pr<strong>in</strong>tln(f("Hello", "World")); // ambiguous!!!<br />

}<br />

}<br />

Abbildung 21: Method-Overload<strong>in</strong>g und E<strong>in</strong>deutigkeit<br />

5.4.4 Polymorphismus<br />

Polymorphismus wurde ursprünglich von Christopher Strachey (1967) als<br />

Konzept benannt und dann im Rahmen der funktionalen <strong>Programmierung</strong><br />

weiterentwickelt.<br />

Polymorphismus bei Funktionen (parametrischer P.) me<strong>in</strong>t, dass Funktionen<br />

über Parameter mit Typvariablen def<strong>in</strong>iert werden können:<br />

length : ’a list -> <strong>in</strong>t<br />

ist e<strong>in</strong>e generische Funktion, die die Länge von Listen mit Elementen beliebigen<br />

(aber e<strong>in</strong>heitlichen) Typs liefert.<br />

ML war 1976 die erste Programmiersprache, die polymorphe Typisierung<br />

(zusammen mit starker Typisierung) e<strong>in</strong>geführt hat.<br />

Parametrischer Polymorphismus erlaubt denselben Code, um Argumente<br />

verschiedenen Typs zu behandeln; Overload<strong>in</strong>g me<strong>in</strong>t dagegen die<br />

Wiederverwendung desselben syntaktischen Symbols und verlangt<br />

verschiedenen Code, um verschiedene Typen zu behandeln!<br />

In der objekt-<strong>orientierte</strong>n <strong>Programmierung</strong> me<strong>in</strong>t Polymorphismus, dass<br />

Variablen deklariert werden können, die zur Laufzeit auf <strong>Objekt</strong>e verschiedener<br />

Klassen verweisen können.<br />

5.4.5 Cast<strong>in</strong>g und Polymorphismus<br />

Upcast<strong>in</strong>g <strong>in</strong> der Klassenhierarchie ist immer zulässig.<br />

Wird e<strong>in</strong> Circle-<strong>Objekt</strong> zu e<strong>in</strong>em Shape-<strong>Objekt</strong> gecasted, stehen für den<br />

Zugriff nur noch die Methoden und Felder der Klasse Shape zur Verfügung.<br />

Downcast<strong>in</strong>g ist nur <strong>in</strong> speziellen Fällen möglich: Jeder Circle ist e<strong>in</strong> Shape,<br />

aber nicht jeder Shape ist e<strong>in</strong> Circle.


70 <strong>Informatik</strong> B SS 03<br />

Shape<br />

+ setColor<br />

Upcast<strong>in</strong>g<br />

Shape s;<br />

s.setColor();<br />

Circle<br />

+ area<br />

+ circumference<br />

+ diameter<br />

Rectangle<br />

+ area<br />

+ circumference<br />

+ isSquare<br />

Triangle<br />

+ area<br />

+ circumference<br />

+ ...<br />

Abbildung 22: Polymorphismus<br />

Unerlaubtes Downcast<strong>in</strong>g führt zu e<strong>in</strong>em Laufzeit-Fehler<br />

(RuntimeException), genauer zu e<strong>in</strong>er ClassCastException (siehe<br />

Kapitel ‘Exceptions’).<br />

Mit dem booleschen Operator objectname¡ <strong>in</strong>stanceof Classname¡ kann<br />

vor dem Cast<strong>in</strong>g explizit geprüft werden, ob das <strong>Objekt</strong> zur gewünschten Klasse<br />

gehört oder die Klasse des <strong>Objekt</strong>s von der gewünschten Klasse abgeleitet ist!<br />

(gilt auch für Interfaces)<br />

void checkPos(Circle c, double x, double y) {<br />

if (c <strong>in</strong>stanceof PlaneCircle)<br />

((PlaneCircle)c).isInside(x,y);<br />

Vorteil von Polymorphismus: E<strong>in</strong>e Methode setColor() kann für beliebige<br />

Shape-<strong>Objekt</strong>e def<strong>in</strong>iert werden. Egal, ob die Methode zur Laufzeit zu e<strong>in</strong>em<br />

Circle- oder e<strong>in</strong>em Rectangle-<strong>Objekt</strong> gehört, sie kann auf jeden Fall<br />

angewendet werden.<br />

Dadurch wird Duplizierung von Code vermieden!<br />

Zudem ist ke<strong>in</strong>e Kenntnis nötig, welche Unterklassen von Shape konkret<br />

existieren.<br />

Wird e<strong>in</strong>e Methode f() <strong>in</strong> der Oberklasse def<strong>in</strong>iert, so kann sie <strong>in</strong> der<br />

Unterklasse überschrieben werden. Für e<strong>in</strong> Shape-<strong>Objekt</strong>, das e<strong>in</strong> Circle ist,<br />

wird dann se<strong>in</strong>e spezifische Methode verwendet.<br />

Polymorphismus kann nur zusammen mit dynamic b<strong>in</strong>d<strong>in</strong>g (auch late b<strong>in</strong>d<strong>in</strong>g)<br />

realisiert werden. Viele imperative Sprachen verwenden early b<strong>in</strong>d<strong>in</strong>g, d.h.,<br />

Methodenaufrufe werden schon zur Compilezeit mit dem Methodenkörper<br />

verbunden.<br />

5.4.6 Cast<strong>in</strong>g vs. Parameterisierte Klassen<br />

Unzulässiges Downcast<strong>in</strong>g kann erst zur Laufzeit erkannt werden.


<strong>Informatik</strong> B SS 03 71<br />

Downcast<strong>in</strong>g wird <strong>in</strong> <strong>Java</strong> immer dann verwendet, wenn<br />

Methoden/Datenstrukturen benutzt werden, die für recht allgeme<strong>in</strong>e Klassen<br />

def<strong>in</strong>iert werden. Werden <strong>Objekt</strong>e zurückgeliefert, müssen sie häufig wieder auf<br />

ihren speziellen Typ gecasted werden. Beispiel: Collection-Klassen verwalten<br />

<strong>Objekt</strong>e vom Typ Object.<br />

Alternative Idee: generische Klassen, Templates (C++).<br />

Templates s<strong>in</strong>d parametrisierte Klassen, die für spezielle Typen konkretisiert<br />

werden können. Dies entspricht parametrisierten, polymorphen Typen, wie sie <strong>in</strong><br />

der funktionalen <strong>Programmierung</strong> verwendet werden.<br />

Beispiel: Statt e<strong>in</strong>er Stack-Klasse, die <strong>Objekt</strong>e vom Typ Object verwaltet,<br />

kann e<strong>in</strong>e Stack(T)-Klasse def<strong>in</strong>iert werden. Parameter T kann dann<br />

verschieden belegt werden (zu Integer, Shape, etc.).<br />

Arrays <strong>in</strong> <strong>Java</strong> s<strong>in</strong>d e<strong>in</strong>e Art generische Klasse: Ausgehend von e<strong>in</strong>em<br />

“allgeme<strong>in</strong>en Array-Typ” werden nach Bedarf <strong>Objekt</strong>e zu spezifischen<br />

Element-Typen erzeugt.<br />

vgl. <strong>in</strong>t[] vs. [](<strong>in</strong>t) oder Circle[] vs. [](Circle)


¡<br />

72 <strong>Informatik</strong> B SS 03<br />

6 Exceptions<br />

6.1 Fehler und Ausnahmen<br />

¡ Exception exceptional event (aussergewöhnliches Ereignis).<br />

E<strong>in</strong>e Exception ist e<strong>in</strong> Ereignis, das während der Ausführung e<strong>in</strong>es Programms<br />

auftritt und den normalen Ablauf unterbricht.<br />

In <strong>Java</strong> werden “Fehlerereignisse” als <strong>Objekt</strong>e (vom Typ Exception)<br />

repräsentiert.<br />

Exception und Error erweitern Throwable.<br />

Error-Unterklassen betreffen schwerwiegende Fehler (z.B.<br />

VirtualMach<strong>in</strong>eError) und sollten nicht vom Programmierer behandelt<br />

werden.<br />

Exceptions sollten dagegen behandelt werden.<br />

Exceptions treten typischerweise <strong>in</strong>nerhalb von Methoden auf.<br />

Erkennt e<strong>in</strong>e Methode e<strong>in</strong>e Fehlerbed<strong>in</strong>gung, wird e<strong>in</strong> entsprechendes<br />

Exception-<strong>Objekt</strong> erzeugt und “geworfen”.<br />

Wenn e<strong>in</strong>e Exception auftritt (z. B. FileNotFoundException wenn e<strong>in</strong>e<br />

Datei, die geöffnet werden soll, nicht gefunden wird), wird e<strong>in</strong> Exception<br />

<strong>Objekt</strong> erzeugt und an das Laufzeitsystem gegeben. throw<strong>in</strong>g an exception<br />

Dieses <strong>Objekt</strong> enthält Information über die Art der Exception und über den<br />

Zustand des Programms (Aufrufstack) zum Zeitpunkt zu dem die Exception<br />

auftrat.<br />

Das Laufzeitsystem ist verantwortlich, Code zu f<strong>in</strong>den, der den Fehler behandelt.<br />

Die Behandlung kann <strong>in</strong> der Methode, <strong>in</strong> der der Fehler aufgetreten ist, selbst<br />

oder <strong>in</strong> e<strong>in</strong>er der diese Methode aufrufenden Methoden (Aufrufstack) erfolgen.<br />

Beispiel:<br />

h() -----------------> Exception tritt auf<br />

g() (ruft h() auf)<br />

f() (ruft g() auf) Behandlung der Exception<br />

Exception Handler (catch-Block): Für e<strong>in</strong>e aufgetretene Exception ist derjenige<br />

Handler angemessen, der den entsprechenden Exception-Typ (Klasse oder<br />

Oberklasse des geworfenen Exceptionen-Objects) behandelt.<br />

Wird e<strong>in</strong>e aufgetretene Exception nicht behandelt, term<strong>in</strong>iert das Laufzeitsystem<br />

und damit das Programm.


<strong>Informatik</strong> B SS 03 73<br />

6.2 Vorteile von Exceptions<br />

Separierung von regulärem Code und Fehlerbehandlung: Transparenz,<br />

Strukturiertheit<br />

Propagierung von Fehlern möglich<br />

Gruppierung von Fehler-Typen, Fehlerdifferenzierung<br />

readFile {<br />

// was ist, wenn<br />

open the file;<br />

// file nicht geoeffnet werden kann<br />

determ<strong>in</strong>e its size; // Laenge nicht bestimmt werden kann<br />

allocate that much memory; // nicht genug Speicher belegt werden kann<br />

read the file <strong>in</strong>to memory; // beim Lesen e<strong>in</strong> Fehler auftritt<br />

close the file;<br />

// file nicht geschlossen werden kann<br />

}<br />

6.2.1 Separierung von Code und Fehlerbehandlung<br />

Abfangen mit expliziten bed<strong>in</strong>gten Anweisungen im Code:<br />

errorCodeType readFile {<br />

<strong>in</strong>itialize errorCode = 0;<br />

open the file;<br />

if (theFileIsOpen) {<br />

determ<strong>in</strong>e the length of the file;<br />

if (gotTheFileLength) {<br />

allocate that much memory;<br />

if (gotEnoughMemory) {<br />

read the file <strong>in</strong>to memory;<br />

if (readFailed) {<br />

errorCode = -1;<br />

}<br />

} else {<br />

errorCode = -2;<br />

}<br />

} else {<br />

errorCode = -3;<br />

}<br />

close the file;<br />

if (theFileDidntClose && errorCode == 0) {<br />

errorCode = -4;<br />

} else {<br />

errorCode = errorCode and -4;<br />

}<br />

} else {<br />

errorCode = -5;<br />

}<br />

return errorCode;<br />

}


74 <strong>Informatik</strong> B SS 03<br />

Trennung von “normalem” Code und Fehlerbehandlung:<br />

readFile {<br />

{ // Block mit Anweisungen, bei denen Exceptions auftreten koennten<br />

open the file;<br />

determ<strong>in</strong>e its size;<br />

allocate that much memory;<br />

read the file <strong>in</strong>to memory;<br />

close the file;<br />

} handle fileOpenFailed {<br />

doSometh<strong>in</strong>g;<br />

} handle sizeDeterm<strong>in</strong>ationFailed {<br />

doSometh<strong>in</strong>g;<br />

} handle memoryAllocationFailed {<br />

doSometh<strong>in</strong>g;<br />

} handle readFailed {<br />

doSometh<strong>in</strong>g;<br />

} handle fileCloseFailed {<br />

doSometh<strong>in</strong>g;<br />

}<br />

}<br />

6.2.2 Propagierung von Exceptions<br />

method1 {<br />

call method2;<br />

}<br />

method2 {<br />

call method3;<br />

}<br />

method3 {<br />

call readFile;<br />

}<br />

Nur method1 sei an Fehlern, die <strong>in</strong> readFile() auftreten können <strong>in</strong>teressiert.<br />

Fehler-Propagierung “zu Fuss”:<br />

method1 {<br />

errorCodeType error;<br />

error = call method2;<br />

if (error)<br />

doErrorProcess<strong>in</strong>g;<br />

else proceed;<br />

}<br />

errorCodeType method2 {<br />

errorCodeType error;<br />

error = call method3;<br />

if (error)


<strong>Informatik</strong> B SS 03 75<br />

return error;<br />

else proceed;<br />

}<br />

errorCodeType method3 {<br />

errorCodeType error;<br />

error = call readFile;<br />

if (error)<br />

return error;<br />

else proceed;<br />

}<br />

In <strong>Java</strong> ist es zulässig, dass e<strong>in</strong>e Methode den Fehler nicht selbst fängt (“duck” a<br />

thrown exception), sondern weitere nach oben durchreicht.<br />

method1 {<br />

try {<br />

call method2;<br />

} catch (exception) {<br />

doErrorProcess<strong>in</strong>g;<br />

}<br />

}<br />

method2 throws exception {<br />

call method3;<br />

}<br />

method3 throws exception {<br />

call readFile;<br />

}<br />

Achtung: Wenn <strong>in</strong> e<strong>in</strong>er Methode (oder e<strong>in</strong>em Konstruktur) Exceptions auftreten<br />

können, müssen diese entweder spezifiziert (throws) oder behandelt werden!<br />

InputFile.java:11: Exception java.io.FileNotFoundException<br />

must be caught, or it must be declared <strong>in</strong> the throws clause<br />

of this method.<br />

<strong>in</strong> = new FileReader(filename);<br />

ˆ<br />

6.3 Exception Handl<strong>in</strong>g – ‘try’, ‘catch’, ‘f<strong>in</strong>ally’<br />

// Note: This class won’t compile by design!<br />

// See ListOfNumbersDeclared.java or ListOfNumbers.java<br />

// for a version of this class that will compile.<br />

import java.io.Pr<strong>in</strong>tWriter;<br />

import java.io.FileWriter;<br />

import java.util.Vector;<br />

public class ListOfNumbers {


76 <strong>Informatik</strong> B SS 03<br />

private Vector vec;<br />

private static f<strong>in</strong>al <strong>in</strong>t SIZE = 10;<br />

public ListOfNumbers () {<br />

vec = new Vector(SIZE);<br />

for (<strong>in</strong>t i = 0; i < SIZE; i++)<br />

vec.addElement(new Integer(i));<br />

}<br />

public void writeList() {<br />

Pr<strong>in</strong>tWriter out = new Pr<strong>in</strong>tWriter(new FileWriter("OutFile.txt"));<br />

for (<strong>in</strong>t i = 0; i < SIZE; i++)<br />

out.pr<strong>in</strong>tln("Value at: " + i + " = " + vec.elementAt(i));<br />

}<br />

}<br />

out.close();<br />

Mögliche IOException: Konstruktor FileWriter() kann angegebene Datei<br />

nicht öffnen.<br />

Mögliche RunTimeException: ArrayIndexOutOfBoundsException bei<br />

vec.elementAt(i).<br />

Es macht S<strong>in</strong>n, dass verlangt wird, dass alle Fehler ausser RunTimeExceptions<br />

vom Programmierer behandelt werden.<br />

Der Compiler/das Laufzeitsystem können nicht hellsehen, auf welche Art e<strong>in</strong>e<br />

Exception behandelt werden soll.<br />

Beispiel FileNotFoundException: Programm beenden, andere Datei<br />

e<strong>in</strong>lesen, File dieses Namens erzeugen.<br />

Beispiele für RunTimeExceptions: arithmetic exceptions (division by zero),<br />

po<strong>in</strong>ter exceptions (access object by null reference), <strong>in</strong>dex<strong>in</strong>g exceptions.<br />

Checked Exceptions: Exception ausser RunTimeExceptions, die vom Compiler<br />

geprüft werden (ob sie behandelt oder spezifiziert werden).<br />

import java.io.Pr<strong>in</strong>tWriter;<br />

import java.io.FileWriter;<br />

import java.io.IOException;<br />

import java.util.Vector;<br />

public class ListOfNumbers {<br />

private Vector vec;<br />

private static f<strong>in</strong>al <strong>in</strong>t SIZE = 10;<br />

public ListOfNumbers () {<br />

vec = new Vector(SIZE);<br />

for (<strong>in</strong>t i = 0; i < SIZE; i++)


<strong>Informatik</strong> B SS 03 77<br />

vec.addElement(new Integer(i));<br />

}<br />

public void writeList() {<br />

Pr<strong>in</strong>tWriter out = null;<br />

try {<br />

System.out.pr<strong>in</strong>tln("Enter<strong>in</strong>g try statement");<br />

out = new Pr<strong>in</strong>tWriter(new FileWriter("OutFile.txt"));<br />

} }<br />

for (<strong>in</strong>t i = 0; i < SIZE; i++)<br />

out.pr<strong>in</strong>tln("Value at: " + i + " = " + vec.elementAt(i));<br />

} catch (ArrayIndexOutOfBoundsException e) {<br />

System.err.pr<strong>in</strong>tln("Caught ArrayIndexOutOfBoundsException: " +<br />

e.getMessage());<br />

} catch (IOException e) {<br />

System.err.pr<strong>in</strong>tln("Caught IOException: " + e.getMessage());<br />

} f<strong>in</strong>ally {<br />

if (out != null) {<br />

System.out.pr<strong>in</strong>tln("Clos<strong>in</strong>g Pr<strong>in</strong>tWriter");<br />

out.close();<br />

} else {<br />

System.out.pr<strong>in</strong>tln("Pr<strong>in</strong>tWriter not open");<br />

}<br />

}<br />

Erläuterungen:<br />

Es könnte alternativ zu System.err.pr<strong>in</strong>tln() auch<br />

System.out.pr<strong>in</strong>tln() verwendet werden, um Meldungen auszugeben.<br />

Üblicherweise geht der Fehler-Strom aber auf das Term<strong>in</strong>al, während der<br />

Ausgabestrom beispielsweise <strong>in</strong> e<strong>in</strong>e Datei gehen kann (<strong>in</strong> der man nicht die<br />

Fehlermeldungen haben will).<br />

try-catch-f<strong>in</strong>ally ist e<strong>in</strong>e Kontrollstruktur. Der try-Block kann ohne<br />

folgende catch-Blöcke oder ohne folgenden f<strong>in</strong>ally-Block stehen. (try<br />

alle<strong>in</strong>e wird vom Compiler angemeckert.)<br />

Der try-Block ohne catch-Blöcke bewirkt, dass hier ke<strong>in</strong>e Fehlerbehandlung<br />

erfolg.<br />

Der try-Block wird verlassen, sobald e<strong>in</strong>e Exception dar<strong>in</strong> auftrat. Wenn<br />

bestimmte D<strong>in</strong>ge auf jeden Fall erledigt werden sollen (z.B. Schliessen e<strong>in</strong>er<br />

Datei), so kann dies <strong>in</strong> e<strong>in</strong>em f<strong>in</strong>ally-Block spezifiziert werden.<br />

Es kann maximal e<strong>in</strong>en f<strong>in</strong>ally-Block geben, <strong>in</strong> dem abgesichert werden<br />

kann, dass bestimmte D<strong>in</strong>ge auf jeden Fall ausgeführt werden (wenn vorher ke<strong>in</strong><br />

System.exit() erfolgt), auch wenn etwas schief geht.<br />

Exception-<strong>Objekt</strong>e haben die Methode getMessage(), die den Fehlertext der<br />

Exception liefert.


78 <strong>Informatik</strong> B SS 03<br />

Falls die Datei OutFile.txt nicht zum Schreiben geöffnet werden kann,<br />

reagiert der FileWriter-Konstruktor und erzeugt und wirft das entsprechende<br />

Exception-<strong>Objekt</strong>.<br />

Die ArrayIndexOutOfBoundsException müsste nicht abgefangen werden.<br />

Falls ArrayIndexOutOfBoundsException und IOException <strong>in</strong> e<strong>in</strong>em<br />

e<strong>in</strong>zigen catch-Block behandelt werden sollen, muss die geme<strong>in</strong>same<br />

Oberklasse dieser Exceptions gefangen werden. Die ist nur Exception, was<br />

üblicherweise zu allgeme<strong>in</strong> wäre!<br />

f<strong>in</strong>ally kann Code-Duplizierung vermeiden:<br />

try { . . .<br />

out.close(); // don’t do this; it duplicates code<br />

} catch (ArrayIndexOutOfBoundsException e) {<br />

out.close(); // don’t do this; it duplicates code<br />

System.err.pr<strong>in</strong>tln("Caught ArrayIndexOutOfBoundsException: "<br />

+ e.getMessage());<br />

} catch (IOException e) {<br />

System.err.pr<strong>in</strong>tln("Caught IOException: " + e.getMessage());<br />

}<br />

Was passiert, wenn sowohl catch als auch f<strong>in</strong>ally e<strong>in</strong>e return-Anweisung<br />

enthalten (Übung!)<br />

Drei Möglichkeiten des Programmablaufs:<br />

new FileWriter() geht schief, IOException:<br />

Enter<strong>in</strong>g try statement<br />

Caught IOException: OutFile.txt<br />

Pr<strong>in</strong>tWriter not open<br />

ArrayIndexOutOfBoundsException:<br />

Enter<strong>in</strong>g try statement<br />

Caught ArrayIndexOutOfBoundsException: 10 >= 10<br />

Clos<strong>in</strong>g Pr<strong>in</strong>tWriter<br />

try-Block wird ohne Exception verlassen<br />

Enter<strong>in</strong>g try statement<br />

Clos<strong>in</strong>g Pr<strong>in</strong>tWriter


<strong>Informatik</strong> B SS 03 79<br />

6.4 Spezifikation von Exceptions – ‘throws’<br />

import java.io.Pr<strong>in</strong>tWriter;<br />

import java.io.FileWriter;<br />

import java.io.IOException;<br />

import java.util.Vector;<br />

public class ListOfNumbersDeclared {<br />

private Vector vec;<br />

private static f<strong>in</strong>al <strong>in</strong>t SIZE = 10;<br />

public ListOfNumbersDeclared () {<br />

vec = new Vector(SIZE);<br />

for (<strong>in</strong>t i = 0; i < SIZE; i++)<br />

vec.addElement(new Integer(i));<br />

}<br />

public void writeList() throws<br />

IOException, ArrayIndexOutOfBoundsException {<br />

Pr<strong>in</strong>tWriter out = new Pr<strong>in</strong>tWriter(new FileWriter("OutFile.txt"));<br />

for (<strong>in</strong>t i = 0; i < SIZE; i++)<br />

out.pr<strong>in</strong>tln("Value at: " + i + " = " + vec.elementAt(i));<br />

}<br />

}<br />

out.close();<br />

throws spezifiziert mögliche Fehler und verschiebt deren Behandlung zu<br />

aufrufenden Methoden.<br />

Dies macht S<strong>in</strong>n, wenn es übergeordnete Strukturen gibt, bei denen erst klar ist,<br />

welche Fehler wie abgefangen werden sollen.<br />

Es macht wenig S<strong>in</strong>n, dass die ma<strong>in</strong>-Methode (letzte Methode im Aufrufstack)<br />

Fehler spezifiziert und nicht behandelt. Programmbenutzer wird dann mit den<br />

java-Fehlermeldungen konfrontiert.<br />

throws kann nur für Methoden (und Konstruktoren) deklariert werden.<br />

6.5 Vererbung und ‘throws’<br />

Es ist nicht erlaubt, e<strong>in</strong>er Methode beim Überschreiben weitere<br />

throws-Klauseln h<strong>in</strong>zuzufügen!<br />

Ansonsten wäre ke<strong>in</strong>e Zuweisungskompatibilität mehr gegeben: In allen<br />

Methoden, die diese Methode verwenden, müsste e<strong>in</strong>e Spezifikation bzw.<br />

Behandlung der neu h<strong>in</strong>zugekommenen Exceptions erfolgen, was durch Cast<strong>in</strong>g<br />

zur Oberklasse umgangen werden könnte.<br />

Weglassen e<strong>in</strong>es Teils oder e<strong>in</strong>er kompletten throws-Klausel ist erlaubt.


¡<br />

80 <strong>Informatik</strong> B SS 03<br />

ClassNotFoundException<br />

java.lang-exceptions<br />

©1995-7, Charles L. Perk<strong>in</strong>s<br />

http://rendezvous.com/java<br />

Object<br />

java.lang<br />

CloneNotSupportedException<br />

Throwable<br />

java.lang<br />

Exception<br />

IllegalAccessException<br />

InstantiationException<br />

InterruptedException<br />

ArithmeticException<br />

ArrayStoreException<br />

NoSuchFieldException<br />

NoSuchMethodException<br />

ClassCastException<br />

IllegalArgumentException<br />

IllegalMonitorStateException<br />

IllegalThreadStateException<br />

NumberFormatException<br />

RuntimeException<br />

IllegalStateException<br />

IndexOutOfBoundsException<br />

NegativeArraySizeException<br />

ArrayIndexOutOfBoundsException<br />

Str<strong>in</strong>gIndexOutOfBoundsException<br />

Serializable<br />

java.io-objects<br />

NullPo<strong>in</strong>terException<br />

SecurityException<br />

Abbildung 23: <strong>Java</strong> Language Exception Klassen<br />

6.5.1 Gruppierung von Fehler-Typen<br />

Exception-<strong>Objekt</strong>e s<strong>in</strong>d – wie andere <strong>Java</strong> <strong>Objekt</strong>e – <strong>in</strong> e<strong>in</strong>er Klassenhierarchie<br />

organisiert.<br />

Ganze Gruppen von Exceptions können mit e<strong>in</strong>em e<strong>in</strong>zigen catch behandelt<br />

werden, wenn e<strong>in</strong>e entsprechende Oberklasse verwendet wird.<br />

Wenn mehrere catch-Blöcke den Typ der Exception behandeln, so wird nur der<br />

erste (Reihenfolge im Code) passende ausgeführt.<br />

Falls mehrere Exceptions, die <strong>in</strong> e<strong>in</strong>er Unterklassenbeziehung zu e<strong>in</strong>ander<br />

stehen, abgefangen werden sollen, so müssen diese sequentiell von der<br />

speziellsten zur allgeme<strong>in</strong>sten behandelt werden!<br />

Achtung: Exception-Handler, die zu allgeme<strong>in</strong> s<strong>in</strong>d, können Code wieder<br />

fehleranfällig machen! Es können dadurch Exceptions gefangen werden, die<br />

nicht vorhergesehen wurden und entsprechend nicht korrekt behandelt werden.


<strong>Informatik</strong> B SS 03 81<br />

6.6 Def<strong>in</strong>ition eigener Exception-Klassen und Auslösen von Exceptions<br />

public class IllegalRadiusException extends Exception {<br />

private double <strong>in</strong>validRad;<br />

public IllegalRadiusException (Str<strong>in</strong>g msg, double rad) {<br />

super(msg);<br />

<strong>in</strong>validRad = rad;<br />

}<br />

public Str<strong>in</strong>g getMessage() {<br />

return super.getMessage() + ": " + <strong>in</strong>validRad;<br />

} }<br />

public class CheckedCircle {<br />

public static f<strong>in</strong>al double PI = 3.14159;<br />

protected double r;<br />

protected void checkRadius(double radius) throws IllegalRadiusException {<br />

if (radius < 0.0) throw new IllegalRadiusException<br />

("radius may not be negative", radius);<br />

}<br />

public CheckedCircle(double r) throws IllegalRadiusException {<br />

checkRadius(r);<br />

this.r = r;<br />

}<br />

public double getRadius() { return r; }<br />

public void setRadius(double r) throws IllegalRadiusException {<br />

checkRadius(r);<br />

this.r = r;<br />

}<br />

public static void ma<strong>in</strong> (Str<strong>in</strong>g[] args) {<br />

try { CheckedCircle c = new CheckedCircle(1.0);<br />

c.setRadius(-1.0);<br />

} catch (IllegalRadiusException e) {<br />

System.out.pr<strong>in</strong>tln( e.getMessage() );<br />

}<br />

} }<br />

Nicht verwechseln: throws – Spezifizieren e<strong>in</strong>er Exception, und throw –<br />

Auslösen e<strong>in</strong>er Exception.<br />

In den API’s s<strong>in</strong>d Exceptions auf dieselbe Art realisiert wie hier gezeigt.<br />

Alle checked exceptions werden von Methoden ausgelöst. Laufzeitfehler werden<br />

dagegen aus dem Laufzeitsystem heraus erzeugt.<br />

Im Programmbeispiel CheckedCircle muss nun <strong>in</strong> alle Methoden, <strong>in</strong> denen<br />

die IllegalRadiusException auftreten könnte, entweder die Exception<br />

spezifiziert (geworfen) oder behandelt werden.<br />

Alternativ hätte IllegalRadiusException von RunTimeException<br />

abgeleitet werden können. Dann wäre die Fehlerbehandlung nicht verpflichtend.


¡<br />

¡<br />

82 <strong>Informatik</strong> B SS 03<br />

6.7 Exkurs: UML<br />

Im Skript werden Klassen manchmal graphisch als Kästen und Beziehungen<br />

zwischen Klassen mit Pfeilen veranschaulicht. (z.B. Abb. 19)<br />

E<strong>in</strong>e standardisierte Notation zur Repräsentation von Klassen und ihren<br />

Beziehungen s<strong>in</strong>d UML-Diagramme.<br />

UML (“Unified Model<strong>in</strong>g Language”) ist e<strong>in</strong>e auf Diagrammen basierende<br />

Beschreibungssprache für den objekt-<strong>orientierte</strong>n Entwurf und die<br />

objekt-ortientierte Analyse.<br />

Standardisierung durch: Booch, Jacobson, Rumbaugh (“Die drei Amigos”).<br />

E<strong>in</strong> Meta-Modell legt fest, wie diese Sprache benutzt werden soll. In vielen<br />

Dokumentationen wird auf diesen Aspekt kaum e<strong>in</strong>gegangen.<br />

Vergleiche: BNF als Meta-Sprache zur Repräsentation von Grammatiken für<br />

Programmiersprachen; Meta-Modell als Sprache zur Repräsentation von<br />

Grammatiken für UML-Diagramme.<br />

aktuelle Forschung: Beschreibung von UML-Diagrammen mit<br />

Graph-Grammatiken (Parsierung, syntaktische Korrektheit, ...)<br />

<strong>Objekt</strong>-<strong>orientierte</strong>r Entwurf me<strong>in</strong>t die Konzeption von Klassenstrukturen und<br />

Abhängigkeiten bei der Systementwicklung.<br />

Vorteile e<strong>in</strong>er standardisierten Sprache:<br />

CASE-Tools (Computer Assisted Software Eng<strong>in</strong>eer<strong>in</strong>g) zur Erzeugung von<br />

Code<br />

Austausch von Entwürfen<br />

UML wird <strong>in</strong> der Vorlesung <strong>Informatik</strong> C behandelt. Im folgenden werden die<br />

wesentlichen Komponenten kurz illustriert.<br />

6.7.1 Klassendiagramme <strong>in</strong> UML<br />

Abbildung 24 zeigt den wesentlichen Aufbau von Klassendiagrammen.<br />

UML-Diagramme sollen übersichtlich se<strong>in</strong>!<br />

Immer soviel Information für die Klassen angeben, wie notwendig ist.<br />

Optionale Angabe von Typ-Information bei Feldern und Methoden<br />

Angabe ausgewählter Komponenten (beispielsweise: nur Methoden)<br />

public abstract class Person {<br />

protected Str<strong>in</strong>g personName;<br />

private <strong>in</strong>t age;<br />

public Person (Str<strong>in</strong>g name) {<br />

personName = name;<br />

}


<strong>Informatik</strong> B SS 03 83<br />

Person<br />

−age<br />

#personName<br />

+Person<br />

+getAge<br />

getJob<br />

+makeJob<br />

−splitNames<br />

Kasten aus drei Teilen:<br />

Klassen-Namen (fett),<br />

Felder, Methoden<br />

Sichtbarkeits-Modifikatoren:<br />

public, private, protected<br />

Kursiv: Abstrakte Klassen/Methoden<br />

Unterstrichen: Klassen-Methoden, -Felder<br />

Abbildung 24: Darstellung der Klasse ‘Person’ <strong>in</strong> UML<br />

Person<br />

−age: Integer<br />

#personName: Str<strong>in</strong>g<br />

+Person(Str<strong>in</strong>g)<br />

+getAge(): Integer<br />

getJob(): Str<strong>in</strong>g<br />

+makeJob(): Str<strong>in</strong>g<br />

−splitNames()<br />

Person<br />

+getAge<br />

getJob<br />

+makeJob<br />

−splitNames<br />

Abbildung 25: Darstellungsvarianten für Klassen <strong>in</strong> UML<br />

}<br />

static public Str<strong>in</strong>g makeJob () {return "hired";}<br />

public <strong>in</strong>t getAge () {return age;}<br />

private void splitNames () {}<br />

abstract Str<strong>in</strong>g getJob ();<br />

6.7.2 Klassen-/ Unterklassenbeziehungen <strong>in</strong> UML<br />

Der Klasse/Oberklasse-Pfeil repräsentiert e<strong>in</strong>e Generalisierung (Employee ist<br />

Unterklasse von Person).<br />

Pfeil mit durchgezogener L<strong>in</strong>ie und hohler Pfeilspitze von der Unterklasse zur<br />

Oberklasse<br />

bei Interfaces: gestrichelte L<strong>in</strong>ie, über dem Interface-Namen steht<br />


84 <strong>Informatik</strong> B SS 03<br />

public class Employee extends Person {<br />

public Employee (Str<strong>in</strong>g name) {<br />

super(name);<br />

}<br />

}<br />

public Str<strong>in</strong>g getJob() {<br />

return "Research Staff";<br />

}<br />

Employee<br />

Person<br />

+Employee<br />

+getJob<br />

+getAge<br />

getJob<br />

+makeJob<br />

−splitNames<br />

Abbildung 26: Vererbung <strong>in</strong> UML<br />

6.7.3 Assoziationen<br />

Beziehungen zwischen Klassen/<strong>Objekt</strong>en werden als Assoziationen bezeichnet.<br />

Generalisierung ist e<strong>in</strong>e spezielle Assoziation.<br />

E<strong>in</strong>e Assoziation bezeichnet e<strong>in</strong>e “Rolle”, die e<strong>in</strong>e Klasse bezüglich der anderen<br />

e<strong>in</strong>nimmt.<br />

vergleiche: Casus-Strukturen, Fillmore, 1968, zur Repräsentation der<br />

Tiefenstruktur natürlichsprachiger Sätze.<br />

Bei Generalisierung: “E<strong>in</strong> Employee ist e<strong>in</strong>e Person.”<br />

E<strong>in</strong> Employee arbeitet für e<strong>in</strong>e Company.<br />

Assoziationen werden allgeme<strong>in</strong> durch L<strong>in</strong>ien zwischen Klassen (ohne Pfeil)<br />

dargestellt. Zusätzlich können Zahlenbereiche angegeben werden, die<br />

anzeigen, wieviele Instanzen von <strong>Objekt</strong>en e<strong>in</strong>er Klasse mit e<strong>in</strong>er anderen<br />

Klasse <strong>in</strong> Beziehung stehen können.<br />

– ¡ gibt an, dass null bis beliebig viele Instanzen e<strong>in</strong>er Klasse mit e<strong>in</strong>er<br />

anderen assoziiert se<strong>in</strong> können.<br />

– 1 gibt an, dass genau e<strong>in</strong>e Instanz mit e<strong>in</strong>er Klasse/<strong>Objekt</strong> assoziiert ist<br />

(entspricht 1..1).<br />

Assoziationen e<strong>in</strong>er Klasse mit sich selbst heissen rekursiv.<br />

E<strong>in</strong>e spezielle Art von Assoziation ist die Teil-Ganzes-Beziehung: Aggregation.<br />

(“E<strong>in</strong> Auto hat Räder.”)


<strong>Informatik</strong> B SS 03 85<br />

public class Company { public class Company1 {<br />

Employee empt1;<br />

Employee[] emp1;<br />

Person per1; public Company1() {}<br />

public Company() {} }<br />

}<br />

Person<br />

0..1<br />

per1<br />

Company<br />

Employee<br />

0..1<br />

emp1<br />

Employee<br />

*<br />

emp1<br />

Company1<br />

Abbildung 27: Assoziationen <strong>in</strong> UML<br />

Komposition ist wiederum e<strong>in</strong>e spezielle Aggregation, bei der die Teile ohne das<br />

Ganze nicht existieren können.<br />

Aggregationen werden durch e<strong>in</strong>e L<strong>in</strong>ie mit e<strong>in</strong>er Raute im Ursprung dargestellt.<br />

Bei Komposition ist die Raute gefüllt.<br />

Die Klasse NeighborQueen (siehe Kapitel 2) def<strong>in</strong>iert, dass e<strong>in</strong>e<br />

NeighborQueen e<strong>in</strong>e Queen als l<strong>in</strong>ke Nachbar<strong>in</strong> hat. Diese Assoziation ist<br />

eher ke<strong>in</strong>e Aggregation: Wir formulieren “E<strong>in</strong>e NeighborQueen hat e<strong>in</strong>e Queen<br />

als Nachbar<strong>in</strong>”, aber nicht “E<strong>in</strong>e NeighborQueen hat als Teil/besteht aus e<strong>in</strong>er<br />

Queen.”<br />

6.7.4 Kommentare und Annotierung <strong>in</strong> UML<br />

Kommentare als eigene Boxen mit umgeklappter Ecke<br />

Pfeile mit durchgezogenen L<strong>in</strong>ien und vollen Köpfen, um anzuzeigen, welche<br />

Klasse e<strong>in</strong>e Methode welcher anderen Klasse aufruft.<br />

6.7.5 UML-Tools<br />

Zur Kommunikation, zum Entwurf: Papier und Bleistift, beliebiges<br />

Graphik-Programm


86 <strong>Informatik</strong> B SS 03<br />

Spezielle Tools: Erzeugung von UML aus Graphik-Bauste<strong>in</strong>en, Erzeugung von<br />

UML aus Code, Erzeugung von Code aus UML (spezielle syntaktische<br />

Konventionen e<strong>in</strong>zuhalten!)<br />

Beispiele: Rational Rose, Together, argouml<br />

6.8 Exkurs: Design Patterns – Factory Pattern<br />

Design Patterns: (abstrakter) Code, der wiederverwendet werden kann<br />

(bewährte Standardlösungen von Experten)<br />

Beispiele: Factory Pattern, Adapter Pattern, Proxy Pattern, Patterns für GUI<br />

Cognitive Science: Abstraktion als Ergebnis des Lernens beim Analogen<br />

Problemlösen!<br />

E<strong>in</strong> Simple Factory Pattern liefert e<strong>in</strong>e Instanz für e<strong>in</strong>e oder mehrere Klassen <strong>in</strong><br />

Abhängigkeit von den gelieferten Daten.<br />

Üblicherweise haben alle Klassen, für die die Factory <strong>Objekt</strong>e erzeugen kann,<br />

e<strong>in</strong>e geme<strong>in</strong>same Oberklasse und geme<strong>in</strong>same Methoden. Die Klassen<br />

unterscheiden sich dadurch, dass sie für verschiedene Arten von Daten<br />

optimiert s<strong>in</strong>d.<br />

Beispiele: Temperatur kann als Celsius oder Fahrenheit aufgefasst werden, e<strong>in</strong><br />

Nutzerdialog kann <strong>in</strong> verschiedenen Sprachen erfolgen (Internationalisierung),<br />

e<strong>in</strong>e Zeichenkette kann als numerischer Wert verschiedenen Typs aufgefasst<br />

werden, ...


<strong>Informatik</strong> B SS 03 87<br />

X<br />

XFactory<br />

+do_X()<br />

+XFactory()<br />

+newObj() : X<br />

XY<br />

+do_X()<br />

...<br />

XZ<br />

+do_X()<br />

Abbildung 28: Das Factory-Pattern<br />

X als Basis-Klasse, von der XY und XZ abgeleitet s<strong>in</strong>d.<br />

Klasse XFactory entscheidet, Instanzen welcher Unterklasse zurückgeliefert<br />

werden, <strong>in</strong> Abhängigkeit davon, welche Argumente übergeben werden. Die<br />

newObj()-Methode erhält e<strong>in</strong>en Wert und liefert Instanz der “entsprechenden”<br />

Klasse.<br />

Welche Klasse zurückgeliefert wird, ist dem Programmierer egal, da alle<br />

dieselben Methoden (aber <strong>in</strong> unterschiedlichen Implementationen) haben.<br />

Beispiel: E<strong>in</strong>e Zahl wird der Factory als Str<strong>in</strong>g übergeben. Je nach “Form” wird<br />

das <strong>Objekt</strong> e<strong>in</strong>er passenden Wrapper-Klasse zurückgeliefert.<br />

package number;<br />

public class NumberFactory<br />

{<br />

public Number newNumber (Str<strong>in</strong>g value) throws NumberFormatException<br />

{<br />

try<br />

{<br />

return new Byte(value);<br />

// first try Byte<br />

}<br />

catch (NumberFormatException e1)<br />

{


88 <strong>Informatik</strong> B SS 03<br />

java.lang<br />

Number<br />

NumberFactory<br />

<strong>in</strong>tValue()<br />

newNumber(Str<strong>in</strong>g) : Number<br />

Integer<br />

<strong>in</strong>tValue()<br />

...<br />

Double<br />

<strong>in</strong>tValue()<br />

Abbildung 29: E<strong>in</strong>e Number-Factory<br />

try<br />

{<br />

return new Short(value);<br />

// ... then Short<br />

}<br />

catch (NumberFormatException e2)<br />

{<br />

try<br />

{<br />

return new Integer(value);<br />

// .. then Integer<br />

}<br />

catch (NumberFormatException e3)<br />

{<br />

try<br />

{<br />

return new Long(value);<br />

// ... then Long<br />

}<br />

catch (NumberFormatException e4)<br />

{<br />

// ‘new Float(value)’ will return an <strong>in</strong>f<strong>in</strong>ite value<br />

// if the number cannot be represented as a float<br />

// (not a NumberFormatException), so check for this<br />

return new Float(value).isInf<strong>in</strong>ite() <br />

(Number) new Double(value) :<br />

(Number) new Float(value);<br />

}<br />

}


<strong>Informatik</strong> B SS 03 89<br />

}<br />

}<br />

}<br />

}<br />

import number.NumberFactory;<br />

public class NumberFactoryTest<br />

{<br />

// pr<strong>in</strong>t object’s class name and value<br />

public static void pr<strong>in</strong>tNumber (Number number)<br />

{<br />

System.out.pr<strong>in</strong>tln(number.getClass().getName() + ": " + number);<br />

}<br />

public static void ma<strong>in</strong> (Str<strong>in</strong>g args[])<br />

{<br />

NumberFactory factory = new NumberFactory();<br />

// the factory<br />

}<br />

}<br />

try<br />

{<br />

pr<strong>in</strong>tNumber(factory.newNumber("123"));<br />

pr<strong>in</strong>tNumber(factory.newNumber("1234"));<br />

pr<strong>in</strong>tNumber(factory.newNumber("123456"));<br />

pr<strong>in</strong>tNumber(factory.newNumber("1234567890123"));<br />

pr<strong>in</strong>tNumber(factory.newNumber("3.14159"));<br />

pr<strong>in</strong>tNumber(factory.newNumber("1e100"));<br />

pr<strong>in</strong>tNumber(factory.newNumber("abcd"));<br />

}<br />

catch (NumberFormatException e)<br />

{<br />

System.err.pr<strong>in</strong>tln(e);<br />

}<br />

// Byte<br />

// Short<br />

// Integer<br />

// Long<br />

// Float<br />

// Double<br />

// Exception<br />

// pr<strong>in</strong>t error message


90 <strong>Informatik</strong> B SS 03<br />

open a stream<br />

while more <strong>in</strong>formation<br />

read <strong>in</strong>formation<br />

close the stream<br />

open a stream<br />

while more <strong>in</strong>formation<br />

write <strong>in</strong>formation<br />

close the stream<br />

Abbildung 30: Input und Output Stream<br />

7 Input/Output<br />

7.1 E<strong>in</strong>-/Ausgabe-Ströme<br />

Anwendung: E<strong>in</strong>lesen von Information aus e<strong>in</strong>er externen Quelle (source) oder<br />

Ausgabe zu e<strong>in</strong>er externen Dest<strong>in</strong>ation (s<strong>in</strong>k).<br />

Information kann von verschiedenen Quellen stammen: Tastatur, Datei,<br />

Speicher, Internet, anderes Programm, ...<br />

Information kann verschiedener Art se<strong>in</strong>: Zeichen, <strong>Objekt</strong>e, Bilder, ...<br />

Information kann zu verschiedenen Ausgaben gehen: Bildschirm, Datei,<br />

Drucker, Speicher, Internet, anderes Programm, ...<br />

Klassen <strong>in</strong> java.io: sehr viele Klassen, im folgenden werden nur ausgewählte<br />

Aspekte dargestellt<br />

Information wird generell sequentiell gelesen/geschrieben.<br />

7.1.1 Klassenstruktur <strong>in</strong> ‘java.io’<br />

Die wichtigsten Klassen zum Lesen und Schreiben von Daten s<strong>in</strong>d<br />

Reader/Writer für Zeichen-Ströme (ab <strong>Java</strong> 1.1) sowie


<strong>Informatik</strong> B SS 03 91<br />

InputStream/OutputStream für Byte-Ströme (ab <strong>Java</strong> 1.0) und deren<br />

Unterklassen (nächster Abschnitt).<br />

Die Klasse RandomAccessFile erlaubt Lesen und Schreiben von Bytes, Text<br />

und primitiven Datentypen von oder <strong>in</strong> spezifische(n) Positionen e<strong>in</strong>er Datei.<br />

Die Klasse StreamTokenizer liefert e<strong>in</strong>e e<strong>in</strong>fache lexikalische Analyse für<br />

e<strong>in</strong>en E<strong>in</strong>gabestrom und zerlegt ihn <strong>in</strong> “Tokens” (wichtig für Parser-Konstruktion).<br />

Die Klasse File unterstützt plattform-unabhängige Def<strong>in</strong>ition von Datei- und<br />

Verzeichnisnamen. Sie liefert Methoden zum Auflisten von Verzeichnissen,<br />

Prüfen der Schreib-Lese-Rechte und weitere typische Operationen auf Dateien<br />

und Verzeichnissen.<br />

E<strong>in</strong> File-<strong>Objekt</strong> bezeichnet e<strong>in</strong>en Datei-Strom aus dem gelesen/<strong>in</strong> den<br />

geschrieben werden kann.<br />

Da Daten zum Lesen und Schreiben seriell verarbeitet werden, müssen Klassen<br />

das Serializable-Interface implementieren, damit entsprechende <strong>Objekt</strong>e<br />

verarbeitet werden können. Serializable ist e<strong>in</strong> sogenanntes<br />

“Marker”-Interface, das ke<strong>in</strong>e Methoden oder Konstanten def<strong>in</strong>iert.<br />

7.1.2 Character und Byte Ströme<br />

Lesen/Schreiben von 16-Bit (unicode) Zeichen: Abstrakte Reader/Writer<br />

Klasse und entsprechende Unterklassen (ab <strong>Java</strong> 1.1).<br />

Lesen/Schreiben von 8-Bit Bytes: Abstrakte InputStream/OutputStream<br />

Klasse und entsprechende Unterklassen (ab <strong>Java</strong> 1.0).<br />

Ähnliche APIs (Methoden mit gleichem Namen und äquivalenter Signatur) für<br />

verschiedene Character- und Byte-Ströme.<br />

Für Byte-Ströme werden Byte-Arrays, für Character-Ströme Character-Arrays<br />

verarbeitet:<br />

<strong>in</strong>t read()<br />

<strong>in</strong>t read(char cbuf[])<br />

<strong>in</strong>t read(char cbuf[], <strong>in</strong>t offset, <strong>in</strong>t length)<br />

<strong>in</strong>t write(<strong>in</strong>t c)<br />

<strong>in</strong>t write(char cbuf[])<br />

<strong>in</strong>t write(char cbuf[], <strong>in</strong>t offset, <strong>in</strong>t length)<br />

read() und write() können IOExceptions werfen.<br />

read() liefert <strong>in</strong>t zurück: 0 ¡ ¡ 255 für Bytes oder 0 ¡ ¢ 65535<br />

(0x00-0xffff) für Characters und -1 für Ende des Stroms.


92 <strong>Informatik</strong> B SS 03<br />

Reader<br />

Writer<br />

Abstrakte Klassen zum Lesen und Schreiben<br />

von Character−Strömen<br />

Object<br />

InputStream<br />

OutputStream<br />

Abstrakte Klassen zum Lesen und Schreiben<br />

von Byte−Strömen<br />

RandomAccessFile<br />

Lesen und Schreiben an beliebigen Stellen<br />

<strong>in</strong> e<strong>in</strong>er Datei<br />

StreamTokenizer<br />

Zerlegung von Input <strong>in</strong> Tokens<br />

File<br />

Plattform−unabhängige Def<strong>in</strong>ition von Datei− und<br />

Verzeichnisnamen<br />

<br />

Serializable<br />

<br />

Externizable<br />

Abbildung 31: Auswahl von Klassen <strong>in</strong> java.io


<strong>Informatik</strong> B SS 03 93<br />

InputStreamReader<br />

FileReader<br />

FileInputStream<br />

Reader<br />

BufferedReader<br />

InputStream<br />

BufferedInputStream<br />

FilterReader<br />

FilterInputStream<br />

OutputStreamReader<br />

FileWriter<br />

FileOutputStream<br />

Writer<br />

BufferedWriter<br />

OutputStream<br />

BufferedOutputStream<br />

FilterWriter<br />

FilterOutputStream<br />

Abbildung 32: Unterklassen von Reader/Writer sowie InputStream und<br />

OutputStream<br />

Überladene Methoden:<br />

– Lesen/Schreiben e<strong>in</strong>es Zeichens/Bytes.<br />

– Lesen/Schreiben <strong>in</strong>/aus Array von Zeichen/Bytes.<br />

– Lesen/Schreiben von length Zeichen/Bytes <strong>in</strong>/aus Array ab Index<br />

offset.<br />

Bei den Methoden, die Arrays verarbeiten, wird die Anzahl der verarbeiteten<br />

Zeichen/Bytes zurückgeliefert bzw. -1 für Ede des Stroms.<br />

7.1.3 Wichtige Reader- und Writer-Klassen<br />

Um komplexe Funktionalität zu erhalten, werden gerade im I/O-Bereich häufig<br />

<strong>Objekt</strong>e komponiert/<strong>in</strong>e<strong>in</strong>ander verschachtelt.<br />

Viele Konstruktoren und Methoden <strong>in</strong> java.io werfen Exceptions!<br />

Die Klasse InputStreamReader bietet e<strong>in</strong>e Reader-Schnittstelle zu e<strong>in</strong>em<br />

InputStream (analog für OutputStreamWriter): e<strong>in</strong> InputStream-<strong>Objekt</strong><br />

wird <strong>in</strong> e<strong>in</strong> InputStreamReader-<strong>Objekt</strong> e<strong>in</strong>gebettet.<br />

public class InputStreamReader extends Reader {<br />

// public constructor<br />

public InputStreamReader(java.io.InputStream <strong>in</strong>);<br />

// ...<br />

}<br />

Schreiben <strong>in</strong> e<strong>in</strong>e Datei und Lesen aus e<strong>in</strong>er Datei kann mit FileWriter und<br />

FileReader erledigt werden.


94 <strong>Informatik</strong> B SS 03<br />

Die Angabe von Dateien kann über File-<strong>Objekt</strong>e oder (plattform-spezifische)<br />

Date<strong>in</strong>amen als Str<strong>in</strong>gs erfolgen. Konstruktoren:<br />

public class FileReader extends InputStreamReader {<br />

// public constructor<br />

public FileReader(File file) throws FileNotFoundException;<br />

public FileReader(Str<strong>in</strong>g fileName) throws FileNotFoundException;<br />

// ...<br />

}<br />

Puffern von Daten br<strong>in</strong>gt Effizienz: Daten werden (<strong>in</strong> Array) gesammelt und<br />

dann weiterverarbeitet. Die Klasse BufferedReader hat e<strong>in</strong>e<br />

readL<strong>in</strong>e()-Methode.<br />

Die abstrakten Filter-Klassen FilterReader und FilterWriter erlauben<br />

Zusatzfunktionen während des Lesens/Schreibens: z.B. Zählen von Zeichen,<br />

Umwandlung von Zeichen etc.<br />

7.2 Datei-Ströme<br />

import java.io.*;<br />

public class Copy {<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args) throws IOException {<br />

File <strong>in</strong>putFile = new File(".", "farrago.txt");<br />

File outputFile = new File("outaga<strong>in</strong>.txt");<br />

// File Reader/Writer fuer Character-weises Lesen/Schreiben<br />

FileReader <strong>in</strong> = new FileReader(<strong>in</strong>putFile);<br />

FileWriter out = new FileWriter(outputFile);<br />

// Alternativ fuer Bytes<br />

// FileInputStream <strong>in</strong> = new FileInputStream(<strong>in</strong>putFile);<br />

// FileOutputStream out = new FileOutputStream(outputFile);<br />

<strong>in</strong>t c;<br />

while ((c = <strong>in</strong>.read()) != -1)<br />

out.write(c);<br />

}<br />

}<br />

<strong>in</strong>.close();<br />

out.close();<br />

Klasse File: Konstruktor erzeugt File-<strong>Objekt</strong> aus Str<strong>in</strong>g.<br />

Achtung bei Pfadnamen: Pfadseparatoren s<strong>in</strong>d vom Betriebssystem abhängig


<strong>Informatik</strong> B SS 03 95<br />

Alternativ zum obigen Code könnte der Date<strong>in</strong>ame direkt als Str<strong>in</strong>g an<br />

FileReader übergeben werden, z. B. FileReader(farrago.txt");<br />

FileReader ist e<strong>in</strong> Datei-Strom.<br />

FileReader ist Unterklasse von InputStreamReader: E<strong>in</strong>e Datei ist<br />

eigentlich als Folge von Bytes und nicht von Unicode-Zeichen repräsentiert. Die<br />

Klasse FileReader realisiert die Anwendung e<strong>in</strong>es InputStreamReader auf<br />

e<strong>in</strong> InputStream-<strong>Objekt</strong>!<br />

mit new FileReader(...) etc. wird die Datei automatisch geöffnet.<br />

Dateien können (sollten) explizit geschlossen werden, damit ke<strong>in</strong>e Ausgabe im<br />

I/O-Puffer verbleibt (flush). Im Pr<strong>in</strong>zip werden Dateien auch vom Garbage<br />

Collector geschlossen, wenn nicht mehr auf sie zugegriffen wird.<br />

In dem Programm könnte noch e<strong>in</strong>e Abfrage e<strong>in</strong>gebaut werden, ob der Name<br />

der Zieldatei verschieden vom Namen der Quelldatei ist (equals() Methode<br />

der Klasse File).<br />

7.3 Puffern von Daten<br />

Beliebige Reader-<strong>Objekt</strong>e können <strong>in</strong> e<strong>in</strong>en BufferedReader gesteckt werden,<br />

also z.B. FileReader-<strong>Objekt</strong>e. (analog für Writer, analog für Byte-Ströme)<br />

Im zweiten Teil von BufferDemo wird gezeigt, wie Daten aus dem<br />

Standard-E<strong>in</strong>gabestrom (z.B. der Tastatur) gepuffert verarbeitet werden können.<br />

System.<strong>in</strong> ist e<strong>in</strong> InputStream-<strong>Objekt</strong>!<br />

Analog zu<br />

BufferedReader <strong>in</strong> =<br />

new BufferedReader(new FileReader(<strong>in</strong>putFile));<br />

kann natürlich geschrieben werden:<br />

FileReader fr = new FileReader(<strong>in</strong>putFile);<br />

BufferedReader <strong>in</strong> = new BufferedReader(fr);<br />

public class BufferDemo {<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args) throws IOException {<br />

File <strong>in</strong>putFile = new File("farrago.txt");<br />

File outputFile = new File("outaga<strong>in</strong>.txt");<br />

BufferedReader <strong>in</strong> = new BufferedReader(new FileReader(<strong>in</strong>putFile));<br />

BufferedWriter out = new BufferedWriter(new FileWriter(outputFile));<br />

Str<strong>in</strong>g s;<br />

while ((s = <strong>in</strong>.readL<strong>in</strong>e()) != null) {<br />

out.write(s);<br />

out.write(’\n’);


96 <strong>Informatik</strong> B SS 03<br />

}<br />

<strong>in</strong>.close();<br />

out.close(); // probieren Sie, was passiert, wenn Sie out nicht<br />

// schliessen<br />

}<br />

}<br />

// analog von der Standard-E<strong>in</strong>gabe (System.<strong>in</strong>)<br />

BufferedReader console = new BufferedReader<br />

(new InputStreamReader(System.<strong>in</strong>));<br />

System.out.pr<strong>in</strong>t("What is your name: ");<br />

try {<br />

Str<strong>in</strong>g name = console.readL<strong>in</strong>e();<br />

System.out.pr<strong>in</strong>tln("Hello " + name);<br />

}<br />

catch (IOException e) { System.err.pr<strong>in</strong>tln(e); }<br />

Achtung: bei allen gepufferten Ausgabe-Strömen muss die flush-Methode<br />

angewendet werden, damit der Puffer “geleert”, also damit wirklich geschrieben<br />

wird.<br />

close() führt automatisch e<strong>in</strong> flush<strong>in</strong>g aus.<br />

7.4 Filter-Ströme<br />

Die abstrakten Filter-Klassen von java.io erlauben es, beim Lesen und<br />

Schreiben zusätzliche Operationen durchzuführen.<br />

Die default-Implementation der Filter-Klassen leiten die Methodenaufrufe an das<br />

bei Konstruktion übergebene <strong>Objekt</strong> weiter, z.B. übliches read(): Null-Filter.<br />

Wird e<strong>in</strong>e Unterklasse e<strong>in</strong>er Filter-Klasse wie FilterInputStream def<strong>in</strong>iert,<br />

so können genau die Methoden überschrieben werden, von denen man<br />

zusätzliche Funktionalität haben möchte.<br />

Die Filter-Klassen entsprechen dem Decorator-Pattern. Der Name kommt<br />

ursprünglich aus der GUI-<strong>Programmierung</strong> (z.B. Fenster mit verschiedener<br />

Dekoration), ist aber analog für nicht-visuelle Bereiche def<strong>in</strong>iert.<br />

import java.io.*;<br />

public class CaseFilter extends FilterReader {<br />

public CaseFilter(Reader f) {<br />

super(f);<br />

}<br />

public <strong>in</strong>t read() throws IOException {


<strong>Informatik</strong> B SS 03 97<br />

<strong>in</strong>t ch = super.read();<br />

}<br />

}<br />

if (Character.isLowerCase((char) ch))<br />

return Character.toUpperCase((char) ch);<br />

else return ch;<br />

Die CaseFilter-Klasse implementiert e<strong>in</strong>e erweiterte Funktionalität für<br />

read(): Beim E<strong>in</strong>lesen werden Kle<strong>in</strong>buchstaben <strong>in</strong> Grossbuchstaben<br />

umgewandelt.<br />

In der Klasse DecoStream wird nun das read() e<strong>in</strong>es CaseFilter-<strong>Objekt</strong>s<br />

verwendet. Direkt beim E<strong>in</strong>lesen wird der Text nun umformatiert!<br />

public class DecoStream {<br />

private Str<strong>in</strong>g readNormal(Str<strong>in</strong>g fl) {<br />

Str<strong>in</strong>gBuffer s = new Str<strong>in</strong>gBuffer();<br />

try {<br />

FileReader fread = new FileReader(fl);<br />

<strong>in</strong>t c;<br />

while ((c = fread.read()) != -1)<br />

s.append((char) c);<br />

fread.close ();<br />

}<br />

catch(IOException e) { System.err.pr<strong>in</strong>tln(e); }<br />

return s.toStr<strong>in</strong>g();<br />

}<br />

private Str<strong>in</strong>g readFilter(Str<strong>in</strong>g fl) {<br />

Str<strong>in</strong>gBuffer s = new Str<strong>in</strong>gBuffer();<br />

try {<br />

FileReader fread = new FileReader(fl);<br />

CaseFilter ff = new CaseFilter(fread); // CaseFilter class<br />

// provides read() with Uppercase<br />

<strong>in</strong>t c;<br />

while ((c = ff.read()) != -1)<br />

s.append((char) c);<br />

ff.close ();<br />

}<br />

catch(IOException e) { System.err.pr<strong>in</strong>tln(e); }<br />

return s.toStr<strong>in</strong>g();<br />

}<br />

static public void ma<strong>in</strong>(Str<strong>in</strong>g[] argv) {<br />

DecoStream d = new DecoStream();<br />

Str<strong>in</strong>g s = d.readNormal("note.txt");<br />

System.out.pr<strong>in</strong>tln(s);


98 <strong>Informatik</strong> B SS 03<br />

}<br />

}<br />

s = d.readFilter("note.txt");<br />

System.out.pr<strong>in</strong>tln(s);<br />

Anmerkung:<br />

Die Verwendung von Str<strong>in</strong>gBuffer statt Str<strong>in</strong>g ist hier effizienter: Bei s =<br />

s + (char) c; muss jeweils e<strong>in</strong>e Kopie des Str<strong>in</strong>gs angelegt werden.<br />

Str<strong>in</strong>gBuffer besitzt die Methode append(), mit der das neue Zeichen<br />

direkt angefügt werden kann.<br />

7.5 Standard-E<strong>in</strong>- und Ausgabe<br />

Auf Betriebssystem-Ebene existieren für jedes laufende Programm drei Ströme<br />

(siehe Übung): Standard Input, Standard Output und Standard Error.<br />

In <strong>Java</strong> existieren entsprechende <strong>Objekt</strong>e <strong>in</strong> der System-Klasse:<br />

– System.<strong>in</strong>: ist e<strong>in</strong> InputStream<br />

– System.out und Systen.err s<strong>in</strong>d bereits <strong>in</strong> e<strong>in</strong>en Pr<strong>in</strong>tStream<br />

gepackt (statt OutputStream).<br />

Defaultmässig ist Standard Input die Tastatur, Standard Output und Error der<br />

Monitor.<br />

Die Defaults können über das Betriebssystem, aber auch <strong>in</strong>nerhalb e<strong>in</strong>es<br />

Programms (z.B. <strong>Java</strong>) umdef<strong>in</strong><strong>in</strong>ert werden.<br />

Statische Methoden <strong>in</strong> der System-Klasse und die Default-Belegung:<br />

– setIn(InputStream <strong>in</strong>)<br />

– setOut(Pr<strong>in</strong>tStream out)<br />

– setErr(Pr<strong>in</strong>tStream err)<br />

Im Programmbeispiel BufferDemo wurde gezeigt, wie e<strong>in</strong> System.<strong>in</strong>-<strong>Objekt</strong><br />

<strong>in</strong> e<strong>in</strong> Reader-<strong>Objekt</strong> und dann <strong>in</strong> e<strong>in</strong> BufferedReader-<strong>Objekt</strong> gepackt<br />

werden kann.<br />

Bereits bekannt ist die Verwendung der Methoden System.out.pr<strong>in</strong>t() und<br />

System.out.pr<strong>in</strong>tln().<br />

Beispiel für die Umleitung des Ausgabe-Stroms:<br />

import java.io.*;<br />

public class Redirect<strong>in</strong>g {<br />

public static void ma<strong>in</strong> (Str<strong>in</strong>g[] args) throws IOException {<br />

BufferedInputStream <strong>in</strong> = new BufferedInputStream(<br />

new FileInputStream( "Redirect<strong>in</strong>g.java"));<br />

Pr<strong>in</strong>tStream out = new Pr<strong>in</strong>tStream (


<strong>Informatik</strong> B SS 03 99<br />

CharConversionException<br />

java.lang<br />

Exception<br />

java.io<br />

IOException<br />

EOFException<br />

FileNotFoundException<br />

InterruptedIOException<br />

ObjectStreamException<br />

SyncFailedException<br />

UnsupportedEncod<strong>in</strong>gException<br />

UTFDataFormatException<br />

InvalidClassException<br />

InvalidObjectException<br />

NotActiveException<br />

NotSerializableException<br />

OptionalDataException<br />

StreamCorruptedException<br />

WriteAbortedException<br />

Abbildung 33: IO-Exceptions<br />

}<br />

}<br />

new BufferedOutputStream( new FileOutputStream("test.out")));<br />

System.setIn(<strong>in</strong>);<br />

System.setOut(out);<br />

System.setErr(out); // wenn auskommentiert:<br />

// Fehlermeldungen auf Monitor<br />

BufferedReader br = new BufferedReader(<br />

new InputStreamReader(System.<strong>in</strong>));<br />

Str<strong>in</strong>g s;<br />

while ((s = br.readL<strong>in</strong>e()) != null)<br />

System.out.pr<strong>in</strong>tln(s);<br />

out.close(); // nicht vergessen! sonst wird nicht geflushed<br />

7.6 IO-Exceptions<br />

Bei der E<strong>in</strong>- und Ausgabe können zahlreiche Exceptions auftreten: Dateien<br />

können nicht vorhanden oder nicht zugreifbar se<strong>in</strong>, E<strong>in</strong>gaben können vom<br />

falschen Typ se<strong>in</strong>, etc.<br />

7.7 RandomAccess<br />

Die Klasse RandomAccessFile ist fast völlig isoliert vom Rest der Klassen <strong>in</strong><br />

java.io.<br />

RandomAccessFile stammt direkt von Object ab und implementiert die<br />

Interfaces DataInput und DataOutput.


¡<br />

¡<br />

¡<br />

¡<br />

¡<br />

¡<br />

¡<br />

100 <strong>Informatik</strong> B SS 03<br />

Object<br />

RandomAccessFile<br />

+ read()<br />

+ seek()<br />

+ write()<br />

<br />

DataInput<br />

<br />

DataOutput<br />

Abbildung 34: Die Klasse RandomAccessFile<br />

Die Klasse ermöglicht Lesen und Schreiben sowie das Vorwärts- und<br />

Rückwärtsgehen <strong>in</strong> e<strong>in</strong>er Datei.<br />

Sie kann nicht <strong>in</strong> e<strong>in</strong>en BufferedInputStream o.Ä. gepackt werden.<br />

Im Pr<strong>in</strong>zip arbeitet die Klasse wie e<strong>in</strong> komb<strong>in</strong>ierter DataInputStream und<br />

DataOutputStream.<br />

Typische Anwendung: Effizientes Lesen aus ZIP-Archiv<br />

– Sequentieller Zugriff:<br />

Öffnen des ZIP-Archivs<br />

¡<br />

Sequentielle Suche bis das gewünschte File gefunden ist<br />

Extraktion des Files<br />

Schliessen des ZIP-Archivs<br />

– Aufwand: im Mittel halbe Länge des ZIP-Files<br />

– mit Random Access:<br />

¡<br />

Öffnen des ZIP-Archivs<br />

Suche des dir-entry und lokalisiere den E<strong>in</strong>trag für das gewünschte<br />

File<br />

Suche rückwärts zur Position des Files<br />

Extraktion des Files<br />

Schliessen des ZIP-Archivs<br />

7.8 Weitere Aspekte von I/O<br />

7.8.1 Tokenizer<br />

Manchmal kann es nützlich se<strong>in</strong>, e<strong>in</strong>e E<strong>in</strong>gabe <strong>in</strong> e<strong>in</strong>zelne Tokens (Worte) zu<br />

zerlegen.


¡<br />

<strong>Informatik</strong> B SS 03 101<br />

StreamTokenizer führt e<strong>in</strong>e lexikalische Analyse des E<strong>in</strong>gabestroms durch.<br />

(Anwendung: Parser-Konstruktion).<br />

whitespaceChars() spezifiziert die Worttrenner (z.B. Leerzeichen),<br />

ord<strong>in</strong>aryChars() spezifiziert die <strong>in</strong> Worten erlaubten Zeichen.<br />

Verwandt zum StreamTokenizer ist die Klasse Str<strong>in</strong>gTokenizer aus<br />

java.util.<br />

Verwendung <strong>in</strong> späteren Kapiteln.<br />

7.8.2 Serializable, Externalizable<br />

Über Streams werden Daten sequentiell übertragen.<br />

<strong>Objekt</strong>e s<strong>in</strong>d ke<strong>in</strong>e l<strong>in</strong>earen Gebilde<br />

Serialisierung (Implementation des Marker-Interface Serializable).<br />

Um <strong>Objekt</strong>e (z.B. für verteilte Anwendungen übers Netz) zu verschicken, kann<br />

die Serialisierung explizit kontrolliert werden: <strong>in</strong>terface Externalizable<br />

extends Serializable.<br />

Hierfür müssen dann Methoden readExternal() und writeExternal()<br />

implementiert werden.<br />

Daten, die nicht serialisiert werden sollen/können, können als transient<br />

markiert werden. Solche Felder werden nicht serialisiert/deserialisiert.<br />

Genaueres im Zusammenhang mit Netzwerkprogrammierung/Verteilten<br />

Anwendungen (Vorlesung <strong>Informatik</strong> C).<br />

7.8.3 Pipe-Ströme<br />

Zur Kommunikation zwischen Threads (siehe Kapitel ‘Multi-Thread<strong>in</strong>g’) werden<br />

Pipe-Ströme verwendet. Wieder gibt es Klassen für Byte- und<br />

Unicode-Verarbeitung.


¡<br />

<br />

¡<br />

<br />

<br />

<br />

102 <strong>Informatik</strong> B SS 03<br />

8 Vererbung und Typsicherheit<br />

8.1 Formale Modelle für Programmiersprachen<br />

E<strong>in</strong> formales Modell dient dazu, e<strong>in</strong>en Gegenstandsbereich (etwa e<strong>in</strong>e<br />

Programmiersprache) präzise zu beschreiben.<br />

Durch das Auflisten und Beweisen von Eigenschaften werden häufig Aspekte<br />

offengelegt, die beim Entwurf übersehen wurden (Lücken, Widersprüche).<br />

Kompromiss zwischen Vollständigkeit und Kompaktheit: Beispielsweise ist es<br />

kaum möglich, e<strong>in</strong>e komplexe Sprache wie <strong>Java</strong> voll zu formalisieren.<br />

Stattdessen sollten ausgewählte Aspekte formalisiert werden.<br />

Im folgenden: Operationale Semantik für e<strong>in</strong>en <strong>Java</strong>-Kern – “Featherweight<br />

<strong>Java</strong>” (FJ), Arbeit von Igarashi et al., 1999.<br />

Grundidee: Es soll gezeigt werden, dass <strong>Java</strong> (im Kern) typsicher ist.<br />

– Die Syntax von <strong>Java</strong> wird auf FJ reduziert.<br />

– Es werden formale Regeln für die Typisierung angegeben.<br />

– Es werden Reduktionsregeln für Ausdrücke angegeben.<br />

Mittels der Reduktionsregeln wird bewiesen, dass FJ typsicher ist.<br />

Motivation:<br />

E<strong>in</strong>e Operationale Semantik beschreibt <strong>in</strong> Form von Reduktionsregeln, wie e<strong>in</strong><br />

Ausdruck (expression) e<strong>in</strong>er Programmiersprache zu e<strong>in</strong>em e<strong>in</strong>facheren<br />

Ausdruck ausgewertet wird:<br />

¢¡¤£¥£¥¦¨§©¤§ ¡ <br />

<br />

<br />

Solche Regeln können auch auf Teile e<strong>in</strong>es Ausdrucks angewendet werden:<br />

¡¤£¥£¥¦¨§©¤§ ¡ <br />

<br />

¦ <br />

¦<br />

Es werden (<strong>in</strong> beliebiger Reihenfolge) solange Regeln angewendet, bis ke<strong>in</strong>e<br />

Regel mehr anwendbar ist. Der Ausdruck ist dann entweder <strong>in</strong> Normalform<br />

(repräsentiert e<strong>in</strong>e “konstanten Wert”) oder die Auswertung ist nicht vollständig<br />

möglich (Fehler).<br />

Reduktionsregeln entsprechen Termersetzungsregeln (rewrite rules). E<strong>in</strong><br />

e<strong>in</strong>faches Beispiel (Kaffeedosen-Problem) ist <strong>in</strong> Abb. 35 angegeben. E<strong>in</strong>e Reihe<br />

schwarzer und weisser Bohnen kann verkürzt werden, <strong>in</strong>dem nach festen<br />

Regeln der Bed<strong>in</strong>gung Aktion<br />

Form Paare benachbarter Bohnen durch<br />

e<strong>in</strong>e e<strong>in</strong>zelne Bohne ersetzt werden.


¡<br />

¡<br />

©<br />

¡<br />

§<br />

¡<br />

¡<br />

¥<br />

<br />

©<br />

<strong>Informatik</strong> B SS 03 103<br />

Gegeben ist e<strong>in</strong>e Kaffeedose, <strong>in</strong> der schwarze (S)<br />

und weiße (W) Bohnen <strong>in</strong> e<strong>in</strong>er festen Reihenfolge<br />

angeordnet s<strong>in</strong>d, beispielsweise: W W S S W W S S.<br />

Gegeben s<strong>in</strong>d folgende Regeln:<br />

S W<br />

W S<br />

S S<br />

S<br />

S<br />

W<br />

Das Ziel ist, am Ende möglichst wenige Bohnen zu<br />

haben. Die Konfliktlösungs-Strategie sei, immer die<br />

oberste anwendbare Regel auszuwählen.<br />

W W S S W W S S<br />

W W S S W S S<br />

W W S S S S<br />

W S S S S<br />

S S S S<br />

W S S<br />

S S<br />

W<br />

Abbildung 35: Lösung des “Kaffeedose” Problems mit e<strong>in</strong>er Menge von Ersetzungsregeln<br />

Häufig haben Reduktionsregeln Anwendungsbed<strong>in</strong>gungen: Es muss nicht nur<br />

e<strong>in</strong> bestimmter Unter-Ausdruck <strong>in</strong> e<strong>in</strong>em Ausdruck existieren, damit e<strong>in</strong>e Regel<br />

angewendet werden darf, sondern es müssen noch weitere Bed<strong>in</strong>gungen erfüllt<br />

se<strong>in</strong>.<br />

Solche Regeln werden häufig so geschrieben, dass die<br />

Anwendungsbed<strong>in</strong>gungen über e<strong>in</strong>er L<strong>in</strong>ie stehen und die eigentliche Regel<br />

unter e<strong>in</strong>er L<strong>in</strong>ie:<br />

¥ £ * ¡ ¦ £ ¡<br />

der Ausdruck ¦ £¢ £ ¦<br />

£¢ £ ¦ ¥<br />

¦<br />

Lies: Wenn e<strong>in</strong>e Klasse N ¥ ¦ ¡ ¢ ¥<br />

Felder mit § ¦ ¡ ¢ ¨§<br />

Typen<br />

besitzt, dann kann<br />

¥ zu ausgewertet werden. (Zugriff auf das Feld ¥<br />

¥<br />

ergibt den Wert des entsprechenden Feldes – unter der Randbed<strong>in</strong>gung, dass<br />

der Konstruktor alle Felder mit den übergebenen Werten füllt.)<br />

8.2 Featherweight <strong>Java</strong><br />

Reduktion von <strong>Java</strong> auf e<strong>in</strong>en m<strong>in</strong>imalen Kern: Funktionaler Kern<br />

und Cast<strong>in</strong>g.<br />

Funktionaler Kern (Seiteneffekt-Freiheit, vgl. Lambda-Kalkül):<br />

Vererbung<br />

– Alle Felder und Parameter s<strong>in</strong>d implizit f<strong>in</strong>al.<br />

– Felder werden im Konstruktor <strong>in</strong>itialisiert und danach nicht mehr verändert.<br />

– Jede Methode besteht aus e<strong>in</strong>er e<strong>in</strong>zigen return-Anweisung.<br />

Abgedeckt werden alle Aspekte, die für Polymorphismus relevant s<strong>in</strong>d:<br />

– <strong>Objekt</strong>-Erzeugung<br />

– Wechselseitig rekursive Klassen-Def<strong>in</strong>itionen<br />

– Zugriff auf Felder und Aufruf von Methoden


104 <strong>Informatik</strong> B SS 03<br />

– Überschreiben von Methoden<br />

– Unterklassen (subtyp<strong>in</strong>g)<br />

– Cast<strong>in</strong>g<br />

Um Regularität <strong>in</strong> den Klassendef<strong>in</strong>itionen zu haben, wird zu jeder Klasse ihr<br />

Obertyp explizit angegeben (auch wenn es Object ist), wird immer e<strong>in</strong><br />

Konstruktor explizit def<strong>in</strong>iert, wird immer der Empfänger bei Feld-Zugriffen oder<br />

Methoden-Aufrufen angegeben (auch wenn es this ist).<br />

8.2.1 Programmbeispiel<br />

class A extends Object { A() { super(); } }<br />

class B extends Object { B() { super(); } }<br />

class Pair extends Object {<br />

Object fst; // first element of a pair<br />

Object snd; // second element of a pair<br />

Pair(Object fst, Object snd) {<br />

super(); this.fst = fst; this.snd = snd;<br />

}<br />

Pair setfst(Object newfst) {<br />

return new Pair(newfst, this.snd);<br />

}<br />

}<br />

Fünf Arten von (<br />

Ausdrücken: s<strong>in</strong>d Platzhalter für Ausdrücke)<br />

1. Object constructors: new A(), new B(), new Pair(e1, e2)<br />

2. Method <strong>in</strong>vocation: e3.setfst(e4)<br />

3. Field access: this.snd<br />

4. Variable: newfst, this (this wird <strong>in</strong> FJ als Variable aufgefasst)<br />

5. Cast: (Pair) e5<br />

Da (ausser <strong>in</strong> Konstruktoren) ke<strong>in</strong>e Zuweisung erlaubt ist, wird auf das<br />

Schlüsselwort f<strong>in</strong>al verzichtet: Felder werden nur e<strong>in</strong>mal belegt,<br />

Methoden-Parameter werden im Körper nicht manipuliert.<br />

Im Kontext der Klassen-Def<strong>in</strong>itionen A, B und Pair können Ausdrücke ausgewertet<br />

werden:<br />

new Pair(new A(), new B()).setfst(new B())<br />

evaluiert zu new Pair(new B(), new B())<br />

((Pair) new Pair(new Pair(new A(), new B()),<br />

new A()).fst).snd<br />

evaluiert zu new B()<br />

Dabei bezeichnet new X() e<strong>in</strong> <strong>Objekt</strong> vom Typ X.<br />

Die Auswertung (Reduktion) wird im folgenden formal gefaßt.


§<br />

¥<br />

<br />

Leere Sequenz: , Länge von Sequenzen "$# <br />

<br />

¥<br />

¥<br />

¥<br />

©<br />

¥<br />

©<br />

<strong>Informatik</strong> B SS 03 105<br />

§<br />

¡ ::= ¢ ¤£ ¡ £<br />

class ¦ £ ¡ extends ; // class declarations<br />

) super(£<br />

::= ( £ ¢ ¨<br />

£ , £ ¡ £ ¨<br />

); this. £<br />

¦ ¡<br />

::= £ ¡ <br />

£ ( ) return ¡<br />

¡© ;<br />

::= | . | ¥ .© (£ ) | new ¡ (£ ) | (¡ )<br />

<br />

Platzhalter für Klassennamen: A, B, C, D, E<br />

<br />

Platzhalter für Felder: f, g<br />

<br />

£ ;¡ = // constructor declaration<br />

// method declaration<br />

// expression declaration<br />

Platzhalter für Methodennamen: m<br />

<br />

Platzhalter für Variablen: x<br />

<br />

für etc. (bei <br />

<br />

<br />

<br />

Platzhalter für Ausdrücke: d, e<br />

ohne <br />

<br />

Kommata),<br />

für<br />

!<br />

<br />

Sequenzen: <br />

<br />

<br />

Abbildung 36: Syntax für FJ<br />

%'&<br />

8.2.2 Syntax für FJ<br />

Die Syntax von FJ kann abstrakt angegeben werden (siehe Abb. 36): hier e<strong>in</strong>e<br />

Art EBNF mit Zusatzsyntax.<br />

Klassendeklaration: Neue Klasse mit Oberklasse , ¦ ¥ ¦)(¡ ¢ ¡*( <br />

Felder<br />

(ke<strong>in</strong>e primitiven Typen, Instanzvariablen erweitern die Menge der <strong>in</strong> den<br />

Oberklassen deklarierten Variablen – Namen müssen echt verschieden se<strong>in</strong>,<br />

ke<strong>in</strong> shadow<strong>in</strong>g), e<strong>in</strong> Konstruktor + und Methodendeklarationen , ¦ ¡ ¡ ,.- .<br />

Konstruktordeklaration: Es müssen alle Felder der Klasse explizit <strong>in</strong>itialisiert<br />

werden, der Konstruktor muss genauso viele Parameter erhalten, wie es Felder<br />

gibt. Der Oberklassenkonstruktor muss aufgerufen werden.<br />

Methodendeklaration: immer mit Rückgabetyp, Variablen<br />

/<br />

und this s<strong>in</strong>d im<br />

Ausdruck ¡<br />

gebunden. <br />

8.2.3 Subtyp<strong>in</strong>g<br />

Def<strong>in</strong>ition e<strong>in</strong>er Klassentabelle CT: Abbildung von <br />

Klassennamen<br />

0 Klassendeklarationen .<br />

Tabelle CT hat als Doma<strong>in</strong> (dom) e<strong>in</strong>e Menge von Klassennamen .<br />

Zur Vere<strong>in</strong>fachung wird jeweils e<strong>in</strong>e feste Tabelle angenommen.<br />

E<strong>in</strong> Programm ist e<strong>in</strong> Paar (CT, e) e<strong>in</strong>er Klassentabelle und e<strong>in</strong>es Ausdrucks.<br />

(siehe Programmbeispiel weiter oben)<br />

Klasse Object wird speziell behandelt: ersche<strong>in</strong>t nicht <strong>in</strong> CT, hat ke<strong>in</strong>e Felder<br />

und Methoden (Vere<strong>in</strong>fachung gegenüber <strong>Java</strong>)<br />

Untertyp-Relationen können über CT ermittelt werden.<br />

Untertyp-Regeln s<strong>in</strong>d <strong>in</strong> Abb. 37 angegeben.<br />

auf


¡<br />

C<br />

¡<br />

C<br />

,<br />

106 <strong>Informatik</strong> B SS 03<br />

class C extends D ...<br />

C


<strong>Informatik</strong> B SS 03 107<br />

Abbildung 38: Hilfsfunktionen für FJ


¡<br />

<br />

108 <strong>Informatik</strong> B SS 03<br />

8.3 Typisierung und Reduktion <strong>in</strong> FJ<br />

8.3.1 Typisierungsregeln<br />

Die Typisierungsregeln s<strong>in</strong>d <strong>in</strong> Abb. 39 angegeben.<br />

Sie def<strong>in</strong>ieren, unter welchen Bed<strong>in</strong>gungen (über dem Strich angegeben) e<strong>in</strong><br />

Ausdruck zu e<strong>in</strong>em bestimmten Typ auswertet bzw. ob Klassen und Methoden<br />

korrekt def<strong>in</strong>iert wurden.<br />

Es gibt Typisierungsregeln für Klassen, Methoden und Ausdrücke.<br />

Typisierung erfolgt bzgl. e<strong>in</strong>es Environments , das e<strong>in</strong>e endliche Abbildung von<br />

Variablen auf Typen<br />

/ § festlegt.<br />

¡<br />

Typisierungs-Aussagen für Ausdrücke haben die ¢¡ Form:<br />

Environment hat Ausdruck den Typ ”.<br />

Es gibt für jede der fünf syntaktischen Arten von Ausdrücken e<strong>in</strong>e<br />

Typisierungsregel – nur für Casts gibt es drei Regeln.<br />

§ , lies “<strong>in</strong><br />

Für Casts werden Upcast, Downcast und “stupid cast” betrachtet. Der “stupid<br />

cast” behandelt Fälle, <strong>in</strong> denen die Zielklasse bzgl. der Klassenhierarchie<br />

unverbunden mit der Klasse des aktuellen <strong>Objekt</strong>s ist.<br />

“stupid casts” werden im Formalismus benötigt, weil während der Anwendung<br />

von Reduktionsregeln aus zulässigen Casts “stupid casts” entstehen können:<br />

(Beispielprogramm von oben)<br />

(A) (Object)new B() (A)new B()<br />

(Object) new B() wird zunächst zu new B() reduziert: Der Upcast ist<br />

¡<br />

zulässig, aber es handelt sich um e<strong>in</strong> <strong>Objekt</strong> vom Typ B. Der Cast zur<br />

unverbundenen Klasse A ist unzulässig.<br />

Die Typisierungsregeln (bis auf “stupid cast”) entsprechen der Semantik der<br />

Typisierung <strong>in</strong> <strong>Java</strong>.<br />

Typisierungs-Aussagen für Methoden-Deklarationen haben die Form M OK IN<br />

C – “Methoden-Deklaration M ist ok, wenn sie <strong>in</strong> Klasse C auftaucht”.<br />

Für Overrid<strong>in</strong>g gilt: Die Methode der Unterklasse muss denselben Typ haben<br />

wie die der Oberklasse.<br />

Typisierungs-Aussagen für Klassen-Deklarationen: Es muss gelten, dass der<br />

Konstruktoraufruf korrekt ist (Aufruf von super() mit den Feldern der<br />

Oberklasse und Initialisierung der Klassenfelder) und dass jede<br />

Methodendeklaration ok ist.<br />

Über die Hilfsfunktionen kann jede Methode unabhängig von den anderen<br />

Methoden geprüft werden.<br />

8.3.2 Reduktionsregeln<br />

Die Reduktionsregeln s<strong>in</strong>d (zusammen mit Kongruenzregeln) <strong>in</strong> Abb. 40<br />

angegeben.


<strong>Informatik</strong> B SS 03 109<br />

Abbildung 39: Typisierungsregeln für FJ


¡<br />

* <br />

¡<br />

¡<br />

¡<br />

¥<br />

¡<br />

110 <strong>Informatik</strong> B SS 03<br />

Reduktionsregeln beschreiben, wie e<strong>in</strong> Ausdruck ausgewertet wird<br />

(computation).<br />

Anwendung e<strong>in</strong>er Reduktionsregel auf e<strong>in</strong>en gegebenen Ausdruck: Prüfen, ob<br />

Anwendungsbed<strong>in</strong>gungen (über dem Strich) gelten, F<strong>in</strong>den e<strong>in</strong>es<br />

Unterausdrucks, der mit der l<strong>in</strong>ken Seite der Reduktionsregel matched (gleich<br />

ist, wenn Variablen entsprechend substitutiert werden können), ersetzen dieses<br />

Unterausdrucks durch die rechte Regelseite (<strong>in</strong> der Variablen durch substituiert<br />

wurden).<br />

Anmerkungen: Mit Variablen s<strong>in</strong>d hier Platzhalter <strong>in</strong> den Reduktionsregeln<br />

geme<strong>in</strong>t. Beispielsweise ¥ steht für den Namen e<strong>in</strong>es Feldes. Dieses könnnte<br />

beispielsweise für die ¢¡ Klasse Pair durch fst substituiert werden und<br />

me<strong>in</strong>t dann den Wert dieses Feldes, z.B. new B().<br />

Sie haben die ¡ Form (e<strong>in</strong> Ableitungsschritt). Ganze Ketten von<br />

Auswertungen werden mit notiert (reflexive und transitive Hülle)<br />

Es gibt drei Reduktionsregeln: für Feldzugriff, für Methodenaufruf und für<br />

Cast<strong>in</strong>g.<br />

£¢<br />

/¡<br />

¥<br />

Variable ¢<br />

¥¤ me<strong>in</strong>t: <strong>in</strong> ¦¤ Ausdruck werden Variablen durch Ausdrücke * und<br />

<br />

durch / Ausdruck ersetzt.<br />

Die Kongruenzregeln beschreiben, wie mit Teilausdrücken umgegangen wird,<br />

z.B.: ¡ wenn dann ¥ ¡ ¥ auch .<br />

Die Beziehung zwischen Typisierung und Auswertung sollte <strong>in</strong> <strong>Java</strong> (FJ)<br />

typkorrekt se<strong>in</strong>. Das heißt, für korrekt typisierte Ausdrücke sollte die Auswertung<br />

solcher Ausdrücke wieder typkorrekt se<strong>in</strong>!<br />

Dies kann für FJ formal bewiesen werden: Wenn e<strong>in</strong> wohltypisierter Term<br />

reduziert wird, dann ist das Resultat entweder von e<strong>in</strong>em Untertyp des Typs des<br />

Orig<strong>in</strong>alterms oder e<strong>in</strong> Ausdruck, bei dem e<strong>in</strong> unzulässiger Downcast versucht<br />

und abgewiesen wird.<br />

Ist ke<strong>in</strong>e Regel anwendbar, wird die Berechnung gestoppt (run time error).<br />

8.3.3 Veranschaulichung<br />

Die Beweise s<strong>in</strong>d <strong>in</strong> dem entsprechenden Artikel von Igarashi et al.<br />

nachzulesen.<br />

Beweis-Idee: Für beliebige Ausdrücke von FJ wird gezeigt, dass die Anwendung<br />

der Reduktionsregeln auf korrekt typisierte Ausdrücke dann wieder zu korrekt<br />

typisierten Ausdrücken führt, wenn nur upcast<strong>in</strong>g vorkommt.<br />

Anstelle der Beweise wird im folgenden veranschaulicht, wie mit dem formalen<br />

Kalkül von FJ, das die Semantik der Auswertung von Ausdrücken beschriebt,<br />

“gerechnet” werden kann.


<strong>Informatik</strong> B SS 03 111<br />

Abbildung 40: Reduktionsregeln für FJ


¡<br />

¡<br />

¡<br />

112 <strong>Informatik</strong> B SS 03<br />

Feldzugriff: Anwendung von Regel R-FIELD<br />

new Pair(new A(), new B()).snd new B()<br />

Methoden-Aufruf: Anwendung von Regel R-INVK<br />

¡<br />

new Pair(new A(), new B()).setfst(new B())<br />

new B()/newfst¥<br />

¡<br />

new Pair(new A(), B())/this¡<br />

new Pair(newfst, this.snd)<br />

new<br />

new Pair(new B(), new Pair(new A(), new B()).snd)<br />

Cast<strong>in</strong>g:<br />

((Pair) new Pair(new Pair(new A(), new B()), new A()).fst).snd<br />

((Pair) new Pair(new A(), new B())).snd<br />

new Pair(new A(), new B()).snd<br />

new B()<br />

Der Adressat des Cast wird zunächst zu e<strong>in</strong>em <strong>Objekt</strong> (z. B. new A()) reduziert.<br />

Wenn dieses <strong>Objekt</strong> zu e<strong>in</strong>er Unterklasse des Target, gehört wird der Cast entfernt,<br />

anderenfalls ergibt sich e<strong>in</strong> Laufzeitfehler, z. B. für (A) new(B).<br />

Drei Möglichkeiten, dass Berechnung scheitert:<br />

Versuch, auf e<strong>in</strong> Feld zuzugreifen, das nicht zur Klasse gehört.<br />

Passiert nicht <strong>in</strong> wohl-typisierten Programmen (Beweis)<br />

Versuch, e<strong>in</strong>e Methode aufzurufen, die nicht zur Klasse gehört.<br />

(dito)<br />

Versuch, etwas zu e<strong>in</strong>er Klasse zu casten, die nicht Oberklasse ist.<br />

Passiert nicht <strong>in</strong> wohl-typisierten Programmen, die ke<strong>in</strong>e Downcasts<br />

(“narrow<strong>in</strong>g”) enthalten.


¡<br />

¡<br />

<strong>Informatik</strong> B SS 03 113<br />

9 Abstrakte Klassen und Interfaces<br />

9.1 Abstrakte Klassen und Methoden<br />

Beispiel Circle (ist <strong>in</strong>zwischen Teil e<strong>in</strong>es Pakets myshapes2).<br />

Erweiterung: Implementation e<strong>in</strong>er Vielzahl verschiedener Shape-Klassen:<br />

Circle, Rectangle, Square, Ellipse, Triangle, ...<br />

Alle Shape-Klassen sollen area()- und circumference()-Methoden haben.<br />

Arbeiten mit e<strong>in</strong>em Array von Shape-<strong>Objekt</strong>en: Es wäre günstig, wenn e<strong>in</strong>e<br />

geme<strong>in</strong>same Oberklasse Shape existiert, die alle Komponenten def<strong>in</strong>iert, die<br />

allen geometrischen Formen geme<strong>in</strong>sam s<strong>in</strong>d.<br />

Was ist mit den Methoden area() und circumference()<br />

(Ohne konkrete geometrische Gestalt ist Berechnungsvorschrift unbekannt.)<br />

abstrakte Methoden!<br />

Abstrakte Methode: Def<strong>in</strong>ition ohne Implementation (Körper); Modifikator<br />

abstract; Methodenkopf abgeschlossen durch Semikolon.<br />

Jede Klasse, die e<strong>in</strong>e abstrakte Methode enthält, ist selbst abstrakt und muss<br />

als abstract deklariert werden.<br />

E<strong>in</strong>e abstrakte Klasse kann nicht <strong>in</strong>stantiiert werden (ke<strong>in</strong>e <strong>Objekt</strong>-Erzeugung<br />

mit new möglich).<br />

E<strong>in</strong>e Unterklasse e<strong>in</strong>er abstrakten Klasse kann nur <strong>in</strong>stantiiert werden, wenn<br />

alle abstrakten Methoden der Oberklasse implementiert werden.<br />

“konkrete” Unterklasse<br />

E<strong>in</strong>e Unterklasse, die nicht alle abstrakten Methoden implementiert, ist selbst<br />

abstrakt.<br />

static- und f<strong>in</strong>al-Methoden können nicht abstrakt se<strong>in</strong>, da diese nicht von<br />

e<strong>in</strong>er Unterklasse überschrieben werden können.<br />

private Methoden s<strong>in</strong>d implizit f<strong>in</strong>al. Ebenso können f<strong>in</strong>al Klassen ke<strong>in</strong>e<br />

abstrakten Methoden enthalten.<br />

Klassen können abstract deklariert werden, auch wenn sie ke<strong>in</strong>e abstrakten<br />

Methoden enthalten.<br />

H<strong>in</strong>weis, dass Methoden unvollständig s<strong>in</strong>d und dass die Methode als<br />

Oberklasse für konkrete Unterklassen gedacht ist.<br />

(Abstrakte Klassen können generell nicht <strong>in</strong>stantiiert werden.)<br />

<strong>Objekt</strong>e von Unterklassen können direkt (ohne Cast) an Variablen (z. B.<br />

Elemente e<strong>in</strong>es Arrays von Shapes) der Oberklasse zugewiesen werden.<br />

Abstrakte Methoden der Oberklasse können für jedes <strong>Objekt</strong> e<strong>in</strong>er konkreten<br />

Unterklasse aufgerufen werden (dynamic method lookup).


114 <strong>Informatik</strong> B SS 03<br />

public abstract class Shape {<br />

public abstract double area();<br />

// Abstract methods: note<br />

public abstract double circumference(); // semicolon <strong>in</strong>stead of body<br />

}<br />

public class Circle extends Shape {<br />

public static f<strong>in</strong>al double PI = 3.14159265358979323846;<br />

protected double r; // Radius is hidden, but visible to subclasses<br />

< code omitted ... ><br />

}<br />

// Methods to operate on the <strong>in</strong>stance field<br />

// Implementation of abstract shape methods<br />

public double area() { return PI * r * r; }<br />

public double circumference() { return 2 * PI * r; }<br />

public class Rectangle extends Shape {<br />

protected double w, h;<br />

< code omitted ... ><br />

// Instance fields<br />

// width and height<br />

}<br />

// Instance methods<br />

// implementation of abstract methods<br />

public double area() { return w * h; }<br />

public double circumference() { return 2 * (w + h); }<br />

Shape[] shapes = new Shape[3]; // Create an Array to hold shapes<br />

shapes[0] = new Circle(2.0); // Fill <strong>in</strong> the array<br />

shapes[1] = new Rectangle(1.0, 3.0);<br />

shapes[2] = new Rectangle(4.0, 2.0);<br />

double total_area = 0;<br />

for (<strong>in</strong>t i = 0; i < shapes.length; i++)<br />

total_area += shapes[i].area(); // Compute area of shapes<br />

9.2 Interfaces<br />

Nächste Erweiterung des Shapes-Beispiels: Nicht nur Grösse, auch Position<br />

der geometrischen <strong>Objekt</strong>e <strong>in</strong> der Ebene.<br />

Erste Idee: weitere abstrakte Klasse CenteredShape mit Unterklassen<br />

CenteredCircle, CenteredRectangle, ...<br />

CenteredCircle soll natürlich auch die Methoden von Circle erben.<br />

Problem: <strong>Java</strong> erlaubt nicht, dass e<strong>in</strong>e Klasse mehr als e<strong>in</strong>e Oberklasse hat!<br />

(Mehrfachvererbung)


<strong>Informatik</strong> B SS 03 115<br />

<strong>Java</strong> Lösung: Interfaces (Schnittstellen)<br />

E<strong>in</strong>e Klasse kann beliebig viele Interfaces implementieren.<br />

E<strong>in</strong> Interface ist e<strong>in</strong> Referenztyp sehr ähnlich e<strong>in</strong>er Klasse: Def<strong>in</strong>iert wird e<strong>in</strong>e<br />

Funktionalität und nicht e<strong>in</strong>e Implementation (Realisierung), e<strong>in</strong> Interface gibt<br />

Signaturen – Namen und Typen von Methoden (und Konstanten) – vor.<br />

E<strong>in</strong> Interface wird mit dem Schlüsselwort <strong>in</strong>terface deklariert.<br />

E<strong>in</strong> Interface enthält ke<strong>in</strong>erlei Methoden-Implementation.<br />

Alle Methoden s<strong>in</strong>d implizit abstrakt, auch wenn ohne diesen Modifikator<br />

deklariert.<br />

E<strong>in</strong> Interface kann nur Instanz-Methoden enthalten.<br />

E<strong>in</strong> Interface ist ohne Sichtbarkeitsmodifikator paketsichtbar. Als e<strong>in</strong>ziger<br />

Sichtbarkeitsmodifikator darf public angegeben werden. Alle Methoden s<strong>in</strong>d<br />

implizit public, auch wenn der Modifikator nicht explizit angegeben ist.<br />

Es ist e<strong>in</strong> Fehler, protected oder private Methoden <strong>in</strong> e<strong>in</strong>em Interface zu<br />

deklarieren!<br />

E<strong>in</strong> Interface kann ke<strong>in</strong>e Instanz-Felder def<strong>in</strong>ieren, aber als static und f<strong>in</strong>al<br />

deklarierte Konstanten.<br />

Da e<strong>in</strong> Interface nicht <strong>in</strong>stantiiert werden kann, def<strong>in</strong>iert es ke<strong>in</strong>en Konstruktor.<br />

Interfaces s<strong>in</strong>d re<strong>in</strong>e Spezifikationen!<br />

9.2.1 Implementation e<strong>in</strong>es Interfaces<br />

Unterklasse Oberklasse¡<br />

extends<br />

Klasse Interface¡<br />

implements<br />

Schlüsselwort implements folgt nach extends (falls Oberklasse angegeben);<br />

wird von e<strong>in</strong>em oder mehreren (durch Komma getrennte) Namen von Interfaces<br />

gefolgt.<br />

implements bedeutet, dass <strong>in</strong> der implementierenden Klasse die Körper für<br />

Methoden des Interfaces def<strong>in</strong>iert werden. Werden nicht alle Methoden<br />

implementiert, so ist die Klasse abstrakt und muss als solche deklariert werden.<br />

(Alle Methoden des Interfaces werden Teil der Klasse.)<br />

public <strong>in</strong>terface Centered {<br />

public void setCenter(double x, double y);<br />

public double getCenterX();<br />

public double getCenterY();<br />

}<br />

public class CenteredRectangle extends Rectangle implements Centered {<br />

private double cx, cy; // New <strong>in</strong>stance fields<br />

public CenteredRectangle(double cx, double cy, double w, double h) {<br />

super(w, h);


116 <strong>Informatik</strong> B SS 03<br />

}<br />

this.cx = cx;<br />

this.cy = cy;<br />

}<br />

// We <strong>in</strong>herit all the methods of Rectangle, but must<br />

// provide implementations of all the Centered methods.<br />

public void setCenter(double x, double y) { cx = x; cy = y; }<br />

public double getCenterX() { return cx; }<br />

public double getCenterY() { return cy; }<br />

9.2.2 Interfaces und Konstanten<br />

Konstanten dürfen <strong>in</strong> Interface-Deklarationen vorkommen.<br />

Alle Felder, die <strong>in</strong> e<strong>in</strong>em Interface deklariert werden, werden implizit als static<br />

und f<strong>in</strong>al aufgefasst, auch wenn nicht explizit so deklariert.<br />

Es ist jedoch guter Stil, diese Modifikatoren explizit anzugeben!<br />

Jede Klasse, die das Interface implementiert, erbt die Konstanten und kann sie<br />

benutzen, als wären sie direkt <strong>in</strong> der Klasse selbst deklariert (ke<strong>in</strong>e<br />

Voranstellung des Interface-Namens vor den Konstanten-Namen notwendig).<br />

Konstanten müssen nicht unbed<strong>in</strong>gt mit festen Werten <strong>in</strong>itialisiert werden:<br />

public <strong>in</strong>terface RandVals {<br />

<strong>in</strong>t r<strong>in</strong>t = (<strong>in</strong>t) (Math.random() * 10);<br />

long rlong = (long) (Math.random() * 10);<br />

float rfloat = (float) (Math.random() * 10);<br />

double rdouble = Math.random() * 10;<br />

}<br />

Manchmal nützlich: Interface, das nur Konstanten enthält.<br />

Konstanten, die von mehreren Klassen benutzt werden (wie Port-Nummern, die<br />

von Client und Server benutzt werden).<br />

Beispiel: java.io.ObjectStreamConstants (Konstanten für <strong>Java</strong>s<br />

Serialisierungs-Mechanismus)<br />

9.2.3 Benutzung von Interfaces<br />

Wenn e<strong>in</strong>e Klasse e<strong>in</strong> Interface implementiert, können <strong>Objekt</strong>e dieser Klasse an<br />

e<strong>in</strong>e Variable vom Typ des Interfaces zugewiesen werden.<br />

<strong>in</strong>stanceof Interface/Klasse¡ liefert Wahrheitswert.<br />

Object¡<br />

Shape[] shapes = new Shape[3]; // Create an array to hold shapes<br />

// Create some centered shapes, and store them <strong>in</strong> the Shape[]


<strong>Informatik</strong> B SS 03 117<br />

Shape<br />

Circle<br />

CenteredCircle<br />

<br />

Centered<br />

Rectangle<br />

CenteredRectangle<br />

Square<br />

CenteredSquare<br />

Abbildung 41: Struktur der Shape-Klassen<br />

// No cast necessary<br />

shapes[0] = new CenteredCircle(1.0, 1.0, 1.0);<br />

shapes[1] = new CenteredSquare(2.5, 2, 3);<br />

shapes[2] = new CenteredRectangle(2.3, 4.5, 3, 4);<br />

// Compute average area of the shapes and average distance from the orig<strong>in</strong><br />

double totalArea = 0;<br />

double totalDistance = 0;<br />

for (<strong>in</strong>t i = 0; i < shapes.length; i++) {<br />

totalArea += shapes[i].area(); // Compute the area of the shapes<br />

if (shapes[i] <strong>in</strong>stanceof Centered) { // The shape is a Centered shape<br />

// Note the required cast from Shape to Centered<br />

// No cast would be required to go from CenteredSquare to Centered etc.<br />

Centered c = (Centered) shapes[i]; // Assign it to a Centered variable<br />

double cx = c.getCenterX();<br />

double cy = c.getCenterY();<br />

totalDistance += Math.sqrt(cx*cx + cy*cy);<br />

}<br />

}<br />

System.out.pr<strong>in</strong>tln("Average area: " + totalArea/shapes.length);<br />

System.out.pr<strong>in</strong>tln("Average distance: " + totalDistance/shapes.length);<br />

9.2.4 Interface vs. Abstrakte Klasse<br />

Entwurfsentscheidung zwischen abstrakter Klasse und Interface.<br />

Interface: Jede Klasse kann es implementieren. Zwei nicht verwandte Klassen<br />

können dasselbe Interface implementieren.<br />

Abstrakte Klasse: Nur e<strong>in</strong>e Klasse kann Oberklasse e<strong>in</strong>er anderen Klasse<br />

se<strong>in</strong>.<br />

Interface: Nur abstrakte Methoden; wenn Methoden für viele Klassen gleich<br />

s<strong>in</strong>d, so müssen sie immer neu implementiert werden.


118 <strong>Informatik</strong> B SS 03<br />

Abstrakte Klasse: Kann Default-Implementation für typische Methoden liefern.<br />

Kompatibilität: Wenn Interface zum public API h<strong>in</strong>zugefügt wird und später das<br />

Interface um e<strong>in</strong>e Methode erweitert wird, so s<strong>in</strong>d alle Klassen, die das Interface<br />

implementieren, “kaputt”.<br />

Zu abstrakten Klassen können implementierte Methoden gefahrlos h<strong>in</strong>zugefügt<br />

werden.<br />

Manchmal nützlich: Abstrakte Klasse, die e<strong>in</strong> Interface implementiert und<br />

Default-Implementationen für Methoden der Unterklassen liefert.<br />

(Adapter-Klasse)<br />

Alternativ: Delegate-Pattern (Support-Klasse), die Methoden e<strong>in</strong>es Interface<br />

implementieren. Eigene Klasse, die das Interface implementiert, kann als<br />

Komponente e<strong>in</strong> <strong>Objekt</strong> e<strong>in</strong>er Support-Klasse (die dasselbe Interface<br />

implementiert) erzeugen (siehe Vorlesung Infomatik C).<br />

// Here is a basic <strong>in</strong>terface. It represents a shape that fits <strong>in</strong>side<br />

// a rectangular bound<strong>in</strong>g box. Any class that wants to serve as a<br />

// RectangularShape can implement these methods from scratch.<br />

public <strong>in</strong>terface RectangularShape {<br />

public void setSize(double width, double height);<br />

public void setPosition(double x, double y);<br />

public void translate(double dx, double dy);<br />

public double area();<br />

}<br />

// Here is a partial implementation of that <strong>in</strong>terface.<br />

// Many implementations may f<strong>in</strong>d this a useful start<strong>in</strong>g po<strong>in</strong>t.<br />

public abstract class AbstractRectangularShape implements RectangularShape {<br />

// The position and size of the shape<br />

protected double x, y, w, h;<br />

}<br />

// Default implementations of some of the <strong>in</strong>terface methods<br />

public void setSize(double width, double height)<br />

{ w = width; h = height; }<br />

public void setPosition(double x, double y) { this.x = x; this.y =y; }<br />

public void translate(double dx, double dy) { x += dx; y += dy; }<br />

9.2.5 Implementation mehrerer Interfaces und Erweitern von Interfaces<br />

Wenn die Klasse, die mehrere Interfaces implementiert, nicht abstrakt se<strong>in</strong> soll,<br />

so müssen die Methoden aller Interfaces implementiert werden.<br />

public class SuperDuperSquare extends Shape<br />

implements Centered, Scalable {<br />

// Methods omitted<br />

}


<strong>Informatik</strong> B SS 03 119<br />

Wie Klassen Unterklassen haben können, so können Interfaces Unter-Interfaces<br />

haben.<br />

Bei Interfaces dürfen h<strong>in</strong>ter extends mehrere andere Interfaces stehen.<br />

public <strong>in</strong>terface Positionable extends Centered {<br />

public void setUpperRightCorner(double x, double y);<br />

public double getUpperRightX();<br />

public double getUpperRightY();<br />

}<br />

public <strong>in</strong>terface Transformable extends<br />

Scalable, Translatable, Rotatable {}<br />

public <strong>in</strong>terface SuperShape extends<br />

Positionable, Transformable {}<br />

Hier kann man sich das Problem der Mehrfachvererbung teilweise doch<br />

e<strong>in</strong>handeln. Es gelten folgende Regeln, wenn Methoden gleichen Namens <strong>in</strong><br />

verschiedenen (zu implementierenden, zu erweiternden) Interfaces deklariert<br />

werden:<br />

– Methoden mit gleichem Namen und gleicher Signatur werden e<strong>in</strong>mal<br />

aufgenommen.<br />

– Methoden mit gleichem Namen und verschiedenen Signaturen s<strong>in</strong>d<br />

überladen.<br />

– Methoden mit gleichem Namen, gleichen Parametern und verschiedenem<br />

Rückgabetyp führen zu Übersetungs-Fehler.<br />

– Bei Methoden mit gleicher Signatur und verschiedenen spezifizierten<br />

Exceptions muss die “Schnittmenge” dieser Exceptions oder e<strong>in</strong>e<br />

Teilmenge davon spezifiziert werden.<br />

<strong>in</strong>terface X {<br />

void setup() throws SomeException;<br />

}<br />

<strong>in</strong>terface Y {<br />

void setup();<br />

}<br />

class Z implements X, Y {<br />

public void setup() {<br />

// ...<br />

}<br />

}<br />

Folgender Code führt zu e<strong>in</strong>em Übersetzungs-Fehler:<br />

<strong>in</strong>terface X {


120 <strong>Informatik</strong> B SS 03<br />

}<br />

void setup() throws FileNotFoundException;<br />

<strong>in</strong>terface Y {<br />

void setup() throws IOException;<br />

}<br />

// Schnittmenge ist FileNotFoundException<br />

class Z implements X, Y {<br />

public void setup() throws IOException {<br />

// ...<br />

}<br />

}<br />

9.2.6 Marker-Interfaces<br />

Manchmal ist es nützlich, e<strong>in</strong> leeres Interface zu def<strong>in</strong>ieren.<br />

Klasse, die dieses Interface implementiert, muss ke<strong>in</strong>e Methoden<br />

implementieren.<br />

Jede Instanz der Klasse ist zulässige “Instanz” des Interfaces.<br />

Prüfbar mit <strong>in</strong>stanceof<br />

Beispiele: Cloneable und java.io.Serializable<br />

MyClass o; // Initialized elsewhere<br />

MyClass copy;<br />

if (o <strong>in</strong>stanceof Cloneable) copy = o.clone();<br />

else copy = null;<br />

9.3 Das Enumeration-Interface<br />

Idee: Für e<strong>in</strong>e Datenstruktur, die Elemente hält, sollen diese Elemente<br />

aufgezählt werden.<br />

Typisch für Collection-Klassen (siehe Kapitel ‘Collection-Klassen’), <strong>in</strong> denen<br />

<strong>Objekt</strong>e gehalten werden.<br />

Beispiele: Stack, L<strong>in</strong>kedList<br />

Vordef<strong>in</strong>iertes Enumeration-Interface mit zwei Methoden:<br />

– boolean hasMoreElements(): true, wenn die Aufzählung noch weitere<br />

Elemente enthält, false sonst<br />

– Object nextElement(): liefert das nächste Element, falls es existiert,<br />

sonst wird e<strong>in</strong>e Exception ausgelöst.<br />

Beispiel: Collection ist e<strong>in</strong>fache Array-Liste<br />

<strong>Objekt</strong>e werden <strong>in</strong> e<strong>in</strong>em Array abgelegt (Methode add())


<strong>Informatik</strong> B SS 03 121<br />

Wenn der Array gefüllt ist, werden ke<strong>in</strong>e weiteren Elemente mehr angenommen.<br />

Es fehlen Methoden zum Löschen, zum Prüfen, ob e<strong>in</strong> Element enthalten ist (mit<br />

entsprechender equals()-Methode), evtl. e<strong>in</strong>e Methode zum Vergrößern des<br />

Arrays, etc.<br />

public class MyList {<br />

public static f<strong>in</strong>al <strong>in</strong>t MAX = 100;<br />

protected Object[] list = new Object[MAX];<br />

protected <strong>in</strong>t numOfEls = 0;<br />

public boolean add(Object o) {<br />

if (numOfEls >= MAX)<br />

return false;<br />

else {<br />

list[numOfEls++] = o;<br />

return true;<br />

}<br />

}<br />

public Object get (<strong>in</strong>t i) {<br />

return list[i];<br />

}<br />

}<br />

public <strong>in</strong>t size() {<br />

return numOfEls;<br />

}<br />

E<strong>in</strong>e unelegante Art, den Inhalt e<strong>in</strong>er Liste aufzuzählen, ist es, mit e<strong>in</strong>er Schleife über<br />

die Liste zu laufen:<br />

MyList <strong>in</strong>tlist = new MyList();<br />

for (<strong>in</strong>t i=0; i < 10; i++) {<br />

<strong>in</strong>tlist.add(new Integer(i));<br />

}<br />

// enumerate elements by <strong>in</strong>dex<br />

for (<strong>in</strong>t i=0; i < <strong>in</strong>tlist.size(); i++) {<br />

System.out.pr<strong>in</strong>tln(<strong>in</strong>tlist.get(i));<br />

Aufzählen über Index geht nur für Collection-Klassen, die Index-Zugriff erlauben.<br />

Alternativ: Erzeugen e<strong>in</strong>es Enumerators für MyList-<strong>Objekt</strong>e.<br />

E<strong>in</strong> Enumerator für MyList muss das Enumeration-Interface<br />

implementieren:


122 <strong>Informatik</strong> B SS 03<br />

public class ListEnumerator implements Enumeration {<br />

protected <strong>in</strong>t current = 0;<br />

protected MyList collection;<br />

}<br />

public ListEnumerator(MyList c) { collection = c; }<br />

public boolean hasMoreElements() {<br />

return current < collection.numOfEls;<br />

}<br />

public Object nextElement() {<br />

if (! hasMoreElements())<br />

throw new java.util.NoSuchElementException();<br />

return collection.list[current++];<br />

}<br />

Jetzt kann der Inhalt der Liste folgendermaßen ausgegeben werdenn:<br />

ListEnumerator enum = new ListEnumerator(<strong>in</strong>tlist);<br />

while (enum.hasMoreElements()) System.out.pr<strong>in</strong>tln(enum.nextElement());


<strong>Informatik</strong> B SS 03 123<br />

10 Innere Klassen<br />

Bisher: “top-level” Klassen (direkte Mitglieder e<strong>in</strong>es Pakets).<br />

Seit <strong>Java</strong> 1.1: Innere Klassen: def<strong>in</strong>iert <strong>in</strong>nerhalb e<strong>in</strong>er anderen Klasse<br />

(Komponente e<strong>in</strong>er Klasse, ähnlich Felder und Methoden)<br />

Vier Arten von <strong>in</strong>neren Klassen:<br />

– Member Classes (“echte” <strong>in</strong>nere Klasse)<br />

– Static Member Classes (Nested Top-Level Classes)<br />

– Local Classes<br />

– Anonymous Classes<br />

10.1 Member Classes<br />

10.1.1 Anschauliches Beispiel<br />

E<strong>in</strong> Auto besteht aus vielen Teilen – Motor, Gangschaltung, Auspuff, etc.<br />

Manche Teile bilden s<strong>in</strong>nvollerweise e<strong>in</strong>e eigene Klasse, aber können dennoch<br />

nicht unabhängig vom Auto existieren.<br />

Beispiel: Klimaanlage<br />

– Interaktion zwischen Klimaanlage und Auto ist notwendig.<br />

– Leistung der Klimaanlage ist abhängig von Geschw<strong>in</strong>digkeit des Autos. Je<br />

langsamer das Auto fährt, desto mehr Energie muss die Klimaanlage zum<br />

Kühlen aufbr<strong>in</strong>gen.<br />

Lösung: Member Klasse<br />

/** The general AirConditioner class */<br />

class AirConditioner {<br />

...<br />

public float getTemperatureM<strong>in</strong>() { };<br />

public float getTemperatureMax() { };<br />

}<br />

/** The Automobile class which has an <strong>in</strong>ner class which is an AC */<br />

class Automobile {<br />

private Eng<strong>in</strong>e eng<strong>in</strong>e;<br />

private GearBox gearBox;<br />

...<br />

private class AutoAirConditioner extends AirConditioner {<br />

private float default = ...;<br />

private float factor = ...;<br />

...<br />

public float getTargetTemperature () {


124 <strong>Informatik</strong> B SS 03<br />

}<br />

}<br />

float temperature = default - factor * eng<strong>in</strong>e.getSpeed();<br />

...<br />

}<br />

public AirConditioner getAirConditioner () {<br />

return new AutoAirConditioner();<br />

}<br />

Der AutoAirConditioner ist abhängig von Parametern e<strong>in</strong>es bestimmten Autos.<br />

Beachte: E<strong>in</strong> <strong>Objekt</strong> der <strong>in</strong>neren Klasse kann nur zusammen mit e<strong>in</strong>em <strong>Objekt</strong><br />

der umschließenden Klasse existieren.<br />

Erst neues Automobil erzeugen, dann die AutoAirCondition!<br />

Es ist nicht möglich, e<strong>in</strong> <strong>Objekt</strong> vom Typ AutoAirCondition zu erzeugen,<br />

ohne dass e<strong>in</strong> Auto, zu dem diese Klimaanlage gehört existiert.<br />

Innere Klasse ist privat:<br />

– Andere Klassen/<strong>Objekt</strong>e können nur auf das öffentliche Inferface<br />

(AirConditioner) zugreifen.<br />

– Die umschließende Klasse hat e<strong>in</strong>e Methode, um e<strong>in</strong> “Handle” (Referenz)<br />

auf die öffentlichen Teile des <strong>Objekt</strong>s der <strong>in</strong>neren Klasse zu liefern<br />

(getAirConditioner).<br />

– Beachte: return new AutoAirConditioner() ist explizit: return<br />

this.new AutoAirConditioner()<br />

Alternatives Beispiel: Organe können nicht ohne Körper existieren.<br />

Weitere Beispiele: Enumerator (bzw. Iterator)<br />

Im Kontext von Auto: z.B. Aufzählen aller Schrauben (mit unterschiedlichen<br />

Typen, aber geme<strong>in</strong>samer Oberklasse), um z.B. mittlere Größe, mittlere Kosten<br />

zu bestimmen.<br />

10.1.2 Beispiel ‘Enumerator’<br />

public class MyListMC {<br />

public static f<strong>in</strong>al <strong>in</strong>t MAX = 100;<br />

protected Object[] list = new Object[MAX];<br />

protected <strong>in</strong>t numOfEls = 0;<br />

public boolean add(Object o) {<br />

if (numOfEls >= MAX)<br />

return false;<br />

else {<br />

list[numOfEls++] = o;


<strong>Informatik</strong> B SS 03 125<br />

}<br />

}<br />

return true;<br />

}<br />

}<br />

public <strong>in</strong>t size() {<br />

return numOfEls;<br />

}<br />

public java.util.Enumeration enumerate() {<br />

return new Enumerator();<br />

}<br />

protected class Enumerator implements java.util.Enumeration {<br />

protected <strong>in</strong>t current = 0;<br />

// constructor not necessary<br />

public Enumerator() { current = 0; }<br />

public boolean hasMoreElements() {<br />

return current < numOfEls;<br />

}<br />

public Object nextElement() {<br />

if (! hasMoreElements())<br />

throw new java.util.NoSuchElementException();<br />

return list[current++];<br />

}<br />

Erzeugen und Anwenden des Enumerators:<br />

MyListMC <strong>in</strong>tlist = new MyListMC();<br />

for (<strong>in</strong>t i=0; i < 10; i++) {<br />

<strong>in</strong>tlist.add(new Integer(i));<br />

}<br />

java.util.Enumeration enum = <strong>in</strong>tlist.enumerate();<br />

while (enum.hasMoreElements()) System.out.pr<strong>in</strong>tln(enum.nextElement());<br />

10.1.3 Eigenschaften von Member-Klassen<br />

Member-Klassen s<strong>in</strong>d die typischen, “echten” <strong>in</strong>neren Klassen.<br />

Member-Klassen s<strong>in</strong>d wie Instanz-Felder und -Methoden mit e<strong>in</strong>er Instanz der<br />

Klasse, <strong>in</strong> der sie def<strong>in</strong>iert s<strong>in</strong>d, assoziiert.<br />

Also: Zugriff auf alle Komponenten der umschliessenden Klasse.<br />

Member-Klassen können beliebig tief geschachtelt werden. D. h., e<strong>in</strong>e <strong>in</strong>nere<br />

Klasse kann weitere <strong>in</strong>nere Klassen enthalten.


¡<br />

126 <strong>Informatik</strong> B SS 03<br />

E<strong>in</strong>e Member-Klasse kann mit allen Sichtbarkeits-Modifikatoren deklariert<br />

werden.<br />

Name muss verschieden vom Namen der umschliessenden Klasse se<strong>in</strong>.<br />

Member-Klassen dürfen ke<strong>in</strong>e statischen Komponenten enthalten. Ausnahme:<br />

static und f<strong>in</strong>al deklarierte Konstanten.<br />

Interfaces können nicht als Member-Klassen def<strong>in</strong>iert werden, da Interfaces<br />

ke<strong>in</strong>e Instanz-Variablen besitzen dürfen (also ke<strong>in</strong> this-Verweis möglich).<br />

Wichtigstes Merkmal: Zugriff auf Instanz-Felder und -Methoden der<br />

umschliessenden Klasse.<br />

current < numOfEls<br />

Wie funktioniert explizite Referenz<br />

this.current < this.numOfEls<br />

Problem: this.numOfEls ist nicht zulässig (this bezieht sich auf<br />

Enumerator-<strong>Objekt</strong>)<br />

Erweiterte Syntax:<br />

this.current < MyListMC.this.numOfEls<br />

Diese Zugriffsform ist dann notwendig, wenn man sich auf e<strong>in</strong>e Komponente<br />

e<strong>in</strong>er äusseren Klasse beziehen will, die denselben Namen hat wie e<strong>in</strong>e<br />

Komponente der <strong>in</strong>neren Klasse.<br />

Analoge Erweiterung der super-Syntax (Zugriff auf e<strong>in</strong>e überdeckte oder<br />

überschriebene Komponente der Oberklasse der umschliessenden Klasse):<br />

Klassenname¡ .super. feld¡<br />

methode¡<br />

Ausführung des Member-Klassen Konstruktors bewirkt, dass die neue Instanz<br />

mit dem this <strong>Objekt</strong> der umschliessenden Klasse assoziiert wird.<br />

Gleichbedeutende Schreibweisen:<br />

public Enumeration enumerate() return new Enumerator(); ¡<br />

public Enumeration enumerate() return this.new Enumerator();¡<br />

Klassenname¡ .super.<br />

Anstelle der Def<strong>in</strong>ition von enumerator() könnte e<strong>in</strong>e Enumeration auch so<br />

erzeugt werden:<br />

MyListMC <strong>in</strong>tlist = new MyListMC(); // Create empty list<br />

Enumeration enum = <strong>in</strong>tlist.new Enumerator();<br />

// Create Enum for it<br />

Da die umschliessende Instanz implizit den Namen der umschliessenden<br />

Klasse spezifiziert, ist die explizite Angabe der Klasse e<strong>in</strong> Syntaxfehler:<br />

Enumeration e = <strong>in</strong>tlist.new MyListMC.Enumerator();<br />

// Syntax error<br />

10.1.4 Implementation von Member-Klassen<br />

Innere Klassen seit <strong>Java</strong> 1.1.


<strong>Informatik</strong> B SS 03 127<br />

Erweiterung der Sprache (“syntactic sugar”) aber nicht der JVM: <strong>Java</strong> Compiler<br />

wandelt Repräsentation von <strong>in</strong>neren Klassen entsprechend um.<br />

(Disassemblierung mit javap, um zu sehen, welche Tricks der Compiler<br />

benutzt.)<br />

Compilation <strong>in</strong> eigene top-level-Datei.<br />

Compiler muss Code so manipulieren, dass Zugriff auf Komponenten zwischen<br />

<strong>in</strong>nerer und äusserer Klasse funktioniert.<br />

this$0 Feld für jede Member-Klasse (Assoziation mit Instanz der<br />

umschliessenden Klasse; Abspeichern der entsprechenden Referenz). Für<br />

weitere Referenzen zu umschliessenden Klassen wird entsprechend<br />

weitergezählt (this$1, etc.).<br />

Jeder Member-Klassen Konstruktor erhält e<strong>in</strong>en zusätzlichen Parameter, um<br />

dieses Feld zu <strong>in</strong>itialisieren.<br />

protected Member-Klassen werden public; private Member-Klassen<br />

werden default-sichtbar.<br />

10.1.5 Member-Klassen und Vererbung<br />

Es ist erlaubt, dass e<strong>in</strong>e top-level Klasse als Unterklasse e<strong>in</strong>er Member-Klasse<br />

def<strong>in</strong>iert wird.<br />

Damit hat die Unterklasse ke<strong>in</strong>e umschliessende Klasse, aber ihre Oberklase!<br />

Wegen unklarer Semantik argumentieren e<strong>in</strong>ige dafür, dass diese Art der<br />

Vererbung verboten werden soll.<br />

Atsushi Igarashi and Benjam<strong>in</strong> C. Pierce (2001). On <strong>in</strong>ner classes. Information<br />

and Control.<br />

Die Autoren haben bei der Def<strong>in</strong>ition e<strong>in</strong>er Reduktions-Semantik für <strong>in</strong>nere<br />

Klassen und Vererbung Unterspezifikationen der Sprache <strong>Java</strong> aufgedeckt.<br />

// A top-level class that extends a member class<br />

class SpecialEnumerator extends MyListMC.Enumerator {<br />

// The constructor must explicitely specify a conta<strong>in</strong><strong>in</strong>g <strong>in</strong>stance<br />

// when <strong>in</strong>vok<strong>in</strong>g the superclass constructor<br />

public SpecialEnumerator(MyListMC l) { l.super(); }<br />

// Rest of class omitted<br />

}<br />

(Igarashi and Pierce, 2001)<br />

class C {<br />

void who(){ System.out.pr<strong>in</strong>tln("I’m a C object"); }<br />

class D extends C {<br />

void m(){ C.this.who(); }<br />

void who(){ System.out.pr<strong>in</strong>tln("I’m a C.D object"); }


128 <strong>Informatik</strong> B SS 03<br />

}<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args){<br />

new C().new D().m();<br />

}<br />

Qualified this: Für e<strong>in</strong>e <strong>in</strong>nere Klasse C1.C2...Ci...Cn denotiert Ci.this<br />

¦ £ die -te direkt umschliessende Instanz; aber was ist, wenn Ci Superklasse<br />

von Cn ist<br />

Compiliert mit JDK 1.1.7: I’m a C.D object (Compilerfehler),<br />

compiliert mit JDK 1.2.2: I’m a C object<br />

Zwei hierarchische Strukturen: Klassenhierarchie und Enthaltense<strong>in</strong>-Hierarchie<br />

(Conta<strong>in</strong>ment)<br />

Es können Namenskonflikte zwischen vererbten Komponenten (Oberklasse)<br />

und Komponenten der umschliessenden Klasse auftreten.<br />

class A {<br />

<strong>in</strong>t x;<br />

}<br />

class B {<br />

<strong>in</strong>t x;<br />

class C extends A {<br />

x; // <strong>in</strong>herited field<br />

this.x; // <strong>in</strong>herited field<br />

B.this.x; // field of conta<strong>in</strong><strong>in</strong>g class<br />

}<br />

}<br />

10.2 Static Member Classes<br />

10.2.1 Anschauliches Beispiel<br />

E<strong>in</strong> Autoradio gehört als Teil zum Auto, se<strong>in</strong>e Eigenschaften s<strong>in</strong>d aber<br />

unabhängig vom Auto selbst.<br />

Autoradios s<strong>in</strong>d <strong>Objekt</strong>e, die unabhängig von e<strong>in</strong>em konkreten Auto existieren<br />

können: diese Klassen benötigen ke<strong>in</strong>en Zugriff auf Instanz-Felder und/oder<br />

-Methoden der Automobil-Klasse.<br />

Statische <strong>in</strong>nere Klassen dienen vor allem der Strukturierung von<br />

Programmcode.<br />

<strong>in</strong>terface Radio {<br />

void setVolume ();<br />

...


<strong>Informatik</strong> B SS 03 129<br />

}<br />

/** The Automobile class which has a static <strong>in</strong>ner class */<br />

class Automobile {<br />

private Eng<strong>in</strong>e eng<strong>in</strong>e;<br />

private GearBox gearBox;<br />

.....<br />

private static class AutoRadio extends Radio {<br />

private <strong>in</strong>t channel = ...;<br />

private float volume = ...;<br />

...<br />

public void setVolume () {<br />

...<br />

}<br />

}<br />

}<br />

public Radio getRadio () {<br />

return new AutoRadio();<br />

}<br />

10.2.2 Eigenschaften von Static Member Classes<br />

Während Member-Klassen analog zu Instanz-Feldern und -Methoden zu sehen<br />

s<strong>in</strong>d, s<strong>in</strong>d static member classes ähnlich wie Klassen-Felder und -Methoden zu<br />

verstehen. (“class class”)<br />

Sie haben Zugriff auf alle statischen Komponenten der umschliessenden Klasse.<br />

Static member classes werden auch als nested top-level classes bezeichnet.<br />

Interfaces dürfen nur als static members def<strong>in</strong>iert werden.<br />

Static Klassen können <strong>in</strong> e<strong>in</strong>em Interface deklariert werden.<br />

Static member classes (und Interfaces) werden wie top-level Klassen behandelt.<br />

Sie s<strong>in</strong>d nicht mit e<strong>in</strong>er Instanz der umschliessenden Klasse assoziiert (also:<br />

ke<strong>in</strong> umschliessendes this-<strong>Objekt</strong>).<br />

Deklaration mit Zugriffsmodifikator genau wie für andere Komponenten.<br />

Name muss verschieden vom Namen der umschliessenden Klasse se<strong>in</strong>.<br />

(unqualifizierter) Zugriff auf alle (auch privaten) statischen Komponenten der<br />

umschliessenden Klasse (<strong>in</strong>klusiver weiterer static member classes).<br />

Methoden der umschliessenden Klasse haben Zugriff auf alle Komponenten der<br />

Member-Klasse.<br />

Zugriff von externen Klassen: mit qualifiziertem Namen.<br />

Automobile.AutoRadio<br />

Vorteil: Strukturierung, paket-ähnliche Organisation für Klassen <strong>in</strong>nerhalb e<strong>in</strong>er<br />

Datei.


130 <strong>Informatik</strong> B SS 03<br />

Merke: Member-Klassen (“echte” wie static deklarierte) sollen immer dann verwendet<br />

werden, wenn e<strong>in</strong>e Referenz “von <strong>in</strong>nen nach aussen” benötigt wird. Auf der<br />

Modellierungsebene heisst das: E<strong>in</strong> <strong>Objekt</strong> ist aus anderen (<strong>in</strong>neren) <strong>Objekt</strong>en<br />

aufgebaut, es kann nicht ohne diese <strong>in</strong>neren <strong>Objekt</strong>e existieren, und die <strong>in</strong>neren<br />

<strong>Objekt</strong>e benötigen Information über das umschliessende <strong>Objekt</strong>. Bei Member-Klassen<br />

werden Informationen der umschliessenden Instanz benötigt. Bei static<br />

deklarierten <strong>in</strong>neren Klassen werden statische bzw. ke<strong>in</strong>e Komponenten der<br />

umschliessenden Klasse benötigt, aber die umschliessene Klasse kann auf<br />

Komponenten der <strong>in</strong>neren Klasse zugreifen.<br />

10.2.3 Implementation von statischen Member-Klassen<br />

Compiler generiert zwei Klassen-Dateien, z.B. Automobile.class und<br />

Automobile$AutoRadio.class (Innere Klasse AutoRadio wird zu top-level<br />

Klasse).<br />

Compiler qualifiziert Ausdrücke, die auf statische Komponenten der<br />

umschliessenden Klasse zugreifen, mit dem Klassennamen.<br />

Da auch auf private Komponenten zugegriffen werden darf: Automatische<br />

Generierung von nicht-privaten Zugriffsmethoden (mit Default-Zugriffsrechten,<br />

paket-weit) und Umwandlung der entsprechenden Ausdrücke.<br />

10.3 Lokale Klassen<br />

10.3.1 Anschauliches Beispiel<br />

Alternative Modellierungsidee für die Automobil-Klasse<br />

Da die Klasse AutoAirConditioner nur e<strong>in</strong>mal, <strong>in</strong>nerhalb der Methode<br />

getAutoAirConditioner(), benötigt wird, kann die Klasse lokal def<strong>in</strong>iert<br />

werden.<br />

/** The general AirConditioner class */<br />

class AirConditioner {<br />

...<br />

public float getTemperatureM<strong>in</strong>() { };<br />

public float getTemperatureMax() { };<br />

}<br />

/** The Automobile class which has an <strong>in</strong>ner class which is an AC */<br />

class Automobile {<br />

private Eng<strong>in</strong>e eng<strong>in</strong>e;<br />

private GearBox gearBox;<br />

...<br />

public AirConditioner getAirConditioner () {<br />

class AutoAirConditioner extends AirConditioner {<br />

private float default = ...;


<strong>Informatik</strong> B SS 03 131<br />

}<br />

}<br />

private float factor = ...;<br />

...<br />

public float getTargetTemperature () {<br />

float temperature = default - factor * eng<strong>in</strong>e.getSpeed();<br />

...<br />

}<br />

}<br />

return new AutoAirConditioner();<br />

10.3.2 Beispiel: ‘Enumerator’ als lokale Klasse<br />

public class MyListLC {<br />

public static f<strong>in</strong>al <strong>in</strong>t MAX = 100;<br />

protected Object[] list = new Object[MAX];<br />

protected <strong>in</strong>t numOfEls = 0;<br />

public boolean add(Object o) {<br />

if (numOfEls >= MAX)<br />

return false;<br />

else {<br />

list[numOfEls++] = o;<br />

return true;<br />

}<br />

}<br />

public <strong>in</strong>t size() {<br />

return numOfEls;<br />

}<br />

public java.util.Enumeration enumerate() {<br />

class Enumerator implements java.util.Enumeration {<br />

protected <strong>in</strong>t current = 0;<br />

public boolean hasMoreElements() {<br />

return current < numOfEls;<br />

}<br />

}<br />

}<br />

public Object nextElement() {<br />

if (! hasMoreElements())<br />

throw new java.util.NoSuchElementException();<br />

return list[current++];<br />

}<br />

}<br />

return new Enumerator();


132 <strong>Informatik</strong> B SS 03<br />

10.3.3 Eigenschaften Lokaler Klassen<br />

Nicht Komponente e<strong>in</strong>er Klasse, sondern <strong>in</strong>nerhalb e<strong>in</strong>es Blocks def<strong>in</strong>iert.<br />

Typischerweise <strong>in</strong>nerhalb e<strong>in</strong>er Methode, auch <strong>in</strong>nerhalb von<br />

Initialisierungsblöcken.<br />

Analogie: Lokale Variable – lokale Klasse; Instanz-Feld – Member-Klasse<br />

Geltungsbereich: Innerhalb des Blocks<br />

<strong>Java</strong> ist e<strong>in</strong>e lexically scoped Sprache:<br />

Geltungsbereich von Variablen ist durch ihre Position im Code def<strong>in</strong>iert:<br />

<strong>in</strong>nerhalb der geschweiften Klammern, <strong>in</strong> die sie e<strong>in</strong>geschlossen s<strong>in</strong>d.<br />

Wenn e<strong>in</strong>e Member-Klasse nur <strong>in</strong>nerhalb e<strong>in</strong>er e<strong>in</strong>zigen Methode der<br />

umschliessenden Klasse genutzt wird, kann sie ebenso gut als lokale Klasse<br />

def<strong>in</strong>iert werden.<br />

Name muss verschieden vom Namen der umschliessenden Klasse se<strong>in</strong>.<br />

Interfaces können nicht lokal deklariert werden.<br />

Wie Member-Klassen: Zugriff auf alle Komponenten der umschliessenden<br />

Klasse.<br />

Zusätzlich auf alle im Block sichtbaren f<strong>in</strong>al Parameter und Variablen.<br />

Ke<strong>in</strong>e Sichtbarkeits-Modifikatoren erlaubt.<br />

Ke<strong>in</strong>e statischen Felder erlaubt (Ausnahme: static und f<strong>in</strong>al deklarierte<br />

Konstanten. (wie Member-Klassen)<br />

Zugriff auf sichtbare Variablen und Parameter nur, wenn diese f<strong>in</strong>al deklariert<br />

s<strong>in</strong>d, weil die Lebensdauer e<strong>in</strong>er Instanz e<strong>in</strong>er lokalen Klasse länger se<strong>in</strong> kann<br />

als die Ausführung der Methode, <strong>in</strong> der sie def<strong>in</strong>iert ist. D.h., die lokale Klasse<br />

benötigt e<strong>in</strong>e private Kopie aller lokalen Variablen, die sie verwendet<br />

(automatisch vom Compiler erzeugt). E<strong>in</strong>zige Möglichkeit, Konsistenz zu<br />

garantieren (lokale Variablen und deren Kopie bleiben identisch): f<strong>in</strong>al.<br />

public class A {<br />

<strong>in</strong>t f() {<br />

<strong>in</strong>t i = 5;<br />

class B {<br />

<strong>in</strong>t j = i;<br />

}<br />

i = i - 1;<br />

return new B().j;<br />

}<br />

}<br />

public static void ma<strong>in</strong> (Str<strong>in</strong>g[] args) {<br />

A a = new A();<br />

<strong>in</strong>t value1 = a.f();<br />

<strong>in</strong>t value2 = a.f(); // should be same value !<br />

}


<strong>Informatik</strong> B SS 03 133<br />

Erweiterung der <strong>Java</strong>-Syntax: f<strong>in</strong>al-Modifikator darf nicht nur für lokale<br />

Variablen, sondern auch für Parameter von Methoden und Exception Parameter<br />

im catch-Statement angegeben werden.<br />

Wie Member-Klassen haben lokale Klassen Zugriff auf die Instanz der<br />

umschliessenden Klasse, falls sie nicht <strong>in</strong> e<strong>in</strong>er Klassenmethode vere<strong>in</strong>bart<br />

werden (qualifiziertes this um auf Komponenten der umschliessenden Klasse<br />

zuzugreifen).<br />

10.3.4 Geltungsbereich Lokaler Klassen<br />

class A { protected char a = ’a’; }<br />

class B { protected char b = ’b’; }<br />

public class C extends A {<br />

private char c = ’c’; // visible to local class<br />

public static char d = ’d’;<br />

public void createLocalObject(f<strong>in</strong>al char e) {<br />

f<strong>in</strong>al char f = ’f’;<br />

<strong>in</strong>t i = 0; // not f<strong>in</strong>al, not usable by local class<br />

class Local extends B {<br />

char g = ’g’;<br />

public void pr<strong>in</strong>tVars(){<br />

// All of these fields and variable are accessible<br />

System.out.pr<strong>in</strong>tln(g); // this.g, field of Local<br />

System.out.pr<strong>in</strong>tln(f); // f<strong>in</strong>al local variable<br />

System.out.pr<strong>in</strong>tln(e); // f<strong>in</strong>al local parameter<br />

System.out.pr<strong>in</strong>tln(d); // C.this.d, field of conta<strong>in</strong><strong>in</strong>g class<br />

System.out.pr<strong>in</strong>tln(c); // C.this.c<br />

System.out.pr<strong>in</strong>tln(b); // <strong>in</strong>herited by Local<br />

System.out.pr<strong>in</strong>tln(a); // <strong>in</strong>herited by conta<strong>in</strong><strong>in</strong>g class<br />

}<br />

}<br />

Local l = new Local(); // Create <strong>in</strong>stance of Local<br />

l.pr<strong>in</strong>tVars();<br />

// call its method<br />

}<br />

}<br />

public class Weird {<br />

// A static member <strong>in</strong>terface used below<br />

public static <strong>in</strong>terface IntHolder { public <strong>in</strong>t getValue(); }<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args) {<br />

IntHolder[] holders = new IntHolder[10]; // An array to hold 10 objs<br />

for (<strong>in</strong>t i = 0; i < 10; i++) {<br />

// Loop to fill array<br />

f<strong>in</strong>al <strong>in</strong>t fi = i; // f<strong>in</strong>al local var, a new one for each iteration<br />

class MyIntHolder implements IntHolder { // local class<br />

public <strong>in</strong>t getValue() { return fi; }


134 <strong>Informatik</strong> B SS 03<br />

}<br />

}<br />

holders[i] = new MyIntHolder();<br />

// Instantiate local class<br />

// The local class is now out of scope, so we can’t use its name.<br />

// But we’ve got 10 valid <strong>in</strong>stances of that class <strong>in</strong> our array.<br />

// The local variable fi is not <strong>in</strong> our scope here, but each of the<br />

// 10 objects still has access to its local copy for use <strong>in</strong><br />

// the getValue() method.<br />

// So call getValue() for each object and pr<strong>in</strong>t it out.<br />

// This pr<strong>in</strong>ts the digits 0 to 9.<br />

for(<strong>in</strong>t i = 0; i < 10; i++) System.out.pr<strong>in</strong>tln(holders[i].getValue());<br />

}<br />

}<br />

Die Klasse Test kann nicht kompiliert werden: Die lokale Variable value <strong>in</strong><br />

foo() ist nicht f<strong>in</strong>al deklariert.<br />

Veranschaulichung für mögliche Inkonsistenz: Die beiden <strong>in</strong> der lokalen Klasse<br />

Local erzeugten <strong>Objekt</strong>e benutzen die gleiche lokale Variable value. Nach<br />

Verlassen der Methode foo() existiert diese Variable nicht mehr. Jedes Local<br />

<strong>Objekt</strong> hat e<strong>in</strong>e lokale Kopie. Wenn der Wert e<strong>in</strong>er solchen Variable verändert<br />

werden dürfte, könnte diese Variable bei verschiedenen <strong>Objekt</strong>en verschiedene<br />

Werte annehmen!<br />

public class Test {<br />

public static <strong>in</strong>terface Value {<br />

public <strong>in</strong>t getValue ();<br />

public void setValue (<strong>in</strong>t i);<br />

}<br />

public Value[] foo () {<br />

<strong>in</strong>t value = 0;<br />

class Local implements Value {<br />

public <strong>in</strong>t getValue () {<br />

return value;<br />

}<br />

};<br />

public void setValue (<strong>in</strong>t i) {<br />

value = i;<br />

}<br />

}<br />

return new Value[] { new Local(), new Local() };<br />

public static void ma<strong>in</strong> (Str<strong>in</strong>g args[]) {


<strong>Informatik</strong> B SS 03 135<br />

Test test = new Test();<br />

Value[] v = test.foo();<br />

// two value objects<br />

}<br />

}<br />

v[0].setValue(42);<br />

v[1].setValue(24);<br />

System.out.pr<strong>in</strong>tln(v[0].getValue()); // <br />

10.4 Anonyme Klassen<br />

Namenlose lokale Klassen<br />

Komb<strong>in</strong>ation der Syntax von Klassen-Def<strong>in</strong>ition und <strong>Objekt</strong>-Erzeugung<br />

wie lokale Klassen <strong>in</strong>nerhalb e<strong>in</strong>es Ausdrucks def<strong>in</strong>iert.<br />

10.4.1 Beispiel: ‘Enumerator’ als anonyme Klasse<br />

public java.util.Enumeration enumerate() {<br />

// The anonymous class is def<strong>in</strong>ed as part of the<br />

// return statement<br />

return new java.util.Enumeration() {<br />

protected <strong>in</strong>t current = 0;<br />

public boolean hasMoreElements() {<br />

return current < numOfEls;<br />

}<br />

}<br />

public Object nextElement() {<br />

if (! hasMoreElements())<br />

throw new java.util.NoSuchElementException();<br />

return list[current++];<br />

}<br />

}; // semicolon required to f<strong>in</strong>ish return statement<br />

10.4.2 Eigenschaften von Anonymen Klassen<br />

Lokale Klasse ohne Namen.<br />

Def<strong>in</strong>ition und Instantiierung <strong>in</strong> e<strong>in</strong>em e<strong>in</strong>zigen Ausdruck (new Operator).<br />

Zwei Formen:<br />

new class-name¡ ( [ argument-list¡ ] ) ¢ class-body¡ ¡<br />

Konstruktoraufruf der Oberklasse (evtl. auch Default-Konstruktor), Erzeugung<br />

e<strong>in</strong>es <strong>Objekt</strong>s der anonymen Unterklasse.<br />

new <strong>in</strong>terface-name¡ ( ) ¢ class-body¡ ¡


136 <strong>Informatik</strong> B SS 03<br />

Default-Konstruktoraufruf für e<strong>in</strong> Interface, Erzeugung e<strong>in</strong>er anonymen<br />

Unterklasse von Object, die das Interface implementiert.<br />

Lokale Klasse ist Anweisung <strong>in</strong> e<strong>in</strong>em Block, anonyme Klasse Ausdruck als Teil<br />

e<strong>in</strong>es grösseren Ausdrucks (z.B. Methodenaufruf).<br />

Verwendung: lokale Klasse, die nur e<strong>in</strong>mal benutzt wird. (Def<strong>in</strong>ition und Nutzung<br />

genau dort, wo verwendet; weniger “clutter” im Code)<br />

Typische Anwendung: Implementation von Adapter-Klassen.<br />

Def<strong>in</strong>ition von Code, der von anderen <strong>Objekt</strong>en aufgerufen wird.<br />

Beschränkungen wie für lokale Klassen: ke<strong>in</strong>e statischen Komponenten, ausser<br />

static f<strong>in</strong>al Konstanten; nicht als public, private, protected,<br />

static deklarierbar.<br />

Da namenlos: ke<strong>in</strong>e Konstruktor-Def<strong>in</strong>ition möglich. Erbt – ausnahmsweise – die<br />

Konstruktoren der Oberklasse. Im Fall e<strong>in</strong>es Interfaces wird e<strong>in</strong><br />

Default-Konstruktor e<strong>in</strong>gefügt.<br />

Wenn eigener/anderer Konstruktor notwendig, als lokale Klasse def<strong>in</strong>ieren.<br />

Alternative: Instanz-Initialisierer (Initialisierungsblöcke für Instanzen wurden<br />

genau für anonyme Klassen e<strong>in</strong>geführt), Initialisierungsblock wird <strong>in</strong> die<br />

geerbten Konstruktoren/den Default-Konstruktor e<strong>in</strong>gefügt.<br />

10.4.3 Implementation von Lokalen und Anonymen Klassen<br />

Zusätzlich zu den Zugriffsrechten von Member-Klassen, Zugriff auf f<strong>in</strong>al<br />

deklarierte lokale Variablen im Geltungsbereich des Blocks, <strong>in</strong> dem sie def<strong>in</strong>iert<br />

s<strong>in</strong>d.<br />

Compiler gibt der <strong>in</strong>neren Klasse private Instanzfelder, um Kopien der lokalen<br />

Variablen zu halten.<br />

Compiler fügt versteckte Parameter für jeden Konstruktor e<strong>in</strong>er lokalen Klasse<br />

e<strong>in</strong>, um diese private Felder zu <strong>in</strong>italisieren.<br />

Lokale Klasse hat nicht wirklich Zugriff auf die lokalen Variablen, sondern auf<br />

e<strong>in</strong>e private Kopie dieser Variablen. f<strong>in</strong>al garantiert Konsistenz!<br />

“Hoch”-Compilation von anonymen Klassen: Vergabe von Nummern, z.B.<br />

MyListAC$1.class.<br />

10.4.4 Adapter-Klassen als Anonyme Klassen<br />

File f = new File("/src");<br />

// The directory to list<br />

// Now call the list() method with a s<strong>in</strong>gle FilenameFilter argument<br />

// Def<strong>in</strong>e and <strong>in</strong>stantiate an anonymous implementation of FilenameFilter<br />

// as part of the method <strong>in</strong>vocation expression.<br />

Str<strong>in</strong>g[] filelist = f.list(new FilenameFilter() {<br />

public boolean accept(File f, Str<strong>in</strong>g s) {return s.endsWith(".java"); }


<strong>Informatik</strong> B SS 03 137<br />

}); // Don’t forget the parenthesis and semicolon that end the method call!<br />

Methode list() aus java.io.File hat als Argument e<strong>in</strong> <strong>Objekt</strong> vom Typ des<br />

Interfaces FilenameFilter, das die Date<strong>in</strong>amen des zu listenden<br />

Verzeichnisses filtert.<br />

Anonyme Klasse implementiert Interface FilenameFilter aus java.io:<br />

Adaptation der accept()-Methode an konkrete Anforderung!.<br />

Ke<strong>in</strong> extends oder implements kann für anonyme Klassen spezifiziert<br />

werden!<br />

siehe Abschnitt “Adapter-Patterns”<br />

10.4.5 Anwendung und Konventionen<br />

Anonyme Klasse statt lokaler Klasse, wenn<br />

Klasse kle<strong>in</strong>en Körper hat<br />

nur e<strong>in</strong>e Instanz der Klasse benötigt wird<br />

die Klasse direkt nach ihrer Def<strong>in</strong>ition benutzt wird<br />

Name für die Klasse Code nicht leichter verständlich macht.<br />

Layout-Empfehlungen von Sun:<br />

Öffnende geschweifte Klammer <strong>in</strong> selber Zeile wie new, und new <strong>in</strong> selber Zeile<br />

wie der Ausdruck, zu dem die anonyme Klasse gehört.<br />

E<strong>in</strong>rücken des Körpers relativ zu der Zeile mit new.<br />

Schliessende geschweifte Klammer <strong>in</strong> selber Zeile wie Ende des<br />

umschliessenden Ausdrucks. (z.B. Semikolon als Abschluss e<strong>in</strong>er<br />

return-Anweisung)<br />

10.5 Zusammenfassung<br />

Bisher: “top-level” Klassen (direkte Mitglieder e<strong>in</strong>es Pakets).<br />

Seit <strong>Java</strong> 1.1: Innere Klassen: def<strong>in</strong>iert <strong>in</strong>nerhalb e<strong>in</strong>er anderen Klasse<br />

(Komponente e<strong>in</strong>er Klasse, ähnlich Felder und Methoden)<br />

Vier Arten von <strong>in</strong>neren Klassen:<br />

– Static Member Classes (Nested Top-Level Classes):<br />

“class class” (vgl. Klassen-Feld, Klassen-Methode)<br />

Verhält sich ähnlich wie top-level Klasse, hat Zugriff auf die statischen<br />

Komponenten der umschliessenden Klasse.<br />

Interfaces dürfen nur als static member classes, nicht als non-static<br />

def<strong>in</strong>iert werden.


138 <strong>Informatik</strong> B SS 03<br />

– Member Classes (“echte” <strong>in</strong>nere Klasse):<br />

Analog zu Instanz-Feldern und Methoden, Zugriff auf alle Felder der<br />

umschliessenden Klasse.<br />

Spezielle Syntax zum Zugriff auf “umschliessende Instanz”.<br />

– Local Classes:<br />

def<strong>in</strong>iert <strong>in</strong>nerhalb e<strong>in</strong>es Blocks von <strong>Java</strong>-Code (<strong>in</strong>nerhalb e<strong>in</strong>er Methode),<br />

ähnlich zu lokalen Variablen.<br />

Sichtbarkeit: nur <strong>in</strong>nerhalb des Blocks. Zugriffsrechte wie Member-Klassen,<br />

zusätzlich Zugriff auf alle f<strong>in</strong>al lokalen Parameter und Variablen, die im<br />

Block sichtbar s<strong>in</strong>d.<br />

– Anonymous Classes:<br />

Namenlose lokale Klasse; Komb<strong>in</strong>ation der Syntax von Klassen-Def<strong>in</strong>ition<br />

und <strong>Objekt</strong>-Instantiierung; def<strong>in</strong>iert <strong>in</strong>nerhalb e<strong>in</strong>es Ausdrucks.<br />

10.6 Beispiel-Code ‘Enumeration’<br />

// A class that implements a stack as a l<strong>in</strong>ked list<br />

public class L<strong>in</strong>kedStack {<br />

// This static member <strong>in</strong>terface def<strong>in</strong>es how objects are l<strong>in</strong>ked<br />

public static <strong>in</strong>terface L<strong>in</strong>kable {<br />

public L<strong>in</strong>kable getNext();<br />

public void setNext(L<strong>in</strong>kable node);<br />

}<br />

// The head of the list is a L<strong>in</strong>kable Object<br />

L<strong>in</strong>kable head;<br />

}<br />

// Methods<br />

public void push(L<strong>in</strong>kable node){<br />

node.setNext(head);<br />

head = node;<br />

}<br />

public Object pop(){<br />

if (head != null)<br />

{ L<strong>in</strong>kable oldtop = head;<br />

head = head.getNext();<br />

return oldtop;<br />

}<br />

else return "error";<br />

}<br />

// This method returns an Enumeration object for this L<strong>in</strong>kedStack<br />

public java.util.Enumeration enumerate() { return new Enumerator(); }<br />

// short for this.new Enumerator();<br />

// Here is the implementation of the Enumeration <strong>in</strong>terface


<strong>Informatik</strong> B SS 03 139<br />

}<br />

// def<strong>in</strong>ed as a member class<br />

// alternative realization as local class or anonymous class<br />

// see <strong>Java</strong> <strong>in</strong> a Nutshell<br />

protected class Enumerator implements java.util.Enumeration {<br />

L<strong>in</strong>kable current;<br />

// The constructor uses the private head field of the conta<strong>in</strong><strong>in</strong>g class<br />

public Enumerator() { current = head; }<br />

// explicit: this.current = L<strong>in</strong>kedStack.this.head;<br />

public boolean hasMoreElements() {return (current != null); }<br />

public Object nextElement() {<br />

if (current == null) throw new java.util.NoSuchElementException();<br />

Object value = current;<br />

current = current.getNext();<br />

return value;<br />

}<br />

}<br />

// This class implements the static member <strong>in</strong>terface<br />

class L<strong>in</strong>kableInteger implements L<strong>in</strong>kedStack.L<strong>in</strong>kable {<br />

// Here’s the node’s data and constructor<br />

<strong>in</strong>t i;<br />

public L<strong>in</strong>kableInteger(<strong>in</strong>t i) { this.i = i; }<br />

}<br />

// Here are the data and methods required to implement the <strong>in</strong>terface<br />

L<strong>in</strong>kedStack.L<strong>in</strong>kable next;<br />

public L<strong>in</strong>kedStack.L<strong>in</strong>kable getNext() { return next; }<br />

public void setNext(L<strong>in</strong>kedStack.L<strong>in</strong>kable node) { next = node; }<br />

public class IntStack {<br />

public static void ma<strong>in</strong> (Str<strong>in</strong>g[] args) {<br />

L<strong>in</strong>kedStack <strong>in</strong>tstack = new L<strong>in</strong>kedStack();<br />

L<strong>in</strong>kableInteger li = new L<strong>in</strong>kableInteger(1);<br />

System.out.pr<strong>in</strong>tln("Push! New Integer-Element: " + li.i);<br />

<strong>in</strong>tstack.push(li);<br />

System.out.pr<strong>in</strong>tln ("Top of Stack is " +<br />

((L<strong>in</strong>kableInteger)(<strong>in</strong>tstack.head)).i);<br />

L<strong>in</strong>kableInteger li2 = new L<strong>in</strong>kableInteger(2);<br />

System.out.pr<strong>in</strong>tln("Push! New Integer-Element: " + li2.i);<br />

<strong>in</strong>tstack.push(li2);<br />

System.out.pr<strong>in</strong>tln ("Top of Stack is " +<br />

((L<strong>in</strong>kableInteger)(<strong>in</strong>tstack.head)).i);<br />

<strong>in</strong>tstack.pop();<br />

System.out.pr<strong>in</strong>tln("pop!");<br />

System.out.pr<strong>in</strong>tln ("Top of Stack is " +<br />

((L<strong>in</strong>kableInteger)(<strong>in</strong>tstack.head)).i);


140 <strong>Informatik</strong> B SS 03<br />

<strong>in</strong>tstack.push(li2);<br />

}<br />

}<br />

// Enumerator<br />

java.util.Enumeration <strong>in</strong>tstackenum = <strong>in</strong>tstack.enumerate();<br />

while (<strong>in</strong>tstackenum.hasMoreElements())<br />

{System.out.pr<strong>in</strong>tln(<br />

((L<strong>in</strong>kableInteger)(<strong>in</strong>tstackenum.nextElement())).i);}<br />

10.7 Adapter-Patterns und <strong>Java</strong> Adapter-Klassen<br />

Adapter: Konvertierung e<strong>in</strong>es API e<strong>in</strong>er Klasse <strong>in</strong> das API e<strong>in</strong>er anderen.<br />

Anwendung: Zusammenarbeit unverbundener Klassen <strong>in</strong> e<strong>in</strong>em Programm.<br />

Konzept: Schreibe Klasse mit dem gewünschten Interface und lasse diese<br />

Klasse mit der Klasse kommunizieren, die e<strong>in</strong> anderes Interface hat.<br />

Zwei Möglichkeiten zur Realisierung:<br />

– Klassen-Adapter: Ableitung e<strong>in</strong>er neuen Klasse von der nicht-angepassten<br />

Klasse und H<strong>in</strong>zufügen von Methoden so, dass die neue Klasse dem<br />

gewünschten Interface genügt.<br />

class A {<br />

// a class which "nearly" meets specification B<br />

}<br />

<strong>in</strong>terface B {<br />

// ...<br />

}<br />

class AdA extends A implements B {<br />

// implement methods of B us<strong>in</strong>g A<br />

}<br />

– <strong>Objekt</strong>-Adapter: E<strong>in</strong>betten e<strong>in</strong>es <strong>Objekt</strong>s der ursprünglichen Klasse <strong>in</strong> die<br />

neue Klasse und Def<strong>in</strong>ition von Methoden, um Aufrufe <strong>in</strong> der neuen Klasse<br />

entsprechend zu übersetzen.<br />

class AdA implements B {<br />

A a;<br />

// ...<br />

}<br />

siehe Cooper, <strong>Java</strong> Design Patterns, Kap. 9.<br />

In <strong>Java</strong> wird der Begriff “Adapter” für Klassen im GUI-Bereich verwendet.


<strong>Informatik</strong> B SS 03 141<br />

Hier s<strong>in</strong>d Adapter-Klassen Klassen, die nur Methoden mit leerem Körper zur<br />

Verfügung stellen.<br />

Anwendung: Erzeugung e<strong>in</strong>es entsprechenden <strong>Objekt</strong>s und Überschreiben der<br />

benötigten Methoden.<br />

Beispiel: ‘W<strong>in</strong>dowAdapter’<br />

// illustrates us<strong>in</strong>g the W<strong>in</strong>dowAdapter class<br />

// make an extended w<strong>in</strong>dow adapter.<br />

// This class closes the frame<br />

// when the clos<strong>in</strong>g event is received<br />

class MyW<strong>in</strong>dowAdapter extends W<strong>in</strong>dowAdapter {<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e) {<br />

System.exit(0);<br />

} }<br />

public class Closer {<br />

public Closer() {<br />

MyW<strong>in</strong>dowAdapter w<strong>in</strong> = new MyW<strong>in</strong>dowAdapter();<br />

Frame f = new Frame();<br />

f.addW<strong>in</strong>dowListener(w<strong>in</strong>);<br />

f.setSize(new Dimension(100,100));<br />

f.setVisible(true);<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args) {<br />

new Closer();<br />

}<br />

}<br />

Kompaktere Realisierung mit anonymer Klasse:<br />

// create w<strong>in</strong>dow listener for w<strong>in</strong>dow close<br />

addW<strong>in</strong>dowListener(new W<strong>in</strong>dowAdapter()<br />

{ public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{System.exit(0);}<br />

});<br />

10.8 Innere Klassen und Lexical Closures<br />

Lexical Closure: Funktion, die ihren Kontext er<strong>in</strong>nert und freie Variablen darüber<br />

b<strong>in</strong>det.<br />

Konzept aus der funktionalen <strong>Programmierung</strong>. Siehe, z.B. Steele, Common<br />

Lisp, Kap. 7.1.<br />

Brent W. Benson (1999), <strong>Java</strong> Reflections: Inner Classes – Closures for the<br />

masses, ACM SIG-Plan Notices, 34(2), 32-35.


142 <strong>Informatik</strong> B SS 03<br />

[1]> (defun adder (n) (function (lambda (m) (+ m n))))<br />

ADDER<br />

[2]> (setq add3 (adder 3))<br />

#<br />

[3]> (funcall add3 4)<br />

7<br />

Für die <strong>in</strong>nere, namenlose Funktion (lambda-Ausdruck) ist n e<strong>in</strong>e freie Variable<br />

Ergebnis von (adder 3) ist e<strong>in</strong>e Funktion, die die Zahl 3 zu ihrem Argument<br />

(m) addiert.<br />

10.8.1 Code as Data<br />

Behandlung von Code als Daten ist mächtige Eigenschaft e<strong>in</strong>er<br />

Programmiersprache.<br />

Common Lisp, Scheme, C: Funktionen als “First Class”-Objects, die <strong>in</strong> Variablen<br />

gespeichert, manipuliert, und auf Daten angewendet werden können.<br />

In <strong>Java</strong>: <strong>Objekt</strong>e als “First Class” Objects (Funktionen/Methoden s<strong>in</strong>d<br />

untergeordnet).<br />

Fähigkeit, dass e<strong>in</strong> Stück Code den Kontext er<strong>in</strong>nert, <strong>in</strong> dem es erzeugt wurde<br />

(Closure).<br />

10.8.2 Adder-Beispiel<br />

<strong>in</strong>terface Adder{<br />

<strong>in</strong>t add(<strong>in</strong>t m);<br />

}<br />

public Adder makeAdder(f<strong>in</strong>al <strong>in</strong>t n) {<br />

return new Adder() {<br />

public <strong>in</strong>t add (<strong>in</strong>t m) {return m + n;}<br />

};<br />

}<br />

In makeAdder wird das Adder-Interface für e<strong>in</strong>e spezielle Anforderung<br />

adaptiert!<br />

Die anonyme <strong>in</strong>nere Klasse realisiert das Konzept der lexical closure.


¡<br />

¡<br />

¡<br />

<strong>Informatik</strong> B SS 03 143<br />

11 Abstrakte Datentypen und Collections<br />

11.1 Abstrakte Datentypen<br />

11.1.1 Grundlagen<br />

Abstrakter Datentyp (ADT): Sammlung von <strong>Objekt</strong>en e<strong>in</strong>es Typs, die <strong>in</strong> e<strong>in</strong>er<br />

festen Struktur organisiert s<strong>in</strong>d. Konstruktoren repräsentieren die Struktur des<br />

ADT (kle<strong>in</strong>stes Element und Aufbau e<strong>in</strong>er Struktur durch E<strong>in</strong>fügen), empty-Test,<br />

Selektoren, weitere Operatoren. Alle Operatoren werden abstrakt def<strong>in</strong>iert. Ihre<br />

Funktionalität wird über Axiome beschrieben.<br />

Beispiele: Liste, Stack, Menge, Bag, B<strong>in</strong>ärbaum.<br />

ADT s<strong>in</strong>d abstrakt, weil nur die Struktur, nicht aber die konkrete Realisierung<br />

festgelegt wird. Die Realisierung (Implementation) bed<strong>in</strong>gt die Performanz:<br />

Speicheraufwand und Aufwand für Suchen, E<strong>in</strong>fügen, Löschen von <strong>Objekt</strong>en.<br />

Abstrakte Datentypen und konkrete Implementationen <strong>in</strong> <strong>Java</strong>: Collections<br />

(java.util).<br />

Funktionalität <strong>in</strong> <strong>Java</strong>: Interface Signatur der Operationen. Axiome als<br />

natürlichsprachiger Kommentar. Realisierung: Implementation des Interface.<br />

11.1.2 Funktionalitäten von Collections<br />

In den folgenden Abschnitten: Beispielhafte Implementation von Collections.<br />

Ziel: Vertiefung der bisher vermittelten Konzepte der objekt-orientieren<br />

<strong>Programmierung</strong> mit <strong>Java</strong> an e<strong>in</strong>em etwas komplexeren Beispiel (von A.T.<br />

Sche<strong>in</strong>er, Skript zur <strong>Java</strong>-Vorlesung)<br />

Wir betrachten e<strong>in</strong>en ADT als Sammlung (Collection) von <strong>Objekt</strong>en.<br />

Funktionalitäten: E<strong>in</strong>fügen (add()), Löschen (sub()), F<strong>in</strong>den (f<strong>in</strong>d()) von<br />

<strong>Objekt</strong>en, Anzahl von <strong>Objekt</strong>en (count()).<br />

Weitere Funktionalitäten: Aufzählen (Enumeration), ...<br />

Charakterisierung der Basis-Funktionalitäten:<br />

– Man kann nur <strong>Objekt</strong>e mit f<strong>in</strong>d() entdecken, die irgendwann vorher mit<br />

add() e<strong>in</strong>gefügt wurden.<br />

– Man kann nur e<strong>in</strong> <strong>Objekt</strong> mir sub() löschen, das irgendwann vorher mit<br />

add() e<strong>in</strong>gefügt wurde.<br />

– count() wird von add() und sub() bee<strong>in</strong>flusst.<br />

Charakterisierung der abstrakten Struktur von <strong>Objekt</strong>en:<br />

– Kann das gleiche <strong>Objekt</strong> mehr als e<strong>in</strong>mal h<strong>in</strong>zugefügt werden<br />

ne<strong>in</strong> Set, ja Bag


¡<br />

¡<br />

¡<br />

¡<br />

¡<br />

¡<br />

144 <strong>Informatik</strong> B SS 03<br />

– S<strong>in</strong>d die <strong>Objekt</strong>e geordnet<br />

ne<strong>in</strong> Set, ja List<br />

– Kann nur von e<strong>in</strong>er Seite h<strong>in</strong>zugefügt und gelöscht werden<br />

ne<strong>in</strong> Liste, ja Queue oder Stack<br />

– S<strong>in</strong>d die <strong>Objekt</strong>e über e<strong>in</strong>en Index zugreifbar<br />

ne<strong>in</strong> Set oder List, ja Array<br />

Weitere Aspekte:<br />

– Def<strong>in</strong>ition von Gleichheit: Identität oder Äquivalenz<br />

– Status von null: als <strong>Objekt</strong> (das e<strong>in</strong>gefügt, gelöscht, gefunden werden<br />

kann) oder nicht.<br />

11.2 Implementation von Collections<br />

Im Pr<strong>in</strong>zip kann die Funktionalität e<strong>in</strong>es Arrays über e<strong>in</strong>e Listen-Implementation<br />

realisiert werden. Dies ist aber wohl der Performanz nicht zuträglich.<br />

Frage der Implementation: welche Funktionalitäten werden wie am besten<br />

realisiert<br />

Im Folgenden: Set und Bag als Array, als doppelt verkettete Liste, als<br />

B<strong>in</strong>ärbaum.<br />

// adt/MyCollection.java // A.T. Schre<strong>in</strong>er<br />

package adt;<br />

/** adt: rudimentary collection; an implementation<br />

may use equality or identity<br />

may or may not accept null<br />

may return orig<strong>in</strong>al objects or objects from the conta<strong>in</strong>er<br />

may throw RuntimeException for unsuitable objects, overflow, etc.<br />

<br />

*/<br />

public <strong>in</strong>terface MyCollection {<br />

/** <strong>in</strong>sert an object <strong>in</strong>to the collection.<br />

*/<br />

Object add (Object x);<br />

/** locate an object <strong>in</strong> the collection.<br />

*/<br />

Object f<strong>in</strong>d (Object x);<br />

/** remove an object from the collection.<br />

*/<br />

Object sub (Object x);<br />

/** @return number of dist<strong>in</strong>ct objects <strong>in</strong> the collection;<br />

zero, if the collection is empty.<br />

*/<br />

<strong>in</strong>t count ();<br />

}


<strong>Informatik</strong> B SS 03 145<br />

adt<br />

adt.array<br />

MyCollection<br />

><br />

MyCollection Bag Set EquivalenceSet<br />

add(x: Object): Object<br />

f<strong>in</strong>d(x: Object): Object<br />

sub(x: Object): Object<br />

count(): Integer<br />

Abbildung 42: Struktur von adt.array<br />

11.3 Implementation mit Array<br />

11.3.1 MyCollection/Array<br />

Grundlegende Funktionalität:<br />

Array fester Länge, erlaubt ‘null’ als <strong>Objekt</strong>, vergleicht auf Identität, trägt gleiche Werte<br />

mehrfach e<strong>in</strong>, löscht immer nur e<strong>in</strong> <strong>Objekt</strong>.<br />

// adt/array/MyCollection.java // A.T. Schre<strong>in</strong>er<br />

package adt.array;<br />

/** fixed size, array based conta<strong>in</strong>er with multiple <strong>in</strong>sertion.<br />

*/<br />

public class MyCollection implements adt.MyCollection {<br />

protected Object[] element; protected <strong>in</strong>t count;<br />

/** def<strong>in</strong>e capacity of the collection.<br />

@throws RuntimeException if capacity < 0.<br />

*/<br />

public MyCollection (<strong>in</strong>t capacity) {<br />

element = new Object[capacity];<br />

}<br />

/** default capacity 128; permit package to use newInstance().<br />

*/<br />

public MyCollection () { this(128); }<br />

/** <strong>in</strong>sert an object <strong>in</strong>to the collection, do not check if already present.<br />

@param x object to be added, may be null.<br />

@return x.<br />

@throws ArrayIndexOutOfBoundsException for overflow.<br />

*/<br />

public Object add (Object x) {<br />

element[count] = x; ++ count; return x;<br />

}<br />

/** locate an object <strong>in</strong> the collection.<br />

@param x object to be found.<br />

@return object from collection, as determ<strong>in</strong>ed by locate().


146 <strong>Informatik</strong> B SS 03<br />

@throws ArrayIndexOutOfBoundsException if x cannot be found.<br />

*/<br />

public Object f<strong>in</strong>d (Object x) {<br />

return element[locate(x)];<br />

}<br />

/** remove an object from the collection.<br />

@param x object to be removed.<br />

@return object from collection, as determ<strong>in</strong>ed by locate().<br />

@throws ArrayIndexOutOfBoundsException if x cannot be found.<br />

*/<br />

public Object sub (Object x) {<br />

<strong>in</strong>t n = locate(x); x = element[n];<br />

if (count > n+1) System.arraycopy(element, n+1, element, n, count-(n+1));<br />

-- count;<br />

return x;<br />

}<br />

/** @return number of dist<strong>in</strong>ct objects <strong>in</strong> the collection;<br />

zero, if the collection is empty.<br />

*/<br />

public <strong>in</strong>t count () { return count; }<br />

/** locate an object <strong>in</strong> the collection.<br />

@param x object to be found.<br />

@return (last) position if present, based on identity.<br />

@throws ArrayIndexOutOfBoundsException if x cannot be found.<br />

*/<br />

protected <strong>in</strong>t locate (Object x) throws ArrayIndexOutOfBoundsException {<br />

<strong>in</strong>t n = count;<br />

while (element[--n] != x)<br />

;<br />

return n;<br />

}<br />

/** permit symbolic dump.<br />

*/<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g () {<br />

Str<strong>in</strong>gBuffer buf = new Str<strong>in</strong>gBuffer(super.toStr<strong>in</strong>g());<br />

buf.append(" count ").append(count);<br />

for (<strong>in</strong>t n = 0; n < count; ++ n)<br />

buf.append(’\n’).append(element[n]);<br />

return buf.toStr<strong>in</strong>g();<br />

}<br />

/** test: . display, -word remove, else add.<br />

*/<br />

public static void ma<strong>in</strong> (Str<strong>in</strong>g[] args) {<br />

if (args != null) new MyCollection().test(args);<br />

}<br />

protected void test (Str<strong>in</strong>g[] args) {<br />

for (<strong>in</strong>t a = 0; a < args.length; ++ a)<br />

try {


<strong>Informatik</strong> B SS 03 147<br />

}<br />

}<br />

if (args[a].equals(".")) System.out.pr<strong>in</strong>tln(this);<br />

else if (args[a].equals("null")) add(null);<br />

else if (!args[a].startsWith("-")) add(args[a]);<br />

else if (args[a].equals("-null")) sub(null);<br />

else sub(args[a].substr<strong>in</strong>g(1));<br />

} catch (RuntimeException e) { System.err.pr<strong>in</strong>tln(e); }<br />

11.3.2 Erläuterungen zu ‘MyCollection’<br />

Länge des Arrays kann über Konstruktor gesetzt werden.<br />

Beim Löschen e<strong>in</strong>es Elements, das nicht an der letzten Indexposition (count)<br />

steht, muss der Sub-Array rechts von diesem Index um e<strong>in</strong>s nach l<strong>in</strong>ks<br />

verschoben werden. Dies wird durch die Methode System.arraycopy()<br />

realisiert.<br />

sub() und f<strong>in</strong>d() müssen e<strong>in</strong> <strong>Objekt</strong> lokalisieren. Die Methode locate() ist<br />

bisher so realisiert, dass Gleichheit Identität me<strong>in</strong>t (==). Alternativ: equals()<br />

(ist z.B. für Str<strong>in</strong>g-<strong>Objekt</strong>e def<strong>in</strong>iert).<br />

locate() ist protected deklariert: verstecktes Implementationsdetail.<br />

Wenn null als <strong>Objekt</strong> wie andere Werte e<strong>in</strong>gefügt werden kann, müssen<br />

erfolglose Versuche, e<strong>in</strong> <strong>Objekt</strong> zu lokalisieren (<strong>in</strong> sub(), f<strong>in</strong>d()) als<br />

Exception behandelt werden. ArrayIndexOutOfBoundsException ist von<br />

RuntimeException abgeleitet und muss deshalb nicht im Kopf der Methode<br />

angegeben werden. Durch die Deklaration wird explizit darauf h<strong>in</strong>gewiesen,<br />

dass diese Exception auftritt, wenn <strong>Objekt</strong> x nicht gefunden wird. (Hier wird die<br />

ArrayIndexOutOfBoundsException etwas “missbraucht”. Besser könnte<br />

e<strong>in</strong>e NoSuchElementException() verwendet werden.)<br />

Um die Collection zu testen, können wir den Inhalt des Arrays mit toStr<strong>in</strong>g()<br />

sichtbar machen. Die Oberklasse von MyConta<strong>in</strong>er ist Object und für<br />

Object existiert e<strong>in</strong>e Methode toStr<strong>in</strong>g(), die wir benutzen können. Die<br />

return-Anweisung am Ende von toStr<strong>in</strong>g() liefert den Inhalt des<br />

Str<strong>in</strong>gBuffers als Str<strong>in</strong>g zurück. (System.out.pr<strong>in</strong>tln() arbeitet auf mit<br />

toStr<strong>in</strong>g() umgewandelten <strong>Objekt</strong>en.)<br />

Die Methode test() reagiert auf folgende Kommandozeilen-Argumente: .<br />

zeigt Inhalt des Arrays an; -wort löscht, sonst wird h<strong>in</strong>zugefügt.<br />

null wird als Null-Referenz und nicht als Wort<br />

"null" aufgefasst.<br />

11.3.3 Test-Protokoll für ‘MyCollection’<br />

$ javac -classpath ../.. MyCollection.java<br />

$ java -classpath ../.. adt.array.MyCollection null ’ ’ axel null . -null -axel .


¡<br />

148 <strong>Informatik</strong> B SS 03<br />

adt.array.MyCollection@65f57 count 4<br />

null<br />

axel<br />

null<br />

java.lang.ArrayIndexOutOfBoundsException: -1<br />

adt.array.MyCollection@65f57 count 3<br />

null<br />

axel<br />

11.3.4 Bag/Array<br />

Funktionalität:<br />

Array fester Länge, der bei Bedarf verlängert (aber nie verkürzt) wird; erlaubt ‘null’ als<br />

<strong>Objekt</strong>, vergleicht auf Identität, trägt gleiche Werte mehrfach e<strong>in</strong>, löscht immer nur e<strong>in</strong><br />

<strong>Objekt</strong>.<br />

Unterklasse von adt.array.MyCollection, Überschreiben von add(): Bei Bedarf,<br />

Umkopieren des Arrays <strong>in</strong> e<strong>in</strong>en Array mit mehr Indexplätzen.<br />

// adt/array/Bag.java // A.T.Schre<strong>in</strong>er<br />

package adt.array;<br />

/** array based collection with multiple <strong>in</strong>sertion.<br />

*/<br />

public class Bag extends MyCollection {<br />

/** def<strong>in</strong>e <strong>in</strong>itial capacity of the collection.<br />

@throws RuntimeException if capacity < 0.<br />

*/<br />

public Bag (<strong>in</strong>t capacity) { super(capacity); }<br />

/** default <strong>in</strong>itial capacity determ<strong>in</strong>ed by superclass.<br />

*/<br />

public Bag () { }<br />

/** <strong>in</strong>sert an object <strong>in</strong>to the collection, do not check if already present;<br />

dynamically extend element array by 1k.<br />

@param x object to be added, may be null.<br />

@return x.<br />

*/<br />

public Object add (Object x) {<br />

try {<br />

return super.add(x);<br />

} catch (ArrayIndexOutOfBoundsException e) {<br />

Object[] ne = new Object[element.length + 1024];<br />

System.arraycopy(element, 0, ne, 0, element.length);<br />

element = ne;<br />

return super.add(x);<br />

} } }


¡<br />

<strong>Informatik</strong> B SS 03 149<br />

11.3.5 Erläuterungen zu ‘Bag’<br />

Als Unterklasse hat Bag (m<strong>in</strong>destens) die gleichen Klassen- und<br />

Instanzvariablen wie se<strong>in</strong>e Oberklasse MyCollection.<br />

Die Methoden der Oberklasse können problemlos auf die <strong>Objekt</strong>e der<br />

Unterklasse angewendet werde. Die Unterklasse erbt (die für sie sichtbaren)<br />

Methoden der Oberklasse.<br />

Man kann aber Methoden überschreiben (auch mit schwächeren<br />

Sichtbarkeitsrestriktionen versehen). Über super. methodenname¡ hat man<br />

<strong>in</strong> den Methoden immer noch Zugriff auf die Methoden der Oberklasse.<br />

In add() überlässt man das E<strong>in</strong>fügen selbst der Methode der Oberklasse und<br />

stellt nur bei Bedarf e<strong>in</strong>en längeren Array zur Verfügung.<br />

Weil Konstruktoren nicht vererbt werden, müssen auch für die Unterklasse<br />

Konstruktoren def<strong>in</strong>iert werden.<br />

Konstruktoren s<strong>in</strong>d verkettet und werden von “oben” (Object) bis zur<br />

endgültigen Klasse h<strong>in</strong> ausgeführt.<br />

Als allererste Anweisung <strong>in</strong> e<strong>in</strong>em Konstruktor muss entweder e<strong>in</strong> anderer<br />

eigener Konstruktor (this()) oder e<strong>in</strong> Oberklassen-Konstruktor (super())<br />

aufgerufen werden. Ansonsten erfolgt implizit der Aufruf e<strong>in</strong>es parameterlosen<br />

Konstruktors der Oberklasse, der existieren muss.<br />

Wird ke<strong>in</strong> expliziter Konstruktor def<strong>in</strong>iert, so wird automatisch e<strong>in</strong><br />

parameterloser Konstruktor generiert.<br />

Im leeren Konstruktor Bag() wird automatisch der Aufruf des<br />

Oberklassenkonstruktors e<strong>in</strong>gefügt.<br />

11.3.6 Set/Array<br />

Funktionalität:<br />

Array fester Länge, der bei Bedarf verlängert (aber nie verkürzt) wird; erlaubt ‘null’ als<br />

<strong>Objekt</strong>, vergleicht auf Identität, trägt gleiche Werte nur e<strong>in</strong>mal e<strong>in</strong>, löscht immer nur e<strong>in</strong><br />

<strong>Objekt</strong>.<br />

Unterklasse zu adt.array.Bag, Überschreiben von add(): Ist e<strong>in</strong> identisches<br />

<strong>Objekt</strong> schon im Array, so wird es nicht noch e<strong>in</strong>mal e<strong>in</strong>gefügt.<br />

// adt/array/Set.java // A.T. Schre<strong>in</strong>er<br />

package adt.array;<br />

/** array based collection with unique identity <strong>in</strong>sertion.<br />

*/<br />

public class Set extends Bag {<br />

/** def<strong>in</strong>e <strong>in</strong>itial capacity of the collection.<br />

@throws RuntimeException if capacity < 0.<br />

*/<br />

public Set (<strong>in</strong>t capacity) { super(capacity); }


150 <strong>Informatik</strong> B SS 03<br />

}<br />

/** default <strong>in</strong>itial capacity determ<strong>in</strong>ed by superclass.<br />

*/<br />

public Set () { }<br />

/** <strong>in</strong>sert an object <strong>in</strong>to the collection, unless present.<br />

@param x object to be added, may be null.<br />

@return object from collection.<br />

@throws RuntimeException for overflow.<br />

*/<br />

public Object add (Object x) {<br />

try {<br />

return super.f<strong>in</strong>d(x);<br />

} catch (ArrayIndexOutOfBoundsException e) { }<br />

return super.add(x);<br />

}<br />

/** test: . display, -word remove, else add.<br />

*/<br />

public static void ma<strong>in</strong> (Str<strong>in</strong>g[] args) {<br />

if (args != null) new Set().test(args);<br />

}<br />

// avoid <strong>in</strong>advertent override<br />

11.3.7 Erläuterungen zu Set<br />

Es genügt, add() noch e<strong>in</strong>mal zu überschreiben.<br />

Es wird f<strong>in</strong>d() <strong>in</strong> add() verwendet:<br />

Vorsicht! Wenn <strong>in</strong> e<strong>in</strong>er weiteren Unterklasse f<strong>in</strong>d() ersetzt würde, so könnte<br />

die Funktionsweise von add() verändert werden. Denn jede Methode wird <strong>in</strong><br />

der Klasse ihres Empfängers (‘Receiver’) gesucht, unabhängig davon, von wo<br />

aus ihr Aufruf erfolgt.<br />

Mit super. startet die Suche <strong>in</strong> der Oberklasse der Klasse, <strong>in</strong> der der Aufruf mit<br />

super. steht. Dadurch kann add() nicht mehr durch e<strong>in</strong> Ersetzen von f<strong>in</strong>d<br />

bee<strong>in</strong>flusst werden!<br />

E<strong>in</strong>e Funktion wird unmittelbar nach Ausführung e<strong>in</strong>er return Anweisung<br />

verlassen. Wird also e<strong>in</strong> <strong>Objekt</strong> im Array gefunden, so wird das Resultat von<br />

super.f<strong>in</strong>d(x) zurückgeliefert. Wird das <strong>Objekt</strong> nicht gefunden, so liefert<br />

f<strong>in</strong>d() wegen der Benutzung von locate() e<strong>in</strong>e<br />

ArrayIndexOutOfBoundsException und als Rückgabe erfolgt<br />

super.add(x) (das <strong>Objekt</strong> wird also e<strong>in</strong>gefügt).<br />

Achtung: Der <strong>Java</strong>-Compiler verlangt, dass garantiert e<strong>in</strong>e Return-Anweisung<br />

erreicht wird. Würden wir return super.add(x) <strong>in</strong> den catch Block<br />

schreiben, könnte der Compiler nicht feststellen, dass e<strong>in</strong>e der beiden<br />

return-Anweisungen auf jeden Fall ausgeführt wird, und würde mit e<strong>in</strong>er<br />

Fehlermeldung reagieren. Man kann sich <strong>in</strong> solchen Fällen mit der<br />

Programmzeile<br />

return null; // never reached


¡<br />

<strong>Informatik</strong> B SS 03 151<br />

behelfen.<br />

11.3.8 Test-Protokoll für ‘Set’<br />

$ javac -classpath ../.. Set.java<br />

$ java -classpath ../.. adt.array.Set axel null ’ ’ null . -null -axel .<br />

adt.array.Set@65f55 count 3<br />

axel<br />

null<br />

java.lang.ArrayIndexOutOfBoundsException: -1<br />

adt.array.Set@65f55 count 2<br />

axel<br />

11.3.9 EquivalenceSet/Array<br />

Funktionalität:<br />

Array fester Länge, der bei Bedarf verlängert (aber nie verkürzt) wird; erlaubt ‘null’ als<br />

<strong>Objekt</strong>, vergleicht auf Äquivalenz, trägt gleiche Werte nur e<strong>in</strong>mal e<strong>in</strong>, löscht immer nur<br />

e<strong>in</strong> <strong>Objekt</strong>.<br />

Unterklasse von adt.array.Set, Überschreiben von locate(): <strong>Objekt</strong>vergleich<br />

mit equals().<br />

// adt/array/EquivalenceSet.java // A.T. Schre<strong>in</strong>er<br />

package adt.array;<br />

/** array based collection with unique <strong>in</strong>sertion based on equals().<br />

*/<br />

public class EquivalenceSet extends Set {<br />

/** def<strong>in</strong>e <strong>in</strong>itial capacity of the collection.<br />

@throws RuntimeException if capacity < 0.<br />

*/<br />

public EquivalenceSet (<strong>in</strong>t capacity) { super(capacity); }<br />

/** default <strong>in</strong>itial capacity determ<strong>in</strong>ed by superclass.<br />

*/<br />

public EquivalenceSet () { }<br />

/** locate an object <strong>in</strong> the collection.<br />

@param x object to be found, may be null.<br />

@return (last) position if present, based on equals().<br />

@throws ArrayIndexOutOfBoundsException if x cannot be found.<br />

*/<br />

protected <strong>in</strong>t locate (Object x) throws ArrayIndexOutOfBoundsException {<br />

<strong>in</strong>t n = count;<br />

while (!equals(x, element[--n])) ;<br />

return n;<br />

}


152 <strong>Informatik</strong> B SS 03<br />

}<br />

/** @returns x.equals(y), avoids NullPo<strong>in</strong>terException.<br />

*/<br />

public static boolean equals (Object x, Object y) {<br />

return x == null y == null : x.equals(y);<br />

}<br />

/** test: . display, -word remove, else add.<br />

*/<br />

public static void ma<strong>in</strong> (Str<strong>in</strong>g[] args) {<br />

if (args != null) new EquivalenceSet().test(args);<br />

}<br />

11.3.10 Erläuterungen zu ‘EquivalenceSet’<br />

Alle Klassen erben von Object. In Object ist e<strong>in</strong>e Methode equals()<br />

def<strong>in</strong>iert, deren Empfänger sich selbst mit e<strong>in</strong>em Argument vergleicht. Die<br />

Methode ist dort für Identität implementiert.<br />

Neue Klassen können die Methode überschreiben. In java.lang.Str<strong>in</strong>g wird<br />

true zurückgeliefert, wenn zwei Str<strong>in</strong>g-<strong>Objekt</strong>e aus denselben<br />

Character-Folgen bestehen. Die Methode ist symmetrisch, wennn / und ¢<br />

Str<strong>in</strong>gs s<strong>in</strong>d: x.equals(y) == y.equals(x).<br />

In EquivalenceSet werden zwei equals()-Methoden benutzt, die sich durch<br />

ihre Signatur unterscheiden:<br />

Die Methode equals() mit zwei Argumenten benötigt ke<strong>in</strong>en Empfänger, ist<br />

also e<strong>in</strong>e Klassenmethode (static).<br />

Die für Object def<strong>in</strong>ierte und von Str<strong>in</strong>g überschriebene Methode mit e<strong>in</strong>em<br />

Argument benötigt e<strong>in</strong>en von null verschiedenen Empfänger.<br />

return-Statement <strong>in</strong> equals(): Wenn / null ist, dann liefere true, wenn y<br />

ebenfalls null ist, sonst rufe x.equals(y) auf. Falls x und y Str<strong>in</strong>g s<strong>in</strong>d<br />

wird auf Äquivalenz verglichen.<br />

Bisher wurde Duplizierung von Code sorgfältig vermieden. Alle Vergleiche s<strong>in</strong>d<br />

<strong>in</strong> locate() gekapselt (sogar, als add() überschrieben wurde). D.h., es ist<br />

ausreichend, locate() <strong>in</strong> e<strong>in</strong>er Unterklasse zu überschreiben, wobei null als<br />

Spezialfall behandelt wird.<br />

11.3.11 Test-Protokoll für ‘EquivalenceSet’<br />

Set und EquivalenceSet fügen nur <strong>Objekt</strong>e e<strong>in</strong>, die nicht bereits im Array<br />

vorhanden s<strong>in</strong>d. Set arbeitet mit Identität, EquivalenceSet arbeitet mit<br />

equals().<br />

$ java adt.array.Set null ’ ’ axel null . -null -axel .<br />

adt.array.Set@65f55 count 3<br />

null


¡<br />

<strong>Informatik</strong> B SS 03 153<br />

axel<br />

java.lang.ArrayIndexOutofBoundsException: -1<br />

adt.array.Set@65f55 count 2<br />

axel<br />

$ java adt.array.EquivalenceSet null ’ ’ axel null . -null -axel .<br />

adt.array.EquivalenceSet@65f54 count 3<br />

null<br />

axel<br />

adt.array.EquivalenceSet@65f54 count 1<br />

11.4 Implementation mit Offener Hash-Tabelle<br />

11.4.1 Array versus Hash-Tabelle<br />

L<strong>in</strong>eare Suche im Array wird um so teurer, je länger der Array ist (Zahl der<br />

<strong>Objekt</strong>e).<br />

Offene Hash-Tabelle: Feste Länge, E<strong>in</strong>trag über Hash-Code.<br />

Kollisionsauflösung: jeder Indexplatz enthält e<strong>in</strong>e Collection (üblicherweise<br />

Liste).<br />

Hash-Code muss möglichst e<strong>in</strong>deutigen Wert liefern. hashCode() berechnet<br />

(möglichst) e<strong>in</strong>deutigen <strong>in</strong>t-Wert für Empfänger-<strong>Objekt</strong>. Wenn equals()<br />

true liefert, muss hashCode() denselben Wert für beide <strong>Objekt</strong>e (Empfänger<br />

und Argument von equals()) liefern.<br />

Wenn equals() überschrieben wird, muss man daran denken, auch<br />

hashCode() zu überschreiben!<br />

“Gute” Hash-Funktion streut gleichmässig (ke<strong>in</strong>e Clusterbildung!).<br />

Üblich: Key/Data-Paare, Hash-Code wird über Key berechnet.<br />

Im Folgenden: nur Daten, Key wird über Daten direkt berechnet.<br />

11.4.2 MyCollection/Hash<br />

Grundlegende Funktionalität:<br />

Offene Hash-Tabelle für Daten als Array (bucket), bei dem jeder Indexplatz auf e<strong>in</strong>e<br />

Collection verweist. In welcher Collection e<strong>in</strong> Datum abgelegt wird, wird über<br />

hashCode() ermittelt. Die Collections können verschieden realisiert werden, z.B. als<br />

Array oder als Liste.<br />

// adt/hash/MyCollection.java // A.T. Schre<strong>in</strong>er<br />

package adt.hash;<br />

/** base class for open hash based collections.<br />

*/


154 <strong>Informatik</strong> B SS 03<br />

Hash−Tabelle<br />

Daten<br />

Abbildung 43: Offene Hash-Tabelle<br />

adt<br />

adt.hash<br />

MyCollection<br />

><br />

add(x: Object): Object<br />

f<strong>in</strong>d(x: Object): Object<br />

sub(x: Object): Object<br />

count(): Integer<br />

MyCollection<br />

Bag<br />

Set<br />

Abbildung 44: Struktur von adt.hash


<strong>Informatik</strong> B SS 03 155<br />

public abstract class MyCollection implements adt.MyCollection {<br />

protected f<strong>in</strong>al adt.MyCollection[] bucket;<br />

/** def<strong>in</strong>e bucket table.<br />

@throws RuntimeException if capacity < 0.<br />

*/<br />

public MyCollection (<strong>in</strong>t capacity) {<br />

bucket = new adt.MyCollection[capacity]; }<br />

/** default bucket capacity is 16.<br />

Should use this(16), but JDK 1.1.6 compla<strong>in</strong>s about f<strong>in</strong>al.<br />

*/<br />

public MyCollection () { bucket = new adt.MyCollection[16]; }<br />

/** <strong>in</strong>sert an object <strong>in</strong>to the collection.<br />

*/<br />

public Object add (Object x) { return bucket(x, true).add(x); }<br />

/** locate an object <strong>in</strong> the collection.<br />

@throws RuntimeException if not found.<br />

*/<br />

public Object f<strong>in</strong>d (Object x) { return bucket(x, false).f<strong>in</strong>d(x); }<br />

/** remove an object from the collection.<br />

@throws RuntimeException if not found.<br />

*/<br />

public Object sub (Object x) { return bucket(x, false).sub(x); }<br />

/** @return number of dist<strong>in</strong>ct objects <strong>in</strong> the collection;<br />

zero, if the collection is empty.<br />

*/<br />

public <strong>in</strong>t count () {<br />

<strong>in</strong>t result = 0;<br />

for (<strong>in</strong>t n = 0; n < bucket.length; ++ n)<br />

if (bucket[n] != null) result += bucket[n].count();<br />

return result;<br />

}<br />

/** access function for bucket[].<br />

@param x object to hash, may be null.<br />

@param create true to create a miss<strong>in</strong>g bucket.<br />

@return bucket collection or null if create is false.<br />

*/<br />

protected adt.MyCollection bucket (Object x, boolean create) {<br />

<strong>in</strong>t n = x == null 0 : Math.abs(x.hashCode() % bucket.length);<br />

if (create && bucket[n] == null) bucket[n] = newBucket();<br />

return bucket[n];<br />

}<br />

/** factory method for bucket collections, decides class chracteristics.<br />

*/<br />

protected abstract adt.MyCollection newBucket ();<br />

/** permit symbolic dump.<br />

*/


¡<br />

¡<br />

¡<br />

156 <strong>Informatik</strong> B SS 03<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g() {<br />

Str<strong>in</strong>gBuffer buf = new Str<strong>in</strong>gBuffer(super.toStr<strong>in</strong>g());<br />

buf.append(" count ").append(count());<br />

for (<strong>in</strong>t n = 0; n < bucket.length; ++ n)<br />

buf.append(’\n’).append(bucket[n]);<br />

return buf.toStr<strong>in</strong>g();<br />

}<br />

}<br />

}<br />

11.4.3 Erläuterungen zu ‘MyCollection’<br />

Anders als üblich bei Hash-Tabellen, werden nicht Key/Data Assoziationen,<br />

sondern nur Daten e<strong>in</strong>getragen (vgl. HashSet <strong>in</strong> java.util.Collections).<br />

adt.hash.MyCollection benutzt e<strong>in</strong>en Array fester Länge mit<br />

adt.MyCollection-<strong>Objekt</strong>en als Elemente (bucket).<br />

Alle Nachrichten an e<strong>in</strong> MyCollection-<strong>Objekt</strong> werden an die entsprechende<br />

von hashCode() ausgewählte Collection weitergeleitet. (Methode bucket())<br />

Alle Operationen (add(), sub(), f<strong>in</strong>d()) werden über e<strong>in</strong>e Methode<br />

bucket() realisiert, die das entsprechende Element erzeugt oder f<strong>in</strong>det.<br />

Zugriffsmethode (Access-Method) für Array bucket[].<br />

Elemente können “spät” erzeugt werden, Zugriffskontrolle, ...<br />

Konkrete Auswahl e<strong>in</strong>es Collection-Elements und damit das Verhalten von<br />

MyCollection wird über newBucket() realisiert:<br />

abstrakte Methode abstrakte Klasse,<br />

newBucket() regelt Erzeugung von <strong>Objekt</strong>en und wird <strong>in</strong> Unterklassen durch<br />

konkrete Methoden überschrieben: Factory-Pattern!<br />

Weil Array-Indizes positiv se<strong>in</strong> müssen, wird Math.abs() verwendet.<br />

Für null kann man ke<strong>in</strong>e Methode, also auch nicht hashCode() aufrufen.<br />

11.4.4 Bag/Hash<br />

Funktionalität:<br />

Array fester Länge, der bei Bedarf verlängert (aber nie verkürzt) wird; erlaubt ‘null’ als<br />

<strong>Objekt</strong>, vergleicht auf Identität, trägt gleiche Werte mehrmals e<strong>in</strong>mal e<strong>in</strong>, löscht immer<br />

nur e<strong>in</strong> <strong>Objekt</strong>.<br />

newBucket() liefert e<strong>in</strong> adt.array.Bag <strong>Objekt</strong><br />

// adt/hash/Bag.java // A.T. Schre<strong>in</strong>er<br />

package adt.hash;<br />

/** open hash based conta<strong>in</strong>er with multiple <strong>in</strong>sertion.<br />

*/<br />

public class Bag extends MyCollection {<br />

/** def<strong>in</strong>e bucket table.


¡<br />

<strong>Informatik</strong> B SS 03 157<br />

}<br />

@throws RuntimeException if capacity < 0.<br />

*/<br />

public Bag (<strong>in</strong>t capacity) { super(capacity); }<br />

/** default <strong>in</strong>itial capacity determ<strong>in</strong>ed by superclass.<br />

*/<br />

public Bag () { }<br />

/** factory method for bucket collections, decides class chracteristics.<br />

@return adt.array.Bag.<br />

*/<br />

protected adt.MyCollection newBucket () { return new adt.array.Bag(); }<br />

11.4.5 Set/Hash<br />

Funktionalität:<br />

Array fester Länge, der bei Bedarf verlängert (aber nie verkürzt) wird; erlaubt ‘null’ als<br />

<strong>Objekt</strong>, vergleicht auf Identität, trägt gleiche Werte nur e<strong>in</strong>mal e<strong>in</strong>, löscht immer nur e<strong>in</strong><br />

<strong>Objekt</strong>.<br />

newBucket() liefert e<strong>in</strong> adt.list.Set <strong>Objekt</strong><br />

// adt/hash/Set.java // A.T. Schre<strong>in</strong>er<br />

package adt.hash;<br />

/** open hash based collection with unique identity <strong>in</strong>sertion.<br />

*/<br />

public class Set extends MyCollection {<br />

/** def<strong>in</strong>e bucket table.<br />

@throws RuntimeException if capacity < 0.<br />

*/<br />

public Set (<strong>in</strong>t capacity) { super(capacity); }<br />

/** default <strong>in</strong>itial capacity determ<strong>in</strong>ed by superclass.<br />

*/<br />

public Set () { }<br />

/** factory method for bucket collections, decides class chracteristics.<br />

@return adt.list.Set.<br />

*/<br />

protected adt.MyCollection newBucket () { return new adt.list.Set(); }<br />

}<br />

11.5 ADT-Test<br />

11.5.1 Anforderungen an ‘Test’<br />

Da adt.array.MyCollection und adt.hash.MyCollection nur Object<br />

als geme<strong>in</strong>same Oberklasse haben, müsste die für array def<strong>in</strong>ierte Methode<br />

test() kopiert werden.


158 <strong>Informatik</strong> B SS 03<br />

Def<strong>in</strong>ition e<strong>in</strong>er allgeme<strong>in</strong>en Test-Klasse adt/Test.java, mit der beliebige<br />

Realisationen von Bag, Set und EquivalenceSet als array, hash, list<br />

oder tree getestet werden können!<br />

Funktionalität von adt/Test:<br />

. toStr<strong>in</strong>g() Ausgabe der Collection auf Standard-Output.<br />

# count() Anzeige der Anzahl von <strong>Objekt</strong>en <strong>in</strong> der Collection.<br />

- sub() Entferne nächstes Wort.<br />

word add() Trage Wort e<strong>in</strong>.<br />

Das Wort “null” wird als null-Referenz e<strong>in</strong>gefügt/gelöscht.<br />

Test soll die zu testende Collection als Kommandozeilen-Argument (z.B.<br />

adt.hash.Set) übergeben bekommen.<br />

<strong>in</strong>tern() erlaubt, dass Str<strong>in</strong>gs mit identischen Zeichenfolgen als e<strong>in</strong>- und<br />

dasselbe <strong>Objekt</strong> behandelt werden. Mit -i soll dies <strong>in</strong> Test unterbunden<br />

werden.<br />

$ wc -w Test.java<br />

376 Test.java<br />

$ export CLASSPSTH=..<br />

$ { cat Test.java; echo ’#’; } | java adt.Test adt.hash.Set<br />

179<br />

$ { cat Test.java; echo ’#’; } | java adt.Test -i adt.hash.Set<br />

376<br />

Das Unix-Commando wc liefert die Anzahl der Worte (durch Leerzeichen<br />

separierte Zeichenfolgen) <strong>in</strong> e<strong>in</strong>er Datei.<br />

Die tatsächlichen Kommandos (E<strong>in</strong>fügen/Löschen von Worten, ...) sollen als<br />

Kommandozeilen-Argumente (wie bisher) oder von Standard-Input oder aus<br />

e<strong>in</strong>er Datei gelesen werden können (abstrakte Methode next()).<br />

Zentrale Methode <strong>in</strong> Test: test()<br />

<strong>in</strong>tern ja oder ne<strong>in</strong>; Manipulation e<strong>in</strong>er Collection (E<strong>in</strong>fügen/Löschen von<br />

Worten).<br />

11.5.2 Test/ADT<br />

// adt/Test.java // A.T. Schre<strong>in</strong>er<br />

package adt;<br />

import java.io.EOFException;<br />

import java.io.IOException;<br />

import java.io.InputStreamReader;<br />

import java.io.StreamTokenizer;<br />

/** test collections us<strong>in</strong>g str<strong>in</strong>gs with or without <strong>in</strong>tern().<br />

*/<br />

public abstract class Test {<br />

/** @return next <strong>in</strong>put word, maybe null.


<strong>Informatik</strong> B SS 03 159<br />

@throws EOFException if no more <strong>in</strong>put.<br />

@throws IOException if <strong>in</strong>put error.<br />

*/<br />

public abstract Str<strong>in</strong>g next () throws IOException;<br />

/** tests collections with words delivered by next() until exception.<br />

.display collection on stdout<br />

#display count on stderr<br />

-remove next word<br />

nullrepresents null<br />

wordadd word<br />

<br />

@param collection to be tested.<br />

@param <strong>in</strong>tern if true, <strong>in</strong>tern() is used on each word.<br />

@throws EOFException if no more <strong>in</strong>put.<br />

@throws IOException if <strong>in</strong>put error.<br />

*/<br />

public void test (MyCollection collection, boolean <strong>in</strong>tern) throws IOException {<br />

for (;;) {<br />

Str<strong>in</strong>g s = next();<br />

if (s == null) collection.add(null);<br />

else {<br />

Str<strong>in</strong>g i = s.<strong>in</strong>tern();<br />

if (i == ".") System.out.pr<strong>in</strong>tln(collection);<br />

else if (i == "#") System.err.pr<strong>in</strong>tln(collection.count());<br />

else if (i == "-")<br />

try {<br />

s = next();<br />

if (s == null || s.equals("null")) collection.sub(null);<br />

else collection.sub(<strong>in</strong>tern s.<strong>in</strong>tern() : s);<br />

} catch (RuntimeException e) {<br />

System.out.pr<strong>in</strong>tln("sub "+s+" not found");<br />

}<br />

else if (i == "null") collection.add(null);<br />

else collection.add(<strong>in</strong>tern i : s);<br />

}<br />

}<br />

}<br />

/** run test(), either on arguments or on words from standard <strong>in</strong>put.<br />

Optional argument -i suppresses use of <strong>in</strong>tern().<br />

First argument is full class name.<br />

*/<br />

public static void ma<strong>in</strong> (f<strong>in</strong>al Str<strong>in</strong>g[] args) {<br />

if (args == null) return;<br />

<strong>in</strong>t a = 0;<br />

boolean <strong>in</strong>tern = true;<br />

if (a >= args.length) return;


160 <strong>Informatik</strong> B SS 03<br />

if (args[a].equals("-i")) { <strong>in</strong>tern = false; ++ a; }<br />

}<br />

}<br />

MyCollection collection;<br />

if (a >= args.length) return;<br />

try {<br />

collection = (MyCollection)Class.forName(args[a]).newInstance(); ++ a;<br />

} catch (ClassNotFoundException e) { System.err.pr<strong>in</strong>tln(e); return;<br />

} catch (IllegalAccessException e) { System.err.pr<strong>in</strong>tln(e); return;<br />

} catch (InstantiationException e) { System.err.pr<strong>in</strong>tln(e); return;<br />

}<br />

// generate local Test object and run test()<br />

try {<br />

Test test;<br />

if (a < args.length) {<br />

f<strong>in</strong>al <strong>in</strong>t n = a;<br />

test = new Test() {<br />

<strong>in</strong>t a = n;<br />

public Str<strong>in</strong>g next () throws IOException {<br />

if (a < args.length) return args[a++];<br />

throw new EOFException();<br />

}<br />

};<br />

} else<br />

test = new Test() {<br />

StreamTokenizer st =<br />

new StreamTokenizer(new InputStreamReader(System.<strong>in</strong>));<br />

{ st.resetSyntax();<br />

st.wordChars(’\0’, ’˜’); st.whitespaceChars(’\0’, ’ ’);<br />

}<br />

public Str<strong>in</strong>g next () throws IOException {<br />

if (st.nextToken() == st.TT_WORD) return st.sval;<br />

throw new EOFException();<br />

}<br />

};<br />

test.test(collection, <strong>in</strong>tern);<br />

} catch (EOFException e) {<br />

} catch (IOException e) { System.err.pr<strong>in</strong>tln(e);<br />

}<br />

11.5.3 Erläuterungen zu ‘Test’<br />

Für Str<strong>in</strong>g-Konstanten kann mit <strong>in</strong>tern() erzwungen werden, dass<br />

identische Zeichenketten mit e<strong>in</strong>em <strong>Objekt</strong> identifiziert werden.<br />

Es wird generell MyCollection.<strong>in</strong>tern() benutzt, um E<strong>in</strong>gaben wie “null”<br />

oder “#” durch e<strong>in</strong>fache Vergleichsoperationen entdecken zu können.


<strong>Informatik</strong> B SS 03 161<br />

Durch Verwendung von <strong>in</strong>tern() prüft == auf Äquivalenz!<br />

Identitätsprüfung wie vorher realisiert: durch Schalter -i (boolean <strong>in</strong>tern <strong>in</strong><br />

test()).<br />

Abstrakte Methode next() kann null liefern, also muss <strong>in</strong> test() zunächst<br />

geprüft werden, ob dies der Fall ist, bevor <strong>in</strong>tern() oder equals()<br />

aufgerufen wird.<br />

In test() wird Wort “null” als null-Referenz behandelt.<br />

ma<strong>in</strong>() prüft zunächst, ob -i als Option für Test angegeben wurde. Danach<br />

wird der Name der Klasse von der Kommandozeile gelesen und e<strong>in</strong><br />

entsprechendes Collection-<strong>Objekt</strong> erzeugt.<br />

java.lang.Class ist die Klasse der Klassenbeschreibungen <strong>in</strong> <strong>Java</strong>. Jede<br />

Klasse hat e<strong>in</strong> e<strong>in</strong>deutiges Class-<strong>Objekt</strong>, das folgendermassen angesprochen<br />

werden kann:<br />

someObject.getClass()<br />

SomeClass.class<br />

<strong>in</strong>t.class<br />

float[].class<br />

Die Klassen-Methode forName() erzeugt e<strong>in</strong> Class-<strong>Objekt</strong> für e<strong>in</strong>en<br />

vollständigen Klassennamen wie adt.array.Set, das dann die Klasse<br />

adt.array.Set repräsentiert (wenn sie gefunden wird!). newInstance()<br />

kann an e<strong>in</strong> Class-<strong>Objekt</strong> gesendet werden um e<strong>in</strong> neues <strong>Objekt</strong> zu erzeugen<br />

(wenn e<strong>in</strong> paramterloser Konstruktor existiert!).<br />

(siehe Kapitel “Reflections”)<br />

Beim Erzeugen e<strong>in</strong>es <strong>Objekt</strong>s auf diese Art können zahlreiche Exceptions<br />

auftreten! Dafür ist Test aber sehr flexibel.<br />

Es soll möglich se<strong>in</strong>, dass die tatsächlichen Manipulationen der Collection über<br />

weitere Kommandozeilen-Argumente oder von der Standard-E<strong>in</strong>gabe (Tastatur)<br />

kommen. Entsprechend muss <strong>in</strong> ma<strong>in</strong>() <strong>in</strong> Abhängigkeit von den weiteren<br />

Argumenten auf der Kommandozeile e<strong>in</strong> Test-<strong>Objekt</strong> erzeugt werden.<br />

Dies wird mit zwei anonymen Klassen realisiert.<br />

System.<strong>in</strong> liefert Standard-Input als Byte-Folge. E<strong>in</strong> InputStreamReader<br />

erlaubt, Bytes <strong>in</strong> Characters umzuwandeln. E<strong>in</strong> StreamTokenizer konstruiert<br />

Worte aus Zeichen. Hier wurde als Trennung (whitespaceChars) zwischen<br />

Worten (Tokens) Leerzeichen def<strong>in</strong>iert (Initializer-Block, da anonyme Klasse<br />

ke<strong>in</strong>en expliziten Konstruktor def<strong>in</strong>ieren kann!).<br />

11.6 Implementation mit Liste<br />

11.6.1 Dynamische Datenstrukturen<br />

E<strong>in</strong> Array hat e<strong>in</strong>e fest vorgegebene Länge. Wächst die Anzahl der Elemente,<br />

muss der Array evtl. <strong>in</strong> e<strong>in</strong>en längeren Array umkopiert werden. Beim Löschen


162 <strong>Informatik</strong> B SS 03<br />

leere Liste:<br />

prev next<br />

dummy<br />

<strong>in</strong>fo<br />

Bag<br />

null<br />

Löschen von ‘a’:<br />

prev<br />

E<strong>in</strong>fügen von ‘x’:<br />

prev<br />

prev<br />

dummy x<br />

next<br />

p.next<br />

p<br />

next<br />

a<br />

next<br />

n<br />

next<br />

prev prev<br />

n.prev<br />

next<br />

Abbildung 45: Doppelt-verkettete Liste mit Dummy-Element<br />

adt<br />

adt.list<br />

MyCollection<br />

><br />

Bag Set EquivalenceSet<br />

add(x: Object): Object<br />

f<strong>in</strong>d(x: Object): Object<br />

sub(x: Object): Object<br />

count(): Integer<br />

Abbildung 46: Struktur von adt.list<br />

müssen Elemente verschoben werden, um “Löcher” zu vermeiden.<br />

Dynamische Datentypen (Listen, Bäume): haben ke<strong>in</strong>e fest vorgegebene<br />

Kapazität.<br />

Doppelt-verkettete Liste: jedes Element verweist auf se<strong>in</strong>en Nachfolger und<br />

se<strong>in</strong>en Vorgänger.<br />

Organisation der Liste als R<strong>in</strong>g mit ausgezeichnetem, zusätzlichem<br />

“Dummy”-Element (das nicht gelöscht werden kann). Dadurch wird E<strong>in</strong>fügen<br />

und Löschen sehr e<strong>in</strong>fach.<br />

Realisation von Bag als Implementation von adt/MyCollection. Danach:<br />

Set als Unterklasse von Bag und EquivalenceSet als Unterklasse von Set.


<strong>Informatik</strong> B SS 03 163<br />

11.6.2 Bag/List<br />

Funktionalität:<br />

Doppelt-verkettete Liste mit Dummy-Element; erlaubt ‘null’ als <strong>Objekt</strong>, vergleicht auf<br />

Identität, trägt gleiche Werte mehrfach e<strong>in</strong>, löscht immer nur e<strong>in</strong> <strong>Objekt</strong>.<br />

// adt/list/Bag.java // A.T. Schre<strong>in</strong>er<br />

package adt.list;<br />

import adt.MyCollection;<br />

/** doubly l<strong>in</strong>ked list based collection with multiple <strong>in</strong>sertion.<br />

*/<br />

public class Bag implements MyCollection {<br />

/** element structure.<br />

*/<br />

protected static class Element {<br />

public f<strong>in</strong>al Object <strong>in</strong>fo; public Element prev, next;<br />

/** create and doubly l<strong>in</strong>k.<br />

*/<br />

public Element (Object <strong>in</strong>fo, Element prev, Element next) {<br />

this.<strong>in</strong>fo = <strong>in</strong>fo;<br />

this.prev = prev; prev.next = this;<br />

this.next = next; next.prev = this;<br />

}<br />

/** create dummy: null <strong>in</strong>formation, l<strong>in</strong>ked to itself.<br />

*/<br />

public Element () { <strong>in</strong>fo = null; prev = next = this; }<br />

/** detach from with<strong>in</strong> a list.<br />

*/<br />

public void unl<strong>in</strong>k () { prev.next = next; next.prev = prev; }<br />

/** permit symbolic dump.<br />

*/<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g () {<br />

return id()+" prev "+prev.id()+" next "+next.id()+" <strong>in</strong>fo "+<strong>in</strong>fo;<br />

}<br />

protected Str<strong>in</strong>g id () {<br />

Str<strong>in</strong>g result = super.toStr<strong>in</strong>g();<br />

return result.substr<strong>in</strong>g(result.lastIndexOf(’.’)+1);<br />

}<br />

}<br />

/** list header, used as dummy element.<br />

*/<br />

protected Element list = new Element();<br />

protected <strong>in</strong>t count;<br />

/** <strong>in</strong>sert an object <strong>in</strong>to the collection, do not check if already present.<br />

The object is added at list.next.<br />

@param x object to be added, may be null.<br />

@return x.<br />

*/


164 <strong>Informatik</strong> B SS 03<br />

}<br />

public Object add (Object x) {<br />

new Element(x, list, list.next); ++ count; return x;<br />

}<br />

/** locate an object <strong>in</strong> the collection.<br />

@param x object to be found.<br />

@return object from collection, as determ<strong>in</strong>ed by locate().<br />

@throws RuntimeException if x cannot be found.<br />

*/<br />

public Object f<strong>in</strong>d (Object x) throws RuntimeException {<br />

return locate(x).<strong>in</strong>fo;<br />

}<br />

/** remove an object from the collection.<br />

@param x object to be removed.<br />

@return object from collection, as determ<strong>in</strong>ed by locate().<br />

@throws RuntimeException if x cannot be found.<br />

*/<br />

public Object sub (Object x) {<br />

Element e = locate(x); e.unl<strong>in</strong>k(); -- count; return e.<strong>in</strong>fo;<br />

}<br />

/** @return number of dist<strong>in</strong>ct objects <strong>in</strong> the collection;<br />

zero, if the collection is empty.<br />

*/<br />

public <strong>in</strong>t count () { return count; }<br />

/** locate an object <strong>in</strong> the collection.<br />

@param x object to be found.<br />

@return (last) element if present, based on identity.<br />

@throws RuntimeException if x cannot be found.<br />

*/<br />

protected Element locate (Object x) {<br />

for (Element e = list.next; e != list; e = e.next)<br />

if (e.<strong>in</strong>fo == x) return e;<br />

throw new RuntimeException(x+": not found");<br />

}<br />

/** permit symbolic dump.<br />

*/<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g() {<br />

Str<strong>in</strong>gBuffer buf = new Str<strong>in</strong>gBuffer(super.toStr<strong>in</strong>g());<br />

buf.append(" count ").append(count);<br />

Element e = list;<br />

do<br />

buf.append(’\n’).append(e);<br />

while ((e = e.next) != list);<br />

return buf.toStr<strong>in</strong>g();<br />

}


<strong>Informatik</strong> B SS 03 165<br />

11.6.3 Erläuterungen zu ‘Bag’<br />

Innere Klasse Element: ist nested top-level (static).<br />

Ke<strong>in</strong> Zugriff von Element auf Instanz-Felder der umschliessenden Klasse Bag<br />

notwendig.<br />

Inhalt e<strong>in</strong>es Elements ist vom Typ Object und f<strong>in</strong>al!<br />

Konstruktor mit drei Argumenten: E<strong>in</strong>hängen e<strong>in</strong>es neuen Elements mit Inhalt<br />

<strong>in</strong>fo und Verkettung mit Vorgänger prev und Nachfolger next.<br />

Konstruktor ohne Argumente: Erzeugung des Dummy-Element mit sich selbst<br />

als Vorgänger und Nachfolger.<br />

unl<strong>in</strong>k() veranlasst e<strong>in</strong> Element, sich selbst aus e<strong>in</strong>er Liste auszukl<strong>in</strong>ken,<br />

<strong>in</strong>dem es e<strong>in</strong>fach se<strong>in</strong>e Nachbarn aufe<strong>in</strong>ander verweisen lässt.<br />

Ausgabe: id() liefert <strong>Objekt</strong>-Referenz als Str<strong>in</strong>g, wobei der volle Name<br />

abgeschnitten wird. (super.toStr<strong>in</strong>g() ist Aufruf der Methode der<br />

Oberklasse Object).<br />

Element-Konstruktoren, unl<strong>in</strong>k() und toStr<strong>in</strong>g() s<strong>in</strong>d public, damit sie<br />

<strong>in</strong> Unterklassen von Bag benutzt werden können.<br />

Der Struktur-Zugriff von der äußeren Klasse Bag auf Komponenten der<br />

Innneren Klasse Element macht Implementierung effizienter (ke<strong>in</strong>e speziellen<br />

Accessor-Methoden). Es wäre nicht nötig gewesen, die Instanzvariablen von<br />

Element public zu deklarieren, da <strong>in</strong> <strong>Java</strong> die äußere Klasse auf alle<br />

Komponenten der <strong>in</strong>neren Klasse Zugriff hat.<br />

Ausserhalb von adt.list.Bag ist Element unsichtbar! (Element ist nur via<br />

Bag erreichbar. Element ist protected, also nur <strong>in</strong> Unterklassen von Bag<br />

zugreifbar.) Das Innenleben von Bag geht niemand etwas an, Bag ist e<strong>in</strong>e<br />

Struktur, <strong>in</strong> der Elemente aufbewahrt werden.<br />

Die MyCollection-Operationen (add(), sub(), f<strong>in</strong>d() und count()) werden<br />

auf Element-Operationen zurückgespielt.<br />

Nach dem bewährten Schema muss e<strong>in</strong>e e<strong>in</strong>zige Methode – locate() –<br />

bemüht werden, um das Element zu f<strong>in</strong>den, <strong>in</strong> dem e<strong>in</strong> Object gespeichert ist.<br />

Wird e<strong>in</strong> <strong>Objekt</strong> nicht gefunden, wird e<strong>in</strong>e RunTimeExeception geworfen.<br />

(besser wäre spezifische Exception)<br />

Suche beg<strong>in</strong>nt bei next vom R<strong>in</strong>g-Element list (Dummy) und darf list nicht<br />

erreichen. E<strong>in</strong>e leere Liste besteht nur aus list, das dann mit sich selbst<br />

verkettet ist.<br />

Bei der symbolischen Darstellung muss man e<strong>in</strong> bisschen aufpassen: Bag<br />

fordert jedes Element – <strong>in</strong>klusive list – auf, sich selbst darzustellen. E<strong>in</strong><br />

Element darf aber se<strong>in</strong>e Nachbarn nur nach id() und nicht nach ihrer<br />

Darstellung fragen, sonst wird das Ganze im R<strong>in</strong>g herum rekursiv.<br />

Zum Testen wird adt.Test verwendet. Ohne Schalter £ werden<br />

Str<strong>in</strong>g-<strong>Objekt</strong>e mit denselben Zeichenketten als identische <strong>Objekt</strong>e behandelt!


¡<br />

¡<br />

166 <strong>Informatik</strong> B SS 03<br />

Als erstes Element wird immer das Dummy-Element ausgegeben.<br />

$ java adt.Test adt.list.Bag axel null ’ ’ null . - null - axel .<br />

adt.list.Bag@65fcc count 4<br />

Bag$Element@65fca prev Bag$Element@65fbf next Bag$Element@65fbc <strong>in</strong>fo null<br />

Bag$Element@65fbc prev Bag$Element@65fca next Bag$Element@65fbd <strong>in</strong>fo null<br />

Bag$Element@65fbd prev Bag$Element@65fbc next Bag$Element@65fbe <strong>in</strong>fo<br />

Bag$Element@65fbe prev Bag$Element@65fbd next Bag$Element@65fbf <strong>in</strong>fo null<br />

Bag$Element@65fbf prev Bag$Element@65fbe next Bag$Element@65fca <strong>in</strong>fo axel<br />

adt.list.Bag@65fcc count 2<br />

Bag$Element@65fca prev Bag$Element@65fbe next Bag$Element@65fbd <strong>in</strong>fo null<br />

Bag$Element@65fbd prev Bag$Element@65fca next Bag$Element@65fbe <strong>in</strong>fo<br />

Bag$Element@65fbe prev Bag$Element@65fbd next Bag$Element@65fca <strong>in</strong>fo null<br />

11.6.4 Unterklasse ‘Set’<br />

Funktionalität:<br />

Doppelt-verkettete Liste mit Dummy-Element; erlaubt ‘null’ als <strong>Objekt</strong>, vergleicht auf<br />

Identität, trägt gleiche Werte nur e<strong>in</strong>mal e<strong>in</strong>, löscht immer nur e<strong>in</strong> <strong>Objekt</strong>.<br />

Unterschied zu Bag: Methode add() kontrolliert, ob se<strong>in</strong> Argument schon vorhanden<br />

ist.<br />

// adt/list/Set.java // A.T. Schre<strong>in</strong>er<br />

package adt.list;<br />

/** doubly l<strong>in</strong>ked list based collection with unique identity <strong>in</strong>sertion.<br />

*/<br />

public class Set extends Bag {<br />

/** <strong>in</strong>sert an object <strong>in</strong>to the collection, unless present.<br />

@param x object to be added, may be null.<br />

@return object from collection.<br />

*/<br />

public Object add (Object x) {<br />

try {<br />

return super.f<strong>in</strong>d(x); // avoid <strong>in</strong>advertent override<br />

} catch (RuntimeException e) { }<br />

return super.add(x);<br />

}<br />

}<br />

Implementierung analog zu adt.array.Set.java. Ke<strong>in</strong> Reuse, da e<strong>in</strong>e Klasse nur<br />

e<strong>in</strong>e Oberklasse erweitern kann.<br />

11.6.5 Unterklasse ‘EquivalenceSet’<br />

Funktionalität:<br />

Doppelt-verkettete Liste mit Dummy-Element; erlaubt ‘null’ als <strong>Objekt</strong>, vergleicht auf<br />

Äquivalenz, trägt gleiche Werte nur e<strong>in</strong>mal e<strong>in</strong>, löscht immer nur e<strong>in</strong> <strong>Objekt</strong>.<br />

Unterklasse zu Set, Überschreiben von locate().


£<br />

<strong>Informatik</strong> B SS 03 167<br />

// adt/list/EquivalenceSet.java // A.T. Schre<strong>in</strong>er<br />

package adt.list;<br />

/** doubly l<strong>in</strong>ked list based collection with unique <strong>in</strong>sertion based on<br />

equals().<br />

*/<br />

public class EquivalenceSet extends Set {<br />

/** locate an object <strong>in</strong> the collection.<br />

@param x object to be found.<br />

@return (last) element if present, based on equals().<br />

@throws RuntimeException if x cannot be found.<br />

*/<br />

protected Element locate (Object x) {<br />

for (Element e = list.next; e != list; e = e.next)<br />

if (adt.array.EquivalenceSet.equals(x, e.<strong>in</strong>fo)) return e;<br />

throw new RuntimeException(x+": not found");<br />

}<br />

}<br />

Aufruf der public def<strong>in</strong>ierten Klassenmethode von adt.array.EquivalenceSet!<br />

11.7 Implementation mit Suchbaum<br />

11.7.1 Suchbäume<br />

Suchbaum: dynamische Datenstruktur mit Ordnung.<br />

wird effizienter ( +#, ¦<br />

¦ und Listen ( ).<br />

Suche nach <strong>Objekt</strong>en<br />

) als l<strong>in</strong>eares Suchen <strong>in</strong> Arrays<br />

Aber: E<strong>in</strong>fügen und Löschen wird aufwendiger, da die Ordnung im Baum<br />

erhalten bleiben muss.<br />

Häufig: B<strong>in</strong>ärer Baum mit key/data-Paaren <strong>in</strong> den Knoten. Invariante<br />

Eigenschaft: alle keys im l<strong>in</strong>ken Unterbaum s<strong>in</strong>d kle<strong>in</strong>er als der key des<br />

aktuellen Knotens, alle keys im rechten Unterbaum grösser.<br />

Um Suche effizient zu halten, sollte der Baum möglichst ausgewogen se<strong>in</strong><br />

(Höhen-Balance, Gewichts-Balance für Elemente mit annähernd gleichen<br />

Zugriffswahrsche<strong>in</strong>lichkeiten).<br />

Im Folgenden: E<strong>in</strong>getragen werden nicht key/data-Paare sondern Elemente. Die<br />

Elemente müssen Comparable se<strong>in</strong>! (vgl. Marker-Interface).<br />

Das Interface Comparable deklariert die Methode compareTo(), die<br />

anti-symmetrisch ist (Ordnungsrelation!) und angibt, ob ihr Empfänger kle<strong>in</strong>er<br />

(Resultat E ), gleich (Resultat ¡ E ) oder grösser (Resultat ¡ E ) als das<br />

Argument-<strong>Objekt</strong> ist. (Gleichheit muss verträglich mit equals() se<strong>in</strong>.)


168 <strong>Informatik</strong> B SS 03<br />

4<br />

2<br />

6<br />

1 3 5 7<br />

left<br />

Set<br />

sub<br />

root<br />

left<br />

<strong>in</strong>fo<br />

right<br />

4<br />

<strong>in</strong>fo<br />

right left<br />

<strong>in</strong>fo<br />

2 6<br />

right<br />

Abbildung 47: Suchbaum: Klassische Darstellung (l<strong>in</strong>ks) und als adt.tree.set<br />

11.7.2 Set/Tree<br />

Funktionalität:<br />

Speichern e<strong>in</strong>es von add() gelieferten Comparable Wertes <strong>in</strong> e<strong>in</strong>em Element, das<br />

rekursiv auf kle<strong>in</strong>ere und grössere Werte verweist, die ebenfalls mit Element-<strong>Objekt</strong>en<br />

gespeichert s<strong>in</strong>d.<br />

erlaubt ‘null’ als <strong>Objekt</strong>, vergleicht auf Identität, trägt gleiche Werte nur e<strong>in</strong>mal e<strong>in</strong>,<br />

löscht immer nur e<strong>in</strong> <strong>Objekt</strong>.<br />

Grundgedanke: Es wird wieder e<strong>in</strong>e Hilfsfunktion locate() verwendet, um e<strong>in</strong><br />

<strong>Objekt</strong> <strong>in</strong> der Collection aufzuf<strong>in</strong>den, die von add(), f<strong>in</strong>d() und sub()<br />

verwendet wird.<br />

locate() realisiert die Traversierung des Baums.<br />

Funktionalität von locate() im Suchbaum:<br />

– Ist der Wert des gesuchten <strong>Objekt</strong>s gleich dem aktuellen Element, dann<br />

liefere das Element zurück.<br />

– Ist der Wert des gesuchten <strong>Objekt</strong>s kle<strong>in</strong>er dem aktuellen Element, dann<br />

Suche im l<strong>in</strong>ken Unterbaum weiter.<br />

– Ist der Wert des gesuchten <strong>Objekt</strong>s grösser dem aktuellen Element, dann<br />

Suche im rechten Unterbaum weiter.<br />

Spezialbehandlung des null-<strong>Objekt</strong>s ist wieder notwendig: null ist kle<strong>in</strong>stes<br />

Element, also “ganz l<strong>in</strong>ks unten” im Suchbaum.<br />

Wenn locate() das gesuchte <strong>Objekt</strong> nicht gefunden hat, so s<strong>in</strong>d wir bei der<br />

Traverse aber an der Stelle gelandet, wo es stehen sollte.


<strong>Informatik</strong> B SS 03 169<br />

MyCollection<br />

<br />

Set<br />

locate(x: Object): Result<br />

kennt<br />

Set.Element<br />

NotFound<br />

at: Integer<br />

...<br />

Found<br />

at: Integer<br />

...<br />

Result <br />

add(x: Object): Object<br />

f<strong>in</strong>d(): Object<br />

sub(): Object<br />

Abbildung 48: <strong>Objekt</strong>-Orientierter Entwurf von adt.tree.set<br />

Problem: add(), sub() und f<strong>in</strong>d() benötigen die Position im Baum, an der<br />

das gesuchte <strong>Objekt</strong> steht/e<strong>in</strong>getragen werden soll.<br />

Beim Array war dies e<strong>in</strong>fach: (1) Suche liefert Indexplatz, (2) Methode spr<strong>in</strong>gt<br />

an den entsprechenden Platz.<br />

Um weiterh<strong>in</strong> die Lokalisation unabhängig von den MyCollection-Methoden<br />

zu halten, muss man sich die Position im Baum merken, um sie <strong>in</strong> diesen<br />

Methoden nutzen zu können.<br />

<strong>Objekt</strong>-Orientierte Lösung: “E<strong>in</strong>frieren” des Ergebnisses der Traverse <strong>in</strong> e<strong>in</strong>em<br />

<strong>Objekt</strong>! (das das Interface Result) implementiert.<br />

Spezielle Realisierung der Datenstruktur “Suchbaum”: Element ist selbst<br />

Suchbaum, besteht also aus E<strong>in</strong>trag und Verweis auf l<strong>in</strong>ken und rechten<br />

Unterbaum.<br />

// adt/tree/Set.java // A.T. Schre<strong>in</strong>er<br />

package adt.tree;<br />

import adt.MyCollection;<br />

import adt.Visitable;<br />

import adt.Visitor;<br />

/** b<strong>in</strong>ary tree based collection for Comparable with unique <strong>in</strong>sertion.<br />

*/<br />

public class Set implements MyCollection, Visitable {<br />

protected Element[] sub = { null };<br />

/** element structure, not limited to Comparable.<br />

*/<br />

protected static class Element extends Set {<br />

public f<strong>in</strong>al Object <strong>in</strong>fo;<br />

/** create.<br />

*/<br />

public Element (Object <strong>in</strong>fo) { this(<strong>in</strong>fo, null, null); }


170 <strong>Informatik</strong> B SS 03<br />

/** create and l<strong>in</strong>k.<br />

*/<br />

public Element (Object <strong>in</strong>fo, Element left, Element right) {<br />

this.<strong>in</strong>fo = <strong>in</strong>fo; sub = new Element[] { left, right };<br />

}<br />

/** let this disappear.<br />

@return comb<strong>in</strong>ed subtrees as a tree.<br />

*/<br />

public Element unroot () {<br />

if (sub[0] == null) return sub[1]; // noth<strong>in</strong>g at left... right<br />

if (sub[1] == null) return sub[0]; // noth<strong>in</strong>g at right... left<br />

Element e = sub[0];<br />

// else start at left...<br />

while (e.sub[1] != null) e = e.sub[1]; // ...move to it’s bottom right<br />

e.sub[1] = sub[1];<br />

// ...and attach my right there<br />

return sub[0];<br />

}<br />

/** adjust count by current element.<br />

*/<br />

public <strong>in</strong>t count () { return super.count()+1; }<br />

/** permit symbolic dump.<br />

*/<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g () { return <strong>in</strong>fo+""; }<br />

/** receive a visitor.<br />

*/<br />

public boolean visit (Visitor v) {<br />

return visit(0, v) && v.visit(<strong>in</strong>fo) && visit(1, v);<br />

}<br />

// ...all is below left<br />

}<br />

/** <strong>in</strong>efficient, unless the collection is empty.<br />

@return number of dist<strong>in</strong>ct objects <strong>in</strong> the collection;<br />

zero, if the collection is empty.<br />

*/<br />

public <strong>in</strong>t count () {<br />

<strong>in</strong>t result = 0;<br />

for (<strong>in</strong>t n = 0; n < sub.length; ++ n)<br />

if (sub[n] != null) result += sub[n].count();<br />

return result;<br />

}<br />

/** <strong>in</strong>sert an object <strong>in</strong>to the collection.<br />

@throws ClassCastException if x is not Comparable.<br />

*/<br />

public Object add (Object x) throws ClassCastException {<br />

return locate(x).add(x);<br />

}<br />

/** locate an object <strong>in</strong> the collection.<br />

@throws RuntimeException if not found.<br />

@throws ClassCastException if x is not Comparable.<br />

*/


<strong>Informatik</strong> B SS 03 171<br />

public Object f<strong>in</strong>d (Object x) throws ClassCastException {<br />

return locate(x).f<strong>in</strong>d();<br />

}<br />

/** remove an object from the collection.<br />

@throws RuntimeException if not found.<br />

@throws ClassCastException if x is not Comparable.<br />

*/<br />

public Object sub (Object x) throws ClassCastException {<br />

return locate(x).sub();<br />

}<br />

/** operations on comparison state of locate().<br />

*/<br />

protected <strong>in</strong>terface Result {<br />

Object add (Object x);<br />

Object f<strong>in</strong>d ();<br />

Object sub ();<br />

}<br />

/** locate an object <strong>in</strong> the collection.<br />

@param x object to be found.<br />

@return comparison state for further process<strong>in</strong>g.<br />

*/<br />

protected Result locate (Object x) throws ClassCastException {<br />

Comparable <strong>in</strong>fo = (Comparable)x; // can still be null<br />

Set s = this; <strong>in</strong>t at = 0;<br />

for (;;) {<br />

if (s.sub[at] == null) return s.new NotFound(at);<br />

// cannot <strong>in</strong>volve null <strong>in</strong> compareTo...<br />

<strong>in</strong>t c; // ...make null less than anyth<strong>in</strong>g<br />

if (s.sub[at].<strong>in</strong>fo == null) c = <strong>in</strong>fo == null 0 : 1;<br />

else c = <strong>in</strong>fo == null -1 : <strong>in</strong>fo.compareTo(s.sub[at].<strong>in</strong>fo);<br />

if (c == 0) return s.new Found(at);<br />

s = s.sub[at]; at = c < 0 0 : 1;<br />

}<br />

}<br />

/** this.sub[at] is null and should be Element(x).<br />

*/<br />

protected class NotFound implements Result {<br />

protected f<strong>in</strong>al <strong>in</strong>t at;<br />

public NotFound (<strong>in</strong>t at) { this.at = at; }<br />

public Object add (Object x) { sub[at] = new Element(x); return x; }<br />

public Object f<strong>in</strong>d () { throw new RuntimeException("not found"); }<br />

public Object sub () { throw new RuntimeException("not found"); }<br />

}<br />

/** this.sub[at].<strong>in</strong>fo is x.<br />

*/<br />

protected class Found implements Result {<br />

protected f<strong>in</strong>al <strong>in</strong>t at;<br />

public Found (<strong>in</strong>t at) { this.at = at; }


172 <strong>Informatik</strong> B SS 03<br />

}<br />

public Object add (Object x) { return sub[at].<strong>in</strong>fo; }<br />

public Object f<strong>in</strong>d () { return sub[at].<strong>in</strong>fo; }<br />

public Object sub () {<br />

Object result = sub[at].<strong>in</strong>fo; sub[at] = sub[at].unroot(); return result;<br />

}<br />

}<br />

/** receive a visitor.<br />

*/<br />

public boolean visit (Visitor v) { return visit(0, v); }<br />

protected boolean visit (<strong>in</strong>t at, Visitor v) {<br />

return sub[at] != null sub[at].visit(v) : true;<br />

}<br />

/** permit symbolic dump.<br />

*/<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g () {<br />

class Dumper implements Visitor {<br />

protected Str<strong>in</strong>gBuffer buf = new Str<strong>in</strong>gBuffer();<br />

public boolean visit (Object o) {<br />

buf.append(’\n’).append(o); return true;<br />

}<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g () { return buf.toStr<strong>in</strong>g(); }<br />

}<br />

Dumper d = new Dumper(); visit(d);<br />

return super.toStr<strong>in</strong>g()+" count "+count()+d.toStr<strong>in</strong>g();<br />

}<br />

11.7.3 Erläuterungen zu ‘Set’<br />

Visitor, Visitable: Interfaces <strong>in</strong> adt (vgl. Visitor Pattern, Cooper, Kap. 26).<br />

siehe weiter unten<br />

Instanzvariable sub als Array von Element-en.<br />

Idee: Bei Set hat sub e<strong>in</strong> Element, nämlich den kompletten Suchbaum. Bei<br />

Element hat sub zwei Elemente, mit sub[0] als l<strong>in</strong>kem (kle<strong>in</strong>ere Elemente)<br />

und sub[1] als rechtem (grössere Elemente) Unterbaum.<br />

Wieder Element als statische <strong>in</strong>nere Klasse, aber: stammt von Set ab und erbt<br />

deshalb die Instanzvariable sub.<br />

Die Modellierung des ADT Suchbaum über Element als Unterklasse des<br />

Suchbaums selbst ist nicht Standard! (vgl. Realisierung <strong>in</strong> Vorlesung <strong>Informatik</strong><br />

A) Bei der hier gezeigten Implementierung werden dafür objekt-<strong>orientierte</strong><br />

Techniken sehr schön verdeutlicht.<br />

<strong>in</strong>fo-<strong>Objekt</strong>e s<strong>in</strong>d hier allgeme<strong>in</strong> als vom Typ Object deklariert und nicht auf<br />

Comparable beschränkt. (Platz für eigene Def<strong>in</strong>itionen von Vergleichen)<br />

Analog zur Liste gibt es zwei Konstruktoren: Wurzel mit zwei Unterbäumen oder<br />

Blatt.


<strong>Informatik</strong> B SS 03 173<br />

Tabelle 5: Verhalten von locate()<br />

¡<br />

Gesucht E<strong>in</strong>trag Wert weiter<br />

null = null 0 fertig<br />

anderes null 1 rechts<br />

null anderes -1 l<strong>in</strong>ks<br />

anderes anderes compareTo() depends<br />

Analog zu unl<strong>in</strong>k() ist unroot() realisiert: Hat das aktuelle Element ke<strong>in</strong>en<br />

oder nur e<strong>in</strong>en Unterbaum wird dieser (null wenn ke<strong>in</strong> Unterbaum)<br />

zurückgeliefert.<br />

Komplizierterer Fall: Element hat zwei Unterbäume. An das rechteste Element<br />

im l<strong>in</strong>ken Unterbaum (grösstes Element vor dem aktuellen) wird der rechte<br />

Unterbaum angehängt.<br />

count() wird mit super auf Set zurückgespielt und muss zusätzlich das<br />

Element selbst zählen, da super.count() die Anzahl der Elemente der<br />

Unterbäume aufaddiert.<br />

add(), f<strong>in</strong>d() und sub() werden wieder über locate() realisiert.<br />

locate() muss entweder e<strong>in</strong> vorhandenes Element im Suchbaum f<strong>in</strong>den oder<br />

e<strong>in</strong>e geeignete Position liefern, an der e<strong>in</strong> Element e<strong>in</strong>gefügt werden soll.<br />

Neue Realisation: locate() liefert e<strong>in</strong> <strong>Objekt</strong>, das man mit E<strong>in</strong>fügen, F<strong>in</strong>den<br />

oder Löschen beauftragen kann. In diesem <strong>Objekt</strong> wird der Zustand der<br />

aktuellen Baumtraverse e<strong>in</strong>gefroren (Verweis auf aktuellen Knoten Element).<br />

Startet mit s beim Set und Index at bei 0. Das Argument (Object x) muss<br />

Comparable se<strong>in</strong>.<br />

Ist sub[at] null, so gibt es das gesuchte Element nicht. Es hätte aber an<br />

diese Stelle gehört.<br />

Spezielle Behandlung von null-Referenz: Entweder null ist an Position at<br />

e<strong>in</strong>getragen, dann wurde der Wert gefunden; anderenfalls wird -1<br />

zurückgeliefert (null ist kle<strong>in</strong>er als alle anderen Werte).<br />

Das endgültige Vergleichsresultat hängt vom weiteren Verlauf der Suche ab:<br />

Entweder der Vergleich stimmt, dann wurde e<strong>in</strong>e Position für den gesuchten<br />

Wert gefunden, oder die Suche geht im l<strong>in</strong>ken oder rechten Unterbaum weiter.<br />

E<strong>in</strong> Wert wird bei s.sub[at] gefunden oder er müsste dort gespeichert<br />

werden.<br />

Member-Klassen NotFound und Found: zur Weiterverarbeitung des von<br />

locate() gelieferten Result-<strong>Objekt</strong>s.<br />

Sie haben Zugriff auf Instanz-Komponenten der umschliessenden Klasse, für<br />

die sie erzeugt wurden – hier speziell auf Komponenten von s.


174 <strong>Informatik</strong> B SS 03<br />

E<strong>in</strong> NotFound- oder Found-<strong>Objekt</strong> kennt also durch se<strong>in</strong>e Konstruktion das<br />

Element oder Set s, das bei der Suche entdeckt wurde und hat damit Zugriff<br />

auf dessen sub. Es muss sich nur den Index at explizit merken.<br />

Durch die Def<strong>in</strong>ition des Result-Interfaces und der <strong>in</strong>neren Klassen NotFound<br />

und Found lassen sich add(), f<strong>in</strong>d() und sub() wesentlich e<strong>in</strong>facher<br />

realisieren als durch explizite if-Abfragen zur Fallunterscheidung <strong>in</strong> diesen<br />

Methoden selbst.<br />

Der e<strong>in</strong>zig komplizierte Fall – das Löschen e<strong>in</strong>es Wertes – kann mithilfe von<br />

unroot() leicht bewerkstelligt werden.<br />

Nested top-level Klassen s<strong>in</strong>d weniger aufwendig als echte Member-Klassen.<br />

Man sollte echte Member-Klassen nur dann verwenden, wenn man den<br />

impliziten Verweis auf die umschliessende Instanz benötigt.<br />

Häufig wird als Erzeuger für die <strong>in</strong>nere Instanz this (vor new) verwendet.<br />

E<strong>in</strong>e Konstruktion wie die NotFound- und Found-<strong>Objekt</strong>e <strong>in</strong> adt.tree.Set<br />

wird auch als Closure bezeichnet: Bei der Konstruktion wird e<strong>in</strong>e bestimmte<br />

Situation e<strong>in</strong>gefangen, die später durch Nachricht an e<strong>in</strong> solches <strong>Objekt</strong><br />

weiterverarbeitet werden kann (vgl. auch die anonymen Klassen <strong>in</strong> adt.Test).<br />

11.8 Visitor<br />

11.8.1 Konzept e<strong>in</strong>es Visitor<br />

Ganz allgeme<strong>in</strong> kann man von e<strong>in</strong>er Collection verlangen, dass e<strong>in</strong>em Besucher<br />

(Visitor) alle Insassen (Elemente) genau e<strong>in</strong>mal vorgestellt werden.<br />

Interface Visitable: Methode zum Anliefern e<strong>in</strong>es Visitor bei der<br />

Collection.<br />

E<strong>in</strong> beliebiges <strong>Objekt</strong> ist e<strong>in</strong> Visitor, wenn ihm <strong>Objekt</strong>e vorgeführt werden<br />

können.<br />

Interface Visitor: Methode, mit der die Collection dem Visitor ihre Insassen<br />

vorstellt.<br />

E<strong>in</strong>e Collection ist Visitable, wenn sie Besucher empfangen kann.<br />

Ausbaumöglichkeit: visit() <strong>in</strong> Visitor könnte mit verschiedenen Signaturen<br />

verschiedene Arten von Insassen unterscheiden.<br />

Beim Suchbaum hat man die schöne Möglichkeit, E<strong>in</strong>träge sortiert<br />

auszugeben. Beispielsweise kann adt.Test mit adt.tree.Set Wörter<br />

e<strong>in</strong>lesen (Str<strong>in</strong>g ist Comparable) und sortiert ausgeben.<br />

E<strong>in</strong> Suchbaum sollte Visitable se<strong>in</strong>, um die Sortierung ausnutzen zu können.<br />

Implementiert man e<strong>in</strong>en Visitor, der die Insassen <strong>in</strong> e<strong>in</strong>en Str<strong>in</strong>gBuffer<br />

abbildet, kann man den Baum sehr elegant darstellen lassen. (Methoden<br />

visit() und toStr<strong>in</strong>g() <strong>in</strong> adt.tree.Set)


<strong>Informatik</strong> B SS 03 175<br />

Visitable<br />

<br />

visit(v: Visitor): Boolean<br />

Visitor<br />

<br />

visit(x: Object): Boolean<br />

Set<br />

visit(v: Visitor): Boolean<br />

visit(at: Integer, v: Visitor): Boolean<br />

toStr<strong>in</strong>g(): Str<strong>in</strong>g<br />

Dumper<br />

visit(x: Object): Boolean<br />

Set.Element<br />

visit(v: Visitor): Boolean<br />

Abbildung 49: Realisierung des Visitor-Patterns <strong>in</strong> adt.tree.set<br />

Die visit()-Methoden haben als Resultat-Wert boolean. Über das Resultat<br />

der Methode <strong>in</strong> Visitor kann der Besucher steuern, ob die Traverse<br />

fortgesetzt werden soll (Besucher sagt: “weiter” oder “ich will nicht mehr”).<br />

11.8.2 ‘Visitor’, ‘Visitable’/ADT<br />

// adt/Visitable.java // A.T. Schre<strong>in</strong>er<br />

package adt;<br />

/** framework for visitor pattern: ability to be visited.<br />

*/<br />

public <strong>in</strong>terface Visitable {<br />

/** receive a visitor, manage the visit.<br />

@return true if the visitor always replies true.<br />

*/<br />

boolean visit (Visitor v);<br />

}<br />

// adt/Visitor.java // A.T. Schre<strong>in</strong>er<br />

package adt;<br />

/** framework for visitor pattern: visit<strong>in</strong>g object.<br />

*/


176 <strong>Informatik</strong> B SS 03<br />

public <strong>in</strong>terface Visitor {<br />

/** visit an object.<br />

@return true to cont<strong>in</strong>ue visit<strong>in</strong>g.<br />

*/<br />

boolean visit (Object x);<br />

}<br />

11.8.3 Suchbaum mit Visitor<br />

In Set wird e<strong>in</strong>e <strong>in</strong>order Traversierung realisiert. (Drei Möglichkeiten, e<strong>in</strong>en<br />

Baum zu traversieren: Inorder, Präorder, Postorder).<br />

visit()-Methoden <strong>in</strong> Set: visit() mit e<strong>in</strong>em Argument schickt Visitor zu<br />

sub[0], visit() mit zwei Argumenten schickt Visitor zu Position at.<br />

visit()-Methoden <strong>in</strong> Element: visit() mit e<strong>in</strong>em Argument realisiert<br />

Inorder, visit() mit zwei Argumenten wird von Set geerbt.<br />

Für adt.tree.Set implementiert man toStr<strong>in</strong>g() mit e<strong>in</strong>em<br />

Dumper-<strong>Objekt</strong>, das jeden Insassen <strong>in</strong> se<strong>in</strong>en Str<strong>in</strong>gBuffer e<strong>in</strong>trägt und<br />

diesen bei toStr<strong>in</strong>g() (von Dumper) als se<strong>in</strong>e eigene Darstellung abliefert.<br />

Dumper ist e<strong>in</strong> Beispiel für e<strong>in</strong>e lokale Klasse (Es hätte auch e<strong>in</strong>e anonyme<br />

Klasse genügt). E<strong>in</strong>e lokale Klasse kann jedoch mehr als e<strong>in</strong> Interface auf<br />

e<strong>in</strong>mal implementieren.<br />

Innerhalb der lokalen Klasse wird wieder e<strong>in</strong>e visit()-Methode implementiert.<br />

In toStr<strong>in</strong>g() wird die visit()-Methode von Set aufgerufen, der e<strong>in</strong><br />

Dumper-<strong>Objekt</strong> übergeben wird.<br />

Im zwei-parametrigen visit() wird geprüft, ob an der aktuellen Position noch<br />

e<strong>in</strong> Unterbaum existiert. Wenn ja, wird die visit()-Methode für diesen<br />

Unterbaum an Position at aufgerufen.<br />

Die eigentliche Arbeit erledigt die visit()-Methode von Element: Hier wird<br />

die Inorder-Traverse realisiert. Das <strong>Objekt</strong> <strong>in</strong>fo wird an die visit()-Methode<br />

des Visitors übergeben.<br />

11.9 <strong>Java</strong> Collection Classes<br />

11.9.1 Grundstruktur<br />

<strong>Java</strong> collection framework <strong>in</strong> java.util: wichtige Klassen und Interfaces, um<br />

mit Collections zu arbeiten.<br />

Bis <strong>Java</strong> 1.1: nur Vector (jetzt ArrayList) und HashTable (jetzt HashMap).<br />

Zwei Grundtypen von Collections:


<strong>Informatik</strong> B SS 03 177<br />

1. Collection (Interface): Gruppe von <strong>Objekt</strong>en<br />

mit Set (Interface) als Collections ohne Dublikate<br />

und List (Interface) als Collection mit geordneten Elementen<br />

2. Map (Interface): Menge von Assoziationen (Mapp<strong>in</strong>gs) zwischen <strong>Objekt</strong>en.<br />

weitere Interfaces: Iterator, ListIterator.<br />

Ähnlich zum Enumeration-Interface:<br />

public <strong>in</strong>terface Iterator {<br />

boolean hasNext(); // vgl. hasMoreElements() <strong>in</strong> Enumeration<br />

Object next(); // vgl. nextElement() <strong>in</strong> Enumeration<br />

void remove(); // zusaetzlich<br />

}<br />

remove() erlaubt sicheres Löschen von Elementen während der Iteration.<br />

Gelöscht wird das als letztes von next() gelieferte Element.<br />

Modifikation der Collection während ihrer Aufzählung ist mit remove() (und nur<br />

mit remove()) möglich!<br />

Für <strong>Objekt</strong>e eigener Klassen, die <strong>in</strong> Collections gespeichert werden sollen,<br />

sollten die Methoden equals() und hashcode() entsprechend der<br />

gewünschten Funktionalität überschrieben werden.<br />

Die Klasse Collections liefert statische Methoden und Konstanten, die beim<br />

Arbeiten mit Collections nützlich s<strong>in</strong>d.<br />

11.9.2 Illustration<br />

Set s = new HashSet();<br />

// Implementation based on a hash table<br />

s.add("test");<br />

// Add a Str<strong>in</strong>g object to the set<br />

boolean b = s.conta<strong>in</strong>s("test2"); // Check whether a set conta<strong>in</strong>s an obj<br />

s.remove("test");<br />

// Remove a member from a set<br />

Set ss = new TreeSet();<br />

// TreeSet implements SortedSet<br />

ss.add("b");<br />

// Add some elements<br />

ss.add("a");<br />

// Now iterate through the elements (<strong>in</strong> sorted order) and pr<strong>in</strong>t them<br />

for(Iterator i = ss.iterator(); i.hasNext();)<br />

System.out.pr<strong>in</strong>tln(i.next());<br />

List l = new L<strong>in</strong>kedList(); // L<strong>in</strong>kedList implements a doubly l<strong>in</strong>ked list<br />

l = new ArrayList();<br />

// ArrayList is more efficient, usually<br />

Vector v = new Vector(); // Vector is an alternative <strong>in</strong> <strong>Java</strong> 1.1/1.0<br />

l.addAll(ss);<br />

// Append some elements to it<br />

l.addAll(1, ss); // Insert elements aga<strong>in</strong> at <strong>in</strong>dex 1<br />

Object o = l.get(1);<br />

// Get the second element<br />

l.set(3, "new element"); // Set the fourth element


178 <strong>Informatik</strong> B SS 03<br />

java.lang<br />

java.util<br />

Object AbstractCollection AbstractList AbstractSequentialList L<strong>in</strong>kedList<br />

C<br />

S<br />

ArrayList<br />

C<br />

S<br />

Vector<br />

C<br />

S<br />

Stack<br />

AbstractSet<br />

HashSet<br />

C<br />

S<br />

TreeSet<br />

C<br />

S<br />

Collection<br />

<br />

List<br />

<br />

AbstractMap<br />

Set<br />

<br />

HashMap<br />

C<br />

S<br />

SortedSet<br />

<br />

Comparator<br />

<br />

TreeMap<br />

C<br />

S<br />

Iterator<br />

<br />

ListIterator<br />

<br />

Map<br />

<br />

SortedMap<br />

<br />

Abbildung 50: <strong>Java</strong> Collection Classes (S: implements Serializable, C: implements Clonable)


<strong>Informatik</strong> B SS 03 179<br />

l.add("test");<br />

// Append a new element to the end<br />

l.add(0, "test2");<br />

// Insert a new element at the start<br />

l.remove(1);<br />

// Remove the second element<br />

l.remove("a");<br />

// Remove the element "a"<br />

l.removeAll(ss);<br />

// Remove elements from this set<br />

if(!l.isEmpty())<br />

// If list is not empty<br />

System.out.pr<strong>in</strong>tln(l.size()); // pr<strong>in</strong>t out the number of elements <strong>in</strong> it<br />

boolean b1 = l.conta<strong>in</strong>s("a"); // Does it conta<strong>in</strong> this value<br />

booelan b2 = l.conta<strong>in</strong>sAll(ss); // Does it conta<strong>in</strong> all these values<br />

List sublist = l.subList(1,3); // A sublist of the 2nd and 3rd elements<br />

Object[] elements = l.toArray(); // Convert it to an array<br />

l.clear();<br />

// Delete all elements<br />

Map m = new HashMap(); // Hashtable an alternative <strong>in</strong> <strong>Java</strong> 1.1./1.0<br />

m.put("key", new Integer(42)); // Associate a value object with key object<br />

Object value = m.get("key"); // Look up the value association from the Map<br />

m.remove("key");<br />

// Remove association from the Map<br />

Set keys = m.keySet();<br />

// Get the set of keys held by the Map<br />

Arrays und Collections können wechselseitig konvertiert werden:<br />

Object[] members = set.toArray();<br />

// Get set elements as an array<br />

Object[] items = list.toArray();<br />

// Get list elements as an array<br />

Object[] keys = map.keySet().toArray(); // Get map key objects as an array<br />

Object[] values = map.values().toArray(); // Get map value objects as an array<br />

List l = Arrays.asList(elements);<br />

// View array as ungrowable list<br />

List l = new ArrayList(Arrays.asList(elements));<br />

// Make a growable copy of it<br />

So wie java.util.Arrays Methoden zur Manipulation von Arrays anbietet,<br />

def<strong>in</strong>iert java.util.Collections Methoden zum Umgang mit Collections.<br />

Beispiele:<br />

Collections.sort(list);<br />

// Sort a list<br />

<strong>in</strong>t pos = Collections.b<strong>in</strong>arySearch(list, "key"); // List must be sorted first<br />

Collections.max(c);<br />

// F<strong>in</strong>d largest element <strong>in</strong> Collection c<br />

Collections.m<strong>in</strong>(c);<br />

// F<strong>in</strong>d smallest element <strong>in</strong> Collection c<br />

Collections.reverse(list); // Reverse list<br />

Collections.shuffle(list); // Mix up list


180 <strong>Informatik</strong> B SS 03<br />

12 Reflections<br />

12.1 Methoden des Reflection-API<br />

Das Reflection-API repräsentiert (reflektiert) Klassen, Schnittstellen und <strong>Objekt</strong>e<br />

<strong>in</strong> der aktuellem JVM.<br />

Anwendung: vor allem für Debugger, Class-Browser, GUI-Builder.<br />

Dale E. Parson (2000). Us<strong>in</strong>g <strong>Java</strong> Reflection to Automate Extension Language Pars<strong>in</strong>g.<br />

ACM SIGPLAN Notices, 35(1), pp. 67–80.<br />

Methodenübersicht:<br />

Bestimmung der Klasse e<strong>in</strong>es <strong>Objekt</strong>s.<br />

Information über Modifikatoren, Felder, Methoden, Konstruktoren, Oberklassen<br />

e<strong>in</strong>er Klasse.<br />

Information, welche Konstanten und Methoden-Deklarationen zu e<strong>in</strong>em<br />

Interface gehören.<br />

Erzeugung e<strong>in</strong>er Instanz e<strong>in</strong>er Klasse, deren Namen erst zur Laufzeit bekannt<br />

ist.<br />

Zugriff und Belegung e<strong>in</strong>es <strong>Objekt</strong>-Feldes, auch wenn der Name des Feldes erst<br />

zur Laufzeit bekannt ist.<br />

Aufruf (Invocation) e<strong>in</strong>er Methode e<strong>in</strong>es <strong>Objekt</strong>s, auch wenn die Methode erst<br />

zur Laufzeit bekannt ist.<br />

Erzeugen e<strong>in</strong>es neuen Arrays, dessen Grösse und Komponenten-Typ erst zur<br />

Laufzeit bekannt s<strong>in</strong>d, und Modifikation von Komponenten.<br />

12.2 Die Klassen ‘Class’, ‘Method’, ‘Field’ und ‘Constructor’<br />

Die Klasse Class repräsentiert e<strong>in</strong>en <strong>Java</strong>-Typ (Klasse, Interface, primitiver<br />

Typ).<br />

Für jede von der JVM geladene Klasse existiert genau e<strong>in</strong> Class-<strong>Objekt</strong>.<br />

Dieses <strong>Objekt</strong> kann durch Aufruf der getClass()-Methode für jede beliebige<br />

Instanz besorgt werden. Wie man Klassen mit Class-Methoden <strong>in</strong>spizieren<br />

kann, wird im nächsten Abschnitt dargestellt.<br />

public f<strong>in</strong>al class Class extends Object implements Serializable {<br />

public static Class forName(Str<strong>in</strong>g name) throws ClassNotFoundException;<br />

public Field[] getFields() throws SecurityException;<br />

public Method[] getMethods() throws SecurityException;<br />

public Constructor[] getConstructors() throws SecurityException;<br />

// many more<br />

}


<strong>Informatik</strong> B SS 03 181<br />

Die java.lang.reflect-Klassen Method, Field und Constructor<br />

repräsentieren Methoden, Felder und Konstruktoren e<strong>in</strong>er Klasse.<br />

Entsprechende <strong>Objekt</strong>e werden von entsprechenden get-Methoden e<strong>in</strong>es<br />

Class-<strong>Objekt</strong>s zurückgeliefert.<br />

Diese Klassen s<strong>in</strong>d Unterklassen von AccessibleObject und implementieren<br />

das Member-Interface. Wie Klassenkomponenten <strong>in</strong>spiziert werden können,<br />

wird im nächsten Abschnitt dargestellt. Mit newInstance() können zur<br />

Laufzeit neue <strong>Objekt</strong>e erzeugt werden (übernächster Abschnitt).<br />

public f<strong>in</strong>al class Method extends AccessibleObject implements Member {<br />

public Str<strong>in</strong>g getName();<br />

public Class[] getParameterTypes();<br />

public Class getReturnType();<br />

public Object <strong>in</strong>voke(Object obj, Object[] args)<br />

throws IllegalAccessException, IllegalArgumentException,<br />

InvocationTargetException;<br />

// many more<br />

}<br />

public f<strong>in</strong>al class Field extends AccessibleObject implements Member {<br />

public Str<strong>in</strong>g getName();<br />

public Class getType();<br />

public boolean equals(Object obj);<br />

public <strong>in</strong>t hashCode();<br />

// many more<br />

}<br />

public f<strong>in</strong>al class Constructor extends AccessibleObject implements Member {<br />

public Str<strong>in</strong>g getName();<br />

public Class[] getParameterTypes();<br />

public Class[] getExceptionTypes();<br />

public Object newInstance(Object[] <strong>in</strong>itargs) throws<br />

InstantiationException, IllegalAccessException, IllegalArgumentException;<br />

// many more<br />

}<br />

12.3 Inspektion von Klassen<br />

Das Laufzeitsystem hält für jede Klasse/Schnittstelle e<strong>in</strong> nicht-änderbares<br />

Class-<strong>Objekt</strong>, das Informationen über die Klasse/Schnittstelle enthält.<br />

12.3.1 Abruf e<strong>in</strong>es ‘Class’ <strong>Objekt</strong>s<br />

.getClass() Class<br />

object¡<br />

.getSuperClass() Class<br />

¡<br />

¡<br />

class¡


182 <strong>Informatik</strong> B SS 03<br />

Class.forName( Str<strong>in</strong>g¡ ) ¡ Class<br />

class¡ .class ¡<br />

Class<br />

Wenn e<strong>in</strong>e Instanz e<strong>in</strong>er Klasse verfügbar ist, kann dessen Klasse abgefragt<br />

werden: Class c = mystery.getClass();<br />

Abruf der Oberklasse: (Circle-<strong>Objekt</strong> mit Oberklasse Shape)<br />

Circle k = new Circle();<br />

Class c = k.getClass();<br />

Class s = c.getSuperClass();<br />

Wenn der Name e<strong>in</strong>er Klasse zur Compile-Zeit bekannt ist, kann das<br />

Klassenobjekt abgerufen werden, <strong>in</strong>dem .class an den Namen angehängt<br />

wird.<br />

Class c = java.awt.Button.class;<br />

Wenn der Klassen-Name erst zur Laufzeit verfügbar ist, kann das<br />

Klassen-<strong>Objekt</strong> über den Namen (als Str<strong>in</strong>g repräsentiert) erzeugt werden.<br />

Class c = Class.forName(strg);<br />

12.3.2 Abruf des Klassen-Namens e<strong>in</strong>es <strong>Objekt</strong>s<br />

class¡ .getName() ¡<br />

Str<strong>in</strong>g<br />

import java.awt.Button;<br />

class SampleName {<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args) {<br />

Button b = new Button();<br />

pr<strong>in</strong>tName(b);<br />

}<br />

static void pr<strong>in</strong>tName(Object o) { Class c = o.getClass(); Str<strong>in</strong>g s =<br />

c.getName(); System.out.pr<strong>in</strong>tln(s);<br />

} }<br />

Rückgabe des voll qualifizierten Namens der Klasse als Str<strong>in</strong>g:<br />

java.awt.Button


<strong>Informatik</strong> B SS 03 183<br />

12.3.3 Abruf von Klassen-Modifikatoren<br />

<strong>in</strong>t<br />

.getModifiers() class¡ ¡<br />

Modifier.isPublic( ) bool<br />

<strong>in</strong>tcode¡ ¡<br />

Modifier.isAbstract( ) bool<br />

<strong>in</strong>tcode¡ ¡<br />

Modifier.isF<strong>in</strong>al( ) bool<br />

<strong>in</strong>tcode¡ ¡<br />

import java.lang.reflect.Modifier;<br />

class SampleModifier {<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args) {<br />

Str<strong>in</strong>g s = new Str<strong>in</strong>g();<br />

pr<strong>in</strong>tModifiers(s);<br />

}<br />

public static void pr<strong>in</strong>tModifiers(Object o) {<br />

Class c = o.getClass();<br />

<strong>in</strong>t m = c.getModifiers();<br />

if (Modifier.isPublic(m))<br />

System.out.pr<strong>in</strong>tln("public");<br />

if (Modifier.isAbstract(m))<br />

System.out.pr<strong>in</strong>tln("abstract");<br />

if (Modifier.isF<strong>in</strong>al(m))<br />

System.out.pr<strong>in</strong>tln("f<strong>in</strong>al");<br />

}<br />

}<br />

Ausgabe: Modifikatoren von Str<strong>in</strong>g s<strong>in</strong>d:<br />

public<br />

f<strong>in</strong>al<br />

12.3.4 Abruf von Oberklassen<br />

.getSuperclass() Class<br />

class¡ ¡<br />

import java.awt.Button;<br />

class SampleSuper {<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args) { Button b = new Button();<br />

pr<strong>in</strong>tSuperclasses(b);<br />

}<br />

static void pr<strong>in</strong>tSuperclasses(Object o) {<br />

Class subclass = o.getClass();<br />

Class superclass = subclass.getSuperclass();


184 <strong>Informatik</strong> B SS 03<br />

while (superclass != null) {<br />

Str<strong>in</strong>g className = superclass.getName();<br />

System.out.pr<strong>in</strong>tln(className);<br />

subclass = superclass;<br />

superclass = subclass.getSuperclass();<br />

} } }<br />

Die Oberklasse von Button ist Component und deren Oberklasse ist Object.<br />

java.awt.Component<br />

java.lang.Object<br />

12.3.5 Abruf des implementierten Interfaces e<strong>in</strong>er Klasse<br />

class¡ .getInterfaces() ¡<br />

Class[]<br />

import java.io.RandomAccessFile;<br />

import java.io.IOException;<br />

class SampleInterface {<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args) {<br />

try {<br />

RandomAccessFile r = new RandomAccessFile("myfile", "r");<br />

pr<strong>in</strong>tInterfaceNames(r);<br />

} catch (IOException e) { System.err.pr<strong>in</strong>tln(e); }<br />

}<br />

static void pr<strong>in</strong>tInterfaceNames(Object o) {<br />

Class c = o.getClass();<br />

Class[] theInterfaces = c.getInterfaces();<br />

for (<strong>in</strong>t i = 0; i < theInterfaces.length; i++) {<br />

Str<strong>in</strong>g <strong>in</strong>terfaceName = theInterfaces[i].getName();<br />

System.out.pr<strong>in</strong>tln(<strong>in</strong>terfaceName);<br />

} } }<br />

Die Klasse RandomAccessFile implementiert die Interfaces DataOutput und<br />

DataInput:<br />

java.io.DataOutput<br />

java.io.DataInput<br />

12.3.6 Interface oder Klasse<br />

class¡ .isInterface() ¡<br />

bool<br />

import java.util.Observer;<br />

import java.util.Observable;


<strong>Informatik</strong> B SS 03 185<br />

class SampleCheckInterface {<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args) {<br />

Class observer = Observer.class;<br />

Class observable = Observable.class;<br />

verifyInterface(observer);<br />

verifyInterface(observable);<br />

}<br />

static void verifyInterface(Class c) {<br />

Str<strong>in</strong>g name = c.getName();<br />

if (c.isInterface()) {<br />

System.out.pr<strong>in</strong>tln(name + " is an <strong>in</strong>terface.");<br />

} else { System.out.pr<strong>in</strong>tln(name + " is a class."); }<br />

} }<br />

Ausgabe:<br />

java.util.Observer is an <strong>in</strong>terface.<br />

java.util.Observable is a class.<br />

12.3.7 Abruf von Klassen-Feldern<br />

.getFields() class¡<br />

.getType() Class<br />

¡<br />

¡<br />

field¡<br />

Field[]<br />

import java.lang.reflect.Field;<br />

import java.awt.GridBagConstra<strong>in</strong>ts;<br />

class SampleField {<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args) {<br />

GridBagConstra<strong>in</strong>ts g = new GridBagConstra<strong>in</strong>ts();<br />

pr<strong>in</strong>tFieldNames(g);<br />

}<br />

static void pr<strong>in</strong>tFieldNames(Object o) {<br />

Class c = o.getClass();<br />

Field[] publicFields = c.getFields();<br />

for (<strong>in</strong>t i = 0; i < publicFields.length; i++) {<br />

Str<strong>in</strong>g fieldName = publicFields[i].getName();<br />

Class typeClass = publicFields[i].getType();<br />

Str<strong>in</strong>g fieldType = typeClass.getName();<br />

System.out.pr<strong>in</strong>tln("Name: " + fieldName +<br />

", Type: " + fieldType);<br />

}<br />

} }<br />

Beg<strong>in</strong>n der Ausgabe:


186 <strong>Informatik</strong> B SS 03<br />

Name: RELATIVE, Type: <strong>in</strong>t<br />

Name: REMAINDER, Type: <strong>in</strong>t<br />

Name: NONE, Type: <strong>in</strong>t<br />

Name: BOTH, Type: <strong>in</strong>t<br />

Name: HORIZONTAL, Type: <strong>in</strong>t<br />

Name: VERTICAL, Type: <strong>in</strong>t ...<br />

12.3.8 Abruf von Klassen-Konstruktoren<br />

.getConstructors() Constructors[]<br />

class¡<br />

.getParameterTypes() Class[]<br />

¡<br />

¡<br />

constructor¡<br />

import java.lang.reflect.Constructor;<br />

import java.awt.Rectangle;<br />

class SampleConstructor {<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args) {<br />

Rectangle r = new Rectangle();<br />

showConstructors(r);<br />

}<br />

static void showConstructors(Object o) {<br />

Class c = o.getClass();<br />

Constructor[] theConstructors = c.getConstructors();<br />

for (<strong>in</strong>t i = 0; i < theConstructors.length; i++) {<br />

System.out.pr<strong>in</strong>t("( ");<br />

Class[] parameterTypes =<br />

theConstructors[i].getParameterTypes();<br />

for (<strong>in</strong>t k = 0; k < parameterTypes.length; k ++) {<br />

Str<strong>in</strong>g parameterStr<strong>in</strong>g = parameterTypes[k].getName();<br />

System.out.pr<strong>in</strong>t(parameterStr<strong>in</strong>g + " ");<br />

}<br />

System.out.pr<strong>in</strong>tln(")");<br />

}<br />

} }<br />

( )<br />

( <strong>in</strong>t <strong>in</strong>t )<br />

( <strong>in</strong>t <strong>in</strong>t <strong>in</strong>t <strong>in</strong>t )<br />

( java.awt.Dimension )<br />

( java.awt.Po<strong>in</strong>t )<br />

( java.awt.Po<strong>in</strong>t java.awt.Dimension )<br />

( java.awt.Rectangle )<br />

12.3.9 Abruf von Methoden-Information<br />

.getMethods() Method[]<br />

class¡<br />

.getReturnType() Class<br />

¡<br />

¡<br />

method¡


<strong>Informatik</strong> B SS 03 187<br />

method¡ .getParameterTypes() ¡<br />

Class[]<br />

import java.lang.reflect.Method;<br />

import java.awt.Polygon;<br />

class SampleMethod {<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args) {<br />

Polygon p = new Polygon();<br />

showMethods(p);<br />

}<br />

}<br />

static void showMethods(Object o) {<br />

Class c = o.getClass();<br />

Method[] theMethods = c.getMethods();<br />

for (<strong>in</strong>t i = 0; i < theMethods.length; i++) {<br />

Str<strong>in</strong>g methodStr<strong>in</strong>g = theMethods[i].getName();<br />

System.out.pr<strong>in</strong>tln("Name: " + methodStr<strong>in</strong>g);<br />

Str<strong>in</strong>g returnStr<strong>in</strong>g =<br />

theMethods[i].getReturnType().getName();<br />

System.out.pr<strong>in</strong>tln(" Return Type: " + returnStr<strong>in</strong>g);<br />

Class[] parameterTypes = theMethods[i].getParameterTypes();<br />

System.out.pr<strong>in</strong>t(" Parameter Types:");<br />

for (<strong>in</strong>t k = 0; k < parameterTypes.length; k ++) {<br />

Str<strong>in</strong>g parameterStr<strong>in</strong>g = parameterTypes[k].getName();<br />

System.out.pr<strong>in</strong>t(" " + parameterStr<strong>in</strong>g);<br />

}<br />

System.out.pr<strong>in</strong>tln();<br />

}<br />

}<br />

Name: equals<br />

Return Type: boolean<br />

Parameter Types: java.lang.Object<br />

Name: getClass<br />

Return Type: java.lang.Class<br />

Parameter Types:<br />

Name: hashCode<br />

Return Type: <strong>in</strong>t<br />

Parameter Types:<br />

.<br />

.<br />

Name: <strong>in</strong>tersects<br />

Return Type: boolean<br />

Parameter Types: double double double double<br />

Name: <strong>in</strong>tersects<br />

Return Type: boolean<br />

Parameter Types: java.awt.geom.Rectangle2D


188 <strong>Informatik</strong> B SS 03<br />

Name: translate<br />

Return Type: void<br />

Parameter Types: <strong>in</strong>t <strong>in</strong>t<br />

12.4 Manipulation von <strong>Objekt</strong>en zur Laufzeit<br />

Erzeugung und Manipulation von <strong>Objekt</strong>en, deren Klassennamen erst zur<br />

Laufzeit bekannt s<strong>in</strong>d.<br />

12.4.1 Dynamische Erzeugung von <strong>Objekt</strong>en<br />

Der new-Operator kann nicht auf Variablen angewendet werden. Stattdessen:<br />

newInstance().<br />

import java.awt.Rectangle;<br />

class SampleNoArg {<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args) {<br />

Rectangle r = (Rectangle) createObject("java.awt.Rectangle");<br />

System.out.pr<strong>in</strong>tln(r);<br />

}<br />

static Object createObject(Str<strong>in</strong>g className) {<br />

Object object = null;<br />

try {<br />

Class classDef<strong>in</strong>ition = Class.forName(className);<br />

object = classDef<strong>in</strong>ition.newInstance();<br />

} catch (InstantiationException e) { System.err.pr<strong>in</strong>tln(e); }<br />

catch (IllegalAccessException e) { System.err.pr<strong>in</strong>tln(e); }<br />

catch (ClassNotFoundException e) { System.err.pr<strong>in</strong>tln(e); }<br />

return object;<br />

} }<br />

Ausgabe: java.awt.Rectangle[x=0,y=0,width=0,height=0]<br />

12.4.2 Exceptions beim dynamischen Erzeugen von <strong>Objekt</strong>en<br />

ClassNotFoundException: Es wird versucht, e<strong>in</strong> <strong>Objekt</strong> e<strong>in</strong>er Klasse zu<br />

erzeugen, die nicht (auf dem aktuellen Pfad) existiert.<br />

IllegalAccessException: Klasse darf nicht zugegriffen werden (z. B. nicht<br />

public)<br />

InstantiationException: Fehler bei der <strong>Objekt</strong>erzeugung (z.B. es gibt<br />

ke<strong>in</strong>en Konstruktor ohne Argumente)<br />

IllegalArgumentException: beim Aufruf von Konstruktor/Methode mit<br />

Argumenten. (RuntimeException)<br />

InvocationTargetException: “Meta”-Exception; es tritt Exception <strong>in</strong>nerhalb<br />

des aufgerufenen Konstruktors (bzw. der aufgerufenen Methode) auf.


<strong>Informatik</strong> B SS 03 189<br />

12.4.3 Dynamische Erzeugung mit Konstruktor-Argumenten<br />

import java.lang.reflect.Constructor;<br />

import java.lang.reflect.InvocationTargetException;<br />

import java.awt.Rectangle;<br />

class SampleInstance {<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args) {<br />

Rectangle rectangle;<br />

Class rectangleDef<strong>in</strong>ition;<br />

Class[] <strong>in</strong>tArgsClass = new Class[] {<strong>in</strong>t.class, <strong>in</strong>t.class};<br />

Integer height = new Integer(12);<br />

Integer width = new Integer(34);<br />

Object[] <strong>in</strong>tArgs = new Object[] {height, width};<br />

Constructor <strong>in</strong>tArgsConstructor;<br />

try {<br />

rectangleDef<strong>in</strong>ition = Class.forName("java.awt.Rectangle");<br />

<strong>in</strong>tArgsConstructor =<br />

rectangleDef<strong>in</strong>ition.getConstructor(<strong>in</strong>tArgsClass);<br />

rectangle =<br />

(Rectangle) createObject(<strong>in</strong>tArgsConstructor, <strong>in</strong>tArgs);<br />

} catch (ClassNotFoundException e) {<br />

System.err.pr<strong>in</strong>tln(e);<br />

} catch (NoSuchMethodException e) {<br />

System.err.pr<strong>in</strong>tln(e);<br />

}<br />

}<br />

public static Object createObject(Constructor constructor,<br />

Object[] arguments) {<br />

System.out.pr<strong>in</strong>tln ("Constructor: " + constructor);<br />

Object object = null;<br />

try {<br />

object = constructor.newInstance(arguments);<br />

System.out.pr<strong>in</strong>tln ("Object: " + object);<br />

return object;<br />

} catch (InstantiationException e) {<br />

System.err.pr<strong>in</strong>tln(e);<br />

} catch (IllegalAccessException e) {<br />

System.err.pr<strong>in</strong>tln(e);<br />

} catch (IllegalArgumentException e) {<br />

System.err.pr<strong>in</strong>tln(e);<br />

} catch (InvocationTargetException e) {


190 <strong>Informatik</strong> B SS 03<br />

}<br />

}<br />

System.err.pr<strong>in</strong>tln(e);<br />

}<br />

return object;<br />

Ausgabe:<br />

Constructor: public java.awt.Rectangle(<strong>in</strong>t,<strong>in</strong>t)<br />

Object: java.awt.Rectangle[x=0,y=0,width=12,height=34]<br />

12.4.4 Abruf und Belegung von Feldern<br />

.getField( ) Field<br />

¡ class¡ str<strong>in</strong>g¡<br />

object¡ .set( object¡ , field¡ ) object¡ .get( )<br />

field¡<br />

import java.lang.reflect.Field;<br />

import java.lang.reflect.NoSuchFieldException;<br />

import java.awt.Rectangle;<br />

class SampleSet {<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args) {<br />

Rectangle r = new Rectangle(100, 20);<br />

System.out.pr<strong>in</strong>tln("orig<strong>in</strong>al: " + r.toStr<strong>in</strong>g());<br />

modifyWidth(r, new Integer(300));<br />

System.out.pr<strong>in</strong>tln("modified: " + r.toStr<strong>in</strong>g());<br />

}<br />

static void modifyWidth(Rectangle r, Integer widthParam ) {<br />

Field widthField;<br />

Integer widthValue;<br />

Class c = r.getClass();<br />

try {<br />

widthField = c.getField("width");<br />

widthField.set(r, widthParam);<br />

} catch (NoSuchFieldException e) { System.err.pr<strong>in</strong>tln(e); }<br />

catch (IllegalAccessException e) { System.err.pr<strong>in</strong>tln(e); }<br />

} }<br />

Ausgabe:<br />

orig<strong>in</strong>al: java.awt.Rectangle[x=0,y=0,width=100,height=20]<br />

modified: java.awt.Rectangle[x=0,y=0,width=300,height=20]<br />

12.4.5 Method Invocation<br />

.getMethod( , parTypes) Method<br />

¡ class¡ str<strong>in</strong>g¡<br />

object¡ .<strong>in</strong>voke( arguments[]¡ , )<br />

method¡


<strong>Informatik</strong> B SS 03 191<br />

import java.lang.reflect.Method;<br />

import java.lang.reflect.NoSuchMethodException;<br />

import java.lang.reflect.InvocationTargetException;<br />

class SampleInvoke {<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args) {<br />

Str<strong>in</strong>g firstWord = "Hello ";<br />

Str<strong>in</strong>g secondWord = "everybody.";<br />

Str<strong>in</strong>g bothWords = append(firstWord, secondWord);<br />

System.out.pr<strong>in</strong>tln(bothWords);<br />

}<br />

}<br />

public static Str<strong>in</strong>g append(Str<strong>in</strong>g firstWord, Str<strong>in</strong>g secondWord) {<br />

Str<strong>in</strong>g result = null;<br />

Class c = Str<strong>in</strong>g.class;<br />

Class[] parameterTypes = new Class[] {Str<strong>in</strong>g.class};<br />

Method concatMethod;<br />

Object[] arguments = new Object[] {secondWord};<br />

try {<br />

concatMethod = c.getMethod("concat", parameterTypes);<br />

result = (Str<strong>in</strong>g) concatMethod.<strong>in</strong>voke(firstWord, arguments);<br />

} catch (NoSuchMethodException e) {<br />

System.err.pr<strong>in</strong>tln(e);<br />

} catch (IllegalAccessException e) {<br />

System.err.pr<strong>in</strong>tln(e);<br />

} catch (InvocationTargetException e) {<br />

System.err.pr<strong>in</strong>tln(e);<br />

}<br />

return result;<br />

Ausgabe: Hello everybody.<br />

12.5 Bemerkungen<br />

Reflections erlauben es, Entscheidungen auf die Laufzeit zu verschieben, die<br />

man zur Compile-Zeit nicht treffen will oder kann (z.B. Erzeugung von <strong>Objekt</strong>en<br />

von zur Compile-Zeit unbekannten Klassen).<br />

Dadurch müssen bestimmte Arbeiten, die normalerweise der Compiler leistet,<br />

zur Laufzeit erledigt werden (z.B. NoSuchMethodException).<br />

Man sollte nicht unbed<strong>in</strong>gt das Reflection API verwenden, wenn sich andere<br />

Möglichkeiten ergeben.<br />

Beispiel: Anstatt Method-<strong>Objekt</strong>e zu benutzen, ist es meist s<strong>in</strong>nvoller, e<strong>in</strong><br />

Interface zu def<strong>in</strong>ieren und <strong>in</strong> der Klasse, die e<strong>in</strong>e bestimmte Methode haben<br />

soll, zu implementieren.


192 <strong>Informatik</strong> B SS 03<br />

Bei dynamischer Erzeugung und Nutzung von <strong>Objekt</strong>en (Methoden) können<br />

viele Exceptions auftreten.<br />

Dynamisches Laden von Klassen kann e<strong>in</strong>fach mit Class realisiert werden:<br />

Class c = Class.forName(classname) (siehe z.B. Kapitel “Collections”,<br />

Test)<br />

Alternativen: java.lang.ClassLoader oder java.net.URLClassLoader.


¡<br />

<strong>Informatik</strong> B SS 03 193<br />

13 Multi-Thread<strong>in</strong>g – Grundlagen der Nebenläufigkeit<br />

13.1 Sequentialität, Determ<strong>in</strong>ismus, Determ<strong>in</strong>iertheit<br />

Sequentielles Programm: Es gibt genau e<strong>in</strong>en nächsten Ausführungsschritt, und<br />

alle Schritte werden nache<strong>in</strong>ander ausgeführt.<br />

v = x;<br />

w = x;<br />

x = y;<br />

und ¢ erhalten den Wert von / , danach erhält / den Wert von ¢<br />

Determ<strong>in</strong>istisches Programm: Programm, dessen Ablauf e<strong>in</strong>deutig<br />

vorherbestimmt ist; vgl. determ<strong>in</strong>istische Automaten: bei gegebener E<strong>in</strong>gabe<br />

gibt es <strong>in</strong> jedem Zustand genau e<strong>in</strong>en klar def<strong>in</strong>ierten Folgezustand.<br />

Determ<strong>in</strong>iertes Programm: Jede Ausführung liefert bei gleichen Ausgangswerten<br />

das identische Ergebnis. (E<strong>in</strong> determ<strong>in</strong>istisches Programm ist immer<br />

determ<strong>in</strong>iert, der Umkehrschluss gilt nicht zwangsläufig.)<br />

Darstellung mit Vor- und Nachbed<strong>in</strong>gungen (vgl. Hoare-Kalkül):<br />

{x = i, y = j}<br />

v = x;<br />

w = x;<br />

x = y;<br />

{v = w = i, x = y = j}<br />

13.1.1 Nebenläufigkeit<br />

Verzicht auf Sequentialität:<br />

Nebenläufigkeit (concurrency)<br />

– Anweisungen können parallel ausgeführt werden (mehrere Prozessoren<br />

oder Threads).<br />

– Anweisungen können sequentiell <strong>in</strong> beliebiger Reihenfolge ausgeführt<br />

werden.<br />

Sequentielle Programme lassen nicht explizit erkennen, ob die sequentielle<br />

Ordnung zur Lösung e<strong>in</strong>es gegebenen Problems tatsächlich erforderlich ist.<br />

Es ist schwierig, <strong>in</strong> e<strong>in</strong>em sequentiellen Programm die potentiell<br />

parallelisierbaren Aktionen herauszuf<strong>in</strong>den.<br />

Nebenläufigkeit explizit sichtbar machen:<br />

{x = i, y = j}<br />

conc w = x || v = x end conc;<br />

x = y;<br />

{v = w = i, x = y = j}


194 <strong>Informatik</strong> B SS 03<br />

willkürliche Sequentialisierung:<br />

w = x; v = x; x = y;<br />

v = x; w = x; x = y;<br />

Im Bereich KI-Planung werden (bei partial order Planern) unabhängig lösbare<br />

Teilprobleme automatisch beim Planaufbau erkannt (z.B. NOAH, Graphplan).<br />

13.1.2 Nicht-Determ<strong>in</strong>ismus<br />

Manchmal s<strong>in</strong>d nicht-determ<strong>in</strong>istische Programme s<strong>in</strong>nvoll: Vermeidung von<br />

Überspezifikation bei Gewährleistung von Determ<strong>in</strong>iertheit.<br />

Beispiel: Maximum zweier Zahlen<br />

if (a > b) max = a; else max = b;<br />

Dieser Algorithmuus ist bzgl. der Problemstellung überspezifiziert: wenn ( ¡ 4<br />

ist, dann wird 4 zurückgeliefert, obwohl es egal ist, ob ( oder 4 zurückgeliefert<br />

wird.<br />

Nicht-determ<strong>in</strong>istische Auswahl mit Wächtern:<br />

select<br />

a >= b -> max = a<br />

[] a max = b<br />

end select<br />

[] “oder wenn”<br />

Wächter (guard) regelt, ob e<strong>in</strong>e Alternative pr<strong>in</strong>zipiell ausgewählt werden kann.<br />

Alternativen s<strong>in</strong>d nicht notwendigerweise disjunkt.<br />

Auswertung der Wächter <strong>in</strong> willkürlicher Reihenfolge.<br />

13.1.3 Nicht-Determ<strong>in</strong>iertheit<br />

Beispiel:<br />

In der Regel möchte man – auch bei nicht-determ<strong>in</strong>istischen Programmen –<br />

determ<strong>in</strong>ierte Ergebnisse.<br />

Ergebnisse s<strong>in</strong>d determ<strong>in</strong>iert genau dann, wenn ke<strong>in</strong>e Schreib-/Schreib- und<br />

ke<strong>in</strong>e Schreib-/Lese-Konflikte auftreten (Veränderung von Variablen-Werten).<br />

Erwünschte Nicht-Determ<strong>in</strong>iertheit:<br />

Beispiel: Bestimme den kürzesten Weg <strong>in</strong> e<strong>in</strong>em Graphen. Wenn es mehrere<br />

kürzeste Wege gibt, ist es egal, welcher davon zurückgeliefert wird.<br />

{x = i, y = j}<br />

conc w = x || v = x || x = y; end conc;<br />

{v = , w = , x = y = j}


¡<br />

<strong>Informatik</strong> B SS 03 195<br />

willkürliche Sequentialisierung:<br />

w = x; v = x; x = y;<br />

v = x; x = y; w = x;<br />

x = y; w = x; v = x;<br />

...<br />

ist nicht-determ<strong>in</strong>iert.<br />

13.1.4 Verzahnung<br />

conc<br />

w = x; v = w+1<br />

|| y = x; z = y<br />

end conc<br />

Mögliche Sequentialisierungen<br />

(1) w = x; v = w+1; y = x; z = y;<br />

(2) y = x; z = y; w = x; v = w+1;<br />

Weitere Verzahnungen<br />

(3) w = x; y = x; v = w+1; z = y;<br />

(4) w = x; y = x; z = y; v = w+1;<br />

(5) y = x; w = x; v = w+1; z = y;<br />

(6) y = x; w = x; z = y; v = w+1;<br />

Bei der Sequentialisierung nebenläufiger Anweisungsfolgen muss nur die <strong>in</strong><br />

jeder Anweisungsfolge vorgegebene Sequenz beachtet werden. Ansonsten<br />

kann beliebig verzahnt werden (<strong>in</strong>terleav<strong>in</strong>g).<br />

Hier ist das Ergebnis trotz nicht-determ<strong>in</strong>istischer Auswahl determ<strong>in</strong>iert, da ke<strong>in</strong>e<br />

Konflike auftreten und die vorgeschriebene Sequentialisierung erhalten bleibt.<br />

Bei jeder Programmausführung kann e<strong>in</strong>e andere Verzahnung auftreten. Die<br />

Programmausführung ist nicht reproduzierbar.<br />

Fehler können sich je nach Verzahnung nur gelegentlich bemerkbar machen.<br />

nebenläufige Programm s<strong>in</strong>d schwerer zu testen (validieren) und zu<br />

verifizieren als sequentielle!<br />

Vielfalt möglicher Zustandsübergänge bei der Programmausführung.<br />

13.2 Nebenläufigkeit <strong>in</strong> <strong>Java</strong>: Threads<br />

Greifen verschiedene (nebenläufige) Aktivitäten auf verschiedene Adressräume<br />

zu, so spricht man von (parallelen) Prozessen.<br />

F<strong>in</strong>det Nebenläufigkeit im selben Adressraum statt (z.B. e<strong>in</strong>e <strong>Java</strong> VM), so<br />

spricht man von Threads (“Programmfäden”).<br />

(Addressraum: Menge aller zugreifbaren Speicherbereiche.)


196 <strong>Informatik</strong> B SS 03<br />

Thread<br />

MAX_PRIORITY<br />

...<br />

sleep()<br />

Runnable<br />

<br />

run()<br />

run()<br />

start()<br />

jo<strong>in</strong>()<br />

...<br />

ErsterThread<br />

run()<br />

runs<br />

ZweiterThread<br />

run()<br />

Abbildung 51: Def<strong>in</strong>ition von Threads<br />

In <strong>Java</strong> s<strong>in</strong>d Threads <strong>in</strong>tegraler Bestandteil der Sprache und nahtlos <strong>in</strong> das<br />

Konzept der <strong>Objekt</strong>-Orientierung e<strong>in</strong>gebaut (Threads s<strong>in</strong>d <strong>Objekt</strong>e).<br />

Probleme: Synchronisation, Deadlocks<br />

13.2.1 Def<strong>in</strong>ition von Threads<br />

Zwei Möglichkeiten:<br />

Als Unterklasse der Klasse Thread.<br />

Durch Implementieren der Runnable-Schnittstelle (immer dann, wenn Klasse<br />

bereits e<strong>in</strong>e andere Klasse erweitert; z.B. bei Applets)<br />

13.2.2 Die Klasse Thread<br />

Die run()-Methode def<strong>in</strong>iert, was e<strong>in</strong> Thread (quasi gleichzeitig mit anderen<br />

Threads) ausführen möchte (analog zu ma<strong>in</strong>()).<br />

Wie bei allen Methoden kann auch die run()-Methode auf Methoden und<br />

Felder anderer <strong>Objekt</strong>e zugreifen.


<strong>Informatik</strong> B SS 03 197<br />

Wichtige Methoden:<br />

– start(): Starten des Threads (Aufruf der run()-Methode beim<br />

Runnable durch das System).<br />

– jo<strong>in</strong>(): Warten auf das Zuendegehen e<strong>in</strong>es Threads.<br />

– sleep(long): Schlafenlegen e<strong>in</strong>es Threads.<br />

– yield(): Pausieren, um anderen Threads e<strong>in</strong>e Chance zu geben.<br />

Prioritäten: setPriority(<strong>in</strong>t) / getPriority()<br />

MIN_PRIORITY = 1 MAX_PRIORITY = 10 NORM_PRIORITY = 5<br />

Konventionen:<br />

10 Crisis Management<br />

7-9 Interactive, event-driven<br />

4-6 IO-bound<br />

2-3 Background computation<br />

1 Run only, if noth<strong>in</strong>g else can<br />

Deprecated (Term<strong>in</strong>ation bzw. Unterbrechung <strong>in</strong> <strong>in</strong>konsistenten Zuständen<br />

möglich): stop(), suspend(), resume().<br />

(Anmerkung: Später wird e<strong>in</strong>e sichere Realisierung des Unterbrechens von<br />

Threads über <strong>in</strong>terrupt() dargestellt.)<br />

// Set a thread t to lower than normal priority<br />

t.setPriority(Thread.NORM_PRIORITY-1);<br />

// Set a thread to lower priority than the current thread<br />

t.setPriority(Thread.currentThread().getPriority()-1);<br />

// Threads that don’t pause for I/O should explicitely yield the CPU<br />

// to give other threads with the same priority a chance to run.<br />

Thread t = new Thread(new Runnable() {<br />

public void run() {<br />

for(<strong>in</strong>t i = 0; i < data.length; i++) { // Loop through a bunch of data<br />

process(data[i]);<br />

// Process it<br />

if (i % 10 == 0)<br />

// But after every 10 iterations<br />

Thread.yield();<br />

// Let other threads run.<br />

}<br />

});<br />

}<br />

13.2.3 E<strong>in</strong>faches Beispiel: ‘ThreadDemo’<br />

class ErsterThread extends Thread {<br />

public void run () {<br />

for (<strong>in</strong>t i = 0; i < 10; i++)


198 <strong>Informatik</strong> B SS 03<br />

}<br />

}<br />

try {<br />

Thread.sleep( Math.round (1000 * Math.random ()) );<br />

System.out.pr<strong>in</strong>tln (this + " " + i);<br />

}<br />

catch (InterruptedException e) {<br />

System.err.pr<strong>in</strong>tln (e);<br />

}<br />

class ZweiterThread implements Runnable {<br />

public void run () {<br />

for (<strong>in</strong>t i = 0; i < 10; i++)<br />

try {<br />

Thread.sleep (Math.round (1000 * Math.random ()));<br />

System.out.pr<strong>in</strong>tln (Thread.currentThread().toStr<strong>in</strong>g () + " " + i);<br />

}<br />

catch (InterruptedException e) {<br />

System.err.pr<strong>in</strong>tln (e);<br />

} } }<br />

public class ThreadDemo {<br />

static public void ma<strong>in</strong> (Str<strong>in</strong>g args[]) {<br />

ErsterThread thread1 = new ErsterThread ();<br />

thread1.start ();<br />

Thread thread2 = new Thread(new ZweiterThread ());<br />

thread2.start ();<br />

try {<br />

thread1.jo<strong>in</strong> ();<br />

thread2.jo<strong>in</strong> ();<br />

}<br />

catch (InterruptedException e) {<br />

System.err.pr<strong>in</strong>tln (e);<br />

} } }<br />

Thread[Thread-5,5,ma<strong>in</strong>] 0<br />

Thread[Thread-5,5,ma<strong>in</strong>] 1<br />

Thread[Thread-4,5,ma<strong>in</strong>] 0<br />

Thread[Thread-5,5,ma<strong>in</strong>] 2<br />

Thread[Thread-5,5,ma<strong>in</strong>] 3<br />

Thread[Thread-4,5,ma<strong>in</strong>] 1<br />

Thread[Thread-5,5,ma<strong>in</strong>] 4<br />

Thread[Thread-4,5,ma<strong>in</strong>] 2<br />

Thread[Thread-5,5,ma<strong>in</strong>] 5<br />

Thread[Thread-4,5,ma<strong>in</strong>] 3<br />

Thread[Thread-5,5,ma<strong>in</strong>] 6<br />

Thread[Thread-4,5,ma<strong>in</strong>] 4<br />

Thread[Thread-5,5,ma<strong>in</strong>] 7<br />

Thread[Thread-5,5,ma<strong>in</strong>] 8<br />

Thread[Thread-5,5,ma<strong>in</strong>] 9


<strong>Informatik</strong> B SS 03 199<br />

ma<strong>in</strong><br />

start thread1<br />

start thread2<br />

jo<strong>in</strong> thread2<br />

jo<strong>in</strong> thread1<br />

thread1<br />

thread2<br />

Zeit<br />

Abbildung 52: Zusammenspiel von Threads <strong>in</strong> ThreadDemo<br />

Thread[Thread-4,5,ma<strong>in</strong>] 5<br />

Thread[Thread-4,5,ma<strong>in</strong>] 6<br />

Thread[Thread-4,5,ma<strong>in</strong>] 7<br />

Thread[Thread-4,5,ma<strong>in</strong>] 8<br />

Thread[Thread-4,5,ma<strong>in</strong>] 9<br />

13.2.4 Erläuterungen zu ‘ThreadDemo’<br />

Klasse Thread implementiert Runnable. Dort ist e<strong>in</strong>e Methode run()<br />

spezifiziert. Mit der Methode start() wird e<strong>in</strong> Thread gestartet, <strong>in</strong>dem run()<br />

aufgerufen wird. Methode jo<strong>in</strong>() wartet auf Beendigung des Thread.<br />

ErsterThread ist Unterklasse von Thread. Die run-Methode von Thread<br />

wird überschrieben.<br />

sleep() legt e<strong>in</strong>en Thread für e<strong>in</strong>e spezifizierte Zeit (<strong>in</strong> Millisekunden)<br />

schlafen. Hier wird zufällig e<strong>in</strong>e Zeit zwischen 0 und 1000 ms gewartet und dann<br />

der Wert des Schleifenzählers ausgegeben.<br />

Mit toStr<strong>in</strong>g() wird die Information des aktuellen Thread-<strong>Objekt</strong>s<br />

zurückgeliefert: se<strong>in</strong> Name, se<strong>in</strong>e Priorität (zwischen 1 und 10), und se<strong>in</strong><br />

erzeugender Thread.<br />

ZweiterThread implementiert Runnable. Hier muss explizit e<strong>in</strong>e Instanz von<br />

Thread erzeugt werden.<br />

Die Instanz von ZweiterThread wird e<strong>in</strong>em Thread-<strong>Objekt</strong> übergeben, der<br />

die run()-Methode “betreibt”.<br />

ErsterThread kann sich selber schlafen legen. ZweiterThread legt die<br />

benutzte Instanz von Thread schlafen.<br />

Bei Programmstart existiert immer automatisch e<strong>in</strong> erster Thread, dessen<br />

Ausführung <strong>in</strong> der ma<strong>in</strong>()-Methode beg<strong>in</strong>nt. Dieser ma<strong>in</strong>-Thread startet nun<br />

zwei weitere Threads (fork) und wartet (jo<strong>in</strong>()) auf das Ende dieser Threads.<br />

Bei jedem Ablauf ergibt sich nicht-determ<strong>in</strong>istisch e<strong>in</strong>e andere Reihenfolge der<br />

Ausgaben.


200 <strong>Informatik</strong> B SS 03<br />

vorhanden<br />

bereit<br />

laufend<br />

fertig<br />

wartend<br />

(blockiert)<br />

vorhanden: mit new als Instanz erzeugt.<br />

bereit: ablaufbereit, aber die Ablaufsteuerung hat alle CPUs an andere Threads vergeben.<br />

laufend: hat e<strong>in</strong>e CPU des Systems.<br />

wartend: es fehlen Betriebsmittel (z. B. File ist nicht zum Lesen/Schreiben freigegeben), sleep()<br />

oder Warten auf Signal e<strong>in</strong>es anderen Threads.<br />

fertig: Ende der run()-Methode erreicht.<br />

(Anmerkung: Diese Abbildung wird später für Monitore verfe<strong>in</strong>ert.)<br />

Abbildung 53: Zustände von Threads<br />

13.2.5 Zustände von Threads<br />

Threads haben verschiedene Zustände, die sie zum Teil selbst bee<strong>in</strong>flussen können,<br />

die aber auch von ihrer Umgebung manipulierbar s<strong>in</strong>d (siehe Abb. 53).<br />

13.3 Kooperierende und Konkurrierende Prozesse<br />

13.3.1 Kooperierende Prozesse<br />

Nebenläufige Prozesse, die Koord<strong>in</strong>ation erfordern, heissen vone<strong>in</strong>ander<br />

abhängig. Grundlegende Kommunikationsformen (siehe Abb. 54):<br />

– Erzeuger/Verbraucher Muster (producer/consumer): E<strong>in</strong> Prozess nimmt<br />

Daten auf, die e<strong>in</strong> anderer erzeugt hat.<br />

Beispiel: Buchungssystem im Supermarkt<br />

– Auftraggeber/Auftragnehmer Muster (client/server)<br />

Beispiel: Verkehrsleitzentrale.<br />

Beispiel: Buchungssystem im Supermarkt<br />

Strichcode-Leser, Buchungsprozessor, Drucker<br />

Jedem Gerät wird e<strong>in</strong> Prozess zugeordnet (wichtiges<br />

Programm-Strukturierungs-Pr<strong>in</strong>zip)<br />

Leseprozess erfasst über Strichcodeleser die Kennung des Artikels<br />

Information wird an Buchungsprozess weitergegeben, der Bezeichung und Preis<br />

des Artikels feststellt


<strong>Informatik</strong> B SS 03 201<br />

Produzent<br />

Auftraggeber<br />

1 2<br />

Konsument<br />

Auftragnehmer<br />

Abbildung 54: Arten der Kooperation<br />

Butter 2 1,29<br />

Kaffee 1 7,99<br />

...<br />

Lesen Buchen Drucken<br />

Abbildung 55: Beispiel: Buchungssystem im Supermarkt<br />

Werte werden an Druckprozess weitergegeben, der sie auf Drucker ausgibt.<br />

Prozesse s<strong>in</strong>d nebenläufig: Während der Buchungsprozess alte E<strong>in</strong>gabedaten<br />

verarbeitet, kann der Leseprozess neue Daten lesen.<br />

Prozesse s<strong>in</strong>d abhängig: Buchungsprozess kann erst arbeiten, wenn er Daten<br />

vom Leseprozess erhalten hat.<br />

Beispiel: Verkehrsleitzentrale<br />

gibt Auskunft über aktuellen Straßenzustand und schlägt Fahrstrecken vor.<br />

Prozess im Bordcomputer (Client) stellt Anfrage an Auskunftsprozess <strong>in</strong> der<br />

Zentrale (Server).<br />

Auskunftsprozess gibt die Information erst, wenn nach ihr gefragt wird.<br />

Prozess im Bordcomputer wartet, bis er die gewünschte Information erhält.<br />

Prozesse s<strong>in</strong>d wechselseitig abhängig.<br />

13.3.2 Konkurrierende Prozesse<br />

Abhängigkeit nebenläufiger Prozesse kann auch aus Konkurrenz der Prozesse<br />

(um geme<strong>in</strong>same Ressourcen) resultieren.<br />

Die Aktivität e<strong>in</strong>es Prozesses beh<strong>in</strong>dert e<strong>in</strong>en anderen Prozess.<br />

Beispiel: E<strong>in</strong>gleisige Teilstrecke im Eisenbahnverkehr


202 <strong>Informatik</strong> B SS 03<br />

Beispiel: Drucker im Mehrbenutzersystem<br />

Schreib-/Schreib-Konflikt: Beispiel<br />

conc<br />

x = x + 1 || x = x + 1<br />

end conc;<br />

kann zu e<strong>in</strong>- oder zweimaliger Inkrementierung von / führen.<br />

Folgende Verzahnung führt zur e<strong>in</strong>maligen Inkrementierung:<br />

(Notation <strong>in</strong> Pseudo-Assembler mit als Prozesse und ¡ als Register)<br />

P1 P2 x r1 r2<br />

i <br />

LOAD x, r1 i i <br />

LOAD x, r2 i i i<br />

INCR r1 i i+1 i<br />

INCR r2 i i+1 i+1<br />

STORE r1,x i+1 i+1 i+1<br />

STORE r2,x i+1 i+1 i+1<br />

(“STORE r1,x direkt nach “INCR r1” resultiert <strong>in</strong> zweimaliger Inkrementierung.)<br />

Schreib-/Lese-Konflikt: Beispiel<br />

<strong>in</strong>t schecks;<br />

<strong>in</strong>t gesamt;<br />

void e<strong>in</strong>nahme(boolean zahlungsart, <strong>in</strong>t betrag) {<br />

// zahlungsart false = bar, true = scheck<br />

if (zahlungsart) schecks = schecks + betrag;<br />

gesamt = gesamt + betrag;<br />

}<br />

void kassensturz () {<br />

System.out.pr<strong>in</strong>tln("E<strong>in</strong>nahmen: " + gesamt);<br />

if (gesamt > 0)<br />

System.out.pr<strong>in</strong>tln("Davon prozentual als Scheckzahlung: " +<br />

100 * schecks/gesamt);<br />

}<br />

Gegebener Kontostand sei 100,- Euro. Wenn e<strong>in</strong>nahme(1, 200) und<br />

kassensturz() nebenläufig abgearbeitet werden, kann es zu <strong>in</strong>konsistenten<br />

Informationen kommen:<br />

P1 P2 gesamt scheck Output<br />

100 0<br />

schecks+200 100 200<br />

kassensturz 100 200 200%<br />

gesamt+200 300 200


£<br />

@<br />

¡<br />

@<br />

£<br />

£<br />

©<br />

<strong>Informatik</strong> B SS 03 203<br />

13.4 Synchronisation<br />

Die gezielte Sequentialisierung nebenläufiger Prozesse heisst Synchronisation.<br />

Synchronisierte Prozesse müssen Information austauschen, also<br />

kommunizieren.<br />

Kommunikation kann realisiert werden durch:<br />

(1) geme<strong>in</strong>samen Datenbereich, auf den mehrere Prozesse zugreifen können.<br />

(2) Operationen, die Daten vom Datenbereich e<strong>in</strong>es Prozesses <strong>in</strong> den des<br />

anderen transportieren.<br />

Anstelle ganzer Prozesse können auch kritische Abschnitte (Anweisungsblöcke)<br />

synchronisiert werden.<br />

Arten von Synchronisation:<br />

¦ und @ seien verschiedenen Prozessen zugeordnete Aktivitäten)<br />

(@<br />

Kausale Abhängigkeit @ ¦<br />

(Produzent/Konsument):<br />

Transitiver Abschluss (partielle Ordnung der @ ¦ ¡¡ ¨@<br />

Aktivitäten )<br />

E<strong>in</strong>seitige Synchronisation (blockiert wird @ höchstens ) erzw<strong>in</strong>gt Reihenfolge.<br />

(vgl. Supermarkt-Buchungssystem)<br />

Ausschluss der Nebenläufigkeit (Schreib-/Schreib- und Schreib-/Lese-Konflikte):<br />

¦¡<br />

£<br />

@<br />

Symmetrie von “nicht zusammen mit”<br />

Mehrseitige Synchronisation: Bei gleicher Priorität ist ke<strong>in</strong>e Reihenfolge<br />

festgelegt.<br />

(vgl. e<strong>in</strong>gleisiges Eisenbahnstück)<br />

13.5 Monitore <strong>in</strong> <strong>Java</strong><br />

13.5.1 Synchronized<br />

Wird mehr als e<strong>in</strong> Thread verwendet, so tritt häufig Synchronisations-Bedarf auf.<br />

Zugriff auf kritische Daten muss kontrolliert werden.<br />

Hierzu wird e<strong>in</strong> Monitor-<strong>Objekt</strong> e<strong>in</strong>gesetzt, das den Zugang von Threads zu den<br />

Daten steuert. (Jedes beliebige <strong>Objekt</strong> kann als Monitor verwendet werden.)<br />

In <strong>Java</strong> wird synchronized verwendet, um e<strong>in</strong>en kritischen Bereich zu<br />

def<strong>in</strong>ieren und diesem Bereich e<strong>in</strong> Monitor-<strong>Objekt</strong> zuzuordnen.<br />

synchronized(monitor) für Blöcke (kritische Abschnitte)<br />

Wenn sich e<strong>in</strong> synchronized(this)-Block auf e<strong>in</strong>e komplette Methode<br />

bezieht, kann alternativ der Modifikator synchronized für diese Methode<br />

angegeben werden.<br />

Der Modifikator synchronized kann auch für Klassen-Methoden angegeben<br />

werden. Hier wird das Klassen-<strong>Objekt</strong> zum Monitor.


204 <strong>Informatik</strong> B SS 03<br />

Monitor<br />

geschützte Daten<br />

Zugang zum Monitor<br />

blockiert für alle<br />

anderen Threads<br />

Kommunikation bei Monitoren:<br />

Abbildung 56: Monitor<br />

– wait() (Warte und gib den Monitor frei),<br />

– notify() (Benachrichtige e<strong>in</strong>en auf den Monitor wartenden Thread) und<br />

– notifyAll() (Benachrichtige alle auf den Monitor wartenden Threads).<br />

Diese Methoden gehören nicht zur Klasse Thread, sondern zu e<strong>in</strong>em<br />

Monitor-<strong>Objekt</strong>.<br />

synchronized void e<strong>in</strong>nahme(boolean zahlungsart, <strong>in</strong>t betrag) {<br />

if (zahlungsart) schecks = schecks + betrag;<br />

gesamt = gesamt + betrag;<br />

}<br />

synchronized void kassensturz () {<br />

System.out.pr<strong>in</strong>tln("E<strong>in</strong>nahmen: " + gesamt);<br />

if (gesamt > 0)<br />

System.out.pr<strong>in</strong>tln("Davon prozentual als Scheckzahlung: " +<br />

100 * schecks/gesamt);<br />

}<br />

// This method swaps two array elements <strong>in</strong> a synchronized block<br />

public static void swap(Object[] array, <strong>in</strong>t <strong>in</strong>dex1, <strong>in</strong>t <strong>in</strong>dex2) {<br />

synchronized(array) {<br />

Object tmp = array[<strong>in</strong>dex1];<br />

array[<strong>in</strong>dex1] = array[<strong>in</strong>dex2];<br />

array[<strong>in</strong>dex2] = tmp;<br />

}<br />

}


¡<br />

<strong>Informatik</strong> B SS 03 205<br />

vorhanden<br />

bereit<br />

laufend<br />

fertig<br />

wenn Zutritt<br />

zum Monitor<br />

wartend<br />

notify()<br />

e<strong>in</strong>es anderen<br />

Threads<br />

blockiert<br />

wait()<br />

Monitor durch anderen Thread besetzt<br />

Abbildung 57: Zustände von Threads (bzgl. Monitor-<strong>Objekt</strong>)<br />

13.5.2 Funktion von ‘synchronized’<br />

Wenn <strong>in</strong> e<strong>in</strong>er Klasse e<strong>in</strong>e Methode mit dem Modifikator synchronized<br />

angegeben ist – also this als Monitor def<strong>in</strong>iert wird – so verwenden<br />

verschiedene Aufrufe bei verschiedenen Instanzen dieser Klasse verschiedene<br />

Monitore.<br />

Monitore können “Wettrennen” (race-conditions) zwischen Threads verh<strong>in</strong>dern.<br />

Zu jedem Zeitpunkt kann nur e<strong>in</strong>e der synchronized()-Aktivitäten für<br />

dasselbe Monitor-<strong>Objekt</strong> aufgerufen werden.<br />

Beispiel Produzent/Konsument bzw. Schreib-/Lese-Konflikt: Falls derjenige<br />

Thread, der die Daten ausliest, unterbrochen wird und e<strong>in</strong> zweiter Thread mit<br />

Schreibzugriffen aktiv wird, so muss der zweite Thread warten, bis der erste<br />

Thread (Leser) den Monitor verlassen hat.<br />

<strong>Java</strong>-Monitore s<strong>in</strong>d “re-entrant”: E<strong>in</strong> Thread, der im Monitor ist, wird nicht durch<br />

sich selbst dadurch blockiert, dass er e<strong>in</strong>e andere auf denselben Monitor<br />

synchronisierte Methode aufruft.<br />

13.5.3 Warten auf Ereignisse mit Monitoren<br />

Beispiel (Produzent/Konsument): Thread wartet auf Daten, die von e<strong>in</strong>em<br />

anderen Thread geliefert werden müssen.<br />

Aktives Warten blockiert CPU unnötig: Schleife, die ständig e<strong>in</strong>e Variable<br />

abfragt.<br />

Alternativ: wait() Thread verbraucht ke<strong>in</strong>e CPU-Zeit, verlässt den Monitor<br />

und “schläft”. E<strong>in</strong> mit notify() bzw. notifyAll() aufgeweckter Thread<br />

wartet wieder auf Zugang zum Monitor (ist blockiert, solange bis Monitor<br />

freigegeben ist).<br />

wait() versus sleep()


206 <strong>Informatik</strong> B SS 03<br />

– sleep() ist e<strong>in</strong>e Thread-Methode während wait() e<strong>in</strong>e Monitor-Methode<br />

ist.<br />

– Wenn e<strong>in</strong> Thread im Besitz des Monitors sich schlafen legt, so bleibt er <strong>in</strong><br />

Besitz des Monitors.<br />

E<strong>in</strong> wartender Thread gibt dagegen den Monitor ab.<br />

– Wenn e<strong>in</strong> wartender Thread aufwacht, kann es se<strong>in</strong>, dass er nicht<br />

ausgeführt werden kann, weil er den Monitor nicht (sofort) wieder belegen<br />

kann.<br />

– E<strong>in</strong> wartender Thread kann für immer warten, wenn nie e<strong>in</strong> notify erfolgt.<br />

– Sowohl wait() als auch sleep() können durch <strong>in</strong>terrupt()<br />

unterbrochen werden. (E<strong>in</strong>bettung <strong>in</strong><br />

try{ ... } catch (InterruptedException e) { ... } )<br />

13.6 Beispiel: Produzent/Konsument<br />

public class ConsumerProducer {<br />

public static void ma<strong>in</strong> (Str<strong>in</strong>g[] args) {<br />

Object[] d = { null };<br />

Producer p = new Producer(d, 0);<br />

Producer p2 = new Producer(d, 100);<br />

Consumer c = new Consumer(d);<br />

p.start();<br />

p2.start();<br />

c.start();<br />

try {<br />

p.jo<strong>in</strong>();<br />

p2.jo<strong>in</strong>();<br />

c.jo<strong>in</strong>();<br />

} catch (InterruptedException e) { System.err.pr<strong>in</strong>tln(e); }<br />

}<br />

public static class Producer extends Thread {<br />

protected Object[] data;<br />

protected <strong>in</strong>t count;<br />

protected <strong>in</strong>t offset;<br />

public Producer (Object[] d, <strong>in</strong>t i) {<br />

data = d;<br />

offset = i;<br />

}<br />

public void run () {


<strong>Informatik</strong> B SS 03 207<br />

Integer o;<br />

while (count < 8) {<br />

o = new Integer (count++ + offset);<br />

while (!store(o));<br />

report(o);<br />

} }<br />

protected boolean store (Object o) {<br />

if (data[0] == null) {<br />

try { sleep(500);<br />

} catch (InterruptedException e) { System.err.pr<strong>in</strong>tln(e); }<br />

data[0] = o;<br />

return true;<br />

} else return false;<br />

}<br />

protected void report(Object o) {<br />

System.out.pr<strong>in</strong>tln(this + " produced " + o);<br />

}<br />

} // end Producer<br />

public static class Consumer extends Thread {<br />

protected Object[] data;<br />

protected <strong>in</strong>t count;<br />

public Consumer (Object[] d) {<br />

data = d;<br />

}<br />

public void run () {<br />

while (count < 16) {<br />

Object o;<br />

o = fetch();<br />

if (o != null) {<br />

report(o);<br />

emptyStore();<br />

count++;<br />

}<br />

} }<br />

protected Object fetch() {<br />

return data[0];<br />

}<br />

protected void report(Object o) {<br />

System.out.pr<strong>in</strong>tln(this + " consumed " + o);


208 <strong>Informatik</strong> B SS 03<br />

}<br />

protected void emptyStore() {<br />

data[0] = null;<br />

}<br />

} // end Consumer<br />

}<br />

Anmerkung: Producer und Consumer s<strong>in</strong>d als nested top-level Klassen <strong>in</strong><br />

derselben Datei wie ConsumerProducer.<br />

Producer und Consumer s<strong>in</strong>d Unterklassen von Thread, die run-Methoden<br />

werden also nebenläufig ausgeführt.<br />

Der Consumer soll e<strong>in</strong> <strong>Objekt</strong> abholen, der Producer soll e<strong>in</strong> <strong>Objekt</strong> ablegen.<br />

Problem: Mehrere Threads wollen lesend bzw. schreibend auf data zugreifen.<br />

Klassischer Schreib-Lese-Konflikt!<br />

synchronized für die run()-Methoden macht ke<strong>in</strong>en S<strong>in</strong>n: this würde als<br />

Monitor verwendet, und jeder Thread hätte damit se<strong>in</strong>en eigenen Monitor!<br />

Synchronisationsblöcke <strong>in</strong> den jeweiligen run-Methoden mit data als Monitor<br />

verh<strong>in</strong>dern zwar gleichzeitigen Zugriff, bergen aber die Gefahr von deadlocks.<br />

Lösung: Conditional Critical Region (siehe Kapitel “Semaphoren und<br />

Deadlocks”).


<strong>Informatik</strong> B SS 03 209<br />

14 Multi-Thread<strong>in</strong>g: Semaphoren und Deadlocks<br />

14.1 Semaphoren<br />

14.1.1 Konzept<br />

Von Dijkstra zur Synchronisation nebenläufiger Prozesse e<strong>in</strong>geführt.<br />

Begriff aus der Seefahrt: Optisches Signal zum Passeeren/passer (Passieren)<br />

und Vrijgeven/verlaat (Freigeben).<br />

Idee: Prozess wird im Synchronisationsfall blockiert und <strong>in</strong> Warteschlange<br />

e<strong>in</strong>geordnet.<br />

Semaphoren s<strong>in</strong>d abstrakte Datentypen:<br />

– <strong>Objekt</strong>e bestehen aus Zähler und Warteschlange.<br />

– Operation P: Zähler wird um e<strong>in</strong>s erniedrigt. Wenn negativer Wert, dann ist<br />

Prozess blockiert. (Warten auf das E<strong>in</strong>treten e<strong>in</strong>er Bed<strong>in</strong>gung)<br />

– Operation V: Zähler wird um e<strong>in</strong>s erhöht. (Signalisieren des E<strong>in</strong>tretens<br />

e<strong>in</strong>er Bed<strong>in</strong>gung).<br />

Die Warteschlange muss <strong>in</strong> <strong>Java</strong> nicht explizit def<strong>in</strong>iert werden. Sie existiert<br />

implizit als Menge der blockierten Prozesse. Durch notify() wird e<strong>in</strong> Prozess<br />

aktiviert (nicht unbed<strong>in</strong>gt der, der am längsten wartet).<br />

14.1.2 Klasse ‘Semaphore’<br />

/** A class for the classical semaphore -- A.T. Schre<strong>in</strong>er */<br />

public class Semaphore {<br />

/** the value, nonnegative.<br />

*/<br />

protected <strong>in</strong>t n;<br />

/** set <strong>in</strong>itial value.<br />

*/<br />

public Semaphore (<strong>in</strong>t n) { this.n = n; }<br />

/** passer: decrement; may block until decrement<strong>in</strong>g is possible.<br />

*/<br />

public synchronized void P () {<br />

while (n 0)


210 <strong>Informatik</strong> B SS 03<br />

}<br />

}<br />

notify(); // nur _e<strong>in</strong>_ wartender Prozess wird aus Blockierung entlassen<br />

14.1.3 E<strong>in</strong>seitige und Mehrseitige Synchronisation<br />

E<strong>in</strong>seitige Synchronisation<br />

Jeder Synchronisationsbed<strong>in</strong>gung wird e<strong>in</strong>e Semaphorvariable zugeordnet.<br />

-Operation <strong>in</strong> e<strong>in</strong>em Prozess wartet auf -Operation <strong>in</strong> e<strong>in</strong>em anderen<br />

<br />

Prozess.<br />

Semaphore s = new Semaphore(0);<br />

void process1 () { void process2 () {<br />

// ... // ...<br />

s.V(); // Ereignis signalisieren s.P(); // Ereignis abwarten<br />

// ... // ...<br />

} }<br />

Mehrseitige Synchronisation<br />

Kritischer Abschnitt (critical region): Bereich, zu dem nur e<strong>in</strong>e Aktivität zu e<strong>in</strong>er<br />

Zeit Zugang hat.<br />

Initialwert des Semaphorzählers legt die maximale Anzahl von Prozessen fest,<br />

die den kritischen Abschnitt betreten dürfen.<br />

und umschliessen kritischen Abschnitt.<br />

Semaphore s = new Semaphore(1);<br />

void process1 () { void process2 () {<br />

// ... // ...<br />

s.P();<br />

s.P();<br />

// ... kritischer Abschnitt // ... kritischer Abschnitt<br />

s.V();<br />

s.V();<br />

// ... // ...<br />

} }<br />

14.1.4 Erzeuger-/Verbraucher-Problem mit Semaphoren<br />

/** semaphore demonstration with member class pattern.<br />

*/<br />

public class ProdConsDemo {<br />

/** controls access to data.<br />

*/<br />

protected Semaphore available = new Semaphore(0);<br />

/** signals that data has been copied.<br />

*/<br />

protected Semaphore copied = new Semaphore(1);


<strong>Informatik</strong> B SS 03 211<br />

/** "global" buffer.<br />

*/<br />

protected Str<strong>in</strong>g data;<br />

/** producer: has command l<strong>in</strong>e copied by several threads <strong>in</strong> consumer.<br />

*/<br />

public static void ma<strong>in</strong> (Str<strong>in</strong>g[] args) {<br />

if (args != null) {<br />

<strong>in</strong>t nt = args.length+1 / 2; // divide by 2<br />

ProdConsDemo producer = new ProdConsDemo(); // owns semaphores<br />

for (<strong>in</strong>t n = 0; n < nt; ++ n)<br />

producer.new Consumer(""+n).start(); // performs copy<br />

// <strong>in</strong>t as Str<strong>in</strong>g<br />

producer.produce (args);<br />

producer.term<strong>in</strong>ateConsumers (nt);<br />

}<br />

}<br />

/** produc<strong>in</strong>g some data<br />

*/<br />

protected void produce (Str<strong>in</strong>g[] args) {<br />

for (<strong>in</strong>t n = 0; n < args.length; ++ n) {<br />

copied.P();<br />

// critical section<br />

data = args[n];<br />

// writes <strong>in</strong>formation<br />

available.V();<br />

}<br />

}<br />

/** term<strong>in</strong>ate consumer threads<br />

*/<br />

protected void term<strong>in</strong>ateConsumers (<strong>in</strong>t nt) {<br />

for (<strong>in</strong>t n = 0; n < nt; ++ n) { // done: <strong>in</strong>form all<br />

copied.P();<br />

data = null;<br />

// term<strong>in</strong>ation marker<br />

available.V();<br />

}<br />

}<br />

/** consumer thread: copies data to standard output until data == null.<br />

*/<br />

protected class Consumer extends Thread {<br />

/** save name.<br />

*/<br />

public Consumer (Str<strong>in</strong>g name) { super(name); }<br />

/** performs copy.<br />

*/<br />

public void run () {<br />

Str<strong>in</strong>g copy;<br />

do {


212 <strong>Informatik</strong> B SS 03<br />

}<br />

}<br />

}<br />

available.P(); copy = data; copied.V(); // critical section<br />

if (copy != null)<br />

System.out.pr<strong>in</strong>tln(getName()+" "+copy);<br />

} while (copy != null);<br />

$ java ProdConsDemo a b c d e f g h i j<br />

1 b<br />

1 c<br />

0 a<br />

0 e<br />

0 f<br />

0 g<br />

0 h<br />

0 i<br />

0 j<br />

1 d<br />

Erläuterungen:<br />

Das Produzent/Konsument Muster ist hier mit Member-Klasse realisiert: Im<br />

ma<strong>in</strong>-Thread wird produziert (e<strong>in</strong> Produzent). Es werden halb soviele<br />

Consumer-Threads generiert, wie Argumente über die E<strong>in</strong>gabezeile angegeben<br />

werden.<br />

Über das Feld data werden Str<strong>in</strong>g-<strong>Objekt</strong>e ausgetauscht.<br />

Zwei Semaphoren: copied schützt Schreibzugriff auf data; available<br />

schützt Lesezugriff.<br />

14.2 Conditional Critical Regions<br />

Konzept von Hoare, allgeme<strong>in</strong>er als Semaphoren.<br />

Es existiert e<strong>in</strong> kritischer Abschnitt, wobei der Zugang durch e<strong>in</strong>e Bed<strong>in</strong>gung<br />

geschützt wird.<br />

Typischerweise mit while-Schleife realisiert.<br />

Würde man if anstelle von while verwenden, so würden aufgeweckte<br />

Threads nicht merken, dass e<strong>in</strong> anderer Thread die condition verändert hat.<br />

(Während man schläft können andere arbeiten.)<br />

Typisches Muster:<br />

synchronized(o) { synchronized(o) {<br />

while (!condition ) {<br />

// condition == false<br />

// ... // tu was


¡<br />

<strong>Informatik</strong> B SS 03 213<br />

}<br />

o.wait();<br />

condition = true;<br />

// ... o.notifyAll();<br />

} }<br />

// tu was<br />

// evtl. condition = false<br />

14.2.1 Monitore, CCRs, Semaphoren<br />

Monitor: E<strong>in</strong> <strong>Java</strong>-<strong>Objekt</strong>, das den Zugang zu synchronisierten<br />

(Instanz)-Methoden/Blöcken kontrolliert.<br />

Conditional Critical Region: kritischer Abschnitt (<strong>in</strong> e<strong>in</strong>em synchronisierten<br />

Block), bei dem auf Zugang gewartet wird (wait()), solange bis e<strong>in</strong>e<br />

Bed<strong>in</strong>gung erfüllt ist. E<strong>in</strong> anderer Block macht die Bed<strong>in</strong>gung wahr und gibt den<br />

kritischen Abschnitt frei (notify(), notifyAll()).<br />

Semaphoren: Sperren und Freigeben, realisiert durch Zähler und<br />

Warteschlange. Spezielle Technik, um CCRs zu realisieren.<br />

14.3 Deadlocks<br />

E<strong>in</strong>e Situation <strong>in</strong> der zwei oder mehr Prozesse/Threads nicht weiterarbeiten<br />

können, weil jeder darauf wartet, dass m<strong>in</strong>destens e<strong>in</strong> anderer etwas<br />

bestimmtes erledigt, heisst Deadlock.<br />

Standardbeispiel: D<strong>in</strong><strong>in</strong>g Philosophers<br />

Fünf Philosophen sitzen um e<strong>in</strong>en runden Tisch. Vor jedem Philosoph steht e<strong>in</strong><br />

Teller, zwischen jedem Teller-Paar liegt e<strong>in</strong>e Gabel. Es existieren also fünf<br />

Gabeln, aber um zu essen braucht jeder Philosoph zwei Gabeln (die zu se<strong>in</strong>er<br />

rechten und zu se<strong>in</strong>er l<strong>in</strong>ken Seite).<br />

Wenn alle fünf Philosophen zur Gabel zu ihrer Rechten greifen, entsteht e<strong>in</strong><br />

Deadlock!<br />

Zweites Problem: Aushungern e<strong>in</strong>es Philosophen (die anderen s<strong>in</strong>d immer<br />

schneller beim Zugreifen). Kann mit Semaphoren oder kritischen Abschnitten<br />

alle<strong>in</strong>e nicht verh<strong>in</strong>dert werden. <strong>Java</strong> erlaubt immer e<strong>in</strong>em beliebigen Thread,<br />

dass er zum Zug kommt.<br />

14.3.1 Lösung mit Semaphoren<br />

Nicht verklemmungsfrei:<br />

// Anzahl von Gabeln ist 5.<br />

// Jede Gabel sei e<strong>in</strong>e Semaphore, die mit 1 <strong>in</strong>itialisiert wird.<br />

//<br />

// Für e<strong>in</strong>en Philosophen i:


214 <strong>Informatik</strong> B SS 03<br />

Abbildung 58: D<strong>in</strong><strong>in</strong>g Philosophers<br />

while(true) {<br />

th<strong>in</strong>k();<br />

gabel[i].P();<br />

gabel[(i+1)%anzahl].P(); // wg. i+1 größer 4<br />

eat();<br />

gabel[i].V();<br />

gabel[(i+1)%anzahl].V();<br />

}<br />

Verklemmungsfreie Lösung mit Semaphoren<br />

E<strong>in</strong>e globale Semaphore “table”, die jeweils nur e<strong>in</strong>em Philosoph erlaubt, zu<br />

prüfen, ob se<strong>in</strong>e l<strong>in</strong>ke und rechte Gabel frei s<strong>in</strong>d und diese dann aufzunehmen.<br />

Deadlocks können auftreten, wenn<br />

Exklusive Belegung: Betriebsmittel s<strong>in</strong>d entweder von genau e<strong>in</strong>em Prozess<br />

belegt oder frei. (e<strong>in</strong>e Gabel)<br />

Belegen und Warten: Prozesse belegen Betriebsmittel und warten während der<br />

Belegung auf die Zuteilung weiterer Betriebsmittel. (l<strong>in</strong>ke und rechte Gabel)<br />

Ke<strong>in</strong> zwangsweises Freigeben: Betriebsmittel können nicht entzogen werden,<br />

sondern müssen vom Prozess zurückgegeben werden (H<strong>in</strong>legen e<strong>in</strong>er Gabel)<br />

Zyklische Wartebed<strong>in</strong>gung: Es muss e<strong>in</strong>en R<strong>in</strong>g aus zwei oder mehr Prozessen<br />

bestehen, bei der jeder Prozess auf e<strong>in</strong> von e<strong>in</strong>em anderen Prozess aus der<br />

Kette belegtes Betriebsmittel wartet. (5 Philosophen)<br />

14.3.2 D<strong>in</strong><strong>in</strong>g Philosophers – Lösung mit globaler Kontrolle<br />

(nach Jobst, Programmieren <strong>in</strong> <strong>Java</strong>, Hanser)<br />

Conditional Critical Region Konzept: takeForks() und putForks()


<strong>Informatik</strong> B SS 03 215<br />

Aushungern theoretisch möglich.<br />

Erweiterung: Änderung von Prioritäten, z.B. “hungrig” und “satt” als Eigenschaft<br />

der Philosophen (Threads). Fairness!<br />

// Der Manager handelt nach der Philosophie: gib nur dann Gabeln an e<strong>in</strong>en<br />

// Philosophen, wenn ALLE benoetigten Gabeln frei s<strong>in</strong>d. Damit werden<br />

// Deadlocks vermieden (vgl. Literaturangaben)<br />

//<br />

public class Manager {<br />

public f<strong>in</strong>al static <strong>in</strong>t N = 5;<br />

// fuenf Philosophen<br />

private static <strong>in</strong>t phils[] = new <strong>in</strong>t [N]; // Zustaende der Philosophen<br />

private f<strong>in</strong>al static <strong>in</strong>t NOTHING = 0;<br />

private f<strong>in</strong>al static <strong>in</strong>t EATING = 1;<br />

private <strong>in</strong>t left (<strong>in</strong>t i) { return (i-1+N) % N; } // L<strong>in</strong>ker Nachbar<br />

private <strong>in</strong>t right (<strong>in</strong>t i) { return (i+1) % N; } // Rechter Nachbar<br />

synchronized public void takeForks (<strong>in</strong>t no) {<br />

while (phils[left(no)] == EATING || // Wenn hoechstens e<strong>in</strong>er<br />

phils[right(no)] == EATING) { // der Nachbarn isst:<br />

try {<br />

wait ();<br />

// Warte bis fertig.<br />

} catch (InterruptedException e) { System.err.pr<strong>in</strong>tln(e); }<br />

}<br />

phils[no] = EATING;<br />

}<br />

synchronized public void putForks (<strong>in</strong>t no) {<br />

phils[no] = NOTHING;<br />

// Markiere frei<br />

notifyAll ();<br />

// Nachricht an Wartende<br />

}<br />

synchronized public void display () {<br />

Str<strong>in</strong>gBuffer s = new Str<strong>in</strong>gBuffer("Philosophen : ");<br />

for (<strong>in</strong>t i = 0; i < N; i++)<br />

if (phils[i] == EATING)<br />

s.append(" " + i);<br />

System.out.pr<strong>in</strong>tln (s);<br />

}<br />

public static void ma<strong>in</strong> (Str<strong>in</strong>g[] args) {<br />

Manager m = new Manager ();<br />

// Manager zuerst <strong>in</strong>stallieren<br />

Thread p[] = new Thread [N];<br />

for (<strong>in</strong>t i = 0; i < p.length; i++)<br />

p[i] = new Philosopher (i, m);<br />

for (<strong>in</strong>t i = 0; i < p.length; i++)<br />

p[i].start ();<br />

for (<strong>in</strong>t i = 0; i < p.length; i++) {


216 <strong>Informatik</strong> B SS 03<br />

}<br />

}<br />

}<br />

try {<br />

p[i].jo<strong>in</strong> ();<br />

} catch (InterruptedException e) {<br />

System.err.pr<strong>in</strong>tln (e);<br />

}<br />

class Philosopher extends Thread {<br />

protected <strong>in</strong>t no;<br />

// Die (unpersoenliche) Nummer des Philosophen<br />

protected Manager m; // Referenz zum Manager<br />

public Philosopher (<strong>in</strong>t no, Manager m) {<br />

super ("Phil. " + no);<br />

this.no = no;<br />

this.m = m;<br />

}<br />

protected void eat () {<br />

m.display ();<br />

try {<br />

sleep (Math.round (1000 * Math.random ()));<br />

}<br />

catch (InterruptedException e) {}<br />

}<br />

protected void th<strong>in</strong>k () {<br />

try {<br />

sleep (Math.round (1000 * Math.random ()));<br />

} catch (InterruptedException e) {}<br />

}<br />

}<br />

public void run () {<br />

for (<strong>in</strong>t j = 0; j < 5; j++) {<br />

th<strong>in</strong>k (); // Denken ....<br />

m.takeForks (no);<br />

// Auf Zugang warten<br />

eat ();<br />

// Essen<br />

m.putForks (no);<br />

// Zugang fuer die Kollegen ermoeglichen<br />

}<br />

}<br />

Zu e<strong>in</strong>em Zeitpunkt können höchstens zwei, nicht nebene<strong>in</strong>ander sitzende<br />

Philosophen essen:<br />

Philosophen : 4<br />

Philosophen : 3


<strong>Informatik</strong> B SS 03 217<br />

Philosophen : 0 3<br />

Philosophen : 1 3<br />

Philosophen : 1 3<br />

Philosophen : 0 3<br />

Philosophen : 0 2<br />

Philosophen : 1 4<br />

Philosophen : 1 4<br />

Philosophen : 2 4<br />

Philosophen : 0 2<br />

Philosophen : 0 3<br />

Philosophen : 4<br />

Philosophen : 1 4<br />

Philosophen : 2 4<br />

Philosophen : 0 2<br />

Philosophen : 2 4<br />

Philosophen : 1 4<br />

Philosophen : 0 3<br />

Philosophen : 0 3<br />

Philosophen : 2<br />

Philosophen : 2 4<br />

Philosophen : 1 3<br />

Philosophen : 1 3<br />

Philosophen : 2<br />

14.3.3 D<strong>in</strong><strong>in</strong>g Philosophers – Bed<strong>in</strong>gter Zugriff auf Gabel<br />

// Philosopher A.T. Schre<strong>in</strong>er<br />

import java.util.Random;<br />

/** the D<strong>in</strong><strong>in</strong>g Philosophers.<br />

*/<br />

public class Philosopher2 extends Thread {<br />

/** the fork between two philosophers.<br />

*/<br />

protected static class Fork {<br />

protected <strong>in</strong>t me;<br />

// number for trace<br />

protected boolean <strong>in</strong>Use; // true if fork is <strong>in</strong> use<br />

public Fork (<strong>in</strong>t me) {<br />

this.me = me;<br />

}<br />

/** returns true if fork is obta<strong>in</strong>ed, false if not.<br />

*/<br />

public synchronized boolean get (<strong>in</strong>t who) {<br />

System.err.pr<strong>in</strong>tln(who+(<strong>in</strong>Use " misses " : " grabs ")+me);<br />

return <strong>in</strong>Use false : (<strong>in</strong>Use = true);<br />

}<br />

/** drops the fork.<br />

*/<br />

public synchronized void put (<strong>in</strong>t who) {


218 <strong>Informatik</strong> B SS 03<br />

System.err.pr<strong>in</strong>tln(who+" drops "+me);<br />

<strong>in</strong>Use = false; notify();<br />

}<br />

/** returns once fork is obta<strong>in</strong>ed.<br />

*/<br />

public synchronized void waitFor (<strong>in</strong>t who) {<br />

while (! get(who))<br />

try {<br />

wait();<br />

} catch (InterruptedException e) { e.pr<strong>in</strong>tStackTrace(); }<br />

}<br />

}<br />

/** make one d<strong>in</strong>er.<br />

*/<br />

public Philosopher2 (<strong>in</strong>t me, Fork left, Fork right) {<br />

this.me = me; this.left = left; this.right = right;<br />

}<br />

protected static Random random = new Random();<br />

// randomize<br />

protected <strong>in</strong>t me;<br />

// number for trace<br />

protected Fork left, right; // my forks<br />

/** philosopher’s body: th<strong>in</strong>k and eat 5 times.<br />

*/<br />

public void run () {<br />

for (<strong>in</strong>t n = 1; n


<strong>Informatik</strong> B SS 03 219<br />

}<br />

}<br />

Fork f[] = new Fork[5];<br />

for (<strong>in</strong>t n = 0; n < 5; ++ n) f[n] = new Fork(n);<br />

Philosopher2 p[] = new Philosopher2[5];<br />

p[0] = new Philosopher2(0, f[4], f[0]);<br />

for (<strong>in</strong>t n = 1; n < 5; ++ n) p[n] = new Philosopher2(n, f[n-1], f[n]);<br />

for (<strong>in</strong>t n = 0; n < 5; ++ n) p[n].start();<br />

Lösung führt zum Aushungern von 2 durch 1 und 3!!!<br />

0 th<strong>in</strong>ks<br />

1 th<strong>in</strong>ks<br />

2 th<strong>in</strong>ks<br />

3 th<strong>in</strong>ks<br />

4 th<strong>in</strong>ks<br />

1 grabs 0<br />

1 grabs 1<br />

1 eats<br />

3 grabs 2<br />

3 grabs 3<br />

3 eats<br />

1 drops 1<br />

1 drops 0<br />

1 th<strong>in</strong>ks<br />

2 grabs 1<br />

2 misses 2<br />

2 drops 1<br />

2 grabs 1<br />

2 misses 2<br />

2 drops 1<br />

2 grabs 1<br />

...<br />

14.3.4 Deadlocks durch falsche Anordnung<br />

// When two threads try to lock two objects, deadlock can occur unless<br />

// they always request the locks <strong>in</strong> the same order.<br />

f<strong>in</strong>al Object resource1 = new Object(); // Here are two objects to lock<br />

f<strong>in</strong>al Object resource2 = new Object();<br />

Thread t1 = new Thread(new Runnable() { // Locks resource1 then resource2<br />

public void run() {<br />

synchronized(resource1) {<br />

synchronized(resource2) { compute(); }<br />

}<br />

}<br />

});<br />

Thread t2 = new Thread(new Runnable() { // Locks resource2 then resource1


220 <strong>Informatik</strong> B SS 03<br />

public void run() {<br />

synchronized(resource2) {<br />

synchronized(resource1) { compute(); }<br />

}<br />

}<br />

});<br />

t1.start(); // Locks resource1<br />

t2.start(); // Locks resource2 and now neither thread can progress!<br />

14.4 Threads: Ergänzungen<br />

Threads können durch setDaemon(true) zu Dämon-Threads gemacht<br />

werden: Der Interpreter wird beendet, wenn alle Nicht-Dämon-Threads beendet<br />

s<strong>in</strong>d.<br />

Threads können Thread-Gruppen zugeordnet werden und dann geme<strong>in</strong>sam<br />

behandelt werden. Ohne explizite Angabe e<strong>in</strong>er Gruppe gehört e<strong>in</strong> Thread zur<br />

Gruppe ‘System’.<br />

Vordef<strong>in</strong>ierte Klassen, die Collection, Set, List oder Map implementieren,<br />

haben <strong>in</strong> der Regel ke<strong>in</strong>e synchronized()-Methoden (z.B. ArrayList). Es<br />

können synchronisierte Wrapper-<strong>Objekt</strong>e erzeugt werden:<br />

List synclist = Collections.synchronizedList(list);<br />

Map syncmap = Collections.synchronizedMap(map);<br />

Ordentliches Unterbrechen e<strong>in</strong>es Threads durch <strong>in</strong>terrupt().<br />

wait() und sleep() erlauben e<strong>in</strong>en Interrupt, also kann e<strong>in</strong> Thread nicht<br />

“mitten beim Arbeiten” angehalten werden.<br />

class T extends Thread {<br />

public void run() {<br />

while(true) { // This thread runs until asked to stop<br />

process();<br />

// Do someth<strong>in</strong>g<br />

try { Thread.sleep(1000); } // Wait 1000 millisecs<br />

catch (InterruptedException e) { // Handle the <strong>in</strong>terrupt<br />

return;<br />

}<br />

}<br />

}<br />

}<br />

public stopThread() {<br />

<strong>in</strong>terrupt();<br />

}


<strong>Informatik</strong> B SS 03 221<br />

Reverse<br />

Sort<br />

Reverse<br />

List of words<br />

List of reversed<br />

words<br />

List of reversed<br />

sorted words<br />

List of rhym<strong>in</strong>g<br />

words<br />

Reverse<br />

Sort<br />

Reverse<br />

List of words<br />

List of rhym<strong>in</strong>g<br />

words<br />

14.5 Pipe-Ströme<br />

Abbildung 59: Kommunikation ohne/mit PipeStreams<br />

Pipes werden benutzt, um von e<strong>in</strong>em Thread erzeugte Daten e<strong>in</strong>em anderen<br />

Thread zur Verfügung zu stellen.<br />

Klassen PipedReader/PipedWriter (Character-Stream) bzw.<br />

PipedInputStream/PipedOutputStream<br />

Beispiel: Klasse, die verschiedene Str<strong>in</strong>g-Manipulationen durchführt (Sortieren,<br />

Umkehren von Text).<br />

Ermittlung von reimenden Worten: Jedes Wort e<strong>in</strong>er Liste umdrehen, sortieren,<br />

wieder umdrehen.<br />

Ohne Pipe-Ströme müssen die Operationen Umdrehen, Sortieren, Umdrehen<br />

sequentiell abgearbeitet werden, da jedes Zwischenergebnis explizit, z. B. <strong>in</strong><br />

e<strong>in</strong>er Datei, gespeichert werden muss.<br />

Mit Pipe-Strömen kann der Output e<strong>in</strong>er Operation direkt an die nachfolgende<br />

Operation übergeben werden.<br />

In der ma<strong>in</strong>-Methode wird e<strong>in</strong> Reader-<strong>Objekt</strong> erzeugt. Die Ermittlung der<br />

reimenden Worte wird über den verschachtelten Methodenaufruf<br />

reverse(sort(reverse(words))) realisiert.<br />

Die Methoden reverse() und sort() arbeiten mit Threads und<br />

kommunizieren über Pipe-Ströme (siehe Abb. 60):


222 <strong>Informatik</strong> B SS 03<br />

Abbildung 60: Direkte Kommunikation mit PipeStream<br />

– E<strong>in</strong> PipeReader wird “auf” e<strong>in</strong>em PipeWriter erzeugt: Was auf den<br />

PipeWriter geschrieben wird, kann vom PipeReader gelesen werden!<br />

– Dazu gibt es e<strong>in</strong>en BufferedReader, der die E<strong>in</strong>gabe (aus e<strong>in</strong>er Quelle, z.B.<br />

der Pipe) erhält, und e<strong>in</strong>en Pr<strong>in</strong>tWriter, der die Ausgabe <strong>in</strong> e<strong>in</strong>e S<strong>in</strong>k (z.B.<br />

Pipe) schreibt.<br />

Anmerkung: Im Pr<strong>in</strong>zip kann mit Pipes nebenläufig gearbeitet werden. Z.B. kann<br />

e<strong>in</strong> Thread bereits konsumieren (lesen und verarbeiten), während e<strong>in</strong> anderer<br />

noch produziert. Die Methode sort() kann jedoch erst arbeiten, wenn alle<br />

Worte von reverse() umgedreht wurden. E<strong>in</strong> Beispiel für “parallele”<br />

Verarbeitung mit Pipe-Strömen ist im begleitenden Code zur Vorlesung<br />

angegeben (SuffixExtract.java).<br />

import java.io.*;<br />

public class Rhym<strong>in</strong>gWords {<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args) throws IOException {<br />

FileReader words = new FileReader("words.txt");<br />

// do the revers<strong>in</strong>g and sort<strong>in</strong>g<br />

Reader rhymedWords = reverse(sort(reverse(words)));<br />

// write new list to standard out<br />

BufferedReader <strong>in</strong> = new BufferedReader(rhymedWords);<br />

Str<strong>in</strong>g <strong>in</strong>put;<br />

}<br />

while ((<strong>in</strong>put = <strong>in</strong>.readL<strong>in</strong>e()) != null)<br />

System.out.pr<strong>in</strong>tln(<strong>in</strong>put);<br />

<strong>in</strong>.close();<br />

public static Reader reverse(Reader source) throws IOException {<br />

// BufferedReader <strong>in</strong> = new BufferedReader(source);<br />

PipedWriter pipeOut = new PipedWriter();<br />

PipedReader pipeIn = new PipedReader(pipeOut);


<strong>Informatik</strong> B SS 03 223<br />

// Pr<strong>in</strong>tWriter out = new Pr<strong>in</strong>tWriter(pipeOut);<br />

}<br />

// new ReverseThread(out, <strong>in</strong>).start();<br />

new ReverseThread(pipeOut, source).start();<br />

return pipeIn;<br />

public static Reader sort(Reader source) throws IOException {<br />

// BufferedReader <strong>in</strong> = new BufferedReader(source);<br />

PipedWriter pipeOut = new PipedWriter();<br />

PipedReader pipeIn = new PipedReader(pipeOut);<br />

// Pr<strong>in</strong>tWriter out = new Pr<strong>in</strong>tWriter(pipeOut);<br />

// new SortThread(out, <strong>in</strong>).start();<br />

new SortThread(pipeOut, source).start();<br />

}<br />

}<br />

return pipeIn;<br />

import java.io.*;<br />

public class ReverseThread extends Thread {<br />

private Pr<strong>in</strong>tWriter out = null;<br />

private BufferedReader <strong>in</strong> = null;<br />

public ReverseThread(Writer out, Reader <strong>in</strong>) {<br />

this.out = new Pr<strong>in</strong>tWriter(out);<br />

this.<strong>in</strong> = new BufferedReader(<strong>in</strong>);<br />

}<br />

public void run() {<br />

if (out != null && <strong>in</strong> != null) {<br />

try {<br />

Str<strong>in</strong>g <strong>in</strong>put;<br />

// for every l<strong>in</strong>e you read, reverse it<br />

while ((<strong>in</strong>put = <strong>in</strong>.readL<strong>in</strong>e()) != null) {<br />

out.pr<strong>in</strong>tln(new Str<strong>in</strong>gBuffer(<strong>in</strong>put).reverse().toStr<strong>in</strong>g());<br />

out.flush();<br />

}<br />

out.close();<br />

} catch (IOException e) {<br />

System.err.pr<strong>in</strong>tln("ReverseThread run: " + e);<br />

}<br />

}<br />

}


224 <strong>Informatik</strong> B SS 03<br />

}<br />

import java.io.*;<br />

import java.util.Arrays;<br />

public class SortThread extends Thread {<br />

private Pr<strong>in</strong>tWriter out = null;<br />

private BufferedReader <strong>in</strong> = null;<br />

public SortThread(Writer out, Reader <strong>in</strong>) {<br />

this.out = new Pr<strong>in</strong>tWriter(out);<br />

this.<strong>in</strong> = new BufferedReader(<strong>in</strong>);<br />

}<br />

public void run() {<br />

<strong>in</strong>t MAXWORDS = 50;<br />

if (out != null && <strong>in</strong> != null) {<br />

try {<br />

Str<strong>in</strong>g[] listOfWords = new Str<strong>in</strong>g[MAXWORDS];<br />

<strong>in</strong>t numwords = 0;<br />

}<br />

}<br />

}<br />

// first read all<br />

while ((listOfWords[numwords] = <strong>in</strong>.readL<strong>in</strong>e()) != null)<br />

numwords++;<br />

// then sort it<br />

Arrays.sort(listOfWords, 0, numwords);<br />

for (<strong>in</strong>t i = 0; i < numwords; i++)<br />

out.pr<strong>in</strong>tln(listOfWords[i]);<br />

out.close();<br />

} catch (IOException e) {<br />

System.err.pr<strong>in</strong>tln("SortThread run: " + e);<br />

}


<strong>Informatik</strong> B SS 03 225<br />

15 Reguläre Ausdrücke und Pattern-Match<strong>in</strong>g<br />

Achtung: Dieses Kapitel wird noch überarbeitet und erweitert!<br />

15.1 Str<strong>in</strong>g Pattern-Match<strong>in</strong>g<br />

Zu den Klassen von Algorithmen, die jeder <strong>Informatik</strong>er kennen sollte, gehören<br />

neben Such-, Sortier-, und Graphalgorithmen auch Pattern-Match<strong>in</strong>g<br />

Algorithmen.<br />

Im folgenden werden die Grundlagen für Str<strong>in</strong>g Pattern-Match<strong>in</strong>g e<strong>in</strong>geführt<br />

(siehe Baase & van Gelder, Kap. 11).<br />

Der Schwerpunkt liegt auf den Algorithmen, nicht auf der <strong>Java</strong>-Implementierung.<br />

Entsprechend werden die Algorithmen <strong>in</strong> Pseudo-Code angegeben,<br />

15.1.1 Motivation<br />

Problem: F<strong>in</strong>den e<strong>in</strong>es Teilstr<strong>in</strong>gs (Muster/Pattern) <strong>in</strong> e<strong>in</strong>em anderen Str<strong>in</strong>g<br />

(Text).<br />

Anwendungsbereiche: Textverarbeitung, Information-Retrieval, Suche über<br />

Verzeichnisbäume, Bio<strong>in</strong>formatik, etc.<br />

15.1.2 Straightforward Lösung<br />

Algorithmische Idee:<br />

– Starte beim ersten Zeichen im Text und prüfe, ob die Zeichenfolge des<br />

Patterns mit der anfänglichen Zeichenfolge im Text matched.<br />

– Wenn ja: Erfolg,<br />

Wenn ne<strong>in</strong>: Starte neu beim zweiten Zeichen im Text<br />

ist O(/ ) für / ¡<br />

¡<br />

Länge des Patterns und Länge des Texts!<br />

Nicht gerade effizient: Anzahl von Zeichen-Vergleichen<br />

P: ABABC ABABC ABABC<br />

vvvvv vvvvv vvvvv<br />

T: ABABABCCA ABABABCCA ABABABCCA<br />

15.1.3 Str<strong>in</strong>g-Match<strong>in</strong>g mit endlichen Automaten<br />

Idee: Repräsentation e<strong>in</strong>es Patterns als endlichen Automaten (siehe Kapitel<br />

1.4.1 “Formale Sprachen”). Text als Input <strong>in</strong> den Automaten, Term<strong>in</strong>ation <strong>in</strong><br />

Endzustand, wenn Pattern entdeckt.


226 <strong>Informatik</strong> B SS 03<br />

Tabelle 6: E<strong>in</strong>faches Str<strong>in</strong>g Pattern-Match<strong>in</strong>g<br />

<strong>in</strong>t simpleScan(char[] P, char[] T, <strong>in</strong>t m)<br />

<strong>in</strong>t match; // value to return<br />

<strong>in</strong>t i; // current guess where P beg<strong>in</strong>s <strong>in</strong> T<br />

<strong>in</strong>t j; // <strong>in</strong>dex of current char <strong>in</strong> T<br />

<strong>in</strong>t k; // <strong>in</strong>dex of current char <strong>in</strong> P<br />

match = -1;<br />

i = j = 1; k = 1;<br />

while (!endText(T, j))<br />

if (k ¡ m)<br />

match = i; // match found<br />

break;<br />

if (T[j] == P[k])<br />

else<br />

j++;<br />

k++;<br />

// Back up over matched chars<br />

<strong>in</strong>t backup = k-1;<br />

j = j-backup;<br />

k = k-backup;<br />

// Slide pattern forward, start over<br />

j++;<br />

i = j;<br />

// Cont<strong>in</strong>ue loop<br />

return match;


¡<br />

<strong>Informatik</strong> B SS 03 227<br />

B<br />

Start<br />

B, C<br />

A<br />

1<br />

A A B C<br />

2 3 4 *<br />

B, C<br />

C<br />

Abbildung 61: Endlicher Automat für P = AABC<br />

Vorteil: Jeder Buchstabe im Text muss nur e<strong>in</strong>mal angeschaut werden: O( );<br />

allerd<strong>in</strong>gs gilt dieser Vorteil nur, wenn die automatische Konstruktion des<br />

endlichen Automaten aus dem Pattern effizient durchgeführt werden kann.<br />

In Abb. 61 ist e<strong>in</strong> endlicher Automat für das Pattern “AAABC” angegeben. “*”<br />

markiert e<strong>in</strong>en Endzustand, <strong>in</strong> allen anderen Zuständen wird das nächste<br />

Zeichen auf dem Band gelesen und der Schreib-Lese-Kopf um e<strong>in</strong> Zeichen nach<br />

rechts verschoben.<br />

Leider ist das Erstellen des endlichen Automaten (die entsprechende<br />

Übergangstabelle) recht aufwendig.<br />

der Knuth-Morris-Pratt Algorithmus arbeitet mit e<strong>in</strong>er e<strong>in</strong>facheren Variante<br />

zum endlichen Automat – e<strong>in</strong>er sogenannten Verschiebetabelle, die <strong>in</strong> l<strong>in</strong>earer<br />

Zeit <strong>in</strong> der Pattern-Länge konstruiert werden kann!<br />

15.1.4 Der Knuth-Morris-Pratt (KPM) Algorithmus<br />

KPM-Idee: Verschiebetabelle statt Übergangstabelle<br />

Grundidee: Wenn <strong>in</strong> e<strong>in</strong>em Text nach e<strong>in</strong>em Pattern gesucht wird, und bereits<br />

der Anfang des Patterns (Präfix) matched,<br />

12345678<br />

P: ABABABCB<br />

T: ...ABABABx....<br />

i=1<br />

dann soll bei Miss-Match das Pattern so (nach rechts) über dem Text<br />

verschoben werden, dass die nächstmögliche Position des Patterns im Text<br />

gefunden werden kann.<br />

Möglicher nächster Beg<strong>in</strong>n des Patterns im Text ist das größte Präfix des<br />

Patterns, das mit e<strong>in</strong>em Suffix (Endstück) des bisher verarbeiteten Texts<br />

übere<strong>in</strong>stimmt:


228 <strong>Informatik</strong> B SS 03<br />

Tabelle 7: Konstruktion der Verschiebetabelle<br />

void kmpSetup(char[] P, <strong>in</strong>t m, <strong>in</strong>t[] fail)<br />

fail[1] = 0;<br />

k = 0; // Position des Anfangsabschnitts<br />

for (<strong>in</strong>t q = 2; q m; q++)<br />

while ((k ¡ 0) && (P[k+1] ¡£¢ P[q])) k = fail[k];<br />

if (P[k+1] == P[q]) k++;<br />

fail[q] = k;<br />

12345678<br />

P: ABABABCB<br />

T: ...ABABABx....<br />

i=1<br />

Problem: Die Verschiebetablle soll nur bezüglich des Patterns, also unabhängig<br />

von e<strong>in</strong>em konkreten Text, konstruiert werden.<br />

Trick: Man weiss etwas über den bisher verarbeiteten Text. Das bisher<br />

gematchte Präfix des Patterns muss das Suffix des Texts se<strong>in</strong>!<br />

Berechnung der Rücksprünge:<br />

i 12345678<br />

P ABABABCB<br />

fail 00123400<br />

Rücksprung ist jeweils derjenige Indexplatz im Pattern, der das größte Präfix für<br />

das bisher verarbeitete Pattern darstellt.<br />

Beispiel:<br />

12345678910<br />

T: ...ABABABABCB...<br />

T[1] A ok P[1]<br />

T[2] B ok P[2]<br />

T[3] A ok P[3]<br />

T[4] B ok P[4]<br />

T[5] A ok P[5]<br />

T[6] B ok P[6]<br />

T[7] A missmatch --> gehe zu P[4] (nimm ABAB als gematched an)<br />

T[7] A ok P[5]<br />

T[8] B ok P[6]<br />

T[9] C ok P[7]<br />

T[10]B ok P[8] --> output 10-8 = 2<br />

Erläuterung: Berechnung der Verschiebetabelle


<strong>Informatik</strong> B SS 03 229<br />

Tabelle 8: Der KPM-Algorithmus<br />

<strong>in</strong>t kpmScan(char[] P, char[] T, <strong>in</strong>t m, <strong>in</strong>t[] fail)<br />

<strong>in</strong>t q = 0; // Zahl der Pos. <strong>in</strong> denen T und P übere<strong>in</strong>stimmen<br />

for (<strong>in</strong>t i = 0; i n; i++)<br />

while ((q ¡ 0) && (P[q+1] ¡£¢ T[i])) q = fail[q];<br />

if (P[q+1] = T[i]) q++;<br />

if (q == m) output (i-m)<br />

Konstruktion der Verschiebetabelle<br />

für “ABABABCB”<br />

q k P[k+1] P[q] k’ fail[q]<br />

2 0 A B - 0<br />

3 0 A A 1 1<br />

4 1 B B 2 2<br />

5 2 A A 3 3<br />

6 3 B B 4 4<br />

7 4 A C 2 -<br />

2 A C 0 -<br />

0 A C - 0<br />

8 0 A B - 0<br />

Tabelle 9: Illustration<br />

KPM-Algorithmus: Pattern “ABA-<br />

BABCB” und Text “ABABABABCB”<br />

i q P[q+1] T[i] q’<br />

1 0 A A 1<br />

2 1 B B 2<br />

3 2 A A 3<br />

4 3 B B 4<br />

5 4 A A 5<br />

6 5 B B 6<br />

7 6 C A 4<br />

4 A A 5<br />

8 5 B B 6<br />

9 6 C C 7<br />

10 7 B B 8<br />

output: 10-8<br />

Bestimmung der Verschiebungen: Ausgehend von fail[1] = 0 lassen sich<br />

die weiteren Verschiebungen mithilfe der bereits errechneten bestimmen. Für<br />

die Pattern-Position ¢ ¡ G ist 5 immer maximal gewählt, so daß gilt:<br />

G ¡ ¡ A5 G ¡ ¢ 5 ¡ ¢ ¢ . Daraus ergibt sich fail[q] = k + 1.<br />

Aufwand: For-Schleife ¢ (Index ) geht über Länge des / Patterns ; 5 Die -Werte<br />

können <strong>in</strong>nerhalb der while-Schleife maximal den ¢ G Wert annehmen. Die<br />

maximale Anzahl von Änderungen 5 des -Werts kann / auf abgeschätzt<br />

werden.<br />

Damit gilt für die Berechnung der ¦ / Verschiebetabelle . Da der<br />

Match<strong>in</strong>g-Algorithmus l<strong>in</strong>ear <strong>in</strong> der Länge des Textes ist, ergibt sich e<strong>in</strong><br />

Gesamtaufwand ¦ / von .<br />

15.1.5 Pattern-Match<strong>in</strong>g mit Regulären Ausdrücken<br />

Statt konstanter Str<strong>in</strong>gs will man häufig nach allgeme<strong>in</strong>eren Muster suchen.<br />

Beispiele:


230 <strong>Informatik</strong> B SS 03<br />

– F<strong>in</strong>de alle Folgen aus drei Zeichen, die mit “T” beg<strong>in</strong>nen und mit “r” enden:<br />

T.r<br />

(“.” matched jedes Zeichen ausser “newl<strong>in</strong>e”)<br />

– F<strong>in</strong>de alle Worte, die mit “F” beg<strong>in</strong>nen und mit “l” enden: F.*l<br />

(Der Kleene-Stern def<strong>in</strong>iert e<strong>in</strong>e Folge aus 0 bis n Zeichen der<br />

angegebenen Menge.)<br />

– F<strong>in</strong>de Jahreszahlen zwischen 1970 und 1999 19[789][0-9]<br />

(Eckige Klammern geben e<strong>in</strong>e Zeichen-Klasse/Menge an. Mit x-y kann<br />

man Bereiche für geordnete Zeichenmengen def<strong>in</strong>ieren.)<br />

Auch für Pattern-Match<strong>in</strong>g mit regulären Ausdrücken existieren Pattern-Matcher.<br />

Diese Matcher basieren typischerweise auf endlichen Automaten.<br />

Das grep-Kommando implementiert e<strong>in</strong>en Matcher für reguläre Ausdrücke. Die<br />

Programmiersprache Perl ist im wesentlichen e<strong>in</strong> Matcher für reguläre<br />

Ausdrücke<br />

Natürlich können Matcher für reguläre Ausdrücke auch Str<strong>in</strong>g-Pattern-Match<strong>in</strong>g<br />

– konstante Str<strong>in</strong>gs s<strong>in</strong>d spezielle reguläre Ausdrücke.<br />

15.2 <strong>Java</strong> 1.4 ‘regex’<br />

Vor <strong>Java</strong> 1.4: Pattern-Match<strong>in</strong>g konnte mithilfe der Str<strong>in</strong>gTokenizer und<br />

charAt()-Methoden nur sehr umständlich realisiert werden. Neu:<br />

java.util.regex<br />

Flanagan nom<strong>in</strong>iert regular expressions als Nummer 2 der Top-Ten Liste von<br />

“cool new features <strong>in</strong> <strong>Java</strong> 1.4”.<br />

Die Syntax für reguläre Ausdrücke <strong>in</strong> <strong>Java</strong> ist sehr ähnlich zu Perl.<br />

15.2.1 Konstruktion regulärer Ausdrücke<br />

Neben den standardmässig zur Beschreibung regulärer Ausdrücke<br />

verwendbaren Konstrukte werden e<strong>in</strong>ige Zusatzkonstrukte erlaubt, die es<br />

e<strong>in</strong>facher machen reguläre Ausdrücke aufzuschreiben.<br />

Im Tabelle 10 wird e<strong>in</strong> kurzer Überblick gegeben (weitere Information ist <strong>in</strong> der<br />

Dokumentation nachzulesen).<br />

Die Symbole für special characters (wie $ ˆ . * +) und die Syntax für<br />

Zeichenklassen s<strong>in</strong>d Standard für die Notation von regulären Ausdrücken.<br />

Weitere Notationen, <strong>in</strong>sbesondere die Möglichkeit von Kurznotationen für<br />

vordef<strong>in</strong>ierte Zeichenklassen s<strong>in</strong>d spezifisch für <strong>Java</strong> bzw. Perl.<br />

Achtung bei greedy-Quantoren: a.*i würde im Str<strong>in</strong>g gadji beri b<strong>in</strong><br />

blassa glassala laula lonni cadorsu sassala bim bis zu dem<br />

letzten ‘i’ <strong>in</strong> ‘bim’ matchen! (Zeile aus dem Gedicht ’Gadji beri bimba’ von Hugo<br />

Ball)


<strong>Informatik</strong> B SS 03 231<br />

Achtung: Da das Pattern als <strong>Java</strong>-Str<strong>in</strong>g angegeben wird, müssen Zeichen, die<br />

wörtlich gematched werden sollen, mit zweifachem Backslash e<strong>in</strong>geleitet<br />

werden, z. B. \\..<br />

15.2.2 Die Pattern-Klasse<br />

Die Pattern-Klasse repräsentiert e<strong>in</strong>en regulären Ausdruck, der als Str<strong>in</strong>g<br />

spezifiziert wurde.<br />

Mit der Klassenmethode Pattern.compile(str<strong>in</strong>g) wird das Pattern <strong>in</strong><br />

e<strong>in</strong>e effiziente <strong>in</strong>terne Repräsentation umgewandelt.<br />

Pattern p = Pattern.compile("[,\\s]+");<br />

erzeugt e<strong>in</strong> Pattern für Trennung durch Komma oder Whitespace<br />

Weitere Methoden:<br />

– split(): Teilt den gegebenen Input bezüuglich der Matches zum Pattern<br />

Str<strong>in</strong>g[] result = p.split("one,two, three four , five ");<br />

– matcher(): erzeugt e<strong>in</strong>en Matcher, der gegebenen Input gegen das<br />

Pattern vergleicht<br />

Matcher m = p.matcher("onetwothree four five,six");<br />

15.2.3 Die Matcher-Klasse<br />

Die E<strong>in</strong>gabe an e<strong>in</strong>en Matcher muss dem Interface CharSequence genügen.<br />

Im obigen Beispiel wurde e<strong>in</strong> Str<strong>in</strong>g-<strong>Objekt</strong> übergeben. Beispielsweise<br />

implementieren Str<strong>in</strong>g, Str<strong>in</strong>gBuffer und CharBuffer dieses Interface.<br />

Wichtige Methoden:<br />

– matches(): Vergleicht den gesamten Input gegen das Pattern und liefert<br />

true, bei Übere<strong>in</strong>stimmung, false sonst.<br />

– f<strong>in</strong>d(): Scanned die e<strong>in</strong>gegebene Zeichenfolge und sucht die nächste<br />

Teilfolge, die mit dem Pattern übere<strong>in</strong>stimmt.<br />

– appendReplacement(), appendTail(): Sammeln des Ergebnisstr<strong>in</strong>gs<br />

<strong>in</strong> e<strong>in</strong>em Str<strong>in</strong>gBuffer<br />

– replaceAll(): liefert Str<strong>in</strong>g, <strong>in</strong> dem jedes vorkommen des Patterns<br />

durch alternatives Pattern ersetzt wird.<br />

15.2.4 Beispiel: Suche und Ersetze<br />

import java.util.regex.*;<br />

public class Replacement {<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

throws Exception {


232 <strong>Informatik</strong> B SS 03<br />

Tabelle 10: Konstruktion regulärer Ausdrücke<br />

Zeichen<br />

x<br />

Zeichen x<br />

\\ Backslash<br />

\t Tab<br />

\n Newl<strong>in</strong>e<br />

\cx<br />

Control-Zeichen x<br />

... ...<br />

Zeichenklassen<br />

[abc] e<strong>in</strong>fache Klasse (a, b, oder c)<br />

[ˆabc] Negation (e<strong>in</strong> beliebiges Zeichen ausser a, b, oder c)<br />

[a-zA-Z] <strong>in</strong>klusiver Bereich (alle Gross- und Kle<strong>in</strong>buchstaben)<br />

[a-z-[bc]] Subtraktion (a bis Z ausser b und c)<br />

[a-z-[m-p]] Subtraktion mit <strong>in</strong>kl. Bereich (a bis z ausser m bis p)<br />

[a-z-[ˆdef]] (d, e, oder f)<br />

Vordef<strong>in</strong>ierte Zeichenklassen<br />

. beliebiges Zeichen (evtl. exklusive Zeilenendzeichen)<br />

\d e<strong>in</strong>e Ziffer [0-9]<br />

\D ke<strong>in</strong>e Ziffer [ˆ0-9]<br />

\s Trenner (whitespace) [ \t\n\x0B\f\r]<br />

\S ke<strong>in</strong> Trenner<br />

\w Textzeichen [a-zA-Z_0-9]<br />

\W ke<strong>in</strong> Textzeichen<br />

Begrenzer<br />

ˆ Zeilenanfang weitere Verwendung Negation<br />

$ Zeilenende<br />

\b Wortgrenze<br />

\B ke<strong>in</strong>e Wortgrenze<br />

... ...<br />

Greedy Quantoren<br />

* 0 bis n des vorangestellten Zeichens Kleene-Stern<br />

+ 1 bis n des vorangestellten Zeichens<br />

das vorangestellte Zeichen<br />

0- oder 1-mal<br />

... ...<br />

Weitere Quantoren<br />

... ...<br />

Quotation<br />

\ markiert das folgende Zeichen<br />

als “wörtlich zu nehmen”<br />

Logische Operatoren<br />

XY Sequenz (X gefolgt von Y)<br />

X | Y Oder (vgl. BNF)


<strong>Informatik</strong> B SS 03 233<br />

}<br />

}<br />

// Create a pattern to match cat<br />

Pattern p = Pattern.compile("cat");<br />

// Create a matcher with an <strong>in</strong>put str<strong>in</strong>g<br />

Matcher m = p.matcher("one cat," +<br />

" two cats <strong>in</strong> the yard");<br />

Str<strong>in</strong>gBuffer sb = new Str<strong>in</strong>gBuffer();<br />

boolean result = m.f<strong>in</strong>d();<br />

// Loop through and create a new Str<strong>in</strong>g<br />

// with the replacements<br />

while(result) {<br />

m.appendReplacement(sb, "dog");<br />

result = m.f<strong>in</strong>d();<br />

}<br />

// Add the last segment of <strong>in</strong>put to<br />

// the new Str<strong>in</strong>g<br />

m.appendTail(sb);<br />

System.out.pr<strong>in</strong>tln(sb.toStr<strong>in</strong>g());<br />

Allgeme<strong>in</strong> können reguläre Ausdrücke durch Str<strong>in</strong>gs über Variablen ersetzt<br />

werden.<br />

Hierzu werden im regulären Ausdruck Gruppen gebildet:<br />

\[([a-z])\.([A-Z])\] kann durch (\1,\2) ersetzt werden.<br />

Beispiele:<br />

[a.B] durch (a,B)<br />

[x.Y] durch (x,Y)<br />

Gruppen werden durch runde Klammern e<strong>in</strong>geschlossen.<br />

Wie genau Variablen notiert werden, hängt vom verwendeten Programm ab.


234 <strong>Informatik</strong> B SS 03<br />

16 Assertions<br />

Achtung: Dieses Kapitel wird noch überarbeitet und erweitert!<br />

16.1 Zusicherungskalkül<br />

Im Kapitel “Typsicherheit” wurde gezeigt, wie im Pr<strong>in</strong>zip die Korrektheit e<strong>in</strong>er<br />

Programmiersprache bewiesen werden kann.<br />

Schreibt man Programme, so möchte man gegeben die Semantik der<br />

entsprechenden Implementierungssprache, zusichern, dass diese korrekt s<strong>in</strong>d.<br />

Verifikation: Das Programm erfüllt se<strong>in</strong>e Spezifikation (Beweis der partiellen<br />

Korrektheit).<br />

Validierung: Das Programm tut, was es tun soll (Testen, siehe Vorlesung<br />

“<strong>Informatik</strong> C”).<br />

In der Vorlesung “<strong>Informatik</strong> A” wurde das Zusicherungskalkül (Hoare-Kalkül)<br />

e<strong>in</strong>geführt.<br />

Dieses Kalkül ist e<strong>in</strong>e formale Methode zum Korrektheitsbeweis von<br />

Programmen, das bis zu halbautomatischen Beweisern ausgebaut werden<br />

kannn.<br />

Das Zustandskalkül ermöglicht die Angabe von Vor- und Nachbed<strong>in</strong>gungen für<br />

Programmanweisungen.<br />

Für alle Typen von Anweisungen (Zuweisung, bed<strong>in</strong>gte Anweisung, ...) und<br />

Schleifen s<strong>in</strong>d Axiome vorgegeben. Von besonderer Bedeutung s<strong>in</strong>d<br />

Schleifen<strong>in</strong>varianten.<br />

Beispiel:<br />

Aus =¡ 4# -@=! folgt K while 4 loop @ end ¢ ¤£ 4 .<br />

Typischerweise gibt man Zusicherungen als Kommentare vor. Die Beweise<br />

folgen als “symbolische Auswertung” per Hand.<br />

/* P */<br />

while b {<br />

/* P && b */<br />

...<br />

/* P */<br />

}<br />

/* P && !b */<br />

Seit <strong>Java</strong> 1.4 gibt es die assert-Anweisung – von Flanagan als das “Number<br />

one cool feature of <strong>Java</strong> 1.4” nom<strong>in</strong>iert.<br />

Zusicherungen enthalten boolesche Ausdrücke, von denen der Programmierer<br />

annimmt, dass sie an der entsprechenden Stelle gelten.


<strong>Informatik</strong> B SS 03 235<br />

Arbeiten mit Zusicherungen ist e<strong>in</strong> schneller und effizienter Weg um Fehler im<br />

Programm zu erkennen und korrigieren. Sie dienen gleichzeitig als<br />

Dokumentation der Funktionalität des Programms und erhöhen damit se<strong>in</strong>e<br />

Wartbarkeit!<br />

16.2 Die ‘assert’-Anweisung<br />

Ergänzung folgt


¡<br />

236 <strong>Informatik</strong> B SS 03<br />

17 Ausblick: GUIs und Event Handl<strong>in</strong>g<br />

im Detail <strong>in</strong> “<strong>Informatik</strong> C: Oberflächenprogrammierung”<br />

17.1 <strong>Java</strong> Foundation Classes<br />

<strong>Java</strong> Foundation Classes (JFC): Sammlung von Standard <strong>Java</strong> APIs für Graphik<br />

und GUIs.<br />

“Foundation”: weil die meisten <strong>Java</strong> (client-seitigen) Applikationen darauf<br />

aufgebaut s<strong>in</strong>d.<br />

Abstract W<strong>in</strong>dow<strong>in</strong>g Toolkit (AWT): bis <strong>Java</strong> 1.1, rudimentär und “heavyweight”<br />

(auf native GUI Komponenten aufgebaut). Durch native look and feel ergeben<br />

sich viele kle<strong>in</strong>e Unterschiede <strong>in</strong> der Darstellung zwischen verschiedenen<br />

Plattformen.<br />

Erweiterung <strong>Java</strong>2D: unterstützt das Erstellen zweidimensionaler Graphiken,<br />

Erstellung von Zeichenprogrammen und Bildeditoren.<br />

Sw<strong>in</strong>g: state-of-the-art GUI toolkit, das vollständig <strong>in</strong> <strong>Java</strong> geschrieben ist<br />

(“lightweight”). Sw<strong>in</strong>g unterstützt pluggable look and feel, das heisst, dass das<br />

Aussehen der GUI dynamisch an verschiedene Plattformen und<br />

Betriebssysteme angepasst werden kann.<br />

Aber Achtung: Sw<strong>in</strong>g-Klassen s<strong>in</strong>d auf AWT-Klassen aufgebaut.<br />

Applikationen: <strong>Java</strong>-Programme mit graphischer Oberfläche, Ausgabe von<br />

Graphik und Text auf Bildschirm oder Drucker, Daten-Transfer via drag-and-drop.<br />

Applets: Kle<strong>in</strong>e Applikationen, die <strong>in</strong> e<strong>in</strong>em Web-Browser (z.B. Netscape) laufen.<br />

GUI-Builder: s<strong>in</strong>d visuelle Programmierumgebungen, die automatisch Code für<br />

graphisch spezifizierte Oberflächen erzeugen. Dadurch kann sich die<br />

Entwicklungszeit verr<strong>in</strong>gern und es besteht e<strong>in</strong>e e<strong>in</strong>fache Möglichkeit,<br />

verschiedene Design auszuprobieren.<br />

17.2 Sw<strong>in</strong>g-Komponenten<br />

17.2.1 Erstes Beispiel ‘HelloWorldSw<strong>in</strong>g’<br />

import javax.sw<strong>in</strong>g.*;<br />

public class HelloWorldSw<strong>in</strong>g {<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args) {<br />

JFrame frame = new JFrame("HelloWorldSw<strong>in</strong>g");<br />

f<strong>in</strong>al JLabel label = new JLabel("Hello World");<br />

frame.getContentPane().add(label);<br />

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);<br />

frame.pack();<br />

frame.setVisible(true);<br />

} }


¡<br />

<strong>Informatik</strong> B SS 03 237<br />

Paket-Präfix javax: Sw<strong>in</strong>g kann als Erweiterung (eXtension) für <strong>Java</strong> 1.1<br />

benutzt werden. Für <strong>Java</strong> 2 gehört Sw<strong>in</strong>g zu den core-packages.<br />

Klasse JFrame: ist e<strong>in</strong> “top-level” Conta<strong>in</strong>er<br />

Hierarchie, siehe Abb. 62<br />

Methode getContentPane() liefert Conta<strong>in</strong>er und Conta<strong>in</strong>er haben<br />

Methode add().<br />

pack() ist Methode von W<strong>in</strong>dow: macht das Fenster Displayable,<br />

Berechnung der Grösse für das präferierte Layout und die Grössen der<br />

enthaltenen Komponenten.<br />

setVisible(): Methode e<strong>in</strong>er Component, Zeigen oder Verstecken e<strong>in</strong>er<br />

Component. (alternativ show())<br />

Bei Dialog-Fenstern kann es S<strong>in</strong>n machen, dass bei Beenden des Dialogs das<br />

Fenster nicht zerstört, sondern nur unsichtbar gemacht wird<br />

(setVisible(false)).<br />

Generelles Pr<strong>in</strong>zip: immer zunächst e<strong>in</strong>en top-level Conta<strong>in</strong>er erzeugen.<br />

GUI ist aus Bauste<strong>in</strong>en aufgebaut: push buttons, scrollbars, pull-down menus, ...<br />

Jeder Bauste<strong>in</strong> ist e<strong>in</strong>e Komponente (Component).<br />

Sw<strong>in</strong>g hat alle Komponenten von AWT und weitere.<br />

Sw<strong>in</strong>g-Komponenten beg<strong>in</strong>nen mit ‘J’.<br />

Sw<strong>in</strong>g Komponenten s<strong>in</strong>d “light-weight” (plattform-unabhängig).<br />

JComponent ist Unterklasse von awt.Component.<br />

JW<strong>in</strong>dow, JFrame und JDialog s<strong>in</strong>d top-level Komponenten. Typischerweise<br />

verwendet man JFrame als Fenster- und JDialog als Dialogfenster-Klasse.<br />

JW<strong>in</strong>dow ist e<strong>in</strong> Fenster ohne “Dekoration”.<br />

JPanel ist e<strong>in</strong>n typischer Conta<strong>in</strong>er (siehe unten).<br />

Weitere wichtige Komponenten s<strong>in</strong>d JButton (e<strong>in</strong> push-button), JLabel<br />

(Display-Feld für Text oder Graphik), JTextField (Darstellen und Editieren<br />

e<strong>in</strong>er Textzeile).


238 <strong>Informatik</strong> B SS 03<br />

W<strong>in</strong>dow<br />

Dialaog<br />

JDialog<br />

Component<br />

Conta<strong>in</strong>er<br />

Frame<br />

JW<strong>in</strong>dow<br />

JFrame<br />

JComponent<br />

JButton<br />

JLabel<br />

JPanel<br />

java.awt<br />

javax.sw<strong>in</strong>g<br />

JTextField<br />

17.2.2 Properties<br />

Abbildung 62: Wichtige Sw<strong>in</strong>g und AWT Komponenten<br />

Für jede Component kann ihr Aussehen und Verhalten angepasst werden<br />

(customization).<br />

Dies geschieht durch die Spezifikation von Werten für die Properties e<strong>in</strong>er<br />

Komponente.<br />

Properties werden über Accesor-Methoden abgefragt und gesetzt: getprop(),<br />

setprop().<br />

Beispiel: setVisible(true).<br />

Für Properties mit Typ boolean wird anstelle von get is verwendet:<br />

isVisible() liefert true oder false.<br />

17.2.3 Conta<strong>in</strong>er<br />

Komponenten müssen <strong>in</strong>nerhalb e<strong>in</strong>es Conta<strong>in</strong>er gepackt werden.<br />

E<strong>in</strong> Conta<strong>in</strong>er ist e<strong>in</strong>e Component, die andere Components enthalten kann.<br />

Alle speziellen Conta<strong>in</strong>er s<strong>in</strong>d Unterklassen von awt.Conta<strong>in</strong>er.<br />

Typische Conta<strong>in</strong>er: Fenster und Dialog-Boxen.<br />

Häufig werden Conta<strong>in</strong>er <strong>in</strong> andere Conta<strong>in</strong>er verschachtelt.<br />

Achtung: JFrame, JW<strong>in</strong>dow und JDialog können nur als “äussertster”<br />

Conta<strong>in</strong>er verwendet werden! (siehe unten)<br />

Manche Conta<strong>in</strong>er stellen Information auf spezielle Weise dar, manche haben<br />

Restriktionen bzgl. Zahl und Art der Komponenten, die sie enthalten können,<br />

manche s<strong>in</strong>d generisch (beliebig konfigurierbar).


<strong>Informatik</strong> B SS 03 239<br />

http://java.sun.com/docs/books/tutorial/uisw<strong>in</strong>g/components/components_pics.<br />

html<br />

Abbildung 63: Beispiele für Komponenten


¡<br />

240 <strong>Informatik</strong> B SS 03<br />

Mit der W<strong>in</strong>dow-Methode pack() wird e<strong>in</strong> Fenster ge-rendert (render<strong>in</strong>g), also<br />

<strong>in</strong> e<strong>in</strong>e graphische Darstellung gebracht. Dabei wird die Größe des Fensters – <strong>in</strong><br />

Abhängigkeit vom Layout der Unter-Komponenten – ermittelt.<br />

Jedes Fenster ist mit e<strong>in</strong>em LayoutManager (e<strong>in</strong> Interface <strong>in</strong> java.awt)<br />

verbunden. Dieser wird mit pack() aktiviert.<br />

Typische Layout-Manager s<strong>in</strong>d BorderLayout (Unterteilung e<strong>in</strong>es Conta<strong>in</strong>ers<br />

<strong>in</strong> fünf Bereiche, siehe unten) oder ScrollPaneLayout (Default für<br />

JScrollPane).<br />

Typische Schritte zum Erzeugen e<strong>in</strong>er GUI:<br />

1. Erzeugung der Conta<strong>in</strong>er<br />

2. Erzeugen der Components<br />

3. H<strong>in</strong>zufügen der Components zum Conta<strong>in</strong>er<br />

add()<br />

JFrame frame = new JFrame("HelloWorldSw<strong>in</strong>g"); // Conta<strong>in</strong>er<br />

f<strong>in</strong>al JLabel label = new JLabel("Hello World"); // Component<br />

frame.getContentPane().add(label);<br />

Spezielles Verhalten der top-level Conta<strong>in</strong>er JFrame, JW<strong>in</strong>dow, JDialog:<br />

– Wenn solche Conta<strong>in</strong>er erzeugt werden, erzeugen diese automatisch e<strong>in</strong>e<br />

Unterklasse JRootPane, die e<strong>in</strong>e Unterklasse von JComponent ist und<br />

das Interface RootPaneConta<strong>in</strong>er implementiert.<br />

– Alle Komponenten werden <strong>in</strong> JRootPane e<strong>in</strong>gefügt. Die Methode<br />

getContentPane() liefert denjenigen Conta<strong>in</strong>er, <strong>in</strong> den die<br />

Komponenten e<strong>in</strong>gefügt werden sollen.<br />

17.2.4 Layout Management<br />

JFrame und JDialog s<strong>in</strong>d generische Komponenten. Sie benutzen JPanel als<br />

default content pane und spezifizieren ke<strong>in</strong> vordef<strong>in</strong>iertes Layout der<br />

Komponenten.<br />

Hier muss e<strong>in</strong> LayoutManager def<strong>in</strong>iert werden, um die Komponenten im<br />

Conta<strong>in</strong>er anzuordnen.<br />

Default-Layout ist vorgegeben (d.h. entsprechendes <strong>Objekt</strong> existiert). Z.B. für<br />

JFrame: BorderLayout.<br />

BorderLayout erlaubt bis zu 5 Komponenten mit den Positionen North, South,<br />

East, West, Center.<br />

import java.awt.*;<br />

import javax.sw<strong>in</strong>g.*;<br />

public class BorderW<strong>in</strong>dow extends JFrame {


¡<br />

<strong>Informatik</strong> B SS 03 241<br />

public BorderW<strong>in</strong>dow() {<br />

Conta<strong>in</strong>er contentPane = getContentPane();<br />

//Use the content pane’s default BorderLayout.<br />

//contentPane.setLayout(new BorderLayout()); //unnecessary<br />

}<br />

contentPane.add(new JButton("Button 1 (NORTH)"),<br />

BorderLayout.NORTH);<br />

contentPane.add(new JButton("2 (CENTER)"),<br />

BorderLayout.CENTER);<br />

contentPane.add(new JButton("Button 3 (WEST)"),<br />

BorderLayout.WEST);<br />

contentPane.add(new JButton("Long-Named Button 4 (SOUTH)"),<br />

BorderLayout.SOUTH);<br />

contentPane.add(new JButton("Button 5 (EAST)"),<br />

BorderLayout.EAST);<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[]) {<br />

BorderW<strong>in</strong>dow w<strong>in</strong>dow = new BorderW<strong>in</strong>dow();<br />

w<strong>in</strong>dow.setTitle("BorderLayout");<br />

w<strong>in</strong>dow.pack();<br />

w<strong>in</strong>dow.setVisible(true);<br />

}<br />

17.2.5 Anmerkungen<br />

Bei der Gestaltung der Oberfläche ist es meist s<strong>in</strong>nvoll, sich an bereits<br />

e<strong>in</strong>geführte Standards zu halten (z.B. bei der Menu-Organisation, Anordnung,<br />

Aussehen): Benutzer haben sich bereits an e<strong>in</strong>geführte Oberflächen gewöhnt<br />

(Adaptationseffekte), auch wenn diese ergonomisch ke<strong>in</strong>eswegs optimal s<strong>in</strong>d.<br />

pr<strong>in</strong>ciple of least astonishment<br />

Es ist ke<strong>in</strong> guter Stil, e<strong>in</strong>e schicke Oberfläche alle<strong>in</strong> zu verkaufen (d.h. mit “niy”<br />

h<strong>in</strong>ter allen <strong>in</strong>teressanten Menüpunkten und e<strong>in</strong>igen Standardalgorithmen h<strong>in</strong>ter


¡<br />

242 <strong>Informatik</strong> B SS 03<br />

den anderen Menüpunkten).<br />

Software-Ergonomie ist e<strong>in</strong> wichtiges Forschungsgebiet, zu dem<br />

Kognitionswissenschaftler viel beitragen können.<br />

Empirische Studien von grundlegenden psychophysischen Faktoren (Kontrast,<br />

Grösse, Farbe von Schrift) bis zur Nutzerführung.<br />

E<strong>in</strong>e noch so schicke GUI br<strong>in</strong>gt gar nichts, wenn dah<strong>in</strong>ter nicht sorgfältiger<br />

Code steht! (“aussen GUI <strong>in</strong>nen pfui”).<br />

Für die Entwicklung komplexer Projekte kann es jedoch s<strong>in</strong>nvoll se<strong>in</strong>, vorab oder<br />

parallel zum Code bereits die Oberfläche zu entwerfen, um e<strong>in</strong>en Überblick über<br />

alle gewünschten Funktionalitäten und deren Abhängigkeiten zu bekommen.<br />

17.3 Event-Handl<strong>in</strong>g<br />

Bisher: Hübsche Oberflächen, die aber nichts tun.<br />

GUI-Komponenten sollen auf Benutzer-E<strong>in</strong>gaben reagieren können.<br />

Event: Nutzeraktion, wie Mausklick auf Button oder Tastature<strong>in</strong>gabe.<br />

<strong>Objekt</strong>e, die auf Event reagieren.<br />

event listener<br />

17.3.1 Event-<strong>Objekt</strong>e<br />

Basis-Klasse: java.util.EventObject<br />

Sw<strong>in</strong>g: javax.sw<strong>in</strong>g.event-Paket enthält Unterklassen von EventObject<br />

und AWTEvent.<br />

EventObject-Methode getSource(): liefert <strong>Objekt</strong>, das den Event ausgelöst<br />

hat<br />

AWTEvent-Methode getID(): Unterscheidung von verschiedenen Events e<strong>in</strong>er<br />

Klasse<br />

W<strong>in</strong>dowEvent-Methode: getNewState() liefert den neuen Zustand e<strong>in</strong>es<br />

Fensters ( NORMAL, ICONIFIED, MAXIMIZED_VERT ...)<br />

Typische Event-Klassen: W<strong>in</strong>dowEvent, MouseEvent<br />

17.3.2 Event Listener<br />

E<strong>in</strong> <strong>Objekt</strong>, das e<strong>in</strong>en Event erzeugt heisst event source.<br />

E<strong>in</strong> <strong>Objekt</strong>, das auf e<strong>in</strong>en Event reagieren soll heisst event listener.<br />

Event Source <strong>Objekt</strong>e halten e<strong>in</strong>e Liste von listeners, die <strong>in</strong>formiert (notified)<br />

werden wollen und bietet Methoden zum E<strong>in</strong>fügen und Löschen von<br />

Listener-<strong>Objekt</strong>en.


<strong>Informatik</strong> B SS 03 243<br />

Alle Komponenten s<strong>in</strong>d Event-Sources und def<strong>in</strong>ieren entsprechende add()<br />

und remove() Methoden, die per Konvention mit Listener enden. z.B.<br />

addW<strong>in</strong>dowListener(), addActionListener()<br />

Zu jeder Art von Event <strong>Objekt</strong> existiert e<strong>in</strong> korrespondierender Event Listener,<br />

z.B. ActionListener.<br />

Alle Listener erweitern das Marker-Interface java.util.EventListener.<br />

Vordef<strong>in</strong>ierte Listener wie ActionListener s<strong>in</strong>d selbst Interfaces und geben<br />

e<strong>in</strong>e Methode actionPerformed() vor.<br />

Event-Adapter können alternativ verwendet werden: statt alle Methoden e<strong>in</strong>es<br />

ListenerInterfaces zu implementieren kann e<strong>in</strong>e Unterklasse zu e<strong>in</strong>er<br />

entsprechenden Adapterklasse erzeugt werden, <strong>in</strong> der dann die gewünschte(n)<br />

Methode(n) überschieben wird.<br />

Beispiel: Realisieren e<strong>in</strong>es W<strong>in</strong>dowListener über e<strong>in</strong>en W<strong>in</strong>dowAdapter<br />

(siehe unten)<br />

17.3.3 Event Handl<strong>in</strong>g mit Inneren Klassen<br />

Um über e<strong>in</strong>en Event <strong>in</strong>formiert zu werden, muss e<strong>in</strong> entsprechendes<br />

EventListener-Interface implementiert werden.<br />

Manchmal kann dies direkt <strong>in</strong> der Haupt-Klasse der Applikation geschehen.<br />

Typisch ist, anonyme <strong>in</strong>nere Klassen zu verwenden. (Listener waren die<br />

Hauptmotivation für Innere Klassen)<br />

// create w<strong>in</strong>dow listener for w<strong>in</strong>dow close click<br />

addW<strong>in</strong>dowListener(new W<strong>in</strong>dowAdapter()<br />

{ public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{System.exit(0);}<br />

});<br />

/* JFC <strong>in</strong> a Nutshell, Flanagan */<br />

import java.awt.*;<br />

// AWT classes<br />

import javax.sw<strong>in</strong>g.*;<br />

// Sw<strong>in</strong>g components and classes<br />

import javax.sw<strong>in</strong>g.border.*; // Borders for Sw<strong>in</strong>g components<br />

import java.awt.event.*; // Basic event handl<strong>in</strong>g<br />

public class DisplayMessage {<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args) {<br />

/*<br />

* Step 1: Create the components<br />

*/<br />

JLabel msgLabel = new JLabel(); // Component to display the question<br />

JButton yesButton = new JButton(); // Button for an affirmative response<br />

JButton noButton = new JButton(); // Button for a negative response<br />

/*


244 <strong>Informatik</strong> B SS 03<br />

* Step 2: Set properties of the components<br />

*/<br />

msgLabel.setText(args[0]);<br />

// The msg to display<br />

// A 10-pixel marg<strong>in</strong><br />

msgLabel.setBorder(new EmptyBorder(10,10,10,10));<br />

yesButton.setText((args.length >= 2)args[1]:"Yes"); // Text for Yes<br />

noButton.setText((args.length >= 3)args[2]:"No"); // for no button<br />

/*<br />

* Step 3: Create conta<strong>in</strong>ers to hold the components<br />

*/<br />

JFrame w<strong>in</strong> = new JFrame("Message"); // The ma<strong>in</strong> application w<strong>in</strong>dow<br />

JPanel buttonbox = new JPanel(); // A conta<strong>in</strong>er for the two buttons<br />

/*<br />

* Step 4: Specify LayoutManagers to arrange components <strong>in</strong> the conta<strong>in</strong>ers<br />

*/<br />

w<strong>in</strong>.getContentPane().setLayout(new BorderLayout()); // layout on borders<br />

buttonbox.setLayout(new FlowLayout());<br />

// layout left-to-right<br />

/*<br />

* Step 5: Add components to conta<strong>in</strong>ers, with optional layout constra<strong>in</strong>ts<br />

*/<br />

buttonbox.add(yesButton);<br />

// add yes button to the panel<br />

buttonbox.add(noButton);<br />

// add no button to the panel<br />

// add JLabel to w<strong>in</strong>dow, tell<strong>in</strong>g the BorderLayout to put it <strong>in</strong> the middle<br />

w<strong>in</strong>.getContentPane().add(msgLabel, "Center");<br />

// add panel to w<strong>in</strong>dow, tell<strong>in</strong>g the BorderLayout to put it at the bottom<br />

w<strong>in</strong>.getContentPane().add(buttonbox, "South");<br />

/*<br />

* Step 6: Arrange to handle events <strong>in</strong> the user <strong>in</strong>terface.<br />

*/<br />

yesButton.addActionListener(new ActionListener() { // Note: <strong>in</strong>ner class<br />

// This method is called when the Yes button is clicked.<br />

public void actionPerformed(ActionEvent e) { System.exit(0); }<br />

});<br />

noButton.addActionListener(new ActionListener() { // Note: <strong>in</strong>ner class<br />

// This method is called when the No button is clicked.<br />

public void actionPerformed(ActionEvent e) { System.exit(1); }<br />

});<br />

/*<br />

* Step 7: Display the GUI to the user<br />

*/<br />

w<strong>in</strong>.pack(); // Set the size of the w<strong>in</strong>dow based its children’s sizes.


<strong>Informatik</strong> B SS 03 245<br />

}<br />

}<br />

w<strong>in</strong>.show();<br />

// Make the w<strong>in</strong>dow visible.<br />

17.4 Applets<br />

M<strong>in</strong>i-Applikation, die über Netz von e<strong>in</strong>er (untrusted) Quelle geladen werden<br />

kann, und die <strong>in</strong> e<strong>in</strong>em Web-Browser oder e<strong>in</strong>er andere Applet-Viewer<br />

Anwendung ausgeführt werden kann.<br />

Mächtige Möglichkeit, um <strong>Java</strong> Programme an Endbenutzer zu liefern.<br />

Gerade die Applets machten <strong>Java</strong> populär.<br />

17.4.1 Unterschiede zwischen Applets und Applications<br />

E<strong>in</strong> Applet hat ke<strong>in</strong>e ma<strong>in</strong>() Methode.<br />

E<strong>in</strong> Applet wird nicht über die Kommandozeile aufgerufen. Es ist <strong>in</strong> e<strong>in</strong><br />

HTML-File APPLET¡ –<br />

-tag – e<strong>in</strong>gebettet und erhält se<strong>in</strong>e Argumente über<br />

tags im HTML-File.<br />

PARAM¡<br />

Applets unterliegen e<strong>in</strong>igen Sicherheitsbeschränkungen, die verh<strong>in</strong>dern sollen,<br />

dass auf dem Host unsichere und möglicherweise bösartige Applets ausgeführt<br />

werden.<br />

17.4.2 Schreiben von Applets<br />

Unterklasse von java.applet.Applet (Unterklasse von<br />

java.awt.Component


246 <strong>Informatik</strong> B SS 03<br />

<strong>in</strong>it(): Wird beim Laden des Applets ausgeführt – anstelle e<strong>in</strong>es Konstruktors.<br />

Hier werden typischerweise GUI-Komponenten erzeugt.<br />

destroy(): Gegenstück zu <strong>in</strong>it(): Applet wird aus dem Browser entfernt<br />

und sollte alle Resourcen freigeben.<br />

start(): Wird aufgerufen, wenn das Applet sichtbar wird.<br />

stop(): Temporäres nicht-sichtbar machen, stoppen der<br />

Animation/Berechnung.<br />

getImage(): Laden e<strong>in</strong>es Image-Files vom Netz.<br />

getCodeBase(): URL, von der das Applet-Klassen-File geladen wurde.<br />

Auswahl von Methoden der Component-Klasse:<br />

pa<strong>in</strong>t(): Zeichne Dich selbst als wichtigste Methode.<br />

17.4.3 Beispiel<br />

/* JFC <strong>in</strong> a Nutshell, Flanagan */<br />

import java.applet.*;<br />

import java.awt.*;<br />

public class MessageApplet extends Applet {<br />

protected Str<strong>in</strong>g message; // The text to display<br />

protected Font font; // The font to display it <strong>in</strong><br />

// One-time <strong>in</strong>itialization for the applet<br />

public void <strong>in</strong>it() {<br />

message = this.getParameter("message");<br />

font = new Font("Helvetica", Font.BOLD, 48);<br />

}<br />

// Draw the applet whenever necessary.<br />

public void pa<strong>in</strong>t(Graphics g) {<br />

// The p<strong>in</strong>k oval<br />

g.setColor(Color.p<strong>in</strong>k);<br />

g.fillOval(10, 10, 330, 100);<br />

// The red outl<strong>in</strong>e. The browser may not support <strong>Java</strong>2D, so we<br />

// try to simulate a 4-pixel wide l<strong>in</strong>e by draw<strong>in</strong>g four ovals.<br />

g.setColor(Color.red);<br />

g.drawOval(10,10, 330, 100);<br />

g.drawOval(9, 9, 332, 102);<br />

g.drawOval(8, 8, 334, 104);<br />

g.drawOval(7, 7, 336, 106);<br />

// The text<br />

g.setColor(Color.black);<br />

g.setFont(font);


¡<br />

<strong>Informatik</strong> B SS 03 247<br />

}<br />

}<br />

g.drawStr<strong>in</strong>g(message, 40, 75);<br />

HTML-File:<br />

<br />

<br />

<br />

Aufruf:<br />

> appletviewer MessageApplet.html<br />

17.5 GUIs und Threads<br />

Jede GUI-Applikation hat e<strong>in</strong>en event dispatch thread: Thread, der darauf<br />

wartet, dass Ereignisse e<strong>in</strong>treten und diese an die entsprechenden<br />

Event-Handler ausliefert.<br />

Alle event listener Methoden werden vom event dispatch thread aufgerufen<br />

(<strong>in</strong>voke).<br />

alle GUI Manipulationen, die über event listener ausgeführt werden, s<strong>in</strong>d<br />

sicher.<br />

actionPerformed() und pa<strong>in</strong>t() werdem im event-dispatch<strong>in</strong>g thread<br />

ausgeführt.<br />

So wird beispielsweise während e<strong>in</strong>e actionPerformed() Methode<br />

ausgeführt wird, die GUI “e<strong>in</strong>gefroren” (ke<strong>in</strong> re-pa<strong>in</strong>t, ke<strong>in</strong>e Reaktion auf<br />

Maus-Klicks, ...)


248 <strong>Informatik</strong> B SS 03<br />

Sw<strong>in</strong>g Komponenten s<strong>in</strong>d nicht thread-safe: Es muss darauf geachtet werden,<br />

dass nur der event-dispatch thread auf solche Komponenten zugreift.<br />

Übliche Lösung: “S<strong>in</strong>gle-Thread Rule”: Wenn e<strong>in</strong>e Sw<strong>in</strong>g-Komponente realisiert<br />

wurde, sollte der gesamte Code, der diese Komponenten bee<strong>in</strong>flusst oder von<br />

ihr abhängt im event dispatch thread ausgeführt werden.<br />

Code <strong>in</strong> Event-Handlern sollte schnell ausgeführt werden (sonst schlechte<br />

Performanz)<br />

17.6 Beans<br />

Bean: Wiederverwendbare Software-Komponente, die <strong>in</strong> e<strong>in</strong>em Builder-Tool<br />

visuell manipuliert werden kann.<br />

Beispiel: BDK (<strong>Java</strong> Beans Development Kit).<br />

Typisch für graphische Benutzeroberflächen.<br />

Schreiben von Beans: z.B. neue graphische Komponenten<br />

sollten über Properties konfigurierbar se<strong>in</strong> und entsprechende get- und<br />

set-Methoden anbieten.<br />

Nutzen von Beans: Zusammenstecken und Konfigurieren von Komponenten und<br />

mit Code verb<strong>in</strong>den.


<strong>Informatik</strong> B SS 03 249<br />

18 Ausblick: Verteilte Systeme<br />

18.1 Netzwerk-Anwendungen <strong>in</strong> <strong>Java</strong><br />

Im Pakete java.net werden Klassen def<strong>in</strong>iert, mit denen es recht e<strong>in</strong>fach ist,<br />

Netzwerk Anwendungen zu schreiben.<br />

Beispiel: Klasse URL, die e<strong>in</strong>en uniform resource locator def<strong>in</strong>iert.<br />

Unter anderem werden die folgenden Protokolle unterstützt: http: (HyperText<br />

Transfer Protocol), ftp: (File Transfer Protocol), ...<br />

Beispiel: Klasse Socket, um mit e<strong>in</strong>em Server zu kommunizieren.<br />

Unix-Kommando netstat zeigt mit -r die IP Rout<strong>in</strong>g Tabelle und mit -a die aktiven<br />

Internet-Verb<strong>in</strong>dungen (Sockets).<br />

z.B.<br />

> netstat -rn<br />

Kernel IP rout<strong>in</strong>g table<br />

Dest<strong>in</strong>ation Gateway Genmask Flags MSS W<strong>in</strong>dow irtt Iface<br />

131.173.13.0 0.0.0.0 255.255.255.0 U 40 0 0 eth0<br />

Unix-Kommando ifconfig zeigt localhost und Verb<strong>in</strong>dung(en) nach Aussen.<br />

18.2 Grundlagen für Kommunikation im Netz<br />

18.2.1 Open System Interconncetion (OSI) Model<br />

1. Physikalisch: Hardware<br />

Übermittlung von B<strong>in</strong>ärdaten-Sequenzen durch elektromagnetische Signale<br />

(Kabel, Glasfaser, Radiowellen).<br />

Beispiele: ISDN, Ethernet<br />

2. Data L<strong>in</strong>k: Übermittlung von Daten zwischen physikalisch direkt verbundenen<br />

Knoten.<br />

zwischen Routern, Hosts (Wide Area Network, WAN; Local Area Network, LAN)<br />

3. Netzwerk: Übermittlung von Daten zwischen Computern <strong>in</strong> e<strong>in</strong>em bestimmten<br />

Netzwerk.<br />

Beispiel: IP (Internet Protocol), weltweit e<strong>in</strong>deutige Adresse für jeden Rechner<br />

im Netz<br />

4. Transport: Unterste Ebene, auf der Nachrichten (statt Daten-Paketen) bearbeitet<br />

werden.<br />

Nachrichten werden an Ports adressiert, die mit Prozessen assoziiert s<strong>in</strong>d.<br />

Beispiele: TCP, UDP (später etwas genauer)<br />

5. Session: Fehlererkennung und automatic recovery<br />

6. Presentation: Übermittlung von Daten <strong>in</strong> <strong>in</strong> e<strong>in</strong>e Rechner-unabhängige<br />

Repräsentation.<br />

Beispiel: CORBA


250 <strong>Informatik</strong> B SS 03<br />

7. Application: Protokolle, die (meist) e<strong>in</strong> Interface zu e<strong>in</strong>em Service def<strong>in</strong>ieren;<br />

Kommunikations-Anforderungen für spezifische Applikationen.; Beispiele: HTTP,<br />

FTP<br />

18.2.2 TCP und UDP<br />

In der Praxis s<strong>in</strong>d die obersten drei Schichten nicht sauber getrennt.<br />

Schreibt man eigene Anwendungen (oberste Ebene im OSI Modell) <strong>in</strong> <strong>Java</strong>,<br />

muss man kaum etwas über die darunterliegenden Schichten wissen.<br />

Um aber entscheiden zu können, welche Klassen aus java.net man benutzen<br />

sollte, muss man die Unterschiede zwischen TCP und UDP kennen.<br />

TCP (Transmission Control Protocol):<br />

– Verb<strong>in</strong>dungsbasiertes (conncetion-based) Protokoll, das e<strong>in</strong>en<br />

zuverlässigen Datenfluss zwischen zwei Rechnern realisiert.<br />

– Analogie: Telefonleitung<br />

Aufbau e<strong>in</strong>er Verb<strong>in</strong>dung zwischen zwei Parteien und Datenaustausch über<br />

diese Verb<strong>in</strong>dung.<br />

– Garantiert, dass die Daten <strong>in</strong> der selben Reihenfolge ankommen, <strong>in</strong> der sie<br />

gesendet wurden.<br />

– HTTP, FTP, Telnet erfordern solche zuverlässigen Kommunikations-Kanäle.<br />

Wenn z.B. e<strong>in</strong>e URL mit HTTP gelesen wird, so müssen die Daten <strong>in</strong> der<br />

Reihenfolge empfangen werden, <strong>in</strong> der sie gesendet wurden, sonst hat<br />

man beispielsweise e<strong>in</strong>e uns<strong>in</strong>nige HTML Datei oder e<strong>in</strong> unbrauchbares<br />

(korruptes) zip-File.<br />

UDP (User Datagramm Protocol):<br />

– Nicht verb<strong>in</strong>dungsbasiertes Protokoll, das unabhängige Daten-Pakete<br />

(Datagramme), von e<strong>in</strong>em Rechner zu e<strong>in</strong>em anderen schickt, ohne zu<br />

garantieren, das die Daten ankommen.<br />

– Analogie: Briefe per Post verschicken<br />

Reihenfolge der Auslieferung ist unwichtig und nicht garantiert, Nachrichten<br />

s<strong>in</strong>d unabhängig vone<strong>in</strong>ander.<br />

– Es gibt Nachrichten, bei denen Reihenfolge und Zuverlässigkeit essentiell<br />

ist. Manchmal genügen aber schwächere Anforderungen, die dafür wenig<br />

Overhead benötigen und schneller s<strong>in</strong>d.<br />

– Beispiel: Uhrzeit-Service<br />

Clock-Server schickt aktuelle Zeit an Client. Falls der Client e<strong>in</strong> Paket nicht<br />

erhält, macht es ke<strong>in</strong>en S<strong>in</strong>n, die Daten nochmal zu übermitteln, weil die<br />

aktuelle Uhrzeit <strong>in</strong>zwischen e<strong>in</strong>e andere ist.


<strong>Informatik</strong> B SS 03 251<br />

– Beispiel: p<strong>in</strong>g<br />

Kann gar nicht zuverlässigem Service realisiert werden, da ja gerade die<br />

Zahl von verlorenen Paketen und/oder Pakten <strong>in</strong> falscher Reihenfolge<br />

benötigt wird, um die Güte der Verb<strong>in</strong>dung anzugeben.<br />

18.2.3 Ports<br />

E<strong>in</strong> Rechner hat e<strong>in</strong>e e<strong>in</strong>zige physikalische Verb<strong>in</strong>dung zum Netzwerk, auf der<br />

alle Daten ankommen.<br />

Ports werden benutzt, um ankommende Daten e<strong>in</strong>em bestimmten Prozess<br />

zuzuordnen.<br />

Datenübertragung im Internet wird mit Adressen (IPs) realisiert: 32 bit für<br />

Rechner und 16 bit für Port.<br />

Portnummern von 0 bis 1023 s<strong>in</strong>d für “well-known” Dienste (wie HTTP, FTP)<br />

reserviert. Eigene Anwendungen sollten nicht an solche Portnummern<br />

gebunden werden.<br />

Genauere Information, <strong>in</strong> allen Lehrbüchern zum Thema “Verteilte Systeme”, siehe<br />

auch Vorlesungen “Verteilte Systeme” und “<strong>Informatik</strong> C”<br />

18.2.4 Network<strong>in</strong>g Klassen <strong>in</strong> <strong>Java</strong><br />

Über die Klassen <strong>in</strong> java.net können <strong>Java</strong> Programme TCP oder UDP<br />

verwenden, um über das Internet zu kommunizieren.<br />

TCP-Klassen: URL, URLConnection, Socket, ServerSocket<br />

UDP-Klassen: DatagramPacket, DatagramSocket, MulticastSocket<br />

18.3 Die Klasse ‘URL’<br />

18.3.1 Was ist e<strong>in</strong>e URL<br />

URL (Uniform Resource Locator) ist e<strong>in</strong>e Referenz (Adresse) zu e<strong>in</strong>er Resource<br />

im Internet.<br />

E<strong>in</strong>e URL-Adresse hat zwei Haupt-Komponenten:<br />

Protokoll-Identifier: http, ftp, file, news, ...<br />

Resource Name: vollständige Adresse deren Format vom benutzten Protokoll<br />

abhängt<br />

Beispiel: http://java.sun.com<br />

Format für HTTP Resourcen:<br />

– Host Name (Rechner), z.B.: http://java.sun.com,<br />

http://www.<strong>in</strong>formatik.uni-osnabrueck.de


252 <strong>Informatik</strong> B SS 03<br />

– Filename (Pfad zur Datei auf dem Host), z.B. /schmid/research.html<br />

oft nur Verzeichnis und Server lädt <strong>in</strong>dex.html, kann auch e<strong>in</strong> script, e<strong>in</strong><br />

gif, etc. se<strong>in</strong><br />

– Port Number (optional), default ist 80<br />

...¡ – Reference (optional): markierter Ort <strong>in</strong> e<strong>in</strong>er Datei, z.B. name = <strong>in</strong><br />

HTML<br />

18.3.2 Nutzen der URL Klasse<br />

import java.net.*<br />

import java.io.*<br />

// Create some URL objects<br />

URL url=null, url2 = null, url3 = null;<br />

try{<br />

url = new URL("http://www.oreilly.com"); // An absolute URL<br />

url2 = new URL(url, "catalog/books/javanut3/"); // A relative URL<br />

url3 = new URL("http:", "www.oreilly.com", "<strong>in</strong>dex.html");<br />

// protocoll host name file name<br />

} catch (MalformedURLException e) { /* Ignore this exception */ }<br />

// Read the content of a URL from an <strong>in</strong>put stream<br />

InputStream <strong>in</strong> = url.openStream();<br />

// For more control over the read<strong>in</strong>g process, get a URLConnection objecy<br />

URLConnection conn = url.openConnection();<br />

// Now get some <strong>in</strong>formation about the URL<br />

Str<strong>in</strong>g type = conn.getContentType();<br />

Str<strong>in</strong>g encod<strong>in</strong>g = conn.getContentEncod<strong>in</strong>g();<br />

java.util.Date lastModified = new java.util.Date(conn.getLastModified());<br />

<strong>in</strong>t len = conn.getContentLength();<br />

// If necessary, read the contents of the URL us<strong>in</strong>g this stream<br />

InputStream <strong>in</strong> = conn.getInputStream();<br />

Komponenten von URLs können nach der Erzeugung des URL <strong>Objekt</strong>s nicht<br />

mehr verändert werden. (set Methode ist nicht public)<br />

Parsierung von URLs: getProtocol(), getHost(), getPort(),<br />

getFile(), getRef().<br />

Nicht alle URLs haben diese Komponenten (URL Klasse ist etwas<br />

“HTTP”-Zentriert).<br />

Er<strong>in</strong>nerung zum Umgang mit Streams:<br />

BufferedReader my<strong>in</strong> = new BufferedReader(new InputStreamReader(<strong>in</strong>));<br />

// ‘<strong>in</strong>’ als bereits def<strong>in</strong>ierter InputStream, z.B. ueber url.openStream()


<strong>Informatik</strong> B SS 03 253<br />

Beispiel für das Auslesen des Inhalts e<strong>in</strong>er HTML-Datei als Str<strong>in</strong>g:<br />

/Vl24/URLReader<br />

openConnection() <strong>in</strong>itialisiert e<strong>in</strong>e Kommunikationsverb<strong>in</strong>dung zwischen<br />

dem <strong>Java</strong> Programm und der URL über das Netz.<br />

Auch URLConnection <strong>Objekt</strong>e haben Methoden zum Lesen. Vorteil: auch<br />

Methoden zum Schreiben, Abfrage von Eigenschaften.<br />

Abfrage von Eigenschaften: die im Header-File (vom Server erzeugt) mitgeliefert<br />

werden.<br />

18.3.3 Beispiel: URLConnection<br />

import java.net.*;<br />

import java.io.*;<br />

public class URLInfo {<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args) throws Exception {<br />

URL yahoo = new URL("http://www.yahoo.com/");<br />

URLConnection conn = yahoo.openConnection();<br />

// Now get some <strong>in</strong>formation about the URL<br />

Str<strong>in</strong>g type = conn.getContentType();<br />

Str<strong>in</strong>g encod<strong>in</strong>g = conn.getContentEncod<strong>in</strong>g();<br />

java.util.Date lastModified = new java.util.Date(conn.getLastModified());<br />

<strong>in</strong>t len = conn.getContentLength();<br />

System.out.pr<strong>in</strong>tln("ContentType: " + type);<br />

System.out.pr<strong>in</strong>tln("ContentEncod<strong>in</strong>g: " + encod<strong>in</strong>g);<br />

System.out.pr<strong>in</strong>tln("lastModified: " + lastModified);<br />

System.out.pr<strong>in</strong>tln("ContentLength: " + len);<br />

// Read the contents of the URL us<strong>in</strong>g this stream<br />

BufferedReader <strong>in</strong> = new BufferedReader(<br />

new InputStreamReader(<br />

conn.getInputStream()));<br />

Str<strong>in</strong>g <strong>in</strong>putL<strong>in</strong>e;<br />

while ((<strong>in</strong>putL<strong>in</strong>e = <strong>in</strong>.readL<strong>in</strong>e()) != null)<br />

System.out.pr<strong>in</strong>tln(<strong>in</strong>putL<strong>in</strong>e);<br />

<strong>in</strong>.close();<br />

} }<br />

Anfang der Ausgabe:<br />

ContentType: text/html<br />

ContentEncod<strong>in</strong>g: null<br />

lastModified: Thu Jan 01 01:00:00 CET 1970<br />

ContentLength: 16178


254 <strong>Informatik</strong> B SS 03<br />

Abbildung 64: Client-Server Kommunikation über Sockets<br />

18.4 Sockets für Client/Server Kommunikation<br />

18.4.1 Grundidee der Client/Server Kommunikation<br />

URLs s<strong>in</strong>d “high-level” Verb<strong>in</strong>dungen zum Web. Die Implementation baut zum<br />

Teil auf Sockets auf.<br />

E<strong>in</strong> Socket ist e<strong>in</strong> Endpunkt <strong>in</strong> e<strong>in</strong>er zweiseitigen Kommunikationsverb<strong>in</strong>dung<br />

(l<strong>in</strong>k) zwischen zwei Programmen, die auf dem Netz laufen.<br />

E<strong>in</strong> Socket ist an e<strong>in</strong>e Port-Nummer gebunden, so dass die TCP Schicht die<br />

Applikation identifizieren kann, zu/von der die Daten gesendet werden sollen.<br />

Server-Seite: E<strong>in</strong> Server läuft auf e<strong>in</strong>em Rechner (IP-Adresse) und hat e<strong>in</strong><br />

Socket, das an e<strong>in</strong>e spezifische Port-Nummer gebunden ist. Der Server wartet<br />

und hört dem Socket zu, ob e<strong>in</strong> Client e<strong>in</strong>e Verb<strong>in</strong>dung anfordert.<br />

Client-Seite: E<strong>in</strong> Client muss den Hostnamen (IP-Adresse) des Rechners, auf<br />

dem der Server läuft, und die Portnummer, mit der der Server verbunden ist,<br />

kennen. Um e<strong>in</strong>e Verb<strong>in</strong>ung anzufordern schickt der Client e<strong>in</strong>e Anfrage an den<br />

Server.<br />

Verb<strong>in</strong>dungsaufbau/Server-Seite: Wenn der Server die Verb<strong>in</strong>dung akzeptiert,<br />

erhält der Server e<strong>in</strong> neues Socket, das an e<strong>in</strong>en neuen Port gebunden ist. Das<br />

neue Socket dient der Kommunikation mit dem Clienten. Das Orig<strong>in</strong>al-Socket<br />

bleibt bereit für neue Anfragen.<br />

Verb<strong>in</strong>dungsaufbau/Client-Seite: Auf dem Client-Rechner wird ebenfalls e<strong>in</strong><br />

Socket erzeugt und an e<strong>in</strong>e lokale Portnummer gebunden (dies ist nicht die<br />

Portnummer, die bei der ursprünglichen Anfage an den Server verwendet<br />

wurde).<br />

Kommunikation: Wenn Server und Client erfolgreich verbunden s<strong>in</strong>d, kann durch<br />

Schreiben/Lesen von den jeweiligen Sockets komm<strong>in</strong>iziert werden.


<strong>Informatik</strong> B SS 03 255<br />

18.4.2 Sockets <strong>in</strong> <strong>Java</strong><br />

im java.net Paket<br />

ServerSocket:<br />

– Warten auf Client-Anfragen (ServerSocket ssocket = new<br />

ServerSocket( portnumber¡ );)<br />

(Port-Nummer grösser 1023)<br />

– Aufbau e<strong>in</strong>er Verb<strong>in</strong>dung (Socket clientSocket =<br />

ssocket.accept();)<br />

Socket: Erlaubt plattform-unabhängige Kommunikation, die Klasse ist über<br />

plattform-abhängige Implementation def<strong>in</strong>iert und versteckt die spezifischen<br />

Details.<br />

– Aufbau e<strong>in</strong>er Verb<strong>in</strong>dung zum Server: Socket mySocket = new<br />

Socket( hostname¡ , portnumber¡ )<br />

Vollqualifizierter hostname (IP als Zahlencode oder mit Namen), und<br />

Portnummer (muss dem Client bekannt se<strong>in</strong>)<br />

– Öffnen von Schreiber und Leser:<br />

Pr<strong>in</strong>tWriter über mySocket.getOutputStream()<br />

BufferedReader über mySocket.getInputStream()<br />

18.4.3 Beispiel ‘KnockKnockServer’<br />

Quelle: Sun-Tutorial<br />

Idee: Knock Knock Witze, die nach festem Frage-Antwort-Muster Ablaufen. Protokoll<br />

legt Reaktionen des Servers fest. Client-E<strong>in</strong>gaben über Tastatur.<br />

Beispiel:<br />

Server: Knock knock!<br />

Client: Who’s there! (vom Nutzer e<strong>in</strong>zugeben)<br />

Server: Turnip.<br />

Client: Turnip who<br />

Server: Turnip the heat, it’s cold <strong>in</strong> here! Want another (y/n)<br />

Programm-Ausführung:<br />

Starte KnockKnockServer (z.B. auf drako)<br />

Starte KnockKnockClient (Rechner im gleichen lokalen Netz)<br />

Austausch über KnockKnockProtokoll, das die Konventionen von Frage und<br />

Antwort festlegt.


256 <strong>Informatik</strong> B SS 03<br />

import java.net.*;<br />

import java.io.*;<br />

public class KnockKnockServer {<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args) throws IOException {<br />

ServerSocket serverSocket = null;<br />

try {<br />

serverSocket = new ServerSocket(12345);<br />

} catch (IOException e) {<br />

System.err.pr<strong>in</strong>tln("Could not listen on port: 12345.");<br />

System.exit(1);<br />

}<br />

Socket clientSocket = null;<br />

try {<br />

clientSocket = serverSocket.accept();<br />

} catch (IOException e) {<br />

System.err.pr<strong>in</strong>tln("Accept failed.");<br />

System.exit(1);<br />

}<br />

Pr<strong>in</strong>tWriter out =<br />

new Pr<strong>in</strong>tWriter(clientSocket.getOutputStream(), true);<br />

BufferedReader <strong>in</strong> = new BufferedReader(<br />

new InputStreamReader(clientSocket.getInputStream()));<br />

Str<strong>in</strong>g <strong>in</strong>putL<strong>in</strong>e, outputL<strong>in</strong>e;<br />

KnockKnockProtocol kkp = new KnockKnockProtocol();<br />

outputL<strong>in</strong>e = kkp.processInput(null);<br />

out.pr<strong>in</strong>tln(outputL<strong>in</strong>e);<br />

while ((<strong>in</strong>putL<strong>in</strong>e = <strong>in</strong>.readL<strong>in</strong>e()) != null) {<br />

outputL<strong>in</strong>e = kkp.processInput(<strong>in</strong>putL<strong>in</strong>e);<br />

out.pr<strong>in</strong>tln(outputL<strong>in</strong>e);<br />

if (outputL<strong>in</strong>e.equals("Bye."))<br />

break;<br />

}<br />

out.close(); <strong>in</strong>.close();<br />

clientSocket.close(); serverSocket.close();<br />

} }<br />

import java.io.*;<br />

import java.net.*;<br />

public class KnockKnockClient {<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args) throws IOException {<br />

Socket kkSocket = null;<br />

Pr<strong>in</strong>tWriter out = null;<br />

BufferedReader <strong>in</strong> = null;<br />

try {<br />

kkSocket = new Socket("suleika", 12345);<br />

out = new Pr<strong>in</strong>tWriter(kkSocket.getOutputStream(), true);<br />

<strong>in</strong> = new BufferedReader(<br />

new InputStreamReader(kkSocket.getInputStream()));


<strong>Informatik</strong> B SS 03 257<br />

} catch (UnknownHostException e) {<br />

System.err.pr<strong>in</strong>tln("Don’t know about host: suleika.");<br />

System.exit(1);<br />

} catch (IOException e) {<br />

System.err.pr<strong>in</strong>tln("Couldn’t get I/O for the connection to:"<br />

+ "suleika.");<br />

System.exit(1);<br />

}<br />

BufferedReader stdIn =<br />

new BufferedReader(new InputStreamReader(System.<strong>in</strong>));<br />

Str<strong>in</strong>g fromServer;<br />

Str<strong>in</strong>g fromUser;<br />

while ((fromServer = <strong>in</strong>.readL<strong>in</strong>e()) != null) {<br />

System.out.pr<strong>in</strong>tln("Server: " + fromServer);<br />

if (fromServer.equals("Bye."))<br />

break;<br />

fromUser = stdIn.readL<strong>in</strong>e();<br />

if (fromUser != null) {<br />

System.out.pr<strong>in</strong>tln("Client: " + fromUser);<br />

out.pr<strong>in</strong>tln(fromUser);<br />

} }<br />

out.close(); <strong>in</strong>.close();<br />

stdIn.close(); kkSocket.close(); } }<br />

Erläuterungen zu KnockKnockServer<br />

Es wird festgelegt, dass der Server auf Port 12345 auf Anfragen wartet.<br />

(Irgende<strong>in</strong>e Portnummer grösser 1023)<br />

Nach Anfrage durch e<strong>in</strong>en potentiellen Clienten wird auf der Server-Seite e<strong>in</strong><br />

neues Socket für diesen Clienten erzeugt.<br />

Die Kommunikation wird aufgebaut. Das Kommunikationsprotokoll regelt die<br />

Konversation:<br />

– Hole den Input- und Output-Stream des Sockets<br />

– Initialisierung der Kommunkation mit dem Client, <strong>in</strong>dem zum Socket<br />

geschrieben wird.<br />

– Kommunizierte mit dem Clienten (while-Schleife).<br />

Der Server ist “ordentlich” und räumt am Ende alles ab.<br />

Erläuterungen zu KnockKnockProtocol<br />

(Code nicht im Skript, aber im Code-Verzeichnis zur Vorlesung)<br />

Erläuterungen zu KnockKnockClient<br />

Es muss e<strong>in</strong> Socket mit Host (IP des Servers) und Port (Port, auf dem der<br />

KnockKnockServer von aussen angesprochen werden kann) erzeugt werden.


258 <strong>Informatik</strong> B SS 03<br />

Analog zum Server werden der Input- und der Output-Stream des Sockets<br />

geöffnet.<br />

In der while-Schleife wird die Kommunikation geregelt.<br />

Das Programm kann e<strong>in</strong>fach so erweitert werden, dass mehrere Clienten unterstützt<br />

werden.<br />

18.5 Sicherheit<br />

<strong>Java</strong> Programme können dynamisch Klassen aus verschiedenen Quellen laden<br />

(Stärke von <strong>Java</strong>), <strong>in</strong>klusive “untrusted sources”, z.B. Web-Sites über unsichere<br />

Netz-Verb<strong>in</strong>dungen. Gutes Sicherheitskonzept notwendig!<br />

Sicherheits-Risiko Applets: Wenn beliebige Applets ausgeführt werden dürften,<br />

so könnten diese unauthorisiert <strong>in</strong> das System des Nutzers e<strong>in</strong>greifen (Dateien<br />

löschen, emails versenden, Information stehlen, ...)<br />

<strong>Java</strong>’s Sicherheitskonzept heisst Zugriffskontrolle: untrusted Code darf nicht auf<br />

dem Gast-System lesen, schreiben, löschen und darf nur mit dem Web-Server<br />

kommunizieren, von dem er geladen wurde.<br />

<strong>Java</strong> 1.0 Sandbox: Die Installation des java.lang.SecurityManager<br />

<strong>Objekt</strong>s, legt die “Sandkiste” fest, <strong>in</strong> der der fremde Code lesen darf.<br />

checkRead() verbietet zum Beispiel, dass die Datei etc/passwd gelesen<br />

wird.<br />

<strong>Java</strong> 1.1 Digitally Signed Cases: Zusätzlich java.security Paket.<br />

Authentizierung von Code (man weiss, woher der Code kommt).<br />

<strong>Java</strong> 1.2 Permissions und Policies: Fe<strong>in</strong>abgestimmte Vergabe von<br />

Schreib-/Leserechten.<br />

Verschlüsselung und Entschlüsselung javax.crypto.


<strong>Informatik</strong> B SS 03 259<br />

19 Andere <strong>Objekt</strong>-Orientierte Sprachen<br />

Simula: von Ole-Johan Dahl und Kristen Nygaard(Oslo, Norwegen, 1962-1967).<br />

E<strong>in</strong>führung wichtiger OO-Konzepte wie Klassen, <strong>Objekt</strong>e, Vererbung,<br />

dynamische B<strong>in</strong>dung. (v.a. für diskrete Ereignissimulation)<br />

http://java.sun.com/people/jag/SimulaHistory.html<br />

Smalltalk (1972, Alan Kay, XEROX).<br />

Standardisierung 1980, erste Publikation 1981 <strong>in</strong> Byte.<br />

Eiffel: Bertrand Meyer und Jean Marc Nerson (aus Frankreich, 1985).<br />

re<strong>in</strong> objektorientiert, Entwicklung zuverlässiger Software.<br />

C++: Stroustrup (Bell Labs, 1986)<br />

Objective-C: (Cox 86)<br />

Object Pascal (1985 Apple; 1995 Delphi)<br />

<strong>Java</strong><br />

(C# “C-Sharp”, <strong>Java</strong>-Clone)<br />

19.1 Das 8-Damen Problem Revisited<br />

Das 8-Damen Problem und e<strong>in</strong>e objekt<strong>orientierte</strong> Lösung <strong>in</strong> <strong>Java</strong> wurden bereits<br />

zu Beg<strong>in</strong>n (siehe Kapitel “<strong>Java</strong> und <strong>Objekt</strong>orientierung”) besprochen.<br />

Im Folgenden: Lösung <strong>in</strong> anderen OO-Sprachen.<br />

Es wird ersichtlich, dass sich die verschiedenen OO-Sprachen recht ähnlich s<strong>in</strong>d<br />

(ähnliche Grundkonzepte).<br />

siehe: Timothy Budd, 1997, An Introduction to Object-Oriented Programm<strong>in</strong>g.<br />

19.2 Lösung <strong>in</strong> Smalltalk<br />

Erstellung von Programmen über e<strong>in</strong> User-Interface: Smalltalk Browser mit<br />

Po<strong>in</strong>t-and-Click Editor.<br />

Klassen als Typen.<br />

Variablen müssen nicht mit Typ deklariert werden. # kennzeichnet Symbol.<br />

(Korrespondenz zwischen Name und Wert!)<br />

Trennung von Instanz- und Klassenvariablen.<br />

Object subclass: #Queen<br />

<strong>in</strong>stanceVariableNames: ’column row neighbor’<br />

Variablen können <strong>Objekt</strong>e beliebiger Klassen zugewiesen werden: lastQueen<br />

kann mit <strong>Objekt</strong>en der Klasse Queen und anderer Klassen belegt werden.<br />

(Beispiel: l<strong>in</strong>keste Dame als spezieller “Wächter” ohne Nachbar)


260 <strong>Informatik</strong> B SS 03<br />

Methoden können Namen aus mehreren Komponenten haben.<br />

setColumn: aNumber neighbor: aQueen<br />

" <strong>in</strong>itialize the data fields "<br />

column


<strong>Informatik</strong> B SS 03 261<br />

<strong>in</strong>t column;<br />

id neighbor;<br />

}<br />

/* <strong>in</strong>stance methods */<br />

- (void) <strong>in</strong>itialize: (<strong>in</strong>t) c neighbor: ngh;<br />

- (<strong>in</strong>t) advance;<br />

/* ... */<br />

@end<br />

@implementation Queen : Object<br />

/* ... */<br />

- (<strong>in</strong>t) advance<br />

{ if (row < 8) {<br />

row = row + 1;<br />

return [ self f<strong>in</strong>dSolution ];<br />

}<br />

if ( ! [ neighbor advance ] ) return 0;<br />

row = 1;<br />

return [ self f<strong>in</strong>dSolution ];<br />

}<br />

19.4 Lösung <strong>in</strong> C++<br />

Erlaubt Mehrfachvererbung. Möglichkeit zur Def<strong>in</strong>ition von Templates<br />

(Generische Klassen), d.h. Klassen mit Parametern (vgl. Datentypen <strong>in</strong> ML).<br />

template<br />

class List {<br />

public:<br />

void addElement (T newValue);<br />

T firstElement ();<br />

ListIterator iterator(); // Iterator liefert <strong>Objekt</strong><br />

// der Klasse ListIterator<br />

private:<br />

L<strong>in</strong>k * firstL<strong>in</strong>k; // firstL<strong>in</strong>k ist e<strong>in</strong> Zeiger auf e<strong>in</strong>en Knoten<br />

};<br />

Erlaubt Operator-Overload<strong>in</strong>g.<br />

Wie <strong>in</strong> allen genannten OO-Sprachen s<strong>in</strong>d <strong>Objekt</strong>e dynamische Werte und<br />

werden über Po<strong>in</strong>ter repräsentiert.<br />

Queen * lastQueen = 0; vgl. null <strong>in</strong> <strong>Java</strong> und nil <strong>in</strong> Smalltalk<br />

Es gibt lokale <strong>Objekt</strong>e (nicht dynamisch, auf Stack).<br />

Sichtbarkeit: Trennung von privaten und öffentlichen Komponenten (wie bei den<br />

meisten Sprachen, z.B. Objective-C, <strong>Java</strong>).<br />

Konstruktoren (fast wie <strong>Java</strong>).


262 <strong>Informatik</strong> B SS 03<br />

class queen {<br />

public:<br />

// constructor<br />

queen (<strong>in</strong>t, queen *);<br />

// f<strong>in</strong>d and pr<strong>in</strong>t solutions<br />

bool f<strong>in</strong>dSolution();<br />

bool advance();<br />

void pr<strong>in</strong>t();<br />

private:<br />

// data fields<br />

<strong>in</strong>t row;<br />

const <strong>in</strong>t column;<br />

queen * neighbor;<br />

// <strong>in</strong>ternal method<br />

bool canAttack (<strong>in</strong>t, <strong>in</strong>t);<br />

};<br />

queen::queen(<strong>in</strong>t col, queen * ngh)<br />

: column(col), neighbor(ngh)<br />

{<br />

row = 1;<br />

}<br />

bool queen::canAttack (<strong>in</strong>t testRow, <strong>in</strong>t testColumn)<br />

{<br />

// test rows<br />

if (row == testRow)<br />

return true;<br />

// test diagonals<br />

<strong>in</strong>t columnDifference = testColumn - column;<br />

if ((row + columnDifference == testRow) ||<br />

(row - columnDifference == testRow))<br />

return true;<br />

// try neighbor<br />

return neighbor && neighbor->canAttack(testRow, testColumn);<br />

}<br />

// ...<br />

hier fehlt noch Eiffel

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!