Informatik B Objekt-orientierte Programmierung in Java
Informatik B Objekt-orientierte Programmierung in Java
Informatik B Objekt-orientierte Programmierung in Java
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