Der vollständige Begleittext - Institut für Wissenschaftliches Rechnen

Der vollständige Begleittext - Institut für Wissenschaftliches Rechnen
Begleittext: Einfu¨hrung in das Programmieren in
Java fu¨r Nichtinformatiker
Andreas Keese
Institut f. Wissenschaftliches Rechnen
TU Braunschweig
17. November 2014
Der Text, die Abbildungen und Programme wurden mit gr¨
oßter Sorgfalt erarbeitet. Der Autor
kann dennoch f¨
ur m¨
oglicherweise verbliebene fehlerhafte Angaben und deren Folgen weder eine
juristische Verantwortung noch irgendeine Haftung u
ahnten
¨ bernehmen. Die in diesem Text erw¨
Software– und Hardwarebezeichnungen sind in den meisten F¨
allen auch eingetragene Marken und
unterliegen als solche den gesetzlichen Bestimmungen.
Copyright 1999, 2000, Andreas Keese. Alle Rechte vorbehalten
Inhaltsverzeichnis
1 Einleitung
1.1 Lehrb¨
ucher zu Java . . . . . . . . . . . . . . . . . . . . . . . . . .
¨
1.2 Uber
diesen Text . . . . . . . . . . . . . . . . . . . . . . . . . . .
2 Grundlagen: Wie arbeitet ein Computer ?
2.1 Der Aufbau eines Computers . . . . . . . . . . . . .
2.2 Organisation des Arbeitsspeichers — Bits und Bytes
2.3 Die Organisation des Arbeitsspeichers — Adressen .
2.4 Programmierung eines Computers . . . . . . . . . .
2.5 Zusammenfassung des Kapitels . . . . . . . . . . . .
3 Das
3.1
3.2
3.3
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
2
2
3
5
5
7
9
9
12
Java–Programmiersystem JDK
13
Bestandteile des JDK . . . . . . . . . . . . . . . . . . . . . . . . 13
¨
Ubersetzen
und Ausf¨
uhren von Java-Programmen . . . . . . . . . 14
Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . 16
4 Erste Schritte — die Turtlegraphik
17
4.1 Testen der Turtlegraphik . . . . . . . . . . . . . . . . . . . . . . . 17
4.2 Die Bestandteile eines einfachen Java–Programms . . . . . . . . . 18
4.3 Turtlebefehle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1
5 Grundlagen der Java-Syntax
24
5.1 Grunds¨
atzliches zur Syntax von Java–Programmen . . . . . . . . 24
5.2 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . 28
6 Variablen und Datentypen
6.1 Variablen und ihr Typ . . . . . . .
6.1.1 Ganze Zahlen . . . . . . . .
6.1.2 Fließkommazahlen . . . . .
6.1.3 Logischer Datentyp . . . . .
6.1.4 Zeichen und Zeichenketten
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
7 Ausdr¨
ucke
7.1 Was ist ein Ausdruck ? . . . . . . . . . . . . .
7.2 Literal–Ausdr¨
ucke . . . . . . . . . . . . . . .
7.2.1 Ganzzahlige Literale . . . . . . . . . .
7.2.2 Reellwertige Literale . . . . . . . . . .
7.2.3 Litarale vom Typ boolean . . . . . . .
7.2.4 Zeichenliterale . . . . . . . . . . . . .
7.2.5 Zeichenketten . . . . . . . . . . . . . .
7.3 Verwendung von Variablen . . . . . . . . . .
7.4 Ausdr¨
ucke mit Operatoren . . . . . . . . . . .
7.4.1 Arithmetische Operatoren . . . . . . .
7.4.2 Inkrement und Dekrement–Operatoren
7.4.3 Relationale Operatoren . . . . . . . .
7.4.4 Logische Operatoren . . . . . . . . . .
7.4.5 Bitweise Operatoren . . . . . . . . . .
7.4.6 Zuweisungsoperatoren . . . . . . . . .
7.4.7 Weitere Operatoren . . . . . . . . . .
7.5 Zusammenfassung . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
29
29
30
31
31
32
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
33
33
35
35
36
37
38
39
40
42
45
45
48
48
49
50
51
52
8 Typwandlungen
53
8.1 Automatische Typkonvertierungen . . . . . . . . . . . . . . . . . 53
8.2 Manuelle Typkonvertierungen . . . . . . . . . . . . . . . . . . . . 56
8.3 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . 57
9 Anweisungen und Kontrollstrukturen
9.1 Leere Anweisung . . . . . . . . . . . .
9.2 Blockanweisung . . . . . . . . . . . . .
9.3 Variablendefinitionen . . . . . . . . . .
9.4 Ausdrucksanweisungen . . . . . . . . .
9.5 If–Anweisung . . . . . . . . . . . . . .
9.6 Switch–Anweisung . . . . . . . . . . .
9.7 Schleifen . . . . . . . . . . . . . . . . .
2
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
58
58
58
59
60
61
62
63
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
63
65
67
70
71
10 Objekte in Java
10.1 Objekte und primitive Datentypen . . . .
10.2 Der Lebenszyklus eines Objektes . . . . .
10.2.1 Die Erzeugung von Objekten . . .
10.3 Die Identit¨
at eines Objektes . . . . . . . .
10.4 Die Kommunikation mit Objekten . . . .
10.5 Welche Botschaften versteht ein Objekt ?
10.6 Die Zerst¨
orung von Objekten . . . . . . .
10.7 Zusammenfassung . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
72
72
74
75
77
79
81
82
83
11 Arrays
11.1 Definition von Arrays . . . . . . .
11.2 Verwendung von Arrays . . . . . .
11.3 Array–Literale . . . . . . . . . . .
11.4 Primitive Arrays und Objektarrays
11.5 Referenztypen am Array-Beispiel
11.6 Mehrdimensionale Arrays . . . . .
11.7 Zusammenfassung . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
85
85
86
88
88
89
90
92
.
.
.
.
.
.
.
.
.
93
93
95
96
98
99
103
105
105
106
9.8
9.7.1 Die While–Schleife
9.7.2 Die Do–Schleife . .
9.7.3 Die For–Schleife .
9.7.4 break und continue
Zusammenfassung . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
12 Klassen in Java
12.1 Instanz- und Klassenbestandteile . . . . .
12.2 Zugriff auf Methoden und Attribute . . .
12.3 Die Bestandteile einer Java–Klasse . . . .
12.4 Attribute . . . . . . . . . . . . . . . . . .
12.5 Definition von Methoden . . . . . . . . . .
12.6 Mehrere Methoden mit gleichem Namen .
12.7 Konstruktoren . . . . . . . . . . . . . . .
12.8 Die Parameter¨
ubergabe an eine Methode
12.9 Zusammenfassung . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
13 Dokumentieren von Java–Programmen
108
13.1 Wie arbeitet javadoc ? . . . . . . . . . . . . . . . . . . . . . . . . 108
13.2 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . 109
14 Vererbung — Extensionen von
14.1 Ein einf¨
uhrendes Beispiel . .
14.2 Erweitern von Klassen . . . .
¨
14.3 Uberschreiben
von Methoden
Klassen
110
. . . . . . . . . . . . . . . . . . . . 110
. . . . . . . . . . . . . . . . . . . . 112
. . . . . . . . . . . . . . . . . . . . 113
3
14.4 Die super–Variable . . . . . . . . . .
14.5 Vererbung und Konstruktoren . . . .
14.6 Beispiele . . . . . . . . . . . . . . . .
14.7 Die Object-Klasse . . . . . . . . . .
14.8 Abstrakte Klassen . . . . . . . . . .
14.9 Zuweisung an Variablen und Arrays
14.10Zusammenfassung . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
115
116
117
119
119
121
123
15 Packages
125
15.1 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . 127
16 Exceptions
16.1 Try–catch . . . . . . . . . . . . . . .
16.2 Ausnahmen werfen . . . . . . . . . .
16.3 Exceptions in Methoden . . . . . . .
16.4 Ein paar abschließende Bemerkungen
16.5 Zusammenfassung . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
128
128
130
131
132
133
A Anhang: Unix, Editor und CIP-Pool
A.1 Hinweise zu weiterer Unix-Literatur .
A.2 Editoren . . . . . . . . . . . . . . . . .
A.2.1 Der nedit-Editor . . . . . . . .
A.2.2 Der Emacs-Editor . . . . . . .
A.3 Das Hilfe-System . . . . . . . . . . . .
A.3.1 Der man-Befehl . . . . . . . . .
A.3.2 Der Apropos-Befehl . . . . . .
A.4 Ausgabeumleitung . . . . . . . . . . .
A.5 Trennung von Rechner und Bildschirm
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
134
134
134
134
134
135
135
136
137
139
B Anhang: Verwendung von Java unter Unix
B.1 Java-Compiler und Laufzeitumgebung . . . . . . . . . . . . . . .
B.2 Kompilation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
B.3 Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
141
141
141
142
C Probleme
143
C.1 Probleme bei Verwendung von javac . . . . . . . . . . . . . . . . 143
C.2 Probleme bei Verwendung von java . . . . . . . . . . . . . . . . . 144
D Goldene Regeln f¨
urs Programmieren
D.1 Allgemeines . . . . . . . . . . . . .
D.2 Quelldateien . . . . . . . . . . . . . .
D.3 Klassen . . . . . . . . . . . . . . . .
D.4 Methoden . . . . . . . . . . . . . . .
D.5 Variablen, Konstanten und Literale .
4
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
146
146
146
147
147
147
D.6 Kontrollstrukturen . . . . . . . . . . . . . . . . . . . . . . . . . . 149
E Installation von Java und Turtle–Graphik
E.1 Installation von Java . . . . . . . . . . . .
E.2 Der CLASSPATH . . . . . . . . . . . . .
E.3 Installation der Turtle–Graphik . . . . . .
E.4 Erweitern des CLASSPATH . . . . . . . . .
5
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
150
150
150
151
152
1
Einleitung
In der Veranstaltung Einf¨
uhrung in das Programmieren“ wollen wir Sie dabei
”
unterst¨
utzen, das Programmieren in Java1 zu erlernen. Dabei gehen wir davon
aus, daß Ihre bisherige Erfahrung im Programmieren vernachl¨assigbar sind.
Bitte beachten Sie:
Dieser Begleittext ist im Wintersemester 1999/2000 entstanden und ist auf die
damalige Form dieser Veranstaltung zugeschnitten. Im Sommersemester 2000
wurde der Fokus der Veranstaltung etwas ver¨andert.
Momentan passen nur die ersten 4 Kapitel sowie die Anh¨ange zur momentanen
Form der Veranstaltung. Es ist nicht sicher, ob wir auch die sp¨ateren Kapitel
u
¨ berarbeiten werden. Unabh¨angig davon k¨onnen Sie die ersten 4 Kapitel dennoch mit Gewinn f¨
ur sich nutzen.
1.1
Lehrbu
¨ cher zu Java
Es sollte zun¨
achst erw¨
ahnt werden, daß Java sehr viele Bestandteile hat. Zum
einen geh¨
ort zu Java die Java-Programmiersprache, zum anderen enth¨alt Java
viele Werkzeuge, die es erm¨
oglichen, Fenster und Graphiken auf dem Monitor
anzuzeigen. Es gibt Werkzeuge, mit denen der Rechner zur Tonerzeugung verwendet werden kann oder mit denen man Programme f¨
urs Internet schreiben
kann. Alle Bestandteile von Java zusammen nennt man das JDK: Java Development Kit.
Die meisten Lehrb¨
ucher zu Java versuchen, alles u
¨ber Java erz¨ahlen, und das
geht dann zu Lasten der Erkl¨arung der Programmiersprache — meistens ist f¨
ur
die Grundlagen der Programmierung in diesen B¨
uchern weniger als ein Drittel
vorgesehen.
Nun wollen wir Ihnen in dieser Veranstaltung aber gerade die Grundlagen des
Programmierens vermitteln. Die Entwicklung von Internet-, Fenster- und Graphikanwendungen mag ja sehr interessant sein, aber bevor man derartige Programme schreibt, sollte man doch die Grundlagen der Programmierung beherrschen.
Ein sich uneingeschr¨
ankt eignendes Lehrbuch zu Java f¨
ur Programmieranf¨anger,
haben wir leider nicht gefunden. Daher haben wir den vorliegenden Text erstellt.
Die folgenden Lehrb¨
ucher sind bedingt empfehlenswert:
• Computing Concepts with JAVA Essentials Cay S. Horstmann
John Wiley & Sons, 2000, 89,90,– DM
Computing Concepts with JAVA Essentialsßeichnet sich aus durch eine
anf¨
angerorientierte, didaktisch sehr gute Darstellung des Stoffes, die f¨
ur
den Studienanf¨
anger durch die englische Sprache - mit Fachidiom der EDV
und Programmiersprachen - ged¨ampft wird. Das Buch enth¨alt eine Vielzahl an Programmbeispielen und stellt alle Konzepte der Sprache weitgehend umfassend und gut verst¨andlich dar. R¨
uckgriffe auf Rechnerarchitektur und Historie lockern den Stoff auf.
1 http://www.java.sun.com
6
Das Buch ist gut geeignet, die Veranstaltung im Selbststudium zu erg¨anzen.
Auf der Webseite2 des Authors finden Sie weitere Hinweise zum Buch.
• Go To Java 2
Guido Kr¨
uger
Addison–Wesley, 1999, ISBN 3–8273–1370–8, 89,90,– DM
Dieses Buch eignet sich sehr gut f¨
ur Leser mit etwas Programmiererfahrung. Das Buch ist in einer Online–Version frei erh¨altlich, welche wir auf
unserem Server3 spiegeln.
Falls Sie an einem Rechner außerhalb des Netzes der TU Braunschweig
arbeiten, k¨
onnen Sie das Buch auf den Seiten des Authors4 lesen.
• Java in 21 Tagen
Laura Lemay, Charles L. Perkins
Markt u. Technik, 1999, 89,95,– DM.
Dieses Buch eignet sich gut f¨
ur Programmieranf¨anger. Es ist im WWW5
frei verf¨
ugbar.
• The Java Tutorial Mary Campione, Kathy Walrath
Addison Wesley, 1998, 85,- DM
The Java Tutorial“ richtet sich an Personen, die bereits programmieren
”
k¨
onnen. Es beschreibt mit vielen instruktiven Beispielen, wie man in Java
Fenster-, Graphik- oder Internetanwendungen programmiert.
Dabei ist aufgrund der vielen Querverweise im Buch die Html-Version vermutlich besser lesbar als die gedruckte Fassung. Wir spiegeln die Online–
Fassung6 .
Wenn Sie an einem Rechner außerhalb des Netzes der TU Braunschweig
arbeiten, k¨
onnen Sie es auf den Seiten von Sun7 lesen.
1.2
¨
Uber
diesen Text
Aufgrund der Probleme bei der Suche nach einem Lehrbuch haben wir uns entschlossen, einen Begleittext zur Veranstaltung zu schreiben. Er soll gemeinsam
mit den Hausaufgabe als Leitfaden durch die Veranstaltung dienen — wir werden Ihnen in den Hausaufgaben mitteilen, wann Sie welche Kapitel lesen sollen
oder wann Sie auf Sekund¨
arliteratur zur¨
uckgreifen sollen.
Es ist empfehlenswert, den Text parallel zu den Hausaufgaben zu verwenden.
Sie finden in den Hausaufgabenbl¨ocken Hinweise, welche Teile im Begleittext
vor oder w¨
ahrend der Bearbeitung gelesen werden sollten.
Zum Lesen des Textes noch folgende Hinweise:
• Jedes Kapitel endet mit einer Reihe von Fragen, die Sie nach dem Lesen
des Kapitels beantworten k¨onnen sollten. Sie k¨onnen diese Fragen verwenden, um vor dem Lesen des Kapitels einen Eindruck von seinem Inhalt zu
2 http://www.horstmann.com/
3 http://www.tu-bs.de:82/wir/EIP/gj/cover.html
4 http://www.gkrueger.com
5 http://www.mut.de/leseecke/buecher/java2/inhalt.html
6 http://www.tu-bs.de:82/wir/EIP/tutorial/
7 http://java.sun.com/docs/books/tutorial/
7
erhalten. Außerdem k¨
onnen Sie anhand der Fragen pr¨
ufen, ob Sie das
Kapitel verstanden haben.
• Wir pr¨
asentieren viele Beispiele. Bitte vollziehen Sie die Programmierbeispiele und Kommandos am Rechner nach.
• Kommandozeilenbefehle pr¨asentieren, schreiben wir als
> Kommando
Dabei bedeutet das f¨
uhrende Gr¨oßerzeichen, daß Sie diesen Text auf der
Kommandozeile eingeben sollen. Es soll nicht mit eingetippt werden.
Beim Erlernen des Programmierens in Java w¨
unschen wir Ihnen viel Erfolg !
8
2
Grundlagen: Wie arbeitet ein Computer ?
Bevor wir auf die Programmierung von Rechnern eingehen, m¨ochten wir sichergehen, daß Sie ein Grundverst¨andnis vom Aufbau und von der Funktionsweise
eines Rechners haben. Was es bedeutet, einen Rechner zu programmieren wollen
wir Ihnen auch beschreiben.
Im weiteren verwenden wir u
¨brigens den deutschen Begriff Rechner“ und den
”
englischen Begriff Computer“ synonym.
”
Wenn Sie bereits eine klare Vorstellung vom Aufbau und der Funktionsweise
eines Computers haben, k¨
onnen Sie diesen Abschnitt u
¨berfliegen oder u
¨berspringen und Ihr Wissen anhand der Zusammenfassung am Ende des Kapitels
u
ufen.
¨berpr¨
2.1
Der Aufbau eines Computers
Es bedarf eigentlich keines Kommentars, daß Computer heutzutage in allen
Lebensbereichen eingesetzt werden. Computer berechnen, ob geplante Br¨
ucken
stehenbleiben werden, sie steuern Flugzeuge, erledigen Ihre Steuererkl¨arung und
moderne Filme werden meist komplett im Computer nachbearbeitetet.
Es mag vielleicht u
ur diese vielf¨alti¨berraschen — aber es gibt einen Oberbegriff f¨
gen F¨
ahigkeiten von Computern: Datenverarbeitung“. Egal, was ein Com”
puter tut, er verarbeitet immer Daten. Dabei ist ein Computer ziemlich dumm
— ob er Musikdaten verarbeitet, f¨
ur einen Film k¨
unstliche Dinosaurier auferstehen l¨
asst, ob er Ihre Steuerekl¨arung bearbeitet oder Lara Croft aufregende
Abenteuer bestehen l¨
aßt, ist ihm ziemlich egal. Er ist nicht dazu in der Lage,
einen Unterschied zwischen all diesen verschiedenen Daten zu bemerken. F¨
ur
ihn bestehen Daten nur aus Zahlen. Wir Menschen m¨
ussen ihm bis ins kleinste
Detail erkl¨
aren, wie all diese Zahlen verarbeitet und interpretiert werden sollen.
Ob aus einer Zahlenkolonne dann ein Lied, ein Film oder eine Steuererkl¨arung
wird, wird nur durch die Datenverarbeitungsregeln — durch das Programm
— bestimmt.
Die wesentlichen Bestandteile eines Computers zeigt die folgende Graphik:
Graphik 1 : Aufbau eines Computers
Datenausgabe
Dateneingabe
Benutzer−
schnittstelle
Tastatur
Maus
Drucker
Bildschirm
Zentraleinheit (CPU)
Speicher
ROM
RAM
Arbeitsspeicher
Festplatte
Diskette
Nicht−flüchtiger Speicher:
Die zu verarbeitenden Daten m¨
ussen dem Computer irgendwie u
¨bergeben wer9
den — jeder Computer ben¨
otigt also Dateneingabe-M¨oglichkeiten. Die wichtigsten Dateneingabeger¨
ate sind die Tastatur und die Maus.
Auch die Regeln, nach denen er diese Daten verarbeiten soll, m¨
ussen dem Computer irgendwie genannt werden. Da diese Regeln bzw. Programme nichts anderes als spezielle Daten sind, k¨onnen sie auf dieselbe Art und Weise in den
Computer eingeben werden wie alle anderen Daten auch.
Da wir Menschen die vom Computer verarbeiteten Daten irgendwie weiter verwenden wollen, besitzen fast alle Computer M¨oglichkeiten zur Datenausgabe
wie Monitor, Drucker oder Soundkarte.
Die zu verarbeitenden Daten und die Programme muß sich der Computer irgendwo merken. Dies geschieht im Speicher des Computers. Wir unterscheiden
fl¨
uchtigen und nicht-fl¨
uchtigen Speicher.
Die im fl¨
uchtigem Speicher enthaltenen Daten werden bei jedem Ausschalten
des Computers gel¨
oscht. Unter fl¨
uchtigem Speicher versteht man vor allem den
RAM — Random Access Memory — genannten Teil des Arbeitsspeichers.
Wenn man den Computer ausschaltet, gehen alle Inhalte des RAM verloren.
Dem RAM kommt eine besondere Bedeutung zu, weil in der Regel alle vom
Computer zu verarbeitenden Daten und Programme vor der Verarbeitung hierher u
ussen.
¨bertragen werden m¨
Die Datenverarbeitung selber geschieht mit Hilfe der CPU (Central Processing
Unit = Zentraleinheit), welche auch Prozessor genannt wird. Der Prozessor ist
eigentlich immer besch¨
aftigt — sobald der Computer angeschaltet wird, beginnt
er damit, im Arbeitsspeicher enthaltene Programme abzuarbeiten und g¨onnt
sich dabei (fast) keine Pause, bis man den Computer ausschaltet.
Damit die Programme und Daten nicht bei jedem Anschalten des Computers
neu eingetippt werden m¨
ussen, besitzt heutzutage jeder Computer auch nichtfl¨
uchtigen Speicher, also Speicher dessen Inhalt beim Ausschalten des Computers
nicht verloren geht. Als wichtigste Vertreter sind hier die Festplatte, Diskettenlaufwerke und CD-Roms zu nennen. Da die Festplatte meist einer der schnellsten
nicht-fl¨
uchtigen Speicher eines Rechners ist, werden h¨aufig ben¨otigten Daten
dort gespeichert.
Ein weiterer wichtiger nicht-fl¨
uchtigen Speicher ist das ROM (Read-only Memory). Das ROM ist ein nicht-beschreibbarer Teil des Arbeitsspeichers. Sie selbst
k¨
onnen im ROM Ihres Computers also keine Daten ablegen. Trotzdem k¨onnte
kein Computer ohne ROM arbeiten. Der Grund hierf¨
ur ist, daß ein Computer
nichts tun kann, das man ihm nicht in allen Einzelheiten erkl¨art. Er ben¨otigt
sogar ein Programm, das ihm erkl¨art, wie er zu starten hat. Beim Start muß der
Computer n¨
amlich alle angeschlossenen Ger¨ate erkennen und in Gang setzen,
und wie er das zu tun hat, ist im ROM beschrieben.
Außerdem muß der Computer direkt nach dem Anschalten das Betriebssystem starten, auch OS (Operating System) oder DOS (Disk Operating System)
genannt. Ein Betriebssystem ist ein sehr umfangreiches Programm, welches den
Computer durch Menschen benutzbar macht. Das Betriebssystem erm¨oglicht
dem Computer, mit seinen Benutzern zu kommunizieren (wichtige Betriebssysteme sind MS DOS, MS Windows, das Mac-OS sowie Unix-Betriebssysteme
wie Linux oder Solaris). Ohne Betriebssystem w¨
usste der Computer nicht, was
er auf dem Bildschirm anzeigen soll, und man k¨onnte ihn weder per Maus noch
per Tastatur bedienen.
10
Wie wird das Betriebssystem gestartet ? Beim Anschalten weiß der Computer
noch gar nicht, wie er das Betriebssystem von der Festplatte laden und starten
soll. Deshalb hat der Hersteller des Computers die hierf¨
ur ben¨otigten Programme im ROM abgelegt. Beim Start befolgt der Computer zun¨achst das im ROM
gespeicherte Startprogramm und startet dabei das Betriebssystem.
Die Rechner, an denen Sie im CIP-Pool w¨ahrend des Kurses arbeiten werden,
laufen u
¨brigens alle unter einer Variante des Betriebssystems Unix, welche von
der Firma IBM unter dem Namen AIX vertrieben wird. Beim Bearbeiten der
ersten Hausaufgabe werden Sie einige Dinge u
¨ber Unix lernen.
2.2
Organisation des Arbeitsspeichers — Bits und Bytes
Alle Daten und Programme m¨
ussen vor ihrer Verarbeitung im RAM abgelegt
werden. Auf der physikalischen Ebene ist das RAM aus unz¨ahligen elektrischen
Kondensatoren und Transistoren aufgebaut. Ein Kondensator kann dabei in
einem von zwei Zust¨
anden sein: entweder tr¨agt er elektrische Ladung oder er
ist entladen. Zust¨
ande, in denen Kondensatoren nur teilweise aufgeladen sind,
werden heutzutage nicht ber¨
ucksichtigt.
Eine Speichereinheit, die nur zwei Zust¨ande annehmen kann, nennen wir Bit,
und die beiden Zust¨
ande eines Bits beziffern wir mit 0 und 1. Zum Beispiel k¨onnte dem durch einen ungeladenen bzw. geladenen Kondensator repr¨asentierten
Bit der Wert 0 bzw. 1 zugeordnet werden.
Da ein Bit nur sehr kleine Informationsmengen speichern kann, werden mehrere
Bits zu gr¨
oßeren Einheiten gruppiert.
Beispiel: Sicher haben auch Sie schon Werbeslogans geh¨ort, in welchen
f¨
ur 32-Bit-Betriebssysteme oder 32-Bit-Prozessoren geworben wird. Ein
32-Bit-Prozessor ist in der Lage, immer 32 Bit auf einmal aus dem Speicher
zu lesen und als eine Einheit zu verarbeiten.
Ein ¨
alterer 16-Bit-Prozessor hingegen verarbeitet Daten immer in 16-BitPortionen. Da ein 16-Bit-Prozessor nur 16 Bit auf einmal verarbeiten kann,
m¨
usste er zweimal auf den Speicher zugreifen, um 32 Bit zu lesen, und er
m¨
usste zwei Operationen ausf¨
uhren, um 32-Bit zu verarbeiten.
Die Anzahl von Bits, die ein Prozessor auf einmal aus dem Speicher holen
und verarbeiten kann, nennt man u
¨brigens die Bus-Breite des Prozessores (ein Bus ist ein B¨
undel von Leitungen, u
¨ber das Daten transportiert
werden).
Wenn die zu verarbeitenden Daten in gr¨
oßeren Gruppen als 16 Bit vorliegen, kann ein 32-Bit-Prozessor also tats¨
achlich schneller sein als ein
16-Bit-Prozessor. Allerdings m¨
ussen die benutzten Programme dazu auch
wirklich eine Verarbeitung in 32-Bit-Portionen vorsehen.
Bevor wir darauf eingehen, zu was f¨
ur Gruppen man Bits zusammenfaßt, wollen
wir uns u
¨berlegen, wie viele Zust¨ande man mit einer vorgegebenen Anzahl von
Bits darstellen kann.
Beispiel: Wenn wir ein Bit verwenden, k¨onnen wir nur die zwei Zust¨ande
0 und 1 darstellen.
Wenn wir zwei Bits verwenden, k¨
onnen wir die vier Zust¨
ande 00, 01, 10,
11 darstellen.
11
Drei Bits k¨
onnen die acht Zust¨
ande 000,001,010,011,100,101,110,111 darstellen.
Es ist sehr leicht zu sehen, daß n Bits genau 2n verschiedene Zust¨ande annehmen
k¨
onnen. Will man also N verschiedene Zust¨ande beschreiben, ben¨otigt man eine
Anzahl von Bits, die sich durch Aufrunden von log2 N ergibt.
Beispiel: Um jede Zahl von 0 bis 15 darstellen zu k¨onnen, ben¨otigt man
log2 16 = 4 Bits.
Zur Darstellung der Zahlen 0 . . . 100 sind 7 Bits n¨
otig (log2 101 = 6, 66).
Die wichtigsten Einheit, zu denen Bits zusammengefaßt werden, sind:
Byte:
Ein Byte besteht aus 8 Bit und kann 28 = 256 Zust¨ande annehmen.
Word:
Die Gr¨
oße eines Word h¨angt vom verwendeten Computer ab. Es besteht
in der Regel aus sovielen Bits, wie der Computer zugleich verarbeiten
kann. Heutzutage versteht man unter einem Word meist 8 Byte oder
64 Bit.
Meist verwendet man die Begriffe Byte oder Word, um einen einzelnen Zahlenwert zu beschreiben. Jedem Zustand eines Bytes oder Words kann man eine Dezimalzahl zuweisen. Dazu fasst man die Zust¨ande der einzelnen Bits als
Bin¨
arzahl auf.
Der Dezimalwert einer Bin¨
arzahl berechnet sich wie folgt: Die Bits der Zahl
werden von rechts beginnend und mit der Zahl 0 startend durchnumeriert. Anschließend bildet man eine Summe, in welcher man f¨
ur jedes an der Position i
gesetzte Bit die Zahl 2i einsetzt.
Beispiel: Zum Beispiel hat die Bin¨arzahl 010 die Dezimaldarstellung
21 = 2.
Die Zahl 010100100 die Dezimaldarstellung 22 + 25 + 27 = 164.
Beispiel: Ein weiteres Beispiel: Computer verwenden meist Byte-Werte,
um Buchstaben und andere Zeichen zu beschreiben. Dabei wird jedem
Zeichen ein anderer Zahlenwert zugewiesen. Auf meinem Rechner sehen
einige Beispiele f¨
ur diese Zuordnung so aus:
Zeichen
a
b
c
A
#
+
1
Zahlenwert
97
98
99
65
35
43
49
Darstellung in Bits
01100001
01100010
01100011
01000001
00100011
00101011
00110001
Pr¨
ufen Sie bitte nach, ob die dezimale Darstellung und die Bit-Darstellung
zusammenpaßt.
Wir haben nun die Grundeinheiten kennengelernt, mit denen ein Computer
operiert. Diese Einheiten werden Ihnen auch bei der Programmierung mit Java
immer wieder begegnen, denn auch wenn Sie in Java mit Zahlen rechnen, m¨
ussen
12
Sie manchmal angeben, in welcher Grundeinheit die Zahlen gespeichert werden
sollen.
Will man beschreiben, wieviele Daten ein Computer speichern kann, ben¨
utzt
man noch gr¨
oßere Einheiten:
KB, Kilo–Byte: 1 KB (Ein Kilo–Byte) sind 210 = 1024 Byte.
MB, Mega–Byte: 1 MB (Ein Mega–Byte) sind 210 Kilo–Byte oder 220 = 1.048.576
Byte.
GB, Giga–Byte: 1 GB (Ein Giga–Byte) sind 210 Mega–Byte oder 230 = 1.063.641.824
Byte.
TB, Tera–Byte: 1 TB (Ein Tera–Byte) sind 210 Giga–Byte bzw. 240 Byte.
Beispiel: Neue Rechner f¨ur den Heimgebrauch oder f¨urs B¨uro haben
heutzutage in der Regel 128 MB Arbeitsspeicher oder mehr und einige
GB Festplattenspeicher; H¨
ochstleistungsrechner haben mehrere GB Arbeitsspeicher und mehrere hundert GB Festplattenspeicher.
2.3
Die Organisation des Arbeitsspeichers — Adressen
Sie haben erfahren, daß der Arbeitsspeicher eines Rechners aus vielen Bits besteht, die zu Bytes oder gr¨
oßeren Gruppierungen zusammengefaßt werden. Obwohl Sie dies zur Programmierung mit Java nicht unbedingt wissen m¨
ussen,
m¨
ochten wir Ihnen dennoch ganz kurz erz¨ahlen, wie der Prozessor Daten im
Arbeitsspeicher verwendet.
Jedes Byte des Arbeitsspeichers hat eine eindeutige Nummer — die Nummer
eines Bytes nennt man seine Adresse. Der Prozessor kann u
¨ber die Adresse
Daten im Arbeitsspeicher auslesen und Daten in den Arbeitsspeicher schreiben.
Beispiel: Wir k¨onnen dem Prozessor befehlen, eine Zahl in dem Byte
an der Adresse 100 und eine weitere Zahl in dem an der Adresse 200 beginnenden Word zu speichern. Anschließend k¨
onnen wir ihn anweisen, die
beiden Zahlen zu addieren und das Ergebnis an der Adresse 300 abzulegen. Da hierbei ein Byte und ein Word addiert wird, sollte das Ergebnis
mindestens Word-Gr¨
oße haben.
2.4
Programmierung eines Computers
Nachdem Sie nun eine gewisse Vorstellung davon haben, wie ein Computer aufgebaut ist, folgt nun die Erkl¨arung, wie ein Computer Programme verarbeitet.
Es ist hoffentlich bereits klar geworden sein, daß Programme nichts anderes
sind als Regeln, die einem Computer bis ins allerkleinste Detail erkl¨aren, wie
er gewisse Daten verarbeiten soll. Es stellt sich nun die Frage, wie man dem
Computer derartige Regeln mitteilen soll. Vorher stellt sich aber die Frage, was
f¨
ur Regeln man einem Computer mitteilen kann.
Hier sollten Sie sich merken, daß die Regeln, die man einem Computer angibt,
immer eindeutig sein m¨
ussen. Regeln, die exakt und eindeutig sagen, was zu
tun ist, nennt man einen Algorithmus. Ein Computer kann nur Algorithmen
verarbeiten.
13
Beispiel: Die Regeln, nach welchen die Dezimaldarstellung einer
Bin¨
arzahl mit den Bits bn , . . . , b0 berechnet werden, k¨
onnte umgangssprachlich so beschrieben werden wie im vorigen Kapitel (Seite 2.2):
Der Dezimalwert einer Bin¨
arzahl berechnet sich wie folgt: Die
Bits der Zahl werden von rechts beginnend und mit der Zahl 0
startend durchnumeriert. Anschließend bildet man eine Summe, in welcher man f¨
ur jedes an der Position i gesetzte Bit die
Zahl 2i einsetzt.
Allerdings kann diese Beschreibung mißverstanden werden. Sie ist auch
kein Algorithmus, da sie nicht eindeutig ist. Es ist zum Beispiel nicht
festgelegt, in welcher Reihenfolge die Summe gebildet werden soll.
Ein Computer ben¨
otigt immer eine Vorgehensvorschrift, in der jedes Detail genau beschrieben wird. Vor allem darf die Regel nicht den geringsten
Entscheidungsspielraum lassen und muß eindeutig sein. Eine Umsetzung
der obigen umgangssprachlichen Regel in einen Algorithmus k¨
onnte so
aussehen:
• Gegeben sei eine Bin¨
arzahl mit den Bits bn , . . . , b0 .
• Setze dezimal := 0
• Lasse i jeden Wert zwischen 0 und n aufsteigend durchlaufen und
Setze f¨
ur jeden Wert von i
dezimal := dezimal + bi · 2i
• dezimal enth¨
alt nun die Dezimaldarstellung der Bin¨
arzahl bn , . . . , b0
Da die zweite Umrechenregel des obigen Beispiels eindeutig ist, k¨onnte man
sie nun in ein Programm f¨
ur einen Computer umsetzen. Ein Programm ist
die Formulierung eines Algorithmus in einer Programmiersprache. Wie schon
erw¨
ahnt, ist ein Computer ziemlich dumm — er versteht nur eine ganz spezielle,
f¨
ur Menschen ungeeignete Sprache, die Maschinensprache. Maschinensprache
ist eine Sprache, die nur aus Zahlen besteht.
Beispiel: Um Sie von Maschinensprache abzuschrecken, zeigen wir Ihnen
ein Maschinensprache-Programm. Dabei zeigen wir Ihnen nicht die Zahlen, aus denen das Programm besteht sondern eine textliche Darstellung,
in der jeder Zahl ein kurzes Wort wie movl oder imull zugeordnet wurde:
movl
movl
movl
imull
30,12345(%ebp)
50,23456(%ebp)
3456(%ebp),%eax
0xfffffff8(%ebp),%eax
Dieses Programm multipliziert die Zahlen 30 und 50.
Dazu legt es in der ersten Zeile mit dem Befehl movl die Zahl 30 an der
Adresse 12345 ab. Dann legt es in der zweiten Zeile die Zahl 50 an der
Adresse Zahl 23456 ab. In der dritten Zeile merkt es sich, daß das Ergebnis
der Rechnung an der Adresse 3456 gespeichert werden soll. Schließlich
multipliziert es in der vierten Zeile durch den imull-Befehl die Zahlen
und legt das Ergebnis an Adresse 3456 ab.
14
Sie sehen an diesem Beispiel, daß in Maschinensprache die einfachsten Dinge
recht umfangreich werden. Außerdem hat Maschinensprache einen Riesennachteil — jeder Prozessor hat eine andere Maschinensprache, und einen PC mit
Intel-Prozessor in Maschinensprache zu programmieren, ist etwas ganz anders,
als einen Macintosh-Rechner zu programmieren. Wenn Sie Maschinensprache
verwenden wollten, m¨
ussten Sie f¨
ur jeden Rechnertyp, auf dem Ihr Programm
laufen soll, ein v¨
ollig anderes Programm erstellen.
Daher wird heutzutage eigentlich nur noch in h¨oheren Programmiersprachen
wie Pascal, C, Pascal, C++ oder Java programmiert — h¨ohere Programmiersprachen heißen so, weil Sie einen h¨oheren“ Abstraktionsgrad vom Prozessor
”
haben als Maschinensprache. Nur ein paar wenige Freaks, Hacker und Spieleprogrammier programmieren heutzutage noch in Maschinensprache.
Die h¨
oheren Programmiersprachen bieten den großen Vorteil, daß man sich nicht
selbst darum k¨
ummern muß, an welcher Adresse der Computer Daten ablegen
soll, außerdem entspricht ein einzelner Befehl in einer h¨oheren Programmiersprachen einer ganzen Reihe von Befehlen in Maschinensprache. Die h¨oheren
Programmiersprachen kommen dabei umgangssprachlichen Formulierungen sehr
nahe:
Beispiel: Zum Beispiel k¨onnten die Regeln zur Umwandlung einer Bin¨arin eine Dezimalzahl in Java so lauten:
/* die Bits der Bin¨
arzahl seien in b[0], b[1] ... bis in b[n]
* gespeichert
*/
dezimal = 0;
for( i = 0; i < n ; i++ ) {
dezimal = dezimal + b[i] * 2^i;
}
return dezimal;
Sie sehen, daß diese Regeln fast unserem umgangssprachlichen Algorithmus entsprechen. In Maschinensprache s¨
ahe das sehr viel komplizierter
aus.
Nun hatten wir vorhin doch erw¨ahnt, daß ein Computer nur Maschinensprache versteht — wie kann er dann Programme h¨oherer Programmiersprachen
verstehen ? Eigentlich ist die Antwort offensichtlich — immer wenn ein Computer etwas tun soll, ben¨
otigt er eine Vorgehensvorschrift. Damit ein Computer
Programme h¨
oherer Programmiersprachen verstehen kann, muß ihm ein Pro¨
gramm geben, das f¨
ur ihn als Ubersetzer
arbeitet und den Programmtext in
Maschinensprache u
¨bersetzt.
Ein solches Programm nennt sich Compiler. F¨
ur jede Programmiersprache gibt
es f¨
ur jeden Rechnertyp einen speziellen Compiler, der g¨
ultige Texte der Programmiersprache zu Maschinencode des jeweiligen Rechners kompiliert (kompilieren == u
¨bersetzen). Der Compiler dient dem Computer quasi als Dolmetscher f¨
ur die von uns Menschen erstellten Textdateien.
Um ein Programm zu entwickeln und ablaufen zu lassen, sind also die folgenden
Schritte n¨
otig:
15
1. Es muß analysiert werden, was die Problemstellung genau beinhaltet und
welche Ziele mit der Programmentwicklung verfolgt werden sollen (Analysephase).
2. Es muß geplant werden, wie die Problemstellung in ein Programm umgesetzt werden soll. Insbesondere m¨
ussen die Regeln, nach denen das Programm arbeiten soll, umgangssprachlich formuliert werden. Berechnungsvorschriften m¨
ussen hierbei als Algorithmus formuliert werden (Design–
Phase).
3. Dann muß das Programm in einer Programmiersprache programmiert werden, man sagt auch: implementiert werden“ (Programmier–Phase).
”
4. Dann muß das Programm kompiliert werden und kann kann anschließend
getestet oder verwendet werden.
Graphik 2 : Entwicklung eines Programms
Programmvorhaben in umgangssprachlicher Fassung
Analyse und Entwurf
Eindeutige umgangssprachliche Formulierung (Algorithmus)
Programmieren
Formulierung in höherer Programmiersprache
Kompilieren
Maschinensprache
2.5
Zusammenfassung des Kapitels
Sie sollten nun die folgenden Fragen beantworten k¨onnen:
B Wie unterscheidet ein Computer Musik-, Graphik- und andere Daten ?
B Was sind Dateneingabe, Zentraleinheit, Speicher, Datenausgabe ?
B Was sind RAM und ROM ?
B Was ist ein Betriebssystem ?
B Wie ist das RAM aufgebaut ?
B Was sind Bits, Bytes, Words, Kilobytes, Megabytes ?
B Warum sind Bin¨
arzahlen f¨
ur Computer wichtig ?
B Was zeichnet einen Algorithmus aus ?
B Was ist der Unterschied zwischen einem Algorithmus und einem Programm ?
B Warum programmiert man Computer meistens nicht in Maschinensprache ?
B Welche Beziehung besteht zwischen Maschinensprache, h¨oherer Programmiersprache und Compiler ?
16
3
Das Java–Programmiersystem JDK
Im vorigen Kapitel haben Sie erfahren, daß ein Computer nicht in der Lage ist,
von Menschen lesbare Programme direkt zu verstehen. Der Computer ben¨otigt
einen Dolmetscher, der das f¨
ur Menschen verst¨andliche Programm in seine Sprache u
bersetzt.
¨
Zum Java–Programmiersystem geh¨oren sogar zwei solche Dolmetscher — der
Java–Compiler und die sogenannte Java–Virtual–Machine. Bevor wir auf die
Programmierung von Java eingehen, m¨
ussen wir Sie mit dem Java–Programmiersystem und speziell mit diesen beiden Dolmetschern vertraut machen.
3.1
Bestandteile des JDK
Schon in der Einleitung wurde erw¨ahnt, daß Java8 nicht nur aus einer Programmiersprache besteht. Alles, was man zur Java-Programmierung ben¨otigt,
wurde von den Sch¨
opfern von Java, der Firma Sun9 , in einem Paket zusammengeschn¨
urt, das sich JDK: Java Development Kit nennt.
Das JDK enth¨
alt folgende Dinge:
• Eine umfangreiche Sammlung von Programmbausteinen. Im JDK sind
bereits sehr viele Programmbausteine, sogenannte Klassen, enthalten.
Wenn man diese Bausteine in eigene Programme einbaut, kann man mit
Java relativ einfach Internet-, Graphik- oder Fenster-Anwendungen erzeugen.
Wir werden Ihnen in dieser Veranstaltung nur ganz wenige dieser Programmbausteine vorstellen, da wir zum Programmieren lernen lieber selbst
einfache Programme bauen wollen als komplizierte Programme aus diesen
Programmbausteinen zu erstellen.
• Umfangreiche Dokumentation: F¨
ur all diese fertigen Programmbausteine
(Klassen) und alle anderen Werkzeuge von Java liegt umfangreiche Dokumentation von HTML–Dateien vor, welche mit einem WWW–Browser
betrachtet werden kann.
Um die von Java bereitgestellten Programmbausteine zu verwenden, muß
man viel mit dieser Dokumentation arbeiten. Daher werden wir im Rahmen dieser Veranstaltung ein paar Aufgaben stellen, bei denen Sie sich in
der Dokumentation zurechtfinden m¨
ussen.
• Einige Werkzeuge, zum Beispiel ein Werkzeug, das Ihre Programme liest
und die enthaltene Dokumentation in HTML-Dokumenten zusammenfasst. Dieses Werkzeug werden Sie hier auch kennenlernen.
• Java–Compiler und Java–Virtual–Machine. Diese Programme sind die Dolmetscher zwischen Ihnen und dem Computer.
8 http://www.java.sun.com
9 http://www.sun.com
17
3.2
¨
Ubersetzen
und Ausfu
¨ hren von Java-Programmen
Ein Java–Programm besteht aus einem oder mehreren Programmbausteinen,
welche Klassen genannt werden. Jede Klasse ist in der Regel in einer eigenen
Textdatei beschrieben und wird mit einem Text-Editor erstellt.
Bevor Sie weiterlesen, sollten Sie daher mit einem Text-Editor umgehen k¨onnen.
Sie sollten auch mit der Kommandozeile von Unix umgehen k¨onnen; Hinweise
hierzu finden Sie im Anhang A und in der ersten Hausaufgabe. Wenn Sie unter anderen Betriebssystemen als Unix arbeiten wollen, beschaffen Sie sich die
entsprechenden Informationen bitte selbst.
Beispiel: In dem untenstehenden Kasten sehen Sie ein sehr einfaches
Java–Programm. Wenn man es startet, gibt es den Text Hallo, Welt“
”
aus.
Bitte tippen Sie das Programm in Ihrem Text–Editor ab und speichern
es in einer Datei namens Hallo.java. Beachten Sie hierbei bitte, daß die
Zeilennummern nicht zum Programm geh¨
oren — wenn wir Beispielprogramme abdrucken, numerieren wir die Zeilen durch, um auf die einzelnen
Programmteile eingehen zu k¨
onnen.
Außerdem achten Sie beim Abtippen bitte auf die Groß- und Kleinschreibung. F¨
ur Java besteht zwischen großgeschriebenen und kleingeschriebenen Buchstaben u
¨berhaupt keine Verbindung — so ist das Wort class
f¨
ur Java etwas v¨
ollig anderes als das Wort Class.
Beispiel: Einfache Java–Klasse
1
2
3
4
5
6
7
class Hallo
{
public static void main(String[] args)
{
System.out.println("Hallo, Welt");
}
}
Da in diesem Kapitel nur der Java–Compiler behandelt werden soll, wollen
wir hier auf die Funktionsweise von Java–Programmen noch nicht eingehen. Sie brauchen noch u
¨berhaupt nicht zu verstehen, was die ganzen
kryptischen Symbole in dem Programm bedeuten; im n¨
achsten Kapitel
wird das alles viel klarer werden.
Sie sollten nun eine Textdatei namens Hallo.java mit dem Text des vorigen
Beispiels in ihrem Arbeitsverzeichnis besitzen. Als n¨achstes soll aus dieser Textdatei ein vom Computer ausf¨
uhrbares Programm erzeugt werden.
Dazu verwendet man den Java-Compiler. In Kapitel 2.4 wurde ja bereits
erl¨
autert, daß ein Compiler ein f¨
ur Menschen verst¨andliches Programm in ein
f¨
ur Computer verst¨
andliches Programm umwandelt.
Bevor Sie den Compiler verwenden k¨onnen, muß auf Ihrem Computer die JavaProgrammierumgebung installiert und laufbereit sein. Falls Sie im CIP-Pool
der TU Braunschweig arbeiten, dann ist das kein Problem — hier ist alles schon
fertig installiert. Wenn Sie jedoch zuhause arbeiten m¨ochten, m¨
ussen Sie selbst
daf¨
ur sorgen, daß die Java–Umgebung funktioniert. Dabei helfen Ihnen vielleicht
unsere im WWW verf¨
ugbaren Tips weiter (siehe auch WWW-Seite ? ). Wir
18
gehen im weiteren davon aus, daß Sie eine funktionsf¨ahige Java-Programmierumgebung haben.
Aber nun zum Java–Compiler — dieser ist ein Programm namens javac. Um
durch ihn, die zuvor von Ihnen erstellte Datei Hallo.java u
¨bersetzen zu lassen,
wechseln Sie bitte auf einer Kommandozeile in das Verzeichnis, in welchem Sie
die Datei abgespeichert haben. Dort geben Sie dann bitte ein:
> javac Hallo.java
Der Compiler pr¨
uft darauf zun¨achst, ob die Textdatei Hallo.java ein g¨
ultiges
Java–Programm enth¨
alt. Java–Programme m¨
ussen einen ganz bestimmten Aufbau haben, damit sie vom Compiler u
¨bersetzt werden k¨onnen. Nur wenn die
Datei tats¨
achlich ein g¨
ultiges Programm enth¨alt, u
¨bersetzt der Compiler sie in
eine dem Computer verst¨
andliche Form und dient so als Dolmetscher zwischen
uns und dem Computer. Wenn Sie die Klasse korrekt abgetippt haben, sollte
das Kompilier–Kommando zu keiner Bildschirmausgabe f¨
uhren.
Der Compiler ist auch kleinsten Tippfehlern gegen¨
uber sehr ungn¨adig — selbst
wenn Sie beim Abtippen des Programms nur kleine Fehler gemacht haben, kann
es ihnen passieren, daß der Compiler ihr Programm mit einer Fehlermeldung
ablehnt. Falls Ihnen das beim Compiler–Aufruf passiert ist, pr¨
ufen Sie bitte, ob
Sie das Beispielprogramm exakt abgetippt haben, korrigieren es, speichern die
¨
Anderungen
(!) und probieren dann erneut den Aufruf des Compilers. Wenn das
nicht hilft, lesen Sie bitte unsere Hinweise zu Fehlermeldungen in Anhang C.
Wenn Sie auch damit nicht weiterkommen, holen Sie sich bitte einen Hiwi zur
Hilfe.
Wir gehen im folgenden davon aus, daß der Aufruf des Compilers funktioniert
hat. In diesem Fall hat der Compiler Ihr Programm u
¨bersetzt und die u
¨bersetzte
Fassung in einer neuen Datei namens Hallo.class gespeichert. Schauen Sie
bitte nach, ob diese Datei tats¨achlich erzeugt wurde10 .
Die neu erzeugte Datei Hallo.class enth¨alt die f¨
ur einen Computer verst¨andliche Version unserer Klasse. Wenn Sie diese Datei mit dem cat-Kommando auf
den Bildschirm ausgeben, erhalten Sie eine ganz wilde Ausgabe — außerdem
m¨
ussen Sie danach m¨
oglicherweise eine neue Kommandozeile ¨offnen, weil Ihre
alte Kommandozeile nur noch komische Zeichen anzeigt. Wir Menschen k¨onnen
mit dem Inhalt der erzeugten Datei Hallo.class also nicht viel anfangen.
Wir haben Ihnen bisher aber nur die halbe Wahrheit erz¨ahlt — es ist zwar
tats¨
achlich so, daß der Java–Compiler die Textdatei Hallo.java in eine Maschinnensprache–Datei Hallo.class umgewandelt hat. Es kann jedoch kein tats¨achlich
existierender Computer die vom Java–Compiler benutzte Maschinensprache direkt verstehen. Der Java–Compiler u
¨bersetzt n¨amlich jedes Programm in eine
k¨
unstliche Maschinensprache, den Java–Bytecode.
Java–Bytecode ist eine Art Computer–Esperanto. Kein Computer versteht es
direkt, doch es ¨
ahnelt den meisten Computermaschinensprachen. Soll ein Computer das in einer Java–Bytecode–Datei enthaltene Programm abarbeiten, so
ben¨
otigt er einen weiteren Dolmetscher, der den Java–Bytecode f¨
ur ihn u
¨bersetzt. Dieser zweite Dolmetscher nennt sich Bytecode-Interpreter11 .
10 Wenn
11 Das
Sie nicht wissen, wie das geht, haben Sie vermutlich Anhang A noch nicht gelesen
englische Wort Interpreter“ heißt auf deutsch Dolmetscher“
”
”
19
Man tut im u
¨brigen so, als w¨are Bytecode tats¨achlich eine Maschinensprache
und stellt sich gerne vor, daß irgendwo ein Computer existieren k¨onnte, der den
Bytecode direkt versteht. Da dieser Computer aber nur durch den BytecodeDolmetscher existiert, nennt man den Bytecode-Dolmetscher auch Java Virtual Machine (JVM) (virtueller Java-Computer).
Nat¨
urlich k¨
onnen Java-Programme damit nicht so schnell sein wie Programme anderer Programmiersprachen, welche f¨
ur den jeweilige Computer direkt
verst¨
andlichen Programmcode erzeugen. Der Prozessor Ihres Computes f¨
uhrt ja
kompilierte Java–Programme niemals direkt aus sondern benutzt immer seinen
Bytecode–Dolmetscher.
Der Umweg u
¨ber den Java–Bytecode ist aber auch ein sehr großer Vorteil und
hat den Einsatz von Java im Internet erst erm¨oglicht — hierdurch werden
Java-Programme n¨
amlich systemunabh¨angig. In der Theorie l¨auft jedes JavaProgramm auf jeder Hardware und auf jedem Betriebssystem, f¨
ur das eine virtuelle Java–Maschine existiert. Man muß sich also nicht mehr entscheiden, ob
man ein Programm f¨
ur Windows, Macintosh, Unix oder sonst ein Betriebssystem kompiliert. Wenn die virtuelle Java-Maschine auf diesem Betriebssystem
vorhanden ist, l¨
auft hierauf jedes kompilierte Java–Programm.
Das Programm, welches die virtuelle Maschine simuliert, heißt java. Um die
kompilerte Datei Hallo.class auszuf¨
uhren, geben Sie bitte auf der Kommandozeile das Kommando
> java Hallo
ein. Daraufhin f¨
uhrt der Bytecode-Interpreter den in Hallo.class enthaltenen
Bytecode aus. Es sollte folgende Meldung am Bildschirm erscheinen:
Gratuliere, Ihr erstes Java--Programm funktioniert.
Ein Hinweis noch: Beachten Sie, daß die Endung .class“ beim Aufruf des
”
Bytecode-Interpreters nicht mit angegeben werden darf. Hingegen muß beim
Aufruf des Compilers javac immer die Endung .java“ mit angegeben werden.
”
3.3
Zusammenfassung
B Was ist das JDK ?
B Warum ben¨
otigt man einen Compiler ?
B Sie sollten den Java-Compiler aufrufen k¨onnen.
B Was erzeugt der Java-Compiler ?
B Was ist ein Bytecode-Interpreter, und warum wird er ben¨otigt ?
B Wie unterscheiden sich Java-Compiler und Bytecode-Interpreter
bez¨
uglich der Endung von Dateinamen ?
¨
B Uberlegen
Sie sich, wieso der Bytecode Java so gut f¨
ur die Verwendung
im Internet geeignet macht — ber¨
ucksichtigen Sie hierbei, daß f¨
ur
Menschen verst¨
andlicher Programmtext meist als Gesch¨aftsgeheimnis
angesehen wird.
20
4
Erste Schritte — die Turtlegraphik
Dieses Kapitel soll Sie anhand von einfachen Programmen mit einigen zentralen
Begriffen von Java vertraut machen, insbesondere dem Objekt– und Klassenbegriff.
4.1
Testen der Turtlegraphik
Bei der Einf¨
uhrung dieser Begriffe werden wir uns auf eine von uns erstellte
Java–Erweiterung beziehen, auf eine sogenannte Turtlegraphik. Die Idee der
Turtlegraphik stammt u
¨brigens aus der Programmiersprache Logo — da die
Turtlegraphik mit sehr einfachen Mitteln die Erstellung ansprechender Graphiken erm¨
oglicht und die graphische Ausgabe die Arbeitsweise eines Programms
verdeutlicht, wird sie ein wichtiger Bestandteil dieser Veranstaltung sein.
Der Turtlegraphik liegt die Vorstellung zugrunde, auf dem Bildschirm krabbele
ein kleines Tierchen herum, z.B. eine Schildkr¨ote (engl. Turtle). Diesem Tierchen
kann man befehlen, eine gewisse Anzahl von Schritten vorw¨arts oder r¨
uckw¨arts
entlang seiner Blickrichtung zu gehen. Man kann ihm auch befehlen, sich nach
rechts oder links zu drehen und so seine Blickrichtung zu a¨ndern. Die Turtle tr¨agt
mit sich viele verschiedenfarbige Stifte herum, und man kann es dazu auffordern,
einen Stift auszuw¨
ahlen und ihn abzusetzen oder anzuheben. Wenn sich die
Turtle bei abgesetztem Stift bewegt, zeichnet sie eine Linie in der Stiftfarbe.
In den CIP–Pools der TU ist die Turtlegraphik bereits installiert. Dennoch
m¨
ussen Sie gewisse Vorbereitungen treffen, um die Turtle zu verwenden (siehe erste Hausaufgabe). Wenn Sie die Hausaufgaben und den Begleittext jedoch
zuhause bearbeiten wollen, m¨
ussen Sie sie sich dort erst noch installieren. Eine
Installationsanleitung finden Sie im Anhang E.
Testen Sie bitte zuerst, ob die Turtlegraphik bei Ihnen korrekt installiert ist,
indem Sie auf der Kommandozeile
> java eip.TurtleDemo
eingeben. Falls das nicht funktioniert, schauen Sie bitte in den soeben genannten
Anhang.
Auf dem Bildschirm sollte sich ein zweigeteiltes Fenster o¨ffnen. Im oberen Bereich des Fensters sehen Sie eine Schrift sowie ein dreieckiges Symbol, und im unteren, dunkleren Bereich sehen Sie Kn¨opfe, welche mit Step“, Run Slow“, Stop“
”
”
”
und Quit“ betitelt sind.
”
Dr¨
ucken Sie nun einige Male auf den Step“–Knopf. Sie werden feststellen, daß
”
sich das dreieckige Symbol bei jedem Druck bewegt und dabei Striche malt, welche eine Art Baum ergeben. Das dreieckige Symbol werden wir im weiteren eine
Turtle“ nennen — Sie k¨
onnen sich das so vorstellen, daß auf dem Bildschirm
”
eine kleine Schildkr¨
ote herumkrabbelt und dabei Striche zieht.
Nach einigen Schritten wird das Dr¨
ucken des Knopfes m¨
uhselig — dr¨
ucken Sie
dann den Run Slow“ Knopf. Die Turtle l¨auft nun selbstst¨andig u
ber
den Bild¨
”
schirm, ohne auf weitere Knopfdr¨
ucke zu warten. Durch einen Druck auf den
Stop“–Knopf k¨
onnen Sie sie jederzeit anhalten.
”
Probieren Sie das und dr¨
ucken Sie dann den Run“–Knopf. Nun l¨auft die Turtle
”
so schnell, daß sie gar nicht mehr zu sehen ist. Auch hier k¨onnen Sie jederzeit
21
den Stop“–Knopf dr¨
ucken. Wenn Sie das Programm verlassen wollen, k¨onnen
”
Sie jederzeit den Quit“–Knopf verwenden.
”
4.2
Die Bestandteile eines einfachen Java–Programms
Bitte tippen Sie das folgende Beispiel ab und speichern es in einer Datei namens
Kreuz.java:
Kreuz.javaa
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/KapFirst/Kreuz.java
Anschließend kompilieren Sie es bitte durch
> javac Kreuz.java
und starten es dann mit dem Befehl
> java Kreuz
Sie sehen nun das Fenster, welches Sie bereits kennen, und wenn Sie die Turtle wie in Abschnitt 4.1 beschrieben laufen lassen, sollte ein Kreuz gezeichnet
werden.
Lassen Sie uns die einzelnen Bestandteile dieses Programms Zeile f¨
ur Zeile untersuchen — Sie werden sicher nicht alle Erl¨auterungen verstehen. Lesen Sie bitte
die Ausf¨
uhrungen dennoch, um einen ersten Eindruck der Programmierung in
Java zu erhalten.
In Zeile 1–3 steht ein durch die Zeichen /*“ und */“ eingeschlossener Text.
”
”
Dies ist ein Kommentar. Der Java–Compiler ignoriert jeden so
umschlossenen Text, auch wenn er aus mehreren Zeilen besteht.
Kommentare dienen dazu, Programme lesbarer zu machen — man
kann in Kommentare Texte schreiben, welche beim Verst¨andnis
der nebenstehenden Programmbefehle helfen.
Zeile 4
ist leer. Leerzeilen dienen der besseren Lesbarkeit des Programmes
f¨
ur Menschen. F¨
ur Java haben Leerzeilen gar keine Bedeutung.
In Zeile 5–6 wird Java angewiesen, die Programmbausteine eip.TurtleScreen
und eip.Turtle zu importieren.
Jedes Java–Programm setzt sich aus kleineren Bausteinen zusammen, sogenannten Klassen. Einige Klassen sind in Java fest eingebaut und m¨
ussen Java nicht erst bekannt gemacht werden.
Die Klassen TurtleScreen und Turtle sind von uns erstellt worden und sind Java normalerweise nicht bekannt. Will man sie verwenden, muß man Java daher zuerst mitteilen, wo sie auf der
Festplatte zu finden sind.
Dies geschieht durch Angabe eines Verzeichnispfades, gefolgt vom
Namen des Programmbausteins. Wenn wir hier den Befehl
import eip.TurtleScreen
22
In Zeile 8
In Zeile 10
verwenden, so sagen wir Java damit: Wir m¨ochten gerne die
”
Klasse TurtleScreen verwenden, die im Pfad eip zu finden ist.“
Java sucht darauf in gewissen Verzeichnissen12 nach einer Datei
TurtleScreen.class und macht den enthaltenen Programmbaustein f¨
ur uns nutzbar.
sehen Sie den Text class Kreuz {.
So teilen wir dem Java–Compiler mit, daß wir nun selbst einen
Programmbaustein, n¨amlich die Klasse Kreuz, definieren. Java
verlangt, daß der Name einer Programmdatei gleich dem Namen der enthaltenen Klasse, erg¨anzt um das Anh¨angsel .java
ist. Die Klasse Kreuz muss also zwingend in einer Datei namens
Kreuz.java enthalten sein.
Eine Klasse (ein Programmbaustein) besteht aus vielen Java–
Befehlen. Immer, wenn in Java mehrere Anweisungen zu gr¨oßeren
Einheiten gruppiert werden m¨
ussen, benutzt man dazu geschweifte Klammern.
Die geschweifte Klammer in Zeile 8 wird von der geschweiften
Klammer } in Zeile 22 geschlossen. So wird der Text der Zeilen
9–21 zusammengefasst. Java interpretiert so allen Text innerhalb
dieses Klammerpaares als Bestandteil der Klasse Kreuz.
Geschweifte Klammerpaare samt Inhalt nennt man Block. Bl¨ocke
enthalten oft andere Bl¨ocke. In unserem Beispiel enth¨alt dieser die
Klasse definierende Block noch einen anderen Block, der sich u
¨ber
die Zeilen 10–21 erstreckt.
sehen Sie den Beginn des Hauptprogramms der Kreuz–Klasse.
Jede Klasse besteht aus vielen kleineren Programmst¨
ucken. Dabei u
ucke eine spezielle
¨bernimmt jedes der kleineren Programmst¨
Aufgabe innerhalb der Klasse. Diese kleineren Programmst¨
ucke
heissen in Java u
¨brigens Methoden.
Will man etwa ein Programm schreiben, welches zwei Kreuze
zeichnet, so w¨
urde man zuerst ein Programmst¨
uck (eine Methode) schreiben, welches ein Kreuz zeichnet und dann eine weitere
Methode, welche die erste Methode zweimal verwendet. Die Aufteilung eines Programms in viele kleine St¨
ucke ist eine sehr gute
Sache, da sie Programme u
¨berschaubarer und leichter zu erweitern
macht.
Es stellt sich die Frage, welche Methode ablaufen soll, wenn man
ein Programm von der Kommandozeile startet (etwa durch den
Befehl java Kreuz“). In Java ist das so gel¨ost, daß immer die
”
sogenannte main–Methode gestartet wird, welche durch den Text
public static void main(String[] args)
gekennzeichnet ist.
Eine Methode besteht aus einer Reihe von Anweisungen, welche
durch geschweifte Klammern zusammengefasst werden. Im vorliegenden Beispiel erkennen Sie ein Klammerpaar, welches in Zeile
12 In welchen Verzeichnissen gesucht wird, m¨
ussen Sie hier nicht wissen — falls es Sie
interessiert, k¨
onnen Sie es im Anhang ?? nachlesen.
23
10 beginnt und in Zeile 21 geschlossen wird. Die main–Methode
besteht daher aus allen Anweisungen in den Zeilen 11 bis 20.
Zeile 11
Zeile 11 ist die erste Zeile des Hauptprogramms der Kreuz–Klasse.
Wenn Sie die Kreuz–Klasse von der Kommandozeile starten, beginnt hier die Ausf¨
uhrung des Programms.
In Zeile 11 wird auf der rechten Seite des Gleichheitszeichens
durch den Text new TurtleScreen() ein neuer TurtleScreen erzeugt. Ein TurtleScreen ist ein Fenster, in welchem die Turtle
umherl¨
auft. Der Befehl new TurtleScreen() ¨offnet hier ein neues Fenster auf dem Bildschirm Ihres Computers.
Vorher, in Zeile 5, hatten wir Java mitgeteilt, wo es den Programmbaustein TurtleScreen finden kann. H¨atten wir das nicht
getan, w¨
usste Java mit Zeile 11 nichts anzufangen.
Damit wir mit Java u
¨ber den neu angelegten TurtleScreen reden
k¨
onnen, m¨
ussen wir ihm einen Namen geben. Dies geschieht auf
der linken Seite des Gleichheitszeichens, wo wir eine Variable namens ts erzeugen.
Zeile 11 ist somit die programmtechnische Formulierung des Be¨
fehls Offne
ein neues TurtleScreen–Fenster und benenne es mit
”
ts“.
Zeile 12
Zeile 12 ¨
ahnelt Zeile 11. Durch den Befehl new Turtle(ts) wird
eine neue Turtle angelegt, welche auf den in Zeile 11 angelegten
TurtleScreen gesetzt wird. Diese wird daraufhin als dreieckiges
Symbol auf dem Bildschirmfenster angezeigt.
Die neue Turtle erh¨alt dabei den Namen t.
In Zeile 14–19 werden der in Zeile 12 erzeugten Turtle Botschaften geschickt.
In Zeile 14 steht beispielsweise der Programmbefehl
t.pd();
So schicken wir der Turtle namens t die Botschaft pd()“ (pd
”
steht f¨
ur pendown“). Die Turtle senkt daraufhin ihren Stift.
”
Jede Botschaft an die Turtle t bewirkt die Ausf¨
uhrung zur Botschaft passenden Programmcodes, welcher innerhalb der Turtle–
Klasse definiert ist. Auf den Befehl pd() hin merkt sich die Turtle,
daß sie ab sofort beim umherlaufen Linien ziehen soll.
¨
Ahnlich
sind die Anweisungen t.fd(100);“ bzw. t.rt(120);“
”
”
Botschaften an die Turtle t, 100 Einheiten nach vorne zu laufen,
bzw. sich um 120 Grad nach rechts zu drehen ( fd“ steht f¨
ur
”
forward“ und rt“ steht f¨
ur rightturn“).
”
”
”
Nachdem wir nun jede Zeile des Beispielprogramms besprochen haben, sollten
¨
Sie selbst¨
andig kleine Anderungen
vornehmen — was passiert, wenn Sie die Befehle t.fd(100) durch t.fd(200) ersetzen ? Was passiert, wenn Sie die Befehle
t.rt(120) durch t.rt(90) ersetzen ? K¨onnen Sie die Turtle dazu bringen, ein
¨
Rechteck zu zeichnen ? (nat¨
urlich m¨
ussen Sie nach jeder Anderung
der Datei die
¨
Anderungen
speichern, den Compiler aufrufen und dann das Programm erneut
starten).
24
Ein Hinweis noch: Ihnen sollte aufgefallen sein, daß die meisten Zeilen des Beispielprogrammes mit einem Semikolon enden. In Java m¨
ussen die meisten Be¨
fehle durch Semikoli abgeschlossen werden. Beachten Sie dies beim Andern
des
Beispielprogramms.
4.3
Turtlebefehle
Hier stellen wir Ihnen alle Befehle vor, welche die Turtle versteht und zeigen
Ihnen in einigen Beispielprogrammen, wie diese Befehle zu verwenden sind. Bei
der Vorstellung der Befehle gehen wir davon aus, daß t eine Turtle bezeichnet.
pu( )
pen up“ — die Turtle hebt ihren Stift. Beim Bewegen zeichnet
”
die Turtle nicht.
pd( )
pen down“ — die Turtle senkt ihren Stift. Beim Bewegen zeich”
net die Turtle.
fd( n )
forward“ — die Turtle bewegt sich n Schritte vorw¨arts. Dabei
”
ist n eine reelle Zahl.
Beispielsweise bewegt sich die Turtle t durch den Befehl
t.fd( 3.5 );
dreieinhalb Schritte vorw¨arts.
bk( n )
backward“ Die Turtle bewegt sich n Schritte r¨
uckw¨arts. Dabei
”
ist n eine reelle Zahl.
rt( n )
right turn“ — die Turtle dreht sich um n Grad nach rechts.
”
Dabei ist n eine reelle Zahl.
lt( n )
left turn“ — die Turtle dreht sich um n Grad nach links. Dabei
”
ist n eine reelle Zahl.
home()
Bewegt die Turtle in die Mitte des Zeichenfeldes. Wenn ihr Stift
dabei unten ist, wird eine Linie gezeichnet.x
hide()
Macht die Turtle unsichtbar — sie zeichnet dann zwar noch,
verdeckt aber nicht mehr Teile der Zeichnung.
show()
Macht eine zuvor durch hide() versteckte Turtle wieder sichtbar.
setpc( color ) Setzt die Farbe, in welcher die Turtle zeichnet. Um diesen
Befehl, in einem eigenen Programm zu verwenden, muß zum
Beginn des Programms der Befehl
import java.awt.Color;
stehen. G¨
ultige Farben sind:
Color.black, Color.blue, Color.cyan, Color.darkGray, Color.gray,
Color.green, Color.lightGray, Color.magenta, Color.orange,
Color.pink, Color.red, Color.white, Color.yellow
Sie k¨
onnen auch eigene Farben kreieren: Jede Farbe hat einen
Rot–, einen Gr¨
un– und einen Blauanteil zwischen 0 und 255.
Sie k¨
onnen eigene Farben erzeugen, indem Sie die Farbe
25
new Color( Rotanteil, Gruenanteil, Blauanteil )
Z.B.:
t.setpc(
t.setpc(
t.setpc(
t.setpc(
Color.blue );
// Turtle
Color.black ); // Turtle
new Color(255,0,0) );
new Color(100,100,255) );
zeichnet in
zeichnet in
// zeichnet
// zeichnet
Blau
Schwarz
in Rot
in Blaugrau
Weiter unten finden Sie ein Beispiel, das die Verwendung von
Farben demonstriert.
clearScreen( color ) Die Zeichenfl¨ache der Turtle wird in der angegebenen
Farbe gel¨
oscht. Um diesen Befehl, in einem eigenen Programm
zu verwenden, muß zum Beginn des Programms der Befehl
import java.awt.Color;
Wie man Farben verwendet, ist beim Befehl setpc und im unten
stehenden Beispiel beschrieben.
Befehle zur Fehlersuche:
debug=true
Um Ihnen bei der Fehlersuche in Ihren Programmen zu helfen, hat die Turtle einen Debug-Modus (Fehlersuchmodus).
Der Debug-Modus wird in einer Turtle t durch den Befehl
t.debug=true;
debug=false
angestellt. Sobald der Debug-Modus angestellt ist, gibt die
Turtle bei jedem Befehl aus, was sie gerade tut.
Der Debug-Modus kann durch den Befehl
t.debug=false;
wieder ausgestellt werden.
setName( name ) Wenn Sie mit mehreren Turtles arbeiten, macht es Sinn,
jeder einen eigenen Namen zu geben. Dadurch k¨onnen Sie
im Debug–Modus erkennen, welche der Turtles die Debug–
Meldung ausgegeben hat.
Der Befehl
t.setName("Tina Turtle");
report()
bewirkt, daß die Turtle t jeder Debug–Ausgabe den Namen
Tina Turlte“ voranstellt.
”
Gibt die Position und Blickrichtung der Turtle aus.
Das folgende Beispielprogramm verwendet all die oben genannten Befehle:
Demo1.javaa
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/KapFirst/Demo1.java
26
Verbotene Befehle:
Die folgenden Befehle d¨
urfen Sie in den ersten Hausaufgaben nicht benutzen.
Sie durchbrechen den Turtle–Gedanken, der vorsieht, daß die Turtle nur lokal,
d.h. in ihrem eigenen Koordinatensytem, arbeitet.
setPosition( x, y) Setzt die Position der Turtle auf dem Zeichenfeld. x und
y sind dabei reelle Zahlen. Wenn der Stift unten ist, wird bei
der Bewegung eine Linie gezeichnet.
Beispielsweise bewegt sich die Turtle t durch den Befehl
t.setPosition( 10, 10 );
auf die Position mit den Koordinaten (10,10).
double getX()
Liefert die horizontale Koordinate der Turtle auf dem Zeichenfeld. Das Ergebnis ist eine reelle Zahl:
Zum Beispiel speichert
double x = t.getX();
die horizontale Koordinate der Turtle t in der Variablen x.
double getY()
Liefert die horizontale Koordinate der Turtle auf dem Zeichenfeld zur¨
uck.
setHeading( d ) Setzt die Richtung, in welche die Turtle schaut. Dabei schaut
die Turtle genau nach oben, wenn d == 0 ist. d ist eine reelle
Zahl.
Zum Beispiel l¨aßt der Befehl
t.setHeading(90);
die Turtle genau nach rechts schauen.
double getHeading() Ermittelt die Blickrichtung der Turtle.
Zum Beispiel speichert der Befehl
double d = t.getHeading();
die Blickrichtung der Turtle t in der Variable d.
boolean isShown() gibt zur¨
uck, ob die Turtle momentan angezeigt wird.
boolean penIsDown() gibt zur¨
uck, ob der Stift der Turtle abgesetzt ist oder
nicht.
Das folgende Programm verwendet einige dieser verbotenen Befehle, um ein
Rechteck zu zeichnen:
Tabu.javaa
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/KapFirst/Tabu.java
27
5
Grundlagen der Java-Syntax
Es sollen nun einige Grundregeln besprechen, die in jeder Java-Programmdatei
ber¨
ucksichtigt werden m¨
ussen.
5.1
Grunds¨
atzliches zur Syntax von Java–Programmen
Zun¨
achst sollten Sie wissen, daß der Compiler sich nicht um die Zeilenstruktur
Ihres Programms k¨
ummert — f¨
ur ihn gibt es zwischen Leerzeichen und Zeilenumbr¨
uchen keinen besonderen Unterschied.
Wir h¨
atten das Beispiel Kreuz.java aus dem letzten Kapitel auch so schreiben
k¨
onnen:
/*
* Benutzt die Turtle, um ein Kreuz zu zeichnen.
*/import
eip.TurtleScreen;import eip.Turtle;
class Kreuz {public static void main( String[]
args ) {TurtleScreen ts = new
TurtleScreen(); Turtle t = new Turtle( ts );
t.pd();
t.fd(
200 ); t.bk( 100 );
t.rt( 90
);t.fd( 100 );t.bk( 200 );}}
Dem Compiler ist es egal, wie das Programm formatiert ist. F¨
ur uns Menschen
ist aber dieses Programm sehr viel schlechter zu lesen als das urspr¨
ungliche.
Da er nicht die Zeilenstruktur des Programmes verwendet, muß der Compiler
irgendwie anders erkennen, wann ein Befehl im Programm endet und der n¨achste
Befehl beginnt. Darum muß in Java jede Anweisung mit einem Semikolon enden.
Das gilt aber nicht f¨
ur die Definition einer Klasse oder Methode.
Wenn der Compiler eine Datei bearbeitet, sieht er zun¨achst nur einen Strom
von Zeichen. Diese Zeichen faßt er zu gr¨oßeren Einheiten zusammen. Um etwas
f¨
ur den Compiler verst¨
andliches zu programmieren, m¨
ussen Sie wissen, was f¨
ur
Einheiten der Compiler versteht.
Java identifiziert in seinem Eingabestrom die folgenden Einheiten:
Schl¨
usselw¨
orter — Ein Schl¨
usselwort ist ein Wort, welches innerhalb der Programmiersprache eine genau festgelegte Bedeutung hat. In Java sind folgende W¨
orter als Schl¨
usselw¨orter reserviert (das brauchen Sie sich nicht
zu merken):
abstract
catch
default
false
goto
int
package
short
this
try
boolean
char
do
final
if
interface
private
static
true
void
break
class
double
finally
implements
long
protected
super
throw
volatile
28
byte
const
else
float
import
native
public
switch
throws
while
case
continue
extends
for
instanceof
new
return
synchronized
transient
Jedes dieser W¨
orter hat f¨
ur Java eine ganz spezielle Bedeutung. Wannimmer eines im Programmtext auftaucht, muß es in genau der vorgesehenen
Art und Weise benutzt werden.
Neben den Schl¨
usselw¨
ortern kennt Java noch einige Symbole, die beispielsweise beim Rechnen mit Zahlen benutzt werden:
=
==
-^
*=
>>=
>
<=
+
%
/=
>>>=
<
>=
<<
&=
!
!=
*
>>
|=
~
&&
/
>>>
^=
?
||
&
+=
%=
:
++
|
-=
<<=
Bezeichner — Bezeichner werden verwendet, um Objekten, Klassen, Attributen, Methoden und anderen Dingen im Java-Programm Namen zu geben.
Namen sind sehr wichtig; beispielsweise k¨onnen Sie ein Objekt nur verwenden, wenn Sie das Objekt unter einem gewissen Namen kennen.
Beispiel: In der Beispielklasse Kreuz.java auf Seite ?? wurden die
Schl¨
usselw¨
orter import, class, public, static, void, public und new
verwendet.
Es wurden die Bezeichner eip.TurtleScreen, eip.Turtle, Kreuz, main,
args, String, TurtleScreen, ts, Turtle, t, pd, fd, bk und rt benutzt.
Bei der Wahl von Bezeichnern d¨
urfen Sie Ihrer Phantasie fast freien Lauf
lassen. Allerdings sollten Sie daf¨
ur sorgen, daß die von Ihnen gew¨ahlten
Bezeichner aussagekr¨
aftig sind und das bezeichnete Objekt gut charakterisieren. Java ist es egal, ob Sie wir die Turtle mit t oder mit ruebe. Einen
menschlichen Leser w¨
urden Sie dadurch aber verwirren.
Ein Bezeichner darf beliebig lang werden, und bei der Wahl eines Bezeichners werden Sie lediglich durch folgende Bedingungen eingeschr¨ankt:
• Ein Bezeichner darf groß- und kleingeschriebene Buchstaben13 , die
Ziffern 0-9 und die Sonderzeichen _ und $ enthalten. Dabei sollten
Sie von der Verwendung des Zeichens $ aber absehen — es ist f¨
ur
vom Compiler automatisch generierte Bezeichner vorgesehen.
• Ein Bezeichner darf nicht mit einer Ziffer anfangen.
• Schl¨
usselwort d¨
urfen Sie nicht als Bezeichner verwenden.
Wichtig zu bemerken ist noch, daß Java großgeschriebene und kleingeschriebene Buchstaben streng unterscheidet — f¨
ur Java sind einBezeichner
und EinBezeichner zwei v¨ollig verschiedene Dinge.
Wenn Sie Bezeichner w¨ahlen, sollten Sie diese Eigenschaft von Java aber
nicht mißbrauchen — ein menschlicher Leser Ihres Programm k¨onnte m¨oglicherweise durcheinanderkommen. Wenn Sie f¨
ur verschiedene Dinge Bezeichner verwenden, die sich nur durch die Groß- Kleinschreibung unterscheiden, wird es einem Menschen sicher schwerfallen, sich zu merken,
wof¨
ur welcher Bezeichner steht. Vermeiden Sie daher die Verwendung von
Bezeichnern, die sich nur durch die Groß- Kleinschreibung unterscheiden.
13 Es ist sogar m¨
oglich, chinesische oder russische Buchstaben zu verwenden — wie das geht,
werden wir Ihnen aber nicht erz¨
ahlen.
29
Beispiel: Hier eine Liste g¨ultiger Bezeichner:
i3 String
MAX VALUE Lupo1234 392
i
mEiNeKlAsSe meineKlasse
mein segel boot
Nun folgt eine Liste ung¨
ultiger Bezeichner:
1haus
Ung¨
ultig: beginnt mit Ziffer.
class
Ung¨
ultig: class ist ein Schl¨
usselwort.
ab-c
Ung¨
ultig, weil ein Minus nicht in einem Bezeichner
enthalten sein darf. Java w¨
urde denken, daß Sie hier
das durch c bezeichnete Objekt vom mit ab bezeichneten Objekt substrahieren wollen.
#bdd
Ung¨
ultig: enth¨
alt ein nicht erlaubtes Sonderzeichen.
ein Objekt
Ung¨
ultig: enth¨
alt ein Leerzeichen.
Und schließlich eine Liste von Bezeichnern, die zwar erlaubt sind, die
Sie aber dennoch nicht verwenden sollten.
Meine$Klasse Das Symbol $ sollten Sie nicht verwenden. Die JavaUmgebung benutzt es n¨
amlich auf eine ganz spezielle
Weise.
Class
Java unterscheidet zwischen Groß- und Kleinschreibung und wird diesen Bezeichner daher nicht mit
dem Schl¨
usselwort class in Verbindung bringen. Ein
menschlicher Leser k¨
onnte dadurch aber verwirrt werden.
Zahlenkonstanten — Nat¨
urlich d¨
urfen in einem Java-Programm auch Zahlen
vorkommen. Java kennt ganze oder reelle Zahlen. Wie Zahlen in einem
Programm genau aussehen d¨
urfen, beschreiben wir erst sp¨ater. Vorerst sei
gesagt, daß man beim Angeben von Zahlen eigentlich nicht allzuviel falsch
machen kann.
Beispiel: Beispiele f¨ur ganze Zahlen sind:
10, -30, +1043, 1334, 0123, 0xFF
Bei den letzten Zahlen des Beispiels ist eine Bemerkung angebracht
— wenn eine mehrstellige Zahl mit einer Null anf¨
angt, faßt Java Sie
als Zahl in oktaler Darstellung auf. Wenn eine Zahl mit den Zeichen
0x beginnt, faßt Java sie als hexadezimale Zahl auf. Die oktale Zahl
0123 entspricht der dezimalen Zahl 83, und die hexadezimale Zahl
0xFF entspricht der dezimalen Zahl 255. Sp¨
ater gehen wir darauf
nochmal ein.
Beispiele f¨
ur reelle Zahlen sind:
3.141, 493., .5, -503, 120e-9, 140.5E120
Sie sehen, daß in Java als Dezimalpunkt tats¨
achlich ein Punkt und
nicht ein Komma verwendet wird. Das e bzw. das E in den letzten
beiden Zahlen steht f¨
ur “Exponent”. Die Zahl 120e − 9 entspricht der
Zahl 120 · 10−9 , und genauso steht die Zahl 140.5E120 f¨
ur die Zahl
120 · 10120 . Ob Sie bei dieser Schreibweise lieber ein kleines e oder
ein großes E verwenden, ist Ihnen u
¨berlassen — beide Schreibweisen
sind erlaubt und haben die gleiche Wirkung.
30
Zeichenketten — Eine Zeichenkette ist ein in doppelten Anf¨
uhrungszeichen
stehender Text. In der Zeichenkette ist jedes Zeichen erlaubt. Es gibt allerdings einige Zeichen, bei denen man sich etwas anstrengen muß, um Sie
in eine Zeichenkette aufzunehmen. Darauf wird sp¨ater noch eingegangen.
Beispiel: Hier ein paar Beispiele f¨ur g¨ultige Zeichenketten:
"Dies ist eine Zeichenkette"
"Sonderzeichen {}!#/-"
"class"
"510"
Die mittlere Zeichenkette enth¨
alt den Text class. Da der Text in einer
Zeichenkette steht, hat er f¨
ur Java nichts mit dem Schl¨
usselwort class
der Sprache Java zu tun.
Die letzte Zeichenkette enth¨
alt den Text 510. Auch hier gilt: Aufgrund der Anf¨
uhrungszeichen interpretiert Java dies nicht als die Zahl
510 sondern als Text.
Schließlich noch ein Beispiel f¨
ur eine ung¨
ultige Zeichenkette:
"Sein Name war "Hans". "
Das erste Anf¨
uhrungszeichen beendet die Zeichenkette. Java liest dies
als Zeichenkette mit Inhalt “Sein Name war“ gefolgt vom Bezeichner
Hans gefolgt von der Zeichenkette “. ”. Auf diese Art kann man keine Zeichenkette erzeugen, die ein Anf¨
uhrungszeichen enth¨
alt. Sp¨
ater
erfahren Sie, wie das geht.
Kommentare — schließlich k¨onnen Sie in ein Java-Programm Kommentare
aufnehmen. Kommentare dienen dazu, den Programmtext zu kommentieren. Dazu geh¨
ort insbesondere die Dokumentation der Klassen, ihrer Attribute und ihrer Methoden. Alles, was in einem Kommentar steht, wird
vom Java-Compiler v¨
ollig ignoriert. Sie k¨onnen in einen Kommentar also
beliebige Texte schreiben.
Java unterscheidet mehrere Arten von Kommentaren. Ein einzeiliger Kommentar wird durch das Zeichen // eingeleitet. Wenn diese Zeichen in einer
Zeile (außerhalb einer Zeichenkette) stehen, ignoriert Java den gesamten
Rest der Zeile. Unser kleines Beispielprogramm von vorhin k¨onnten wir
mit einzeiligen Kommentaren wie folgt dokumentieren:
class HalloWelt {
// gebe den Text "Hallo, Welt" aus.
// diese Methode wird beim ausf¨
uhren der Klasse ausgef¨
uhrt:
public static void main( String[] args )
{
System.out.println("Hallo, Welt //");
}
}
Zu Demonstrationszwecken haben wir auch in die Zeichenkette "Hallo, Welt"
das Zeichen // eingef¨
ugt. An der Stelle bewirkt es keinen Kommentar, da
es sich innerhalb einer Zeichenkette befindet.
31
Ein mehrzeiliger Kommentar wird durch /* begonnen und endet mit */.
Alles zwischen /* und */ wird vom Compiler ignoriert. Unser Beispiel
k¨
onnten wir auch so dokumentieren:
/*
* Die Klasse HalloWelt gibt auf dem Bildschirm den Text
* "Hallo, Welt" aus.
*/
class HalloWelt {
/***********************************************************
* diese Methode wird beim ausf¨
uhren der Klasse ausgef¨
uhrt:
*/
public static void main( String[] args )
{
System.out.println("Hallo, Welt //");
}
}
Schließlich noch ein Beispiel f¨
ur einen ung¨
ultigen Kommentar:
/* Ein Kommentar /* in einem Kommentar */ ist nicht erlaubt */
Es ist nicht m¨
oglich, einen durch /*...*/ begrenzten Kommentar zu
schreiben, in welchem ein weiterer solcher Kommentar enthalten ist. Der
Compiler w¨
urde hier beim ersten */ denken, der Kommentar sei beendet.
Es ist jedoch m¨
oglich, das //-Zeichen innerhalb eines solchen Kommentars
zu verwenden:
/* Ein einzeiliger Kommentar // in einem Kommentar ist erlaubt. */
Sie sollten Kommentare verwenden, um Ihr Programm verst¨andlicher zu
machen. Zur Verst¨
andlichkeit eines Programms geh¨ort aber nicht nur die
Formulierung hilfreicher Kommentare sondern auch die Verwendung aussagekr¨
aftiger Bezeichner und das Einhalten einer gut lesbaren Programmstruktur. Wenn Sie aussagekr¨aftige Bezeichner verwenden, k¨onnen Sie sich
so manchen Kommentar sparen.
5.2
Zusammenfassung
Nachdem Sie dies Kapitel gelesen haben, sollten Sie folgende Fragen beantworten
k¨
onnen:
B Welchen Zweck hat die Zeilenstruktur eines Programms ?
B Wozu dient ein Semikolon in Java ?
B Geben Sie Beispiele f¨
ur Bezeichner, die zwar erlaubt sind, aber dennoch nicht benutzt werden sollten !
B Was ist eine Zeichenkette ?
32
6
Variablen und Datentypen
Die Programme, die wir bisher gesehen haben, sind ziemlich langweilig — sie
arbeiten eine vorgegebene Folge von Befehlen ab und sind u
¨berhaupt nicht flexibel. F¨
ur flexible Programme, die bei jedem Aufruf etwas anderes tun, ben¨otigt
man Variablen“, etwas das bei jedem Programmablauf einen anderen Wert
”
haben kann.
6.1
Variablen und ihr Typ
Wir verwenden Variablen, wenn wir Objekten einen Namen geben wollen, oder
wenn wir uns etwas merken m¨ochten. Variablen sind Platzhalter f¨
ur Zahlen,
Zeichenketten, Turtles oder sonstirgendwelche Dinge. Auf der Hardwareebene
entspricht jeder Variable ein gewisser Platz im Hauptspeicher des Computers.
Jede Variable hat einen Namen, und dieser Name muß den Vorschriften f¨
ur
Bezeichner gen¨
ugen.
Beispiel: Angenommen, wir wollen in einem Programm einen Bruttopreis berechnen. Wenn wird dies f¨
ur einen Preis von 10, − DM tun wollen,
k¨
onnten wir den Bruttopreis als
15% · 10, − + 10, −
berechnen.
Der Nachteil der obigen Formel ist, daß sie nur f¨
ur einen Nettopreis von
10,- DM korrekt ist. Wenn wir in unserem Programm auch mit anderen
Preisen als 10,- DM rechnen wollen, w¨
urden wir daher lieber einen Platzhalter — eine Variable — f¨
ur den Nettopreis verwenden. M¨
oglicherweise
w¨
urden wir schreiben:
bruttopreis = 0.15 * nettopreis + nettopreis
Diese Formel w¨
are f¨
ur jeden Preis anwendbar. Dazu m¨
usste der Platzhalter
nettopreis den jeweils zu verwendenden Nettopreis enthalten. Das Ergebnis w¨
urden wir dann in einem anderen Platzhalter namens bruttopreis
speichern.
Wir k¨
onnen Variablen u
urden.
¨berall dort einsetzen, wo wir Werte hinschreiben w¨
Nat¨
urlich m¨
ussen wir dabei ein bißchen aufpassen — es w¨are sicher nicht sinnvoll, einen Platzhalter, der eine reelle Zahl enth¨alt, an einer Stelle zu verwenden,
an der nur Zeichenketten hinpassen.
Damit man nicht ausversehen eine Variable an einer Stelle verwendet, wo sie
keinen Sinn macht, hat in Java jede Variable einen Typ.
Beispiel: Beispielsweise gibt es in Java den Datentyp int. Variablen
dieses Typs k¨
onnen ganze Zahlen speichern.
Variablen des Datentyps double Typs k¨
onnen reelle Zahlen speichern.
Wenn Sie eine Variable des Typs double an einer Stelle verwenden, wo
¨
man nur ganze Zahlen verwenden darf, zeigt der Compiler bei der Ubersetzung ihres Programms einen Fehler an.
33
Bevor eine Variable in Java das erste Mal verwendet wird, muß man Java daher
Informationen dar¨
uber zukommen lassen, welchen Typ diese Variable haben soll.
In Java geschieht dies, indem man eine Anweisung des Aufbaus
TYP variablenname;
verwendet. Sobald Java eine solche Anweisung gefunden wird, merkt sich Java:
Aha, hier ist jetzt eine Variable namens variablenname definiert worden, und
diese Variable darf nur Werte des Typs TYP aufnehmen.
Beispiel:
DemoVars.javaa
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/KapVariablen/DemoVars.java
In Zeile 6 wird eine Variable namens zaehler als int definiert. Von dieser
Zeile an weiß Java, daß der Bezeichner zaehler f¨
ur eine Variable steht
und nur ganze Zahlen speichern kann.
In Zeile 7 wird der Variable zaehler der Wert 10 zugewiesen. Vorher hatte
zaehler keinen definierten Wert.
In Zeile 8 wird eine weitere Variable namens inversZaehler definiert, die
reelle Zahlen speichern kann. Außerdem wird dieser Variable gleich der
inverse Wert von zaehler zugewiesen.
In Zeile 9 und 10 werden beide Variablen ausgegeben.
6.1.1
Ganze Zahlen
Java kennt mehrere ganzzahlige und mehrere reellwertige Datentypen. Der Grund
hierf¨
ur ist folgender: Jede Variable belegt einen gewisse Anzahl von Bytes im
Speicher des Computers. Aus Kapitel 2 wissen Sie, daß man mit n Bytes nur
28·n verschiedene Werte darstellen kann.
Um den Typ einer Variable festzulegen, muß man sich darum vorher Gedanken
machen, wieviele verschiedene Werte sie annehmen k¨onnen soll und davon ausgehend, wieviele Bytes man im Computerspeicher daf¨
ur verwenden m¨ochte. Da
man normalerweise nicht mehr Speicherplatz als n¨otig verschwenden will, bietet Java ganzzahlige und reellwerige Datentypen verschiedener L¨angen an (die
L¨
ange des Datentyps gibt an, wieviele Bytes seine Variablen belegen).
Java kennt die ganzzahligen Datentypen byte, short, int und long. Am h¨aufigsten verwendet man den Datentyp int.
Typ
byte
short
int
long
L¨ange
1
2
4
8
Wertebereich
−27 , . . . , 27 − 1
−215 , . . . , 215 − 1
−231 , . . . , 231 − 1
−263 , . . . , 263 − 1
Das folgende Programm demonstriert die Verwendung von ganzen Zahlen:
34
GanzZahl.javaa
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/KapVariablen/GanzZahl.java
Was passiert, wenn eine Variable einen zu großen oder kleinen Wert enth¨alt ?
Um das zu demonstrieren, haben wir ein Programm geschrieben, das eine Zahl
vom Benutzer einliest und anschließend ausgibt:
GanzZahlInput.javaa
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/KapVariablen/GanzZahlInput.java
Wenn Sie das Programm kompilieren und starten, werden Sie aufgefordert, eine
Zahl einzugeben. Die eingegebene Zahl wird dann der int–Variable i zugewiesen.
Probieren Sie es bitte selbst aus ! Wenn Sie die Zahl 231 − 1 eingeben (das
ist die Zahl 2147483647), gibt das Programm sie korrekt wieder aus. Wenn Sie
jedoch gr¨
oßere Zahlen eingeben, kommt es zu komischen Resultaten: statt der
von Ihnen eingegebenen Zahl werden negative Zahlen ausgegeben. Zum Beispiel
macht das Programm bei mir aus der Zahl 123456789012 die Zahl −1097262572.
Diesen Effekt nennt man Overflow — eine Zahl l¨auft u
¨ber, wenn man ihr
einen zu großen oder einen zu kleinen Wert zuweist. Der Wert, den die Zahl
dann enth¨
alt, ist nicht brauchbar.
Sie m¨
ussen also immer darauf achten, daß der ausgew¨ahlte Datentyp zu den
Daten passt, die Sie verarbeiten wollen.
6.1.2
Fließkommazahlen
Java kennt die beiden reellwertige Datentypen float und double. float–Zahlen
belegen 4 Byte und double–Zahlen belegen 8 Byte im Speicher.
Sie sollten vorerst immer double–Variablen verwenden, da hier nicht nur der
Wertebereich sondern auch die Anzahl der gef¨
uhrten Nachkommastellen gr¨oßer
ist als f¨
ur float–Variablen.
Das folgende Programm demonstriert die Verwendung von reellen Zahlen:
ReellZahl.javaa
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/KapVariablen/ReellZahl.java
6.1.3
Logischer Datentyp
Der Datentyp boolean repr¨
asentiert logische Werte. Ein logischer Wert ist entweder wahr (true) oder unwahr (false).
boolean–Variablen k¨
onnen benutzt werden, um die Resultate von Vergleichen zu
35
speichern. Sie k¨
onnen auch benutzt werden, wenn eine Variable nur zwei Werte
annehmen k¨
onnen soll.
Das folgende Beispiel demonstriert die Verwendung von boolean:
DemoBoolean.javaa
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/KapVariablen/DemoBoolean.java
6.1.4
Zeichen und Zeichenketten
Einzelne Zeichen sind in Java vom Typ char. Zeichenketten sind vom Typ
String.
Um ein Zeichen in einem Java–Programm anzugeben, muß man es in einfache Anf¨
uhrungszeichen schreiben. Eine Zeichenkette schreibt man in doppelte
Anf¨
uhrungszeichen.
Wenn man zwei Zeichenketten mit + verkn¨
upft, so entsteht eine Zeichenkette,
die den hintereinander geh¨
angten Text beider Zeichenketten enth¨alt.
Beispiel:
DemoString.javaa
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/KapVariablen/DemoString.java
Auf Zeile 4 wird eine char–Variable namens b definiert, welcher das Zeichen z“ zugewiesen wird. Der Inhalt der Variable hat mit dem Namen
”
der Variable nichts zu tun.
In Zeile 8 wird die Variable name als String definiert. Ihr Inhalt entsteht
durch Hintereinanderh¨
angen anderer Variablen.
In Zeile 9 wird die Variable sequenz als String definiert. Wir bilden den
Inhalt, indem wir hinter den leeren String "" mit Hilfe des +–Operators
die drei Zeichenvariablen anf¨
ugen. Der Leerstring wird hier dazu benutzt,
damit + u
angen von Zeichenketten in¨berhaupt als das Hintereinanderh¨
terpretiert wird.
36
7
Ausdru
¨ cke
In diesem Kapitel geben wir Ihnen erg¨anzende Informationen zu Goto Java 2,
Kapitel 5, die Sie erg¨
anzend zu Hausaufgabe 3 lesen sollten.
7.1
Was ist ein Ausdruck ?
Was ein Ausdruck ist, erkl¨
aren wir Ihnen am einfachsten an einem Beispiel.
Schauen Sie sich das folgende Beispielprogramm an:
01 public class JavaDemo {
02 public static void main( String[] args ) {
03
int
i = 5;
04
float f = 5.0f;
05
System.out.println( 5 );
06
System.out.println("5");
07
System.out.println( i );
08
System.out.println( f );
9
System.out.println( 5 * (i - 3 * f + 1.3 ) );
10 }
11 }
Das Programm gibt einige Werte auf den Bildschirm aus, wobei wir wieder die
bereits bekannte Methode System.out.println verwenden. Auf den ersten Blick
scheint dies ein ganz triviales Programm zu sein.
Aber beachten Sie, auf wie viele unterschiedliche Arten wir die Ausgaberoutine
verwenden: In Zeile 5 geben wir eine Zahl aus. In Zeile 6 geben wir einen String
aus. In Zeile 7 wird der Inhalt einer int–Variable ausgegeben, w¨ahrend in Zeile 8
der Inhalt einer float–Variable ausgegeben wird. Schließlich wird in Zeile 9 noch
vor der Ausgabe eine Rechnung durchgef¨
uhrt und erst dann der berechnete Wert
ausgegeben.
All diese unterschiedlichen Daten m¨
ussen von Java auch unterschiedlich behandelt werden — mit Strings muß ein Computer ganz anders umgehen als mit
Zahlen, und mit ganzen Zahlen vom Typ int muß der Computer ganz anders
umgehen als mit reellen Zahlen vom Typ float. Auch muß die Rechnung in Zeile
09 erst vom Computer durchgef¨
uhrt werden, bevor das Ergebnis ausgegeben
werden kann.
¨
Ahnlich
wie bei im obigen Beispiel bei der Verwendung von System.out.println
kann in einem Java–Programm an jeder Stelle, wo irgendein Wert stehen kann,
auch eine Rechnung oder eine Variable oder sonstetwas stehen, das man auswerten kann. Dabei darf die Auswertung nur Werte der jeweils erlaubten Datentypen ergeben.
Definition: Ausdruck
Ein Ausdruck ist etwas, das man auswerten kann. Die Auswertung
eines Ausdruckes ergibt einen Wert. Der Datentyp dieses Wertes
ergibt sich aus dem Aufbau des Ausdrucks. An jeder Stelle, an der ein
Wert eines gewissen Datentyps stehen darf, darf auch ein Ausdruck
dieses Datentyps stehen.
37
Ausdruck
Die System.out.println–Methode darf man auf all die durch uns demonstrierten
Arten verwenden, da sie sowohl Ausdr¨
ucke vom Typ int als auch Ausdr¨
ucke vom
Typ float als auch Ausdr¨
ucke vom Datentyp String verarbeiten kann. Sie kann
auch Ausdr¨
ucke aller anderen Datentypen verarbeiten und ist damit ziemlich
einzigartig.
Beispiel: Hier ein paar Beispiele f¨ur Ausdr¨ucke. Es seien folgende Variablen definiert:
int i = 5;
float f = 5;
String a = "Hallo";
2
Die Zahl 2 ist ein Zahlenliteral. Jedes Literal ist ein Ausdruck,
dessen Datentyp gleich dem Datentyp des Literals ist. Das ganzzahlige Literal 2 ist somit ein Ausdruck mit Wert 2 und Datentyp
int
“5”
Das String–Literal "5" ist ein Ausdruck mit Wert "5" und Datentyp String
i
Auch jede Variable ist ein Ausdruck, deren Wert sich aus dem
Inhalt der Variable ergibt. Mit den obigen Definitionen ist dies
ein Ausdruck vom Typ int mit Wert 5.
f
Die Variable f ist hier ein Ausdruck vom Typ float mit Wert 5.0.
i+2
In Ausdr¨
ucken k¨
onnen Rechnungen vorkommen . Der Wert einer
Rechnung ergibt sich nat¨
urlich als Resultat der Rechnung. Dabei sind die Operanden einer Rechnung selbst wieder Ausdr¨
ucke.
Der Wert der Rechnung i+2 ergibt sich durch Auswerten des
Ausdrucks links vom +–Zeichen (int mit Wert 5), durch Auswerten des Teils rechts vom +–Zeichen (int mit Wert 2) und durch
Addition der so ermittelten Werte.
Der Datentyp einer Rechnung ergibt sich aus den an der Rechnung beteiligten Datentypen (dazu sagen wir im Kapitel u
¨ber
Typwandlungen mehr). Dieser Ausdruck w¨
urde zum Wert 7 ausgewertet und h¨
atte den Typ int, da in der Rechnung nur Ausdr¨
ucke vom Typ int vorkommen.
a + “ Welt” Auch dies ist ein Ausdruck. Der String a und der String
" Welt" werden zusammengeh¨
angt. Ergebnis ist ein Ausdruck
vom Typ String mit Inhalt "Hallo Welt".
i=5
Auch die Zuweisung eines Wertes an eine Variable ist ein Ausdruck. Zwar ist der eigentliche Zweck der Zuweisung, der Variable
auf der linken Seite des =–Zeichen Variable den Wert des Ausdrucks auf der rechten Seite zuzuweisen. Dar¨
uberhinaus ist die
Zuweisung in Java aber auch ein Ausdruck, dessen Auswertung
den Wert auf der rechten Seite des =–Zeichens ergibt (dazu gleich
mehr).
Beispielsweise gibt das folgende Java–Programm den Wert 5 auf
den Bildschirm aus und weist der Variable i den Wert 5 zu:
public class DemoAssign {
public static void main( String[] args ) {
int i = 0;
System.out.println( i = 5 );
38
System.out.println( i );
}
}
7.2
Literal–Ausdru
¨ cke
Die wohl einfachsten Ausdr¨
ucke sind Literale. Ein Literal ist eine konkrete Zahl
oder ein konkreter String, der in einem Programmtext auftaucht. Jedes Literal
hat einen eindeutig bestimmten Datentyp.
Die n¨
achsten Seiten sollen Ihnen eine gewisse Vorstellung davon vermitteln,
wie Literale geschrieben werden und welchen Datentyp Java welchen Literalen
zuweist.
7.2.1
Ganzzahlige Literale
Es gibt drei Typen von ganzzahligen Literalen: dezimale, oktale und hexadezimale. Sie werden zwar in dieser Veranstaltung weder oktale noch hexadezimale
Literale verwenden, es ist aber wichtig, daß Sie wissen, wie man diese Literale
schreibt. Es ist n¨
amlich sehr leicht m¨oglich, eine Zahl ausversehen als oktales
Literal zu schreiben. F¨
ur diese Zahl k¨onnte sich dann ein unerwarteter Wert
ergeben.
Ein dezimales Literal ist eine Zahl in u
¨blicher dezimaler Darstellung. Ein dezimales Literal darf mit einem Vorzeichen (+ oder −) beginnen und die Ziffern 0
bis 9 enthalten. Der Wert eines dezimalen Literals ist der Wert der angegebenen
Zahl.
Dezimales
Literal
Beispiel: Die folgenden Werte sind dezimale Literale: 1, 2, 3, +4, -5,
+100, -984235, +239048
Eine ganze Zahl wird in einem Java–Programmtext immer dann als oktales
Literal interpretiert, wenn nach dem optionalen Vorzeichen eine f¨
uhrende Null
steht. Nach der f¨
uhrenden Null darf ein oktales Literal nur die Ziffern 0 bis 7
enthalten. Der Wert eines oktalen Literals ergibt sich, indem die oktale Zahl
umgerechnet wird.
oktales Literal
Beispiel: Im oktalen Zahlensystem gibt es nicht wie im dezimalen Zahlensystem 10 sondern nur 8 Ziffern. Wenn man im oktalen Zahlensystem
von 0 bis 16 z¨
ahlt, sieht das so aus: 00, 01, 02, 03, 04, 05, 06, 07, 010, 011,
012, 013, 014, 015, 016, 017, 020
Die oktale Zahl 010 entspricht also der dezimalen Zahl 8. Die oktale Zahl
0123 entspricht der dezimalen Zahl 83.
Weitere Beispiele f¨
ur oktale Zahlen sind: -033, +071, 01237
Da in einer oktalen Zahl keine Ziffern außer 0 bis 7 vorkommen d¨
urfen,
ergibt die folgende Zeile einen Fehler beim kompilieren:
int i = 0129; // Fehler
Wenn eine ganze Zahl nach dem optionalen Vorzeichen mit dem Text “0x” oder
“0X” beginnt, wird sie von Java als hexadezimales Literal interpretiert. Ein
hexadezimales Literal darf die Ziffern 0–9 und die Buchstaben A bis F enthalten
39
hexadez. Literal
(die Buchstaben d¨
urfen dabei klein oder groß geschrieben werden). Der Wert
eines hexadezimalen Literals ergibt sich durch Umrechnung der hexadezimalen
Zahl.
Beispiel: Im hexadezimalen Zahlensystem gibt es nicht wie im dezimalen Zahlensystem 10 sondern ganze 16 Ziffern (die Ziffern 0-9 und die
Buchstaben A-F). Wenn man im hexadezimalen Zahlensystem von 0 bis
18 z¨
ahlt, sieht das so aus: 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9,
0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x10, 0x11, 0x12
Die hexadezimale Zahl 0xB entspricht also der dezimalen Zahl 11. Die
hexadezimale 0xff entspricht der dezimalen Zahl 255. Die hexadizimale
Zahl 0x100 entspricht der dezimalen Zahl 256.
Weitere Beispiele f¨
ur hexadezimale Zahlen: 0xaf, 0X1AfAF, -0xff, -0xfF
Ein ganzzahliges Literales ist immer vom Typ int, es sei denn, man h¨angt den
Buchstaben L an. In diesem Fall erh¨alt es den Datentyp long.
Es stellt sich nat¨
urlich die Frage, warum man u
¨berhaupt ganzzahlige Literale
vom Typ int und Typ long unterscheidet. Der Datentyp long umfaßt doch den
Datentyp int, und daher sollte doch eigentlich der Datentyp long ausreichen,
um alle ganzen Zahlen zu repr¨asentieren.
Der Grund, warum es dennoch den Datentyp int gibt, wird im n¨achsten Kapitel
u
¨ber Typkonvertierungen klar werden. Hier sei nur gesagt, daß Sie dort erfahren werden, daß es zwar gestattet ist, einer long–Variable einen Ausdruck vom
Typ int zuzuweisen, daß es jedoch nicht gestattet ist, einer int–Variable einen
Ausdruck des Typs long zuzuweisen. Das folgende Programmfragment enth¨alt
daher auf Zeile 3 und 4 Fehler:
int
long
int
int
i
m
j
k
=
=
=
=
5;
i;
5L;
m;
// Fehler !
// Fehler !
W¨
urde Java nun auch die Zahlen, hinter denen kein “L” steht, als long–Literale
interpretieren, so w¨
are auch die erste Zeile des obigen Programmfragmentes
ung¨
ultig, und um dies zu vermeiden, gibt es f¨
ur ganzzahlige Literale diese Unterscheidung in die zwei Datentypen int und long.
Beispiel: Beispiele f¨ur Literale:
123
Dezimales Literal, Wert 123, Typ int
123L Dezimales Literal, Wert 123, Typ long
0123 Oktales Literal, Wert 83, Typ int
0123L Oktales Literal, Wert 83, Typ long
0xFF Hexadezimales Literal, Wert 255, Typ int
7.2.2
Reellwertige Literale
Alle in einem Java–Programm auftretenden Zahlen mit Dezimalpunkt oder Zehnerexponenten sind rellwertige Literale.
40
Datentyp
ganzzahliger
Literale
Ein reellwertiges Literal besteht aus dem ganzzahligen Teil vor dem Dezimalpunkt, dem Nachkommateil und einem optionalen Zehnerexponenten. Der Zehnerexponent wird durch den Buchstaben “E” oder “e” eingeleitet. Der Exponent
darf negativ sein.
Beachten Sie bitte, daß in Java der Dezimalpunkt nicht wie in Deutschland
u
¨blich ein Komma ist, sondern ein Punkt ist.
Reelle Zahlen haben in Java normalerweise den Datentyp double. Wenn Sie
hinter die Zahl den Buchstaben f oder F anh¨angen, erh¨alt das Literal den Typ
float. Optional d¨
urfen Sie auch ein d oder D anh¨angen, aber auch dann erh¨alt
die Zahl den Datentyp double.
Beispiel: Beispiele f¨ur reellwertige Literale
1.
Die Zahl 1.0, Datentyp double
1.f
Die Zahl 1.0, Datentyp float
.1D
Die Zahl 0.1, Datentyp double
1.0E0
Die Zahl 1.0, Datentyp double
1e5
Die Zahl 1 · 105 , Datentyp double
1e5f
Die Zahl 1 · 105 , Datentyp float
+3.14159 Die Zahl +3.14159, Datentyp double
4.10E-10 Die Zahl 4.10 · 10−10 , Datentyp double
4.10E-10f Die Zahl 4.10 · 10−10 , Datentyp float
Beachten Sie bitte, daß Sie an einer Stelle im Programmtext, wo ein Ausdruck
vom Typ double erwartet wird, zwar einen Ausdruck vom Typ float verwenden
d¨
urfen. Sie d¨
urfen jedoch an einer Stelle, wo ein Ausdruck vom Typ float erwartet wird, keinen Ausdruck vom Typ double verwenden. Warum das so ist,
erkl¨
aren wir im n¨
achsten Kapitel u
¨ber Typwandlungen.
Beispiel: Das folgende Programmfragment enth¨alt daher auf Zeilen 3
und 4 Fehler.
double
double
float
float
float
7.2.3
d1
d2
f1
f2
f3
=
=
=
=
=
3.14153; //
2.1f;
//
d1;
//
3.14153; //
3.14153f;//
erlaubt:
erlaubt:
Fehler:
Fehler:
erlaubt:
Zuweisung
Zuweisung
Zuweisung
Zuweisung
Zuweisung
double an double
float an double
double an float
double an float
float an float
Litarale vom Typ boolean
Es gibt zwei boolsche Literale, n¨amlich true und false. Beide haben den Datentyp boolean.
Beispiel: Ein Beispiel zu Ihrer Verwendung:
public class DemoBoole {
public static void main( String[] args ) {
boolean b1 = true;
boolean b2 = false;
}
}
41
Das Literal true steht f¨
ur logisch wahre Werte und das Literal false steht f¨
ur
logisch falsche Werte.
Beispiel: Hier ein paar Beispiele f¨ur Ausdrucke, die in der Auswertung
true oder false ergeben:
1
2
2
2
7.2.4
<
<
<=
>
2
1
2
2
//
//
//
//
Ausdruck
Ausdruck
Ausdruck
Ausdruck
vom
vom
vom
vom
Typ
Typ
Typ
Typ
boolean,
boolean,
boolean,
boolean,
Wert
Wert
Wert
Wert
:
:
:
:
true
false
true
false
Zeichenliterale
Zeichenliterale bestehen aus einem einzelnen Zeichen in einfachen Hochkommata. Der Datentyp eines Zeichenliterals ist char, und der Wert des Zeichenliterals
ist eine Zahl zwischen 0 und 65535, welche sich aus dem Unicode–Zeichensatz
ergibt.
Beispiel: Beispiele f¨ur char–Literale sind ’a’, ’B’, ’#’, ’+’, ’1’.
Folgende Texte sind keine g¨
ultigen Zeichenliterale, da sie nicht genau ein
Zeichen enthalten: ’’, ’aa’, ’12’.
Unicode
Unicode ist ein Zeichensatz, in dem jedes Zeichen durch eine 16–Bit–Zahl dargestellt wird. Es k¨
onnen also maximal 65536 verschiedene Zeichen dargestellt
werden k¨
onnen. Jedem Zeichen wird dabei eine eindeutige Zahl zwischen 0 und
65535 zugeordnet. Zum Beispiel ist dem Buchstaben “a” die Zahl 97, dem Buchstaben “A” die Zahl 65 und der Ziffer “1” die Zahl 49 zugeordnet. Zeichen im
Unicode–Zeichensatz sind Buchstaben, Zahlen und alle anderen Sonderzeichen,
die der Computer darstellen kann sowie einige Zeichen mit besonderer Bedeutung wie Tabulator, Zeilenumbruch oder R¨
uckschritt (Backspace). In sind Unicode auch Buchstaben anderer L¨ander (etwa japanische Schriftzeichen) oder
sonstige normalerweise nicht verwendete Zeichen vorgesehen.
Beispiel: Folgendes Programm gibt die Zeichen ’a’, ’B’ und ’#’ und die
diesen zugeordneten Zahlenwerte aus.
Um die Unicode–Zahlenwerte der Zeichen auszugeben, ist es n¨
otig, die Zeichen in einen anderen Datentyp als char umzuwandeln (hier int). Die Ausgaberoutine System.out.println gibt n¨
amlich Werte des Datentyps char
nicht als Zahl sondern als Zeichen aus.
public class DemoChar {
public static void main( String[] args ) {
char a = ’a’; // ’a’ ist ein char-Literal mit Wert 97
char b = ’B’; // ’b’ ist ein char-Literal mit Wert 66
char x = ’#’; // ’#’ ist ein char-Literal mit Wert 35
int ia = a;
// Umwandlung des Datentyps
int ib = b;
int ix = x;
System.out.println( a ); // Ausgabe des Zeichens
System.out.println( ia );// Ausgabe seines Zahlenwertes
System.out.println( b );
42
System.out.println( ib );
System.out.println( x );
System.out.println( ix );
}
}
Es gibt einige besondere Zeichen, die man mit der Tastatur nicht so einfach angeben kann. Dazu geh¨
ort beispielsweise der Zeilenumbruch. Diese Sonderzeichen
kann man eingeben, indem man eine sogenannte Escape–Sequenz verwendet. Eine Escape–Sequenz wird durch das Zeichen \, den Backslash, eingeleitet. Immer
wenn ein Zeichenliteral mit einem Backslash beginnt, werden die auf den Backslash folgenden Zeichen interpretiert, um den Wert des Zeichens zu ermitteln.
Welche Escape–Sequenzen es gibt, ist in Go To Java 2 in Tabelle 4.2 beschrieben. Den Backslash selbst ist durch ’\\’ gegeben.
Beispiel: Die folgenden Ausdr¨ucke sind Zeichenliterale. Obwohl wir zur
Angabe des Zeichens mehr als ein Zeichen ben¨
otigen, wird das Literal
nur als ein Zeichen interpretiert: ’\n’ (Zeilenumbruch), ’\t’ (Tabulator),
’\\’ (der Backslash \), ’\u0020’ (Leerzeichen).
Beispiel: Das folgende Programm verwendet Escape–Sequenzen, um das
Zeichen “a”, zwei Leerzeilen (eine Leerzeile entsteht durch das Zeichen
’\n’ und eine entsteht dadurch, daß die Ausgaberoutine selbst immer
noch einen Zeilenumbruch einf¨
ugt) und danach den Backslash darzustellen.
public class DemoEscape {
public static void main( String[] args ) {
System.out.println(’a’);
System.out.println(’\n’); // zwei Leerzeilen
System.out.println(’\\’); // Ausgabe des Backslash
}
}
7.2.5
Zeichenketten
Als letzte Art von Literalen sind sind schließlich noch die Zeichenketten zu
erw¨
ahnen. Eine Zeichenkette ist ein Text in doppelten Hochkommata. Eine Zeichenkette hat den Datentyp String, und ihr Wert ist die Zeichenfolge zwischen
den doppelten Hochkommata.
Zwischen den doppelten Hochkommata ist jedes Zeichen erlaubt, das auch in
einem Zeichenliteral verwendet werden k¨onnte. Insbesondere k¨onnen in einer
Zeichenkette auch die Escape–Sequenzen verwendet werden, die bei Zeichenliteralen eingesetzt werden konnten.
Zeichenketten unterscheiden sich deutlich von allen anderen bisher behandelten
Literalen. Alle Literale waren bisher einfache Zahlen. Eine Zeichenkette ist aber
eine Folge von Zahlen.
Beispiel: Einige Beispiele zur Verwendung von Zeichenketten:
public class DemoString1 {
43
public static void main( String[] args ) {
String hacker = "Neo";
System.out.println( hacker );
System.out.println("Ein Backslash: \\");
System.out.println("Neue\nZeile\nund");
System.out.println("Ein Anf¨
uhrungszeichen: \"");
}
}
Eine Zeichenkette muß in der Zeile beendet werden, in der sie begonnen wurde. Will man eine Zeichenkette konstruieren, die l¨anger ist, so kann den +–
Operator verwenden. Immer wenn zwischen zwei Zeichenketten ein + steht,
entsteht durch Aneinanderh¨
angen der beiden urspr¨
unglichen Zeichenketten eine
l¨
angere Zeichenkette.
Beispiel: So kann man eine l¨angere Zeichenkette bauen:
public class DemoString2 {
public static void main( String[] args ) {
System.out.println("Hallo, " + "Welt");
System.out.println( "Um eine l¨
angere Zeichenkette" +
" zu bauen,\n kann man den +-Operator" +
" benutzen. Man darf Zeichenketten \n" +
" fast beliebig lang machen.");
}
}
Beispiel: Dies Programm enth¨alt einen Fehler, da eine Zeichenkette nicht
in der Zeile beendet wird, in der sie begonnen wurde:
public class DemoString3 {
public static void main( String[] args ) {
System.out.println("Hallo,
Welt");
// Fehler !
}
}
7.3
Verwendung von Variablen
Nachdem Sie nun die allereinfachste Art von Ausdr¨
ucken – n¨amlich die Literale –
kennengelernt haben, soll die n¨achsteinfache Klasse von Ausdr¨
ucken besprochen
werden, n¨
amlich die Variablen.
Sie erinnern sich — eine Variable ist ein Platzhalter f¨
ur irgendeinen Wert. Wenn
eine Variable in einem Ausdruck auftaucht, wird bei der Auswertung des Ausdrucks f¨
ur die Variable ihr Inhalt eingesetzt. Selbstredend ist der Datentyp eines
Variablen–Ausdrucks der Datentyp der Variable.
Beispiel: Im folgenden Programm wird in Zeile 03 eine Variable definiert,
und ihr wird der Wert 1 zugewiesen. In Zeile 04 wird die Ausgaberoutine
System.out.println aufgerufen.
Als auszugebender Wert wird ihr der Ausdruck i + 1 u
¨bergeben. Dieser
Ausdruck hat den Datentyp int, da hier eine int-Variable zu einem int–
Literal addiert wird. Der Wert des Ausdrucks ist der Wert der Variable i
(also 1) plus dem Wert des Integerliterals “1”.
44
01 public class DemoVar1 {
02
public static void main( String[] args ) {
03
int i = 1;
04
System.out.println( i + 1 );
05
}
06 }
Initialisierung
Der erstmaligen Wertzuweisung einer Variable nach ihrer Definition kommt eine
besondere Bedeutung zu, und gibt es daf¨
ur einen besonderen Namen: Initialisierung. Die Bedeutung der Initialisierung liegt darin, daß eine Variable bis
zur Initialisierung einen unbestimmten Wert enth¨alt.
Nat¨
urlich enth¨
alt eine Variable von dem Moment an, in dem sie definiert wurde,
immer irgendeinen Wert. Mit einer Variable ist ja immer ein gewisser Teil des
Arbeitsspeichers assoziiert, und der Wert der Variablen ergibt aus diesem Teil
des Arbeitsspeichers. Man hat aber keinerlei Kontrolle dar¨
uber, welchen Wert
der Arbeitsspeicher einer neu definierten Variable enth¨alt. Es geht somit nicht
aus dem Programmtext hervor, welchen Wert eine Variable bei ihrer Definition
erh¨
alt, und daher nennt man Variablen, denen noch kein Wert zugewiesen wurde,
nicht initialisiert oder sagt, sie h¨atten einen unbestimmten Wert.
Da man einer Variable nur dann einen Wert zuweisen darf, wenn dem Compiler der Datentyp der Variable klar ist, darf man eine Variable nur nach oder
w¨
ahrend ihrer Definition initialisieren.
Beispiel: Im folgenden Beispiel wird eine Variable als int definiert. In
der n¨
achsten Zeile wird sie zu 2 initialisiert:
int einInt;
einInt = 2;
Man kann Definition und Initialisierung auch mit einem Befehl erledigen:
int einInt = 2;
Hingegen ist die folgende Anweisungen kein g¨
ultiger Java–Code, da eine
Variable nicht vor ihrer Definition initialisiert werden darf:
{
einInt = 2;
int einInt;
}
Da eine Variable vor ihrer Initialisierung einen undefinierten Wert enth¨alt und
da die Verwendung undefinierter Werte normalerweise ein Fehler ist, kontrolliert
der Java–Compiler bei jeder Verwendung einer Variable, ob sie bereits initialisiert sein kann. Falls der Compiler die Verwendung einer nichtinitialisierten
Variable erkennt, meldet er einen Fehler und sch¨
utzt so den Programmierer vor
der Verwendung undefinierter Werten.
Beispiel: Der Compiler w¨urde im folgenden Programm erkennen, daß bei
der Initialisierung der Variable ergebnis die uninitialisierte Variable wert
verwendet wird. Daher w¨
urde er das folgende Programm nicht akzeptieren:
45
public static void main(String[] args)
{
int wert;
int ergebnis = wert * 3; // Fehler
}
In Go To Java, Kapitel 5.1 ist das genauer ausgef¨
uhrt.
7.4
Ausdru
¨ cke mit Operatoren
Neben der Verwendung von Literalen und Variablen gibt es in einem Java–
Programm auch komplexer aufgebaute Ausdr¨
ucke — man kann mit Variablen
oder Literalen rechnen, Variablen oder Literale miteinander vergleichen. Da sich
bei einer solchen Rechnung oder einem Vergleich wieder irgendein Wert ergibt,
sind auch Rechnungen und Vergleiche Ausdr¨
ucke. Auch Zuweisungen an Variablen sind Ausdr¨
ucke, da in Java auch die Zuweisung einen Wert ergibt.
Jeder komplexe Ausdruck kombiniert mit Hilfe von Rechenoperatoren, Vergleichsoperatoren oder Zuweisungsoperatoren andere Ausdr¨
ucke zu einem neuen
Wert. Es gibt Operatoren, die nur auf einen einzigen Ausdruck operieren, es gibt
aber auch Operatoren, die zwei Ausdr¨
ucke zu einem neuen Resultat verbinden.
Es gibt sogar einen Operator, der drei Ausdr¨
ucke kombiniert.
Die Anzahl der Ausdr¨
ucke, auf die ein Operator angewendet wird, um ein Ergebnis zu liefern, nennt man die Stelligkeit des Operators.
Beispiel: Hier einige Beispiele f¨ur 1–stellige Operatoren.
Aus der Mathematik kennen Sie den Fakult¨
at–Operator und den Quadrierungsoperator — es gibt beide nicht in Java, aber da Sie Ihnen vertraut
sind, verwenden wir sie in diesem Beispiel. Beide Operatoren kann an auf
einen einzigen Ausdruck anwenden. Dabei stehen diese Operatoren hinter
dem Ausdruck, auf den sie angewendet werden.
Die folgenden Ausdr¨
ucke sind Beispiele f¨
ur die Verwendung eines einstelligen Operators, der hinter dem Ausdruck steht:
5!, 52 , (1 + 2)!, (1 + 2!)2
Sie kennen außerdem das 1–stellige Minus, welches das Vorzeichen eines
Ausdrucks umdreht. Es steht vor dem Ausdruck, auf das es angewandt
wird — zum Beispiel
−5, −(−3 + 3), −(3 − 2)2
Beispiel: Am h¨aufigsten werden 2–stellige Operatoren verwendet.
Zweistellige Operatoren kombinieren zwei Ausdr¨
ucke zu einem neuen Wert
und stehen normalerweise zwischen den beiden zu verwendenden Ausdr¨
ucken.
Hier ein paar Beispiele, in denen nur zweistellige Operatoren auftauchen:
5 − 3, (3 + 2) · (3 + 1), 2 + 3
Beachten Sie, daß es zwei Minus–Operatoren gibt: Das 1–stellige Minus,
das den folgenden Ausdruck negiert sowie das 2–stellige Minus, das zwei
Ausdr¨
ucke voneinander substrahiert.
Jeder komplexe Ausdruck mit einem 1–stelligen Operator hat entweder die Form
46
Stelligkeit
Ausdruck Operator
Derartige Operatoren nennt man auch “Postfix”–Operatoren,
da sie hinter dem Ausdruck stehen, auf den sie angewandt werden. Beispiele f¨
ur Einstellige Postfix–Operatoren
sind der Quadrat–Operator oder der Fakult¨at–Operator.
Operator Ausdruck
Einen Operator, der vor dem Ausdruck steht, auf den er
angewendet wird, nennt man auch “Pr¨afix”–Operatoren.
Beispiele f¨
ur 1–stellige Pr¨afix–Operatoren sind das 1–
stellige Minus oder der Wurzeloperator.
Ausdr¨
ucke mit 2–stelligen Operatoren haben immer die Form
Ausdruck1 Operator Ausdruck2
Da hier der Operator zwischen den Ausdr¨
ucken steht, nennt man derartige Operatoren auch “Infix”–Operatoren. Beispiele f¨
ur zweistellige Infix–Operatoren sind
+, −, ∗, /.
Sie kennen nun Ausdr¨
ucke, die aus einem Literal bestehen, Ausdr¨
ucke, die aus
einer Variable bestehen und Ausdr¨
ucke, in denen einstellige und zweistellige
Operatoren auftauchen. Durch diese M¨oglichkeiten, Ausdr¨
ucke aufzubauen, sind
bereits fast alle erdenklichen Rechnungen in Java formulierbar.
Wir haben die Operator–Ausdr¨
ucke n¨amlich rekursiv, das heißt selbstbez¨
uglich,
definiert. So hat ja ein zweistelliger–Ausdruck die Form
Ausdruck1 Operator Ausdruck2
Die beiden Ausdr¨
ucke Ausdruck1 und Ausdruck2 k¨onnen nun selbst wieder beliebige Ausdr¨
ucke sein. Beispielsweise k¨onnte Ausdruck1 ein zweistelliger Operator–
Ausdruck mit einem anderen Operator sein, und Ausdruck2 k¨onnte ein einstelliger Pr¨
afix–Ausdruck sein. Wir haben also schon Ausdr¨
ucke der Form
(Ausdruck1,1 Operator Ausdruck1,2 ) Operator P ref ix Ausdruck2,1
beschrieben. Analog kann man beliebig komplizierte Rechnungen durch wiederholtes Anwenden der Operator–Ausdruck–Regeln als Ausdruck beschreiben.
Beispiel: Zum Beispiel kann die Rechnung a+5+6 auf zwei verschiedene
Arten als Ausdruck interpretiert werden. Beide Arten der Interpretation
lassen sich durch Graphiken interpretieren lassen, die einem auf den Kopf
gestellten Baum ¨
ahneln:
Zum einen ist die Rechnung als
Ausdruck a + (5 + 6) interpretier+
bar, also als Ausdruck, in welchem
+
durch den Plus–Operator der AusVariable a
int−Literal 5
int−Literal 6
druck a und der Ausdruck 5 + 6 addiert werden.
+
+
Variable a
int−Literal 5
int−Literal 6
Alternativ k¨
onnte sie als Ausdruck
(a+5)+6 interpretiert werden, also
als Ausdruck in welchem durch den
Plus–Operator der Ausdruck a + 5
und der Ausdruck 6 addiert werden.
47
Beispiel:
Operator *
Operator −
Präfixopt −
Postfixopt. !
int−Literal 5
Variable a
Variable b
Die Rechnung (−5) ∗ (a! − b) l¨
aßt
sich nur auf eine Art als Ausdruck
interpretieren. Die graphische Darstellung der einzig m¨
oglichen Interpretation sehen Sie links.
Die obenstehenden Beispiele zeigen, daß man einen Ausdruck manchmal auf
mehrere Arten aus kleineren Ausdr¨
ucke zusammensetzen kann. Dabei entspricht
jede Art, den Ausdruck zusammenzusetzen, einer unterschiedlichen Klammerung. Selbstverst¨
andlich entspricht nicht jede Art und Weise, den Ausdruck in
kleinere Einheiten aufzubrechen der mathematischen Gewohnheit.
Beispiel: Es macht f¨ur die Rechnung zwar keinen Unterschied, ob der
Ausdruck a + 5 + 6 als a + (5 + 6) oder als (a + 5) + 6 interpretiert wird.
Es macht aber sehr wohl einen Unterschied, ob der Ausdruck a ∗ 5 + 6 als
a ∗ (5 + 6) oder als (a ∗ 5) + 6 interpretiert wird. Genauso macht es einen
Unterschied, ob der Ausdruck a − 5 + 6 als a − (5 + 6) oder als (a − 5) + 6
interpretiert wird.
Um jedem Ausdruck einen eindeutigen Wert zuzuteilen, hat man in der Mathematik vereinbart, daß man Ausdr¨
ucke von links nach rechts lesen soll — der
Ausdruck a − 5 − 6 wird als (a − 5) − 6 interpretiert. Außerdem hat man Rechenregeln wie Punkt– vor Strichrechnung eingef¨
uhrt. Es ist in der Mathematik
auch u
¨blich, daß Einstellige Postfixoperatoren zweistelligen Operatoren vorgehen — der Ausdruck a + 5! wird deshalb nicht als (a + 5)! sondern als a + (5!)
interpretiert.
Auch in Java werden die u
¨blichen Rechenregeln beachtet. In Java haben die
einzelnen Operatoren unterschiedliche Bindungskraft. Wenn in einem Ausdruck
ein Operator mit h¨
oherer Bindungskraft neben einem mit niedrigerer Bindungskraft steht, so zieht der Operator mit der h¨oheren Bindungskraft die daneben
stehenden Ausdr¨
ucke an sich.
Beispiel: Beispielsweise hat der Multiplikationsoperator eine h¨ohere Bindungskraft als der Additionsoperator. Daher wird durch Java der Ausdruck a + 5 ∗ 6 nicht als (a + 5) ∗ 6 sondern als a + (5 ∗ 6) interpretiert —
auch in Java geht also die Multiplikation der Addition vor.
Generell gilt, daß Einstellige Operatoren eine h¨ohere Bindungskraft haben als
zweistellige Operatoren.
Beispiel: Das einstellige Minus hat eine h¨ohere Bindungskraft als der
zweistellige Multiplikationsoperator. Daher wird der Ausdruck −2+5 nicht
als −(2 + 5) sondern als (−2) + 5 interpretiert.
Eine Tabelle, in der alle Java–Operatoren nach Bindungskraft geordnet aufgef¨
uhrt sind, finden Sie in Go To Java in Kapitel 5.8 (Operator–Vorrangregeln).
Es empfiehlt sich, diese Tabelle auszudrucken — Sie werden sie in der 4. Hausaufgabe ben¨
otigen. Jeder in der Tabelle stehende Operator hat h¨ohere Bindungskraft als die sp¨
ater in der Tabelle stehenden Operatoren.
Ein Verst¨
andnis der Vorrangsregeln ist sehr wichtig — wenn Sie sich allerdings
irgendwann mal nicht sicher sind, wie die Auswertereihenfolge eines Ausdrucks
48
ist, k¨
onnen Sie ganz einfach Klammern verwenden, um die Ausdr¨
ucke zusammenzufassen:
Beispiel: Wenn Sie sich nicht sicher sind, ob die Vergleichsoperatoren
h¨
ohreren Vorrang als die Logik–Operatoren haben, k¨
onnten Sie statt
boolean w = 1 < 2 && 2 < 3 && 4 < 5;
auch schreiben
boolean w = (1 < 2) && (2 < 3) && (4 < 5);
7.4.1
Arithmetische Operatoren
Die arithmetischen Operatoren Javas sind in Go To Java 2 in Kapitel 5.2 angef¨
uhrt. Zu den Operatoren ++ und -- sagen wir gleich mehr, zu den anderen
Operatoren in Kapitel 5.2 gibt es nichts hinzuzuf¨
ugen außer einem Beispiel:
Beispiel: Verwendung der arithmetischen Operatoren:
01 public class DemoArith {
02
public static void main( String[] args ) {
03
System.out.println( -3 + +5 );
04
System.out.println( 2.0 / 5.0 - 3.0 * 7.1 );
05
System.out.println("7 / 3 = " + 7 / 3 + ", Rest: " + 7 % 3 );
06
System.out.println( 7.2 % 3 );
07
}
08 }
In Zeile 03 wird das einstellige Minus, das (eigentlich u
ussige) einstel¨berfl¨
lige Plus und das zweistellige Minus verwendet.
In Zeile 04 werden der Multiplikations und Divisionsoperator verwendet.
In Zeile 05 werden zwei Integerzahlen dividiert (7/3 == 2). Dabei bleibt
ein Rest von 1 u
¨brig. Auch der Rest der Division wird ausgegeben. Der
“Rest bei ganzzahliger Division”–Operator heißt %.
In Zeile 06 wird demonstriert, daß man den Restwert–Operator in Java
auch auf reelle Zahlen anwenden kann.
7.4.2
Inkrement und Dekrement–Operatoren
Die u
urfen sicher keiner weiteren
¨blichen Rechenoperatoren wie +,-,*,/,% bed¨
Erl¨
auterung. Hingegen wird auf die Inkrement– und Dekrement–Operatoren von
Java im Text “Go To Java 2” leider nur mangelhaft eingegangen.
Der Operator ++ wird Inkrement–Operator, der Operator -- wird Dekrement–
Operator genannt. Sie unterscheiden sich deutlich von den anderen arithmetischen Operatoren, da sie nicht nur einen Wert berechnen, sondern gleichzeitig
als Nebeneffekt auch eine Variable ver¨andern.
Sie k¨
onnen den Wert einer Variable um eins erh¨ohen, indem Sie entweder vor
oder hinter die Variable den Operator ++ setzen. Analog k¨onnen Sie durch Verwendung des Operators -- den Wert einer Variable um eins verminden.
Inkrement– und Dekrement–Operator existieren in jeweils zwei verschiedenen
Versionen: einmal als Pr¨
afix– und einmal als Postfix–Operator.
49
• Steht ++ vor einer Variable, wie in
int einInt = 5;
++einInt;
so heißt er Pr¨
ainkrement–Operator
• Steht ++ hinter einer Variable wie in
double einDouble = 5.321948;
einDouble++;
so heißt er Postinkrement–Operator
• Analog heisst der -- Operator entweder Pr¨
adekrement bzw. Postdekrement–
Operator, wenn er vor bzw. hinter einer Variable steht.
Inkrement– und Dekrement–Operatoren ver¨andern den Wert einer Variable,
selbst wenn u
¨berhaupt keine Zuweisung verwendet wird. Dabei liefern sie wie
alle Ausdr¨
ucke gleichzeitig einen Wert.
Im folgenden reden wir nur noch vom Inkrement–Operator. F¨
ur den Dekrement–
Operator gilt alles analog.
Beispiel: Zum Beispiel wird in folgendem Beispiel der Wert der Variable
einInt zweimal erh¨
oht, wobei einmal der Postinkrement- und einmal der
Pr¨
ainkrement-Operator verwendet wird. Der Wert der Ausdr¨
ucke wird
dabei nicht verwendet.
int einInt = 5;
einInt++;
++einInt;
Der Postinkrement–Operator und der Pr¨ainkrement–Operator erh¨ohen beide die
Variable und liefern als Wert den Wert der Variable zur¨
uck. Dabei unterscheiden
sie sich dadurch, wann die Variable inkrementiert wird.
Wird der Postinkrement–Operator verwendet, so wird zuerst der Wert der Variable ausgewertet und anschließend die Variable inkrementiert.
Wenn hingegen der Pr¨
ainkrement–Operator benutzt wird, wird zuerst die Variable inkrementiert und anschließend die nun inkrementierte Variable ausgewertet.
Beispiel: Zum Beispiel hat nach Ausf¨uhrung des folgenden Programmtextes die Variable wert den Wert 7, da sie zweimal inkrementiert wurde.
public class DemoInc1 {
public static void main( String[] args ) {
int wert = 5;
int ergebnis1 = wert++;
int ergebnis2 = ++wert;
}
}
50
Aber welchen Wert haben ergebnis1 und ergebnis2 ? Entscheidend hierf¨
ur
ist, ob zuerst die Zuweisung oder zuerst das Inkrementiern stattfindet.
Nach unserer Beschreibung des Pr¨
a– und Postinkrementoperators ist das
obige Programm gleichwertig zu folgendem Programmfragment:
public class DemoInc2 {
public static void main( String[] args ) {
int wert = 5;
int ergebnis1 = wert;
wert = wert + 1;
// Ausf¨
uhrung des Postinkrementoperators
wert = wert + 1;
// Ausf¨
uhrung des Pr¨
ainkrementoperators
int ergebnis2 = wert;
}
}
Nach Ausf¨
uhrung des vorigen Beispiels haben also die einzelnen Variablen
folgende Werte:
wert
== 5
ergebnis1 == 5
ergebnis2 == 7
Es sei hier erw¨
ahnt, daß Sie die Verwendung der Postinkrement– und Pr¨ainkrementoperatoren in komplizierten Ausdr¨
ucken vermeiden sollten, da die durch
sie bewirkten Nebeneffekte oft nur schwer zu verstehen sind.
Beispiel: Erkennen Sie zum Beispiel, welche Werte die Variablen im
folgenden Programm erhalten ?
int i1 = 3;
int i2 = 4;
int ergebnis = ++i1 + --i2 +
i1-- * --i2;
Ich glaube, einfacher zu verstehen ist die ¨
aquivalente Fassung:
int i1 = 3;
int i2 = 4;
i1--;
i2--;
ergebnis = i1 + i2 + i1 * i2;
i1++;
i2--;
Die Verwendung der Dekrement– und Inkrementoperatoren empfiehlt sich, wenn
Sie eine Variable dekrementieren oder inkrementieren m¨ochten. Insbesondere
wenn Ihre Variablennamen lang sind, sparen Sie sich so Schreibarbeit und Fehlerquellen und machen Ihr Programm u
¨bersichtlicher.
Beispiel: Ich hoffe, Sie stimmen mir zu, daß der Programmtext
einFurchtbarLangerVariablenName++;
besser zu lesen ist, als der Programmtext
51
einFurchtbarLangerVariablenName=einFurchtbarLangerVariablenName+1;
Schließlich sei noch erw¨
ahnt, daß die Inkrement– und Dekrementoperatoren ausschließlich auf Variablen anwendbar sind. Das sollte eigentlich klar sein, da man
auch nur Variablen einen Wert zuweisen kann.
Beispiel: Die folgende Anweisungsfolge ist ung¨ultig:
int a=5;
int b=(a*2)++;
// Fehler: Zwischenergebnisse kann man nicht ver¨
andern
Auch die folgende Anweisungsfolge ist ung¨
ultig:
int a=5++;
7.4.3
// Fehler: Literale kann man nicht ver¨
andern
Relationale Operatoren
Kapitel 5.3 in Go To Java bedarf m.E. keiner weiteren Erkl¨arung — es sei nur
gesagt, daß Sie alles, was mit Referenztypen zu tun hat, noch nicht verstehen
k¨
onnen und auch nicht verstehen m¨
ussen.
Relationale Operatoren vergleichen zwei Werte. Ein relationaler Ausdruck hat
immer den Datentyp boolean, liefert als entweder den Wert true oder false
zur¨
uck.
Beispiel: Hier ein Beispiel zur Verwendung relationaler Operatoren:
public class DemoRelational {
public static void main( String[] args )
{
double w1 = 5;
double w2 = 6;
System.out.println( w1 < w2 );
//
System.out.println( w1 == w2 );
//
System.out.println( w1 != w2 );
//
System.out.println( w1 == w2 - 1);
//
System.out.println( w1 * 5 >= w2 - 1);//
}
}
7.4.4
ergibt
ergibt
ergibt
ergibt
ergibt
true
false
true
true
true
Logische Operatoren
Auch zu Kapitel 5.4 u
ugen.
¨ber logischen Operatoren ist nicht viel hinzuzuf¨
Logische Operatoren kombinieren zwei Ausdr¨
ucke vom Typ boolean zu einem
neuen Wert des Datentyps boolean. Sie dienen dazu, Wahrheitswerte zu vergleichen und kombinieren.
Beispiel: Hier noch ein Beispiel zur Verwendung der logischen Operatoren:
public class DemoLogic1 {
public static void main(String[] args)
52
{
System.out.println(
System.out.println(
System.out.println(
System.out.println(
System.out.println(
System.out.println(
true && true );
false & true );
false || true );
false | false );
! false );
! true );
//
//
//
//
//
//
true
false
true
false
true
false
}
}
Beispiel: Noch ein Beispiel zur Verwendung der logischen Operatoren:
public class DemoLogic2 {
public static void main(String[] args)
{
long a = 3;
long b = 4;
boolean w1 = a >= 3 && a < 5;
//
boolean w2 = a == 4 || b == 4;
//
boolean w3 = a >= 3 && ( a==4 || b == 4); //
boolean w4 = !(a == 3);
//
}
}
true
true
true
false
Normalerweise sollten Sie die Short–Circuit–Operatoren (siehe Go To Java, Kap.
5.4) verwenden. Problematisch kann das werden, wenn in einem logischen Ausdruck Nebeneffekte (d.h. Ver¨
anderungen von Variablen) auftreten.
Beispiel: Hier ein warnendes Beispiel zur Short–Circuit–Evaluation.
public class DemoShortCircuit {
public static void main(String[] args)
{
long a = 3;
System.out.println( a > 4 && a++ < 5 );
System.out.println(a);
}
}
In der Auswertung a > 4 && a++ < 5 wird der Teil hinter der logischen
Und–Verkn¨
upfung nicht ausgef¨
uhrt, weil das Ergebnis des gesamten Ausdrucks (false) nach Auswertung von a > 4 klar ist (false && irgendwas ist
immer false).
Daher wird aber auch der Postinkrement–Operator nicht ausgef¨
uhrt. Der
Wert von a ¨
andert sich daher im obigen Beispiel nicht.
¨
Ublicherweise
verwendet man immer die Short-Circuit–Operatoren.
Gew¨
ohnen Sie sich daher an, niemals Operatoren mit Nebeneffekten in
logischen Ausdr¨
ucken zu verwenden !
7.4.5
Bitweise Operatoren
Kapitel 5.5 in Go To Java 2 bedarf m.E. keiner weiteren Erl¨auterung, wenn Sie
sich mit Bin¨
arzahlen auskennen.
53
7.4.6
Zuweisungsoperatoren
Auch Kapitel 5.6 in Go To Java 2 ben¨otigt m.E. kaum weitere Erl¨auterungen.
Eine Besonderheit von Java ist, daß Zuweisungen einen Wert zur¨
uckliefern. Der
Zuweisungsoperator ist in Java ein Operator, der prinzipiell u
¨berall dort eingesetzt werden kann, wo Sie auch eine Addition oder Multiplikation durchf¨
uhren
w¨
urden. Nur haben Zuweisungsoperatoren einen Nebeneffekt, n¨amlich die Zuweisung eines Wertes an eine Variable.
Beispiel: Es ist m¨oglich, mehrere Zuweisungen in einem Befehl durchzuf¨
uhren. Zum Beispiel wird hier den Variablen a und b der Wert 5 zugewiesen:
int a;
int b = a = 5;
Das funktioniert so: Der Compiler erkennt, daß der Variable b der Wert
zugewiesen wird, der rechts vom ersten Gleichheitszeichen steht. Zun¨
achst
wird der Ausdruck daher als
int b = (a = 5);
interpretiert. Dann wertet Java den Ausdruck (a = 5) aus, weist dabei
der Variable a den Wert 5 zu und liefert als Ergebnis des Ausdrucks a = 5
den Wert 5. Dieser wird dann der Variablen b zugewiesen.
Sie k¨
onnen innerhalb jeder Rechnung Zuweisungen vornehmen. Achten Sie dabei
darauf, daß Ihre Programme lesbar bleiben.
Beispiel: Finden Sie den folgenden Programmtext u¨bersichtlich ?
int a;
int b;
int c = 5 * (b = 6 - (a = 5) * 20);
Nur, weil diese Anweisungen erlaubt sind, sollten Sie sie nicht verwenden.
¨
Ubersichtlicher
h¨
atte man dieses Programmfragment schreiben k¨
onnen als
int a = 5;
int b = 6 - a * 20;
int c = 5 * b;
Sehr hilfreich sind die Zuweisungsoperatoren, die bei der Zuweisung noch rechnen.
Beispiel: Zum Beispiel kann man statt
einRelativLangerVariablenName=einRelativLangerVariablenName+5;
auch schreiben
einRelativLangerVariablenName += 5;
54
Die zweite Schreibweise ist besser lesbar und weniger fehleranf¨
allig. Analog
k¨
onnen Sie die Operatoren *=,-=,/= und die u
¨brigen Zuweisungsoperatoren verwenden.
Auch die rechnenden Zuweisungsoperatoren k¨onnen — genau wie die Inkrement–
, Dekrement– und Zuweisungsoperatoren in jedem Ausdruck verwendet werden.
Beispiel: Zum Beispiel:
int a = 5;
int b = 3;
int c = 6 * (a += 5) - (b *= 2);
Auch hier gilt: Achten Sie prim¨
ar auf gute Lesbarkeit Ihres Programms
und nicht auf ausgefallene oder trickreiche Verwendung dieser Operatoren.
Beispiel: Hier noch ein Beispiel, in welchem Zuweisungsoperatoren mißbraucht werden. Dies ist g¨
ultiger Java–Code, aber wer derartigen Code
verwendet, disqualifiziert sich als u
¨bler Hacker:
int a = 5;
int b = 3;
int c = 6 * (b += (a -= ((a += 5) - (b *= 2))));
7.4.7
Weitere Operatoren
Zu Kapitel 5.7 von Go To Java: Die Type–Cast–Operatoren hatten wir bereits
im vorigen Kapitel besprochen. Die Ausf¨
uhrungen zu Strings sollten Sie u
¨berfliegen. Die Ausf¨
uhrungen zu Referenzgleichheit und zu den anderen Operatoren
brauchen Sie noch nicht zu verstehen.
55
7.5
Zusammenfassung
Sie m¨
ussten nun die folgenden Fragen beantworten k¨onnen:
B Was ist ein Ausdruck ?
B Welches sind die relevanten Eigenschaften eines Ausdrucks ?
B Was f¨
ur Arten von Ausdr¨
ucken kennen Sie ?
B Was ist ein Literal ?
B Welche ganzzahligen Literale kennen Sie ?
B Welchen Wert hat das Zahlenliteral 020 ?
B Welchen Wert hat das Zahlenliteral 0x10 ?
B Wie kann man ein ganzzahliges long-Literal erzeugen ?
B Wie ist ein reellwertiges Literale aufgebaut ?
B Wie kann man ein float–Literal erzeugen ?
B Welche boolschen Literale gibt es ?
B Wie muß ein Zeichenliteral aussehen ?
B Beschreiben Sie die Regeln f¨
ur Zeichenketten–Literale !
B Was versteht man unter der Initialisierung einer Variable ?
B Warum m¨
ussen Variablen vor ihrer Verwendung initialisiert werden ?
B Was ist ein Operator ?
B Was ist die Stelligkeit eines Operators ?
B Geben Sie Beispiele f¨
ur 1–stellige und 2–stellige Operatoren !
B Gibt es in Java 3–stellige Operatoren ?
B Was versteht man unter Nebeneffekten ?
B Was sind Operator–Vorrangregeln ?
B Was verstehen wir unter der “Bindungskraft” eines Operators ?
B Warum sind Operatorvorrangregeln wichtig ?
B Was sind Inkrement– und Dekrement–Operatoren ?
B Wie unterscheiden sich Postinkrement– und Pr¨ainkrement–Operator ?
B In welchen F¨
allen sollte man die Inkrement– und Dekrement–
Operatoren vermeiden bzw. verwenden ?
B Welche relationalen Operatoren kennen Sie ?
B Welche Datentypen sind bei der Verwendung relationaler Operatoren
m¨
oglich ?
B Welche Gefahren bestehen bei Verwendung von Short–Circuit Logik–
Operatoren ?
B Welche Zuweisungsoperatoren gibt es in Java ?
B Warum kann man in Java in einem Befehl mehrere Variablenzuweisungen vornehmen ?
56
8
Typwandlungen
Hier geben wir Ihnen weitere Informationen zu Goto Java 2, die Sie erg¨anzend
zu Hausaufgabe 3 lesen sollten.
Sie sollten f¨
ur die weiteren Ausf¨
uhrungen zun¨achst in Go To Java Kapitel 4.6
u
¨ber “Typkonvertierunen” lesen. Dort wird besprochen, welche Typkonvertierungen der Java–Compiler automatisch vornehmen kann — leider werden keine
Beispiele f¨
ur Typkonvertierungen gebracht, und es wird auch nicht erw¨ahnt,
warum Typkonvertierungen wichtig sind.
8.1
Automatische Typkonvertierungen
Im vorigen Kapitel haben Sie gelernt, daß jeder Ausdruck in einem Java–Programm
einen Datentyp hat. Manchmal ist es notwendig, einen Ausdruck eines gewissen Datentyps in einen anderen Datentyp umzuwandeln. Teilweise werden diese
Umwandlungen durch den Java–Compiler automatisch vorgenommen, teilweise
muß man sie explizit befehlen.
Warum Typwandlungen u
¨berhaupt n¨otig sind, soll an einem Beispiel demonstriert werden. Es seien die folgenden Variablen definiert:
int i1 = 5;
int i2 = 3;
float f1 = 3.123f;
float f2 = 2.723f;
double d1 = 3.1234567890123456;
Jede Rechnung mit diesen verschiedenen Variablen muß irgendwie vom Prozessor ausgef¨
uhrt werden. Besprechen wir das an den Beispielen der Addition und
der Multiplikation.
¨
Ubliche
Prozessoren enthalten Funktionen, mit denen zwei Integer addiert oder
multipliziert werden k¨
onnen. Sie haben auch Funktionen, mit denen zwei Float
oder zwei sonstige Datentypen addiert oder multipliziert werden k¨onnen. Das
Ergebnis dieser Funktionen hat dann normalerweise den gleichen Datentyp wie
die Operanden der Rechnung.
Beispiel: Wenn Sie mit zwei int–Variablen rechnen, kann der Prozessor
die Rechnung direkt vornehmen. Das Ergebnis ist dann immer wieder ein
Integer (¨
uberlegen Sie sich, warum das Ergebnis der Division nicht der
Erwartung entspricht !)
i1 + i2; // Ergebnis: int, 8
i1 / i2; // Ergebnis: int, 1
i1 * i2; // Ergebnis: int, 25
Genauso ist das Ergebnis beim Rechnen mit float–Variablen immer wieder ein Float:
f1 + f2; // Ergebnis: float, 5.846
f1 / f2; // Ergebnis: float, 1.1468968...
f1 * f2; // Erbebnis: float, 8.503929
57
Normalerweise enthalten Prozessoren keine Funktionen, die gleichzeitig mit verschiedenen Datentypen rechnen k¨onnen. Will man also mit zwei Werten verschiedener Datentypen miteinander verrechnen, m¨
ussen erst beide Datentypen
in einen gemeinsamen Datentyp konvertiert werden. Danach kann die Rechnung
in diesem Datentyp stattfinden.
Beispiel: Prozessoren sind meist nicht dazu in der Lage, einen IntegerWert und einen Float oder einen Float und einen Double zu addieren.
Wenn Sie die Rechnung
i1 + f1;
durchf¨
uhren wollen, muß sie also vorher noch aufbereitet werden. Der
Prozessor kann entweder zwei Integer oder zwei Float addieren. Es bieten
sich daher zwei M¨
oglichkeiten an, um die Rechnung mit den Funktionen
des Prozessors durchzuf¨
uhren:
• Wandlung der Float-Variable zu einem Integer und Durchf¨
uhrung
der Addition als Integer-Addition:
i1 + (int)f1;
Dabei bedeutet der Ausdruck (int)f1: Wandele f1 in einen Integer–
Wert um. In der Regel geschieht eine solche Umwandlung durch
Wegwerfen aller Nachkommastellen.
Mit den oben angegebenen Werten w¨
urde diese Rechnung wie folgt
ausgef¨
uhrt:
i1 + (int)f1 == 5 + (int)3.123 == 5 + 3 == 8
• Wandlung des Integers zu einem Float und Durchf¨
uhrung der Addition als Float–Addition:
(float)i1 + f1;
Hier bedeutet der Ausdruck (float)i1: Wandle den Integer i1 in
einen Float–Wert um. Dies geschieht ohne Verlust von Information.
Mit den oben angegebenen Werten w¨
urde diese Rechnung so ausgef¨
uhrt:
(float)i1 + f1 == (float)5 + 3.123 == 5.0 + 3.123 == 8.123
Die Antwort, welche der beiden M¨
oglichkeiten gew¨
ahlt wird, f¨
allt leicht:
Wenn wir den Float–Wert zu einem Integer wandeln, verschenken wir
Rechengenauigkeit und kommen zu falschen Ergebnissen. Wenn wir den
Integer hingegen in einen Float wandeln, erhalten wir das erwartete Ergebnis.
Bei Typwandlungen, die der Compiler automatisch vornimmt, wird immer darauf geachtet, keine Genauigkeit zu zerst¨oren. Datentypen werden in Rechnungen
darum nur zu genaueren Datentypen umgewandelt.
Abbildung 4.1 im Kapitel 4.6 von “Go To Java 2” zeigt genau dies — ein short,
der ja nur die ganzen Zahlen von −21 5 bis 21 5 − 1 darstellen kann, kann automatisch in die umfangreicheren Datentypen int, long oder float umgewandelt
werden. Hingegen wird der Datentyp double niemals automatisch in einen anderen Datentyp gewandelt, da dies der genaueste und umfangreichste Datentyp
der Sprache Java ist.
58
Typkonvertierungen treten immer auf, wenn ein Ausdruck eines gewissen Typs
erwartet wird, aber ein anderer Typ vorliegt. Es kommt des o¨fteren vor, daß in einem Ausdruck ein niedriger Genauigkeit ben¨otigt wird, der zu benutzende Wert
aber eine hohe Genauigkeit hat. In solch einem Fall w¨
urde eine Typwandlung
Genauigkeit verschenken. Der Java–Compiler w¨
urde daher statt eine automatische Typwandlung vorzunehmen, das Programm als fehlerhaft bem¨akeln.
Ein gutes Beispiel f¨
ur F¨
alle, in denen eine Typkonvertierung zu ungenaueren Typen n¨
otig ist, bieten die Zuweisungsoperatoren. Der einer Variable zugewiesene
Wert muß ja immer den Datentyp der Variable haben. Wird nun einer Variable
eines “ungenauen” Datentyps ein Wert eines genaueren Datentyps zugewiesen,
so erzeugt das einen Kompilationsfehler.
Beispiel: Zum Beispiel sind folgende Anweisungen erlaubt, da eine
Wandlung zu einem genaueren Typ stattfindet:
f1 = i1;
d1 = i1;
d1 = f1;
// bei Zuweisung: autom. Wandlung i1 zu float
// bei Zuweisung: autom. Wandlung i1 zu double
// bei Zuweisung: autom. Wandlung f1 zu double
Alle oben genannten Zuweisungen sind erlaubt, da hier bei den automatischen Typwandlungen keine Genauigkeit verlorengeht.
Hingegen sind die folgenden Anweisungen nicht erlaubt:
i1 = f1;
i1 = d1;
f1 = d1;
// verboten, da float genauer als int
// verboten, da double genauer als int
// verboten, da double genauer als float
In jedem dieser F¨
alle w¨
urde Genauigkeit verlorengehen, und darum w¨
urde
der Compiler mit einer Fehlermeldung gegen jede der Anweisungen protestieren. Die Fehlermeldung k¨
onnte wie folgt aussehen:
Programmname.java:11: Incompatible type for =. Explicit cast
needed to convert float to int.
i1 = f1;
Beispiel: Sehr h¨aufig kommen derartige Fehlermeldungen in Programmen vor, in denen man Float–Variablen einen Wert zuweist. Sie haben ja
im vorigen Kapitel gelernt, daß reellwertige Zahlenliterale den Datentyp
double erhalten, wenn sie nicht das Anh¨
angsel “f” haben. Die Zuweisung
eines double–Literals an eine float–Variable bedingt einen Genauigkeitsverlust und f¨
uhrt daher zu einem Compiler–Fehler:
01
02
03
04
05
06
07
08
09
10
11
12
int
i;
i = 1;
i = 1L;
i = 3.141;
float f;
f = 1;
f = 3.141f;
f = 3.141;
double d;
d = 1l;
d = 3.141f;
d = 3.141;
// erlaubt: Zuweisung int-Literal an int
// Fehler: long-Literal, aber int erwartet
// Fehler: Zuweisung double-Literal an int
// erlaubt: Zuweisung int-Literal an float
// erlaubt: Zuweisung float-Literal an float
// Fehler: double-Literal, doch float n¨
otig
// erlaubt: Zuweisung long-Literal an float;
// erlaubt: Zuweisung float-Literal an double
// erlaubt: Zuweisung double-Literal an double
59
8.2
Manuelle Typkonvertierungen
Oft will man trotzdem Werte h¨oherer Genauigkeit an Stellen verwenden, an
denen eine niedrigere Genauigkeit ben¨otigt wird. Wieder ist die Zuweisung eines
Wertes an eine Variable das beste Beispiel f¨
ur derartige F¨alle.
Wenn wir beispielsweise wissen, daß in einer float–Variable ein Wert ohne Nachkommastellen enthalten ist, sollten wir in der Lage sein, diesen als Integer aufzufassen.
Oftmals brauchen wir die h¨
ohere Genauigkeit auch nicht wirklich. Es ist zum
Beispiel u
¨blich, die Zwischenschritte einer Rechnung in einer hohen Genauigkeit
durchzuf¨
uhren und dann das Endergebnis der Rechnung nur in einer niedrigen
Genauigkeit zu verwenden.
Java nimmt ohne unser Zutun solche Typwandlungen nicht vor. Wir k¨onnen
Java aber befehlen, eine Typwandlung vorzunehmen. Wir verwenden dazu einen
Typwandlungsoperator und signalisieren Java damit: An dieser Stelle ist es in
Ordnung, Genauigkeit zu verschenken, wir wissen schon was wir tun.
Definition: Type–cast
Eine manuelle Typwandlung nennt man type–cast.
Um einen Wert explizit in anderen Datentyp zu wandeln, schreibt man den
Datentyp, zu dem der Wert gewandelt werden soll, in runde Klammern und den
Wert dahinter:
(datentyp) wert
Dies ist die Anweisung an den Compiler eine Typwandlung vorzunehmen.
Beispiel: Folgende Zuweisungen sind erlaubt, obwohl bei der Wandlung
Genauigkeit verloren geht:
i1 = (int)f1;
i2 = (int)d1;
f1 = (float)d1;
Mit den oben definierten Werten f¨
ur die einzelnen Variablen ergeben sich
hier die folgenden Werte f¨
ur i1 bis f1:
i1 == (int)f1 == (int)3.123 == 3
i2 == (int)d1 == (int)3.1982 == 3
f1 == (float)d1 == (float)3.1234567890123456 == 3.1234567
Ein Wert niedrigerer Genauigkeit ergibt sich dabei meist aus einem Wert
h¨
oherer Genauigkeit durch wegwerfen von Nachkommastellen.
Sie k¨
onnen bei jedem Ausdruck eine Typwandlung vornehmen. Sie d¨
urfen Typwandlungen auch an Stellen vornehmen, an denen sie u
ussig sind.
¨berfl¨
Beispiel: Die folgenden Type–Casts sind zwar erlaubt aber u¨berfl¨ussig,
da Java hier einen automatischen Type–Cast vornehmen w¨
urde¿
60
Type–cast
f1 = (float)i1;
d1 = (double)f1;
Allerdings sind Typwandlungen nur dort gestattet, wo sie Sinn machen.
Beispiel: Beispielsweise kann man Zahlen nur untereinander umwandeln,
wobei der Datentyp char auch als Zahl z¨
ahlt. Folgender Programmtext
f¨
uhrt zu einem Fehler:
int i1 = (int)"Ein Text"; // Fehler
Eine wichtige Besonderheit ist bei Typwandlungen noch zu erw¨ahnen: Die einzelnen primitiven Datentypen sind nicht nur unterschiedlich genau. Auch der
Wertebereich unterscheidet sich. Auf diese Problematik wollen wir hier aber
nicht weiter eingehen — hier nur ein Beispiel dazu:
Beispiel: Wenn Sie einen zu großen Double–Wert in einen Float wandeln,
wird der Wert als Infinity dargestellt:
double d = 1.23e300; // ist nicht als float darstellbar
float f = (float) d; // jetzt enth¨
alt f den Wert Infinity
8.3
Zusammenfassung
Sie m¨
ussten nun die folgenden Fragen beantworten k¨onnen:
B Warum sind Typkonvertierungen n¨otig ?
B Welche Arten von Typkonvertierungen f¨
uhrt Java automatisch durch ?
B Geben Sie einige Beispiele, in denen Typkonvertierungen automatisch
durchgef¨
uhrt werden !
B Warum f¨
uhrt Java manche Typkonvertierungen nicht automatisch
durch ?
B Geben Sie einige Beispiele, in denen statt einer automatischen Typkonvertierung ein Fehler ausgegeben wird !
B Wie kann man Java dazu zwingen, einen Wert in einen anderen Typ
zu wandeln ?
B Nennen Sie einige Beispiele f¨
ur u
ussige Typwandlungen !
¨berfl¨
61
9
Anweisungen und Kontrollstrukturen
Hier beziehen wir uns auf Kapitel 6 von Go To Java 2.
Die Steuerung des Programmablaufs geschieht durch sogenannte Anweisungen.
Jede Anweisung bewirkt irgendwelche Aktionen des Prozessors. Einige Anweisungen haben Sie bereits kennengelernt, so ist z.B. die Verwendung der Ausgaberoutine eine Anweisung. Auch die Definition von Variablen oder die Verwendung
von Ausdr¨
ucken sind Anweisungen.
Jede Anweisung in einem Java–Programm muß mit einem Semikolon enden. Dies
ist n¨
otig, da Zeilenumbr¨
uche f¨
ur den Java–Compiler keine Bedeutung haben und
er daher nicht anhand der Zeilenstruktur erkennen kann, wo eine Anweisung
zuende ist.
9.1
Leere Anweisung
Siehe auch Kapitel 6.1 in Go To Java.
Die einfachste Anweisung in einem Java–Programm ist die leere Anweisung.
Sie hat keine Wirkung. Daß die leere Anweisung erlaubt ist, bedeutet im wesentlichen, daß Sie beliebig viele Semikoli setzen d¨
urfen — auch dort, wo nicht
unbedingt eines stehen muß.
Beispiel:
public class DemoEmpty {
public static void main( String[] args )
{
; // Leere Anweisung
;; // Zwei leere Anweisungen
}
}
9.2
Blockanweisung
Der Block gruppiert eine Gruppe von Anweisungen zu einer Einheit. Sie hat die
Form
{
Anweisung1;
Anweisung2;
...
}
Sie sollten sich angew¨
ohnen, den Inhalt eines Blockes durch Einr¨
uckung der im
Block enthaltenen Zeilen optisch kenntlich zu machen — in allen Beispielprogrammen machen wir Ihnen das auch so vor.
Beachten Sie, daß die Beschreibung der Blockanweisung rekursiv ist — der Block
ist eine Anweisung und in einem Block d¨
urfen wieder Anweisungen stehen. Daher darf in einem Block auch ein weiterer Block enthalten sein.
62
Beispiel: Dies ist ein g¨ultiges Java–Programm. Im Klassenk¨orper ist die
Methode main enthalten. In deren Methodenk¨
orper sind zwei ineinander
verschachtelte Bl¨
ocke enthalten.
Der Klassenk¨
orper wird auch durch geschweifte Klammern abgegrenzt,
sieht also genauso aus wie ein Bl¨
ocke aus. Er ist aber dennoch kein Anweisungsblock, da er keine Anweisungen sondern nur Definitionen enthalten
darf.
public class DemoBlock { // hier startet der Klassenk¨
orper
public static void main( String[] args )
{ // hier startet der Methodenk¨
orper
; // Leere Anweisung
{ // hier startet Block 1
{ // und hier startet Block 2
System.out.println("Hallo, Welt !");
} // hier endet Block 2
} // hier endet Block 1
} // hier endet der Methodenk¨
orper
} // hier endet der Klassenk¨
orper
9.3
Variablendefinitionen
Variablendefinitionen haben wir schon oft benutzt. Sie wissen bereits, daß man
Variablen nur verwenden kann, nachdem man sie definiert hat.
Zu beachten ist, daß eine Variablen Variablendefinition immer nur f¨
ur den Block
gilt, in welchem sie definiert wurde. Sobald die Programmausf¨
uhrung einen
Block verl¨
aßt, haben die in diesem Block definierten Variablen keine Bedeutung
mehr. Man sagt auch: Variablendefinitionen gelten nur lokal im enthaltenden Block.
Wenn in einem Block ein weiterer Block enthalten ist, erbt dieser alle Variablendefinitionen des ¨
außeren Blockes:
Beispiel: Im folgenden Beispiel wird im Block 2 die Variable i1 definiert
und initialisiert. Da Block 3 und Block 4 in Block 2 enthalten sind, kann
i1 auch aus Block 3 und 4 verwendet werden.
Auch in Block A wird die Variable i1 definiert. Diese Variable i1 hat
gar nichts mit der Variable i1 in Block 2 zu tun, da sie außerhalb Block
2 definiert wurde. Die Variablendefinitionen von Block 2 und Block A
beeintr¨
achtigen sich u
¨berhaupt nicht gegenseitig.
In Block 1 ist die Variable i1 nicht bekannt, da sie weder in Block 1 noch in
einem Block 1 umschließenden Block definiert wurde. Die Verwendung der
Variable i1 in Block 1 f¨
uhrt daher zu einer Fehlermeldung des Compilers.
public class DemoBlock1 {
public static void main( String[] args )
{ // Block 1
{ // Block 2
int i1 = 5;
{ // Block 3
{ // Block 4
System.out.println(i1);
63
}
}
}
{ // Block A
String i1 = "Hallo, Welt";
}
System.out.println(i1); // Fehler !
}
}
Sie d¨
urfen in einem Block keine Variable zweimal definieren.
Beispiel: Das folgende Programm ist fehlerhaft, da die Variable i1 im
gleichen Block zweimal definiert wird:
public class DemoBlock2 {
public static void main( String[] args )
{
int i = 1;
double i = 3.5; //Fehler
}
}
Leider d¨
urfen sich in Java Variablen nicht gegenseitig verdecken — sie d¨
urfen
Variablen auch dann nicht in einem Block definieren, wenn sie zuvor nur in
einem diesen Block umschließenden Block definiert wurden.
Beispiel: Im folgenden Beispiel wird in Block 1 eine int–Variable definiert. Block 2 erbt diese Definition, da er in Block 1 enthalten ist. Daher
darf in Block 2 i nicht nochmal definiert werden.
public class DemoBlock3 {
public static void main( String[] args )
{ // Block 1
int i = 1;
{ // Block 2
int i = -5; // Fehler
}
}
}
9.4
Ausdrucksanweisungen
Zu den Ausf¨
uhrungen u
¨ber Ausdrucksanweisunge in Kapitel 6.1 von Go To Java
2 braucht nichts hinzuzugef¨
ugt zu werden außer einem Beispiel:
Beispiel: Hier ein Beispiel mit verschiedenen Ausdrucksanweisungen.
01 public class DemoVar1 {
02 public static void main( String[] args )
03 {
04
System.out.println("Hallo, Welt !");
64
05
06
07
08
09 }
10 }
int i = 5;
i = 6;
i++;
5 + 2;
// Variablendefinition und Initialisierung
// Ausdrucksanweisung: Zuweisung
// Ausdrucksanweisung: Inkrement
//
In der Ausdrucksanweisung in Zeile 08 wird eine Addition durchgef¨
uhrt.
Die Ausdrucksanweisung hat aber keine weitere Wirkung, da der Wert des
Ausdrucks weder ausgegeben noch sonstwie verwendet wird.
Die Ausdr¨
ucke in Zeile 06 und 07 haben dagegen eine Wirkung (n¨
amlich
jeweils eine Ver¨
anderung der Variable i), da in ihnen Operatoren mit Nebeneffekten verwendet werden.
9.5
If–Anweisung
Kapitel 6.2 von Go To Java 2 sollte so verst¨andlich sein. Nur fehlen mal wieder
Beispiele.
Sie sehen ja in Go To Java 2, daß eine if –Anweisung in zwei Versionen auftreten
kann. Einmal mit und einmal ohne else–Zweig. Beachten Sie bei der Besprechung
aller weiteren Anweisungen, daß eine Anweisung auch ein Block sein kann.
Beispiel: Hier ein Beispiel zu if
public class DemoIf1 {
public static void main( String[] args )
{
int temperatur = 30;
if ( temperatur > 25 )
System.out.println("Boah, ganz sch¨
on warm");
}
}
Beispiel: Hier ein Beispiel, bei welchem die Anweisung ein Block ist:
public class DemoIf2 {
public static void main( String[] args )
{
int temperatur = 30;
if ( temperatur > 25 ) {
System.out.println("Boah, ganz sch¨
on warm");
System.out.println("Stell doch mal einer die Klimaanlage an");
}
}
}
Beispiel: Und hier noch ein Beispiel, in dem es auch einen else–Zweig
gibt:
public class DemoIfElse {
public static void main( String[] args )
{
65
int temperatur = 30;
if ( temperatur > 22 ) {
System.out.println("Boah, ganz sch¨
on warm");
System.out.println("Stell doch mal einer die Klimaanlage an");
} else {
System.out.println("Sch¨
on --- zu heiß ist es nicht.");
}
}
}
Das in Go To Java erw¨
ahnte Dangling else (“h¨angendes Else”) bedarf noch einer
weiteren Diskussion: Sie sollten Dangling else—Strukturen immer vermeiden.
Verwenden Sie in F¨
allen, wo ein Dangling else auftritt, immer Bl¨ocke !
Beispiel: Hier tritt ein Dangling else auf — aufgrund der suggestiven
Einr¨
uckung ist nicht auf den ersten Blick klar, daß die else–Anweisung
nicht zur ersten sondern zur zweiten if –Anweisung geh¨
ort. Das Programm
arbeitet daher nicht so, wie gew¨
unscht.
public class DemoDangling1 {
public static void main( String[] args )
{
int temperatur = 30;
boolean istKlimaanlageAn = true;
if ( temperatur < 15 ) // if-Anweisung 1
if ( istKlimaanlageAn ) // if-Anweisung 2
System.out.println("Klimaanlage abstellen");
else
System.out.println("Sch¨
on --- zu kalt ist es nicht.");
}
}
Das Problem kann durch Verwendung von Bl¨
ocken behoben werden. Sie
sollten sich angew¨
ohnen, in solchen leicht mißverst¨
andlichen Situationen
immer Bl¨
ocke zu verwenden !
public class DemoDangling2 {
public static void main( String[] args )
{
int temperatur = 30;
boolean istKlimaanlageAn = true;
if ( temperatur < 15 ) {
if ( istKlimaanlageAn )
System.out.println("Klimaanlage abstellen");
} else
System.out.println("Sch¨
on --- zu kalt ist es nicht.");
}
}
9.6
Switch–Anweisung
Die Switch–Anweisung ist in Go To Java in Kapitel 6.2.2 beschrieben.
66
Zur switch–Anweisung ist hinzuzuf¨
ugen, daß hinter jedem case eine Reihe von
Anweisungen steht. Wird ein case angesprungen, so werden auch die darauf
folgenden case–Bl¨
ocke abgearbeitet, wenn kein break in den Anweisungen steht.
Beispiel: Das folgende Programm gibt die textliche Darstellung einer
Zahl aus. Dastut es einmal mit einer Folge von if –Anweisungen und einmal
mit einer gleichwertigen switch–Anweisung.
Tippen Sie das Programm ab und testen Sie, was passiert, wenn Sie einige
break–Anweisungen entfernen !
public class DemoSwitch {
public static void main( String[] args )
{
int zahl = 1;
if ( zahl == 1) {
System.out.println("Eins");
} else if (zahl == 2) {
System.out.println("Zwei");
} else if ( zahl == 3) {
System.out.println("Drei");
} else if (zahl == 4) {
System.out.println("Vier");
} else {
System.out.println("Diese Zahl kenne ich nicht");
}
// ¨
Aquivalente L¨
osung per switch:
switch(zahl) {
case 1:
System.out.println("Eins");
break;
case 2:
System.out.println("Zwei");
break;
case 3:
System.out.println("Drei");
break;
case 4:
System.out.println("Vier");
break;
default:
System.out.println("Diese Zahl kenne ich nicht-");
break;
}
}
}
9.7
Schleifen
Zu Kapitel 6.3 ist nicht viel hinzuzuf¨
ugen außer einigen Beispielen.
9.7.1
Die While–Schleife
Die While–Schleife k¨
onnen Sie immer dann einsetzen, wenn Sie eine oder mehrere Anweisungen immer wieder ausf¨
uhren wollen, solange eine gewisse Bedingung
erf¨
ullt ist.
Sie hat die Form
while( Schleifenbedingung )
anweisung;
67
Der Anweisungsblock einer while–Anweisung wird immer wieder abgearbeitet,
w¨
ahrend die Schleifenbedingung erf¨
ullt ist.
Im Detail wird eine while–Anweisung wie folgt abgearbeitet: Zun¨achst wird die
Schleifenbedingung ausgewertet, welche ein boolscher Ausdruck sein muß. Falls
sich hier der Wert false ergibt, so wird mit der n¨achsten Anweisung hinter der
while–Schleife weitergemacht.
Falls sich hingegen f¨
ur die Schleifenbedingung der Wert true ergibt, so wird
die Schleifenanweisung durchgef¨
uhrt. Beachten Sie, daß diese Anweisung auch
ein Block sein darf. Nachdem die Schleifenanweisung abgearbeitet ist, wird die
Schleifenbedingung erneut gepr¨
uft, wenn sich hier wieder der Wert true ergibt,
wird die Schleifenanweisung erneut ausgef¨
uhrt. Dies geschieht wieder und wieder, bis die Schleifenbedingung den Wert false ergibt.
Beispiel: Die Anweisung in Zeile 05 wird niemals ausgef¨uhrt, da die
Schleifenbedingung in Zeile 04 immer false ist.
01 public class DemoWhile1 {
02 public static void main( String[] args )
03 {
04
while( false )
05
System.out.println("Hallo, Welt");
06 }
07 }
Beispiel: Eine Schleife, die immer wieder ausgef¨uhrt wird und niemals
wieder verlassen wird, nennt man Endlosschleife. Das folgende Programm ist eine solche, da die Bedingung in Zeile 04 immer true ist.
Dieses Programm beendet sich nie, nachdem Sie es gestartet haben. Es
gibt wieder und wieder “Hallo, Welt” aus. Sie k¨
onnen es auf die harte
Tour unterbrechen, indem Sie die Tastenkombination Ctrl+c bzw. Strg+c
verwenden.
01 public class DemoWhile2 {
02 public static void main( String[] args )
03 {
04
while( true )
05
System.out.println("Hallo, Welt");
06 }
07 }
Beispiel: Das folgende Programm gibt die Zahlen von 1 bis 10 auf den
Bildschirm aus. Es verwendet dazu eine Z¨
ahlervariable. Es demonstriert
außerdem, daß die Schleifenanweisung auch ein Block sein kann.
01 public class DemoWhile3 {
02 public static void main( String[] args )
03 {
04
int i = 1;
04
while( i <= 10 ) {
05
System.out.println(i);
06
i++;
07
}
07 }
08 }
68
Man k¨
onnte das obige Programm auch noch kompakter schreiben, indem
man den Inkrement–Operator in den Ausgabe–Befehl schreibt:
01 public class DemoWhile4 {
02 public static void main( String[] args )
03 {
04
int i = 1;
04
while( i <= 10 )
05
System.out.println(i++);
06 }
07 }
Eine weitere Variante des vorigen Programmes ist lehrreich. Wir k¨
onnen
die Variable i auch innerhalb der Schleifenbedingung inkrementieren. Dabei m¨
ussen wir aber darauf achten, daß die Schleifenbedingung schon vorm
ersten Durchlauf der Schleife einmal ausgef¨
uhrt wird.
Auch das folgende Programm gibt die Zahlen 1 bis 10 aus. Machen Sie sich
das klar ! Achten Sie vor allem darauf, daß nun in der Schleifenbedingung
ein echtes kleiner und nicht ein kleiner–gleich–Zeichen steht.
01 public class DemoWhile4 {
02 public static void main( String[] args )
03 {
04
int i = 0;
04
while( i++ < 10 )
05
System.out.println(i);
06 }
07 }
9.7.2
Die Do–Schleife
Die do–Schleife arbeitet so ¨
ahnlich wie die while–Schleife. Sie hat die Form
do
anweisung;
while ( Schleifenbedingung );
Sie unterscheidet sich von der while–Schleife dadurch, daß der Schleifenk¨orper
in jedem Fall mindestens einmal durchlaufen wird.
Genau wie bei der while–Schleife wird nach jedem Durchlauf des Schleifenk¨orpers
die Schleifenbedingung gepr¨
uft. Immer wenn die Bedingung true ergibt, wird die
Schleife erneut durchlaufen.
Beispiel: Im folgenden Programm wird der Schleifenk¨orper genau einmal
durchlaufen und dabei “Hallo, Welt” ausgegeben. Da die Schleifenbedingung immer false ist, wird die Schleife danach nicht weiter ausgef¨
uhrt.
01 public class DemoDo1 {
02 public static void main( String[] args )
03 {
04
do
69
05
06
07 }
08 }
System.out.println("Hallo, Welt");
while( false );
Beispiel: Im folgenden Programm wird der Schleifenk¨orper immer wieder einmal durchlaufen und dabei “Hallo, Welt” ausgegeben, da Schleifenbedingung immer true ist. Das Programm unterscheidet sich im Resultat
nicht von der vorhin besprochenen while–Endlosschleife.
01 public class DemoDo2 {
02 public static void main( String[] args )
03 {
04
do
05
System.out.println("Hallo, Welt");
06
while( true );
07 }
08 }
Beispiel: Das folgende Programm gibt die Zahlen von 1 bis 10 aus.
Beachten Sie den feinen Unterschied zum ¨
aquivalenten Programm mit
while–Schleife (class DemoWhile4) aus dem vorigen Abschnitt. Machen
Sie sich klar, warum hier nicht mit i=0 sondern mit i=1 begonnen wird.
01 public class DemoWhile4 {
02 public static void main( String[] args )
03 {
04
int i = 1;
04
do {
05
System.out.println(i);
06
} while ( i++ < 10 );
07 }
07 }
Man kann while– in do–Schleife ineinander umschreiben. Eine do–Schleife der
Form
do
anweisung;
while( Schleifenbedingung );
kann als while–Schleife ¨
aquivalent so geschrieben werden:
anweisung;
while ( Schleifenbedingung )
anweisung;
Eine while–Schleife der Form
while ( Schleifenbedingung )
anweisung;
kann als ¨
aquivalente do–Schleife so geschrieben werden:
70
if ( Schleifenbedingung ) {
do
anweisung;
while( Schleifenbedingung);
}
9.7.3
Die For–Schleife
Die For–Schleife ist die wohl am h¨aufigsten verwendete Schleife. W¨ahrend die
while– und do–Schleife meist dann eingesetzt werden, wenn man beim Beginn
der Schleifenabarbeitung noch nicht genau sagen kann, wie oft die Schleife durchlaufen werden soll, setzt man die for–Schleife meist dann ein, wenn man schon
vor Beginn der Schleife weiß, wie oft die Schleife durchlaufen werden soll. Meist
wird die for–Schleife dazu eingesetzt, eine Variable einen gewissen Wertebereich
durchlaufen zu lassen.
Sie hat die Form
for (init; test; update)
anweisung;
In den runden Klammern hinter dem for–Befehl werden drei durch Semikoli
getrennte Ausdr¨
ucke erwartet.
Wie eine for–Schleife arbeitet, wird am besten klar, indem wir sie in eine ¨aquivalente while–Schleife umschreiben. Die for–Schleife kann man als Abk¨
urzung
f¨
ur die folgende Schleife interpretieren:
init;
while( test) {
anweisung;
update;
}
Der erste Teil in den runden Klammern (init) wird vor dem Start der Schleife
ausgewertet. Er dient meist dazu, in der Schleife ben¨otigte Variablen zu definieren oder zu initialisieren.
Folgende Anweisungen und Ausdr¨
ucke sind hier erlaubt:
• “init” darf ein Ausdruck sein, wie in folgendem Beispiel:
for( i=0; i < 10; i++)
System.out.println(i);
• “init” darf eine Liste von Ausdr¨
ucken sein, die dann durch Kommata getrennt sein m¨
ussen, wie in diesem Beispiel:
for(i=0, j=0 ; i < 10; i++, j+=i ) {
System.out.println(i);
System.out.println(j);
}
• “init” darf eine Variablendefinition sein:
71
for(int i=0 ; i < 10; i++)
System.out.println(i);
Eine so definierte Variable ist nur innerhalb der for–Anweisung g¨
ultig.
Daher ist der folgende Programmtext g¨
ultiger Java–Code:
for(int i=0 ; i < 10; i++)
System.out.println(i);
for(double i=0.5 ; i < 10.5; i++)
System.out.println(i);
• “init” darf leer sein:
for( ; false; ) ;
Der zweite Ausdruck (test–Ausdruck) muß boolschen Datentyp haben. Er wird
ausgewertet, um zu pr¨
ufen, ob die Schleife durchlaufen werden soll. W¨ahrend
der test–Ausdruck true ergibt, wird die Schleifenanweisung immer wieder ausgef¨
uhrt.
Nach jedem Ausf¨
uhren der Schleifenanweisung wird der update–Teil in den
runden Klammern ausgewertet. Dabei darf “update” ein Ausdruck oder eine
durch Kommata getrennte Liste von Ausdr¨
ucken sein (siehe Beispiele oben).
Im “update”–Teil werden meist mit Hilfe von Inkrement–Operatoren Variablen
ver¨
andert. Der update–Ausdruck darf leer sein.
Beispiel: Das folgende Programm gibt die Zahlen von 1 bis 10 aus.
01 public class DemoFor1 {
02 public static void main( String[] args )
03 {
04
int i;
05
for(i=1 ; i <= 10 ; i++ )
06
System.out.println(i);
07 }
08 }
Beispiel: Auch das folgende Programm gibt die Zahlen von 1 bis 10 aus.
Es empfiehlt sich, die Variablendefinition in die Initialisierungsanweisung
zu schreiben.
01 public class DemoFor2 {
02 public static void main( String[] args )
03 {
04
for(int i=10 ; i <= 100 ; i+=10 )
05
System.out.println(i / 10 );
06 }
07 }
Beispiel: Das folgende Programm gibt die Zahlen von 1 bis 10 r¨uckw¨arts
aus:
72
01 public class DemoFor3 {
02 public static void main( String[] args )
03 {
04
for(int i=10; i > 0 ; i-- )
05
System.out.println(i);
06 }
07 }
Beispiel: In diesem Programm wird die Anweisung in Zeile 05 niemals
ausgef¨
uhrt, da die Schleifenbedingung in Zeile 04 immer false ist.
01 public class DemoFor4 {
02 public static void main( String[] args )
03 {
04
for( ; false ; )
05
System.out.println("Hallo, Welt");
06 }
07 }
Beispiel: Dies ist eine Endlosschleife, da die Schleifenbedingung immer
true ist — es wird immer wieder der Text “Hallo, Welt” ausgegeben.
01 public class DemoFor5 {
02 public static void main( String[] args )
03 {
04
for( ; true ; )
05
System.out.println("Hallo, Welt");
06 }
07 }
Beispiel: Oft verschachtelt man mehrere Schleifen ineinander. Das folgende Programm gibt eine dreieckige Figur aus. Dazu verwenden wir die
Methode “System.out.print”. Diese arbeitet genauso wie die “println”–
Methode, beginnt aber nicht nach jeder Ausgabe mit einer neuen Zeile
sondern gibt aufeinanderfolgende Ausgaben hintereinander in der gleichen
Zeile aus.
01 public class DemoFor6 {
02 public static void main( String[] args )
03 {
04
for(int i=1 ; i <= 10 ; i++ ) {
05
for(int j = 1; j <= i ; j++ ) {
06
System.out.print("*");
07
}
08
System.out.println(); // neue Zeile beginnen
09
}
10 }
11 }
Beispiel: Sie k¨onnen in einer for–Schleife beliebige Variablen als Kontrollvariablen verwenden, nicht nur int–Variablen. Wir demonstrieren hier
außerdem die Verwendung mehrerer Update–Ausdr¨
ucke.
Das Programm gibt 6–Mal die Zahl 5.0 aus.
73
public class DemoFor7 {
public static void main( String[] args )
{
int i = 5;
for(double d=0 ; d <= 5 ; d++, i-- ) {
System.out.println(d + i);
}
}
}
9.7.4
break und continue
Bitte lesen Sie die Ausf¨
uhrungen zu break und Continue in Go To Java, Kapitel
6.3 nach.
74
9.8
Zusammenfassung
Sie m¨
ussten nun die folgenden Fragen beantworten k¨onnen:
B Was ist ein Block ?
B Wieso darf man in Java auch mehr als ein Semikolon zum Abschluß
eines Befehls verwenden ?
B Was f¨
ur einfache Anweisungen kennen Sie ?
B Erkl¨
aren Sie, wo eine einmal definierte Variable u
¨berall verwendet werden darf — was ist ihr G¨
ultigkeitsbereich ?
B K¨
onnen sich Variablendefinitionen in Java gegenseitig verdecken ?
B Nennen Sie Beispiele f¨
ur Ausdrucksanweisungen !
B Welche Beziehung gibt es zwischen Variablendefinitionen und
Bl¨
ocken ?
B Was ist ein Dangling Else ?
B Wie kann man ein Dangling Else vermeiden ?
B Welche Schleifen kennen Sie ?
B Wie werden do–Schleifen aufgebaut ?
B Wodurch unterscheiden sich do und while–Schleife ?
B Was ist eine Endlosschleife ?
B Wie kann man ein Programm unterbrechen, das in einer Endlosschleife
h¨
angt ?
B Geben Sie ein Beispiel f¨
ur eine Endlosschleife !
B Wie kann man eine Do in eine While–Schleife umschreiben ?
B Wie kann man eine While– in eine Do–Schleife umschreiben ?
B Wie kann man eine for–Schleife in eine while–Schleife umschreiben ?
B Wie muß eine For–Schleife aufgebaut sein ?
B Wie kann der init–Teil einer For–Schleife aussehen ?
B Wenn eine for–Schleife verwendet wird, um eine Variable einen gewissen Zahlenbereich durchlaufen zu lassen — wo sollte diese Variable
definiert werden ?
B Schreiben Sie ein Programm, das r¨
uckw¨arts von 10 bis 1 z¨ahlt – einmal
mit einer do–Schleife, einmal mit einer for– und einmal mit einer while–
Schleife.
75
10
Objekte in Java
Sie kennen nun die sogenannten imperativen Bestandteile von Java: Variablen,
Verzweigungen und Schleifen. Unser n¨achstes Ziel ist, Ihnen die objektorientierten Bestandteile zu vermitteln.
In Kapitel ?? wurde unabh¨
angig von Java erl¨autert, was Klassen und Objekte
sind. Wenn Ihnen die Inhalte nicht mehr gel¨aufig sind, lesen Sie es bitte noch
einmal.
Genau wie in Kapitel ?? beschrieben, ist auch in Java ein Objekt etwas, das
einen Zustand, eine Schnittstelle und Verhalten besitzt. Der Zustand eines Objektes ergibt sich aus seinen Attributen oder Variablen. Seine Schnittstelle und
sein Verhalten ist durch die in seiner Klasse beschriebenen Methoden definiert.
In diesem Kapitel soll nur gezeigt werden, wie man Objekte in Java benutzen
kann, welchen Lebenszyklus ein Objekt hat und wie sich Objekte von den Ihnen
bisher bekannten Datentypen wie int oder float unterscheiden.
Um ein Objekt zu erzeugen, muß zun¨achst seine Klasse definiert sein. Wie man
eigene Klassen definiert, soll erst in Kapitel 12 beschrieben werden. Um das
Verhalten von Objekten zu demonstrieren, werden wir in diesem Kapitel eine
von Java bereitgestellte Klasse verwenden.
10.1
Objekte und primitive Datentypen
Bisher haben Sie mit den primitiven Datentypen von Java und mit Strings
gearbeitet. Die primitiven Datentypen (sie fangen mit einem kleinen Buchstaben
an) sind:
boolean, char, byte, short, int, long long, float, double
Primitive Datentypen sind also die logischen Datentypen, Zeichen sowie alle
bisher benutzten Zahlen. Primitive Datentypen heißen so, da man als Programmierer selbst keine primitiven Datentypen definieren kann — alle primitiven Datentypen sind von den Java–Sch¨opfern fest eingebaut worden. Vielleicht kommt
diese Benennung aber auch daher, daß primitive Daten im Vergleich mit Objekten so “primitiv” sind.
Primitive Datentypen sind keine Klassen (wir erkl¨aren gleich einige Konsequenzen), und darum sind ihre Werte und Variablen auch keine Objekte.
Alle primitiven Datentypen haben gemeinsam, daß man die m¨oglichen Werte
eines primitiven Datentyps durch Literale angegeben kann:
Beispiel: Angabe einiger Werte primitiver Datentypen durch Literale.
int i = 1;
char c = ’a’;
double f = 4.2390823;
Sie haben auch schon Objekte und Klassen kennengelernt, ohne daß Sie es wussten. Zeichenketten sind n¨
amlich keine primitiven Datentypen sondern Objekte
der Klasse String. Da wir bisher nur wenige F¨ahigkeiten von Strings benutzt
haben, konnten Sie aber bisher vermutlich noch keinen Unterschied zwischen
76
Strings und den primitiven Datentypen feststellen. In diesem Kapitel wird sich
das etwas a
¨ndern.
Objekte kann man in der Regel nicht durch Literale erzeugen. Strings und Objekte der in Kapitel 11 behandelten Array–Klassen bilden hier die große Ausnahme. Die Java–Erfinder waren sich dar¨
uber im klaren, daß Strings unheimlich
h¨
aufig verwendet werden und haben daher die M¨oglichkeit geschaffen, Strings
durch Literale zu erzeugen.
Beispiel: Hier werden einige Objekte der Klasse String erzeugt:
String einString = "Hallo, Welt";
String name
= "Homer";
String wunsch = "Pizza";
Einen prinzipiellen Unterschied zwischen Klassen und primitiven Datentypen
k¨
onnen Sie vermutlich immer noch nicht erkennen. Vielleicht f¨allt Ihnen aber
eine ganz banale Sache f¨
allt auf: Die primitiven Datentypen werden alle kleingeschrieben, der erste Buchstabe der String–Klasse hingegen wird großgeschrieben.
Eigentlich ist es Java egal, ob Klassennamen mit einem großen oder einem kleinen Buchstaben beginnen. Es ist aber u
¨blich, alle Klassennamen mit einem
Großbuchstaben beginnen zu lassen. Bitte merken Sie sich das: Alle Klassennamen sollten mit einem Großbuchstaben beginnen.
Primitive Daten und Objekte unterscheiden sich durch das, was man mit ihnen anstellen kann. Den wichtigsten Unterschied k¨onnen wir Ihnen hier leider
noch nicht erkl¨
aren, da Sie vermutlich noch nicht wissen, was wir unter Vererbung verstehen. F¨
ur die fortgeschritteneren Leser sei hier erw¨ahnt, daß man von
primitiven Datentypen nicht erben kann und daß primitive Datentypen keine
Extension einer anderen Klasse sind.
Einen fast genauso wichtigen Unterschied zwischen Objekten und primitiven
Daten werden Sie in diesem Kapitel aber noch kennenlernen: W¨ahrend primitive Daten in einer Variablen gespeichert werden, ist eine Variable, in der Objekte
gespeichert werden, nur ein Verweis auf ein Objekt. Objekte haben im Unterschied zu primitiven Daten eine Identit¨at (dazu mehr in Abschnitt 10.3).
Einen weiteren Unterschied zwischen Objekten und primitiven Daten k¨onnen
wir Ihnen hier schon demonstrieren. Sie erinnern sich sicher, daß wir in Kapitel
?? geschrieben haben, daß Objekte Verhalten und einen Zustand haben und
daß Objekte miteinander kommunizieren k¨onnen.
Primitive Daten haben hingegen nur einen Zustand. Sie sind nicht aktiv und besitzen weder eine Schnittstelle noch Verhalten. Man kann mit primitiven Daten
zwar rechnen oder sie verwenden, man kann primitiven Daten jedoch keine Botschaften schicken. Kurz gesagt: Primitive Daten sind nichts als dumme Daten
ohne Eigenleben.
Einen ersten Eindruck vom Unterschied zwischen primitiven Daten und Objekten liefert das folgende Beispiel. Es demonstriert, wie man String–Objekten
Botschaften schicken kann.
Beispiel: Im folgenden Beispiel erzeugen wir einige String–Objekte und
schicken ihnen dann Botschaften.
77
Klasse StringBotschaften
1
2
3
4
5
6
7
8
9
10
class StringBotschaften {
public static void main( String[] args ) {
String name = "Homer Simpson";
String wunsch = new String("Pizza");
System.out.println( "Bart Simpson".substring(0,4) );
System.out.println( name.substring(0,5) );
System.out.println( name.length() );
System.out.println( name.concat(" will ").concat( wunsch ) );
}
}
In Zeile 3 des Beispiels wird ein neues String–Objekt erzeugt, welches
danach u
¨ber die String–Variable name angesprochen werden kann.
Auch in Zeile 4 wird ein String–Objekt erzeugt, welches den Text "Pizza"
enth¨
alt. Hier wird das Objekt jedoch nicht einfach durch Angabe des
String–Literals sondern durch Verwendung des new–Operators erzeugt.
Der new–Operator wird normalerweise immer ben¨
otigt, um ein Objekt
einer Klasse zu erzeugen. Da Strings auch durch Literale erzeugt werden
k¨
onnen, kann man auf den new–Operator bei Strings aber meist verzichten.
In Zeile 5 wird demonstriert, wie man an ein Objekt eine Botschaft
schicken kann. Hier wird an das String–Objekt "Bart Simpson" die Botschaft substring(0,4) geschickt. Das String–Objekt liefert daraufhin ein
String–Objekt zur¨
uck, das seine ersten vier Buchstaben, also den Text
"Bart", enth¨
alt. In Zeile 6 wird auf die gleiche Weise ein String–Objekt
erzeugt, das die ersten 5 Buchstaben des durch name bezeichneten Objektes enth¨
alt.
In Zeile 7 wird an das durch name bezeichnete Objekt die Botschaft
length() geschickt. Daraufhin liefert das Objekt die L¨
ange des enthaltenen Textes.
In Zeile 8 wird an das durch name bezeichnete Objekt die Botschaft concat(" will ") geschickt. Die Botschaft concat veranlaßt
ein String–Objekt, ein neues Objekt zu erzeugen, indem es das in
der concat–Botschaft mit u
angt.
¨bergebene Objekt an sich selbst anh¨
Das "Homer Simpson"–Objekt erzeugt also das neue String–Objekt
"Homer Simpson will ".
An dieses String–Objekt wird dann die Botschaft "concat(wunsch)" geschickt, woraufhin das String–Objekt "Homer Simpson will Pizza" erzeugt wird.
An primitive Daten kann man keine Botschaften schicken. Beispielsweise gibt
es keine Methode der ganzen Zahlen, die man als
int a;
a.tueWas();
// gibts nicht
5.quadrieren(); // auch ein Fehler
aufrufen k¨
onnte.
10.2
Der Lebenszyklus eines Objektes
Primitive Daten brauchen nicht erst irgendwie erzeugt zu werden. Sie werden
durch Literale gegeben und besitzen keinen Lebenszyklus.
78
Bei Objekten sieht das anders aus — sie haben einen gewissen Lebenszyklus:
Bevor man ein Objekt verwenden kann, muß es erzeugt werden. W¨ahrend seiner
Lebensdauer kann es mit anderen Objekten kommunizieren, und wenn es nicht
mehr ben¨
otigt wird, wird es zerst¨ort.
10.2.1
Die Erzeugung von Objekten
Die Erzeugung eines Objektes geschieht durch Anwendung seines Bauplanes.
Seine Klasse wird als Muster genommen, um die Attribute des Objektes, seine
Schnittstelle und sein Verhalten zu definieren.
Die Objekterzeugung geht so vor sich, daß zun¨achst Speicherplatz f¨
ur das Objekt bereitgestellt wird. Diesen Speicherplatz k¨onnen Sie sich als eine Sammlung
von Variablen vorstellen: Ein Objekt ist eine Art Container, in dem einige Variablen enthalten sind. Dabei entspricht jedes in der Klasse definierte Attribut einer
Variable. Die Variablen, die zu einem Objekt geh¨oren, nennt man auch Instanzvariablen, und der Zustand eines Objektes bestimmt sich aus den Zust¨anden
seiner Instanzvariablen.
Als erster Schritt wird also das Objekt mitsamt den enthaltenen Variablen angelegt. Da neu angelegte Variablen normalerweise einen undefinierten Zustand
haben und Objekte mit nicht definiertem Zustand in der Regel unerw¨
unscht
sind, wird anschließend ein in der Klasse definiertes Initialisierungsverhalten
ausgef¨
uhrt, welches den Instanzvariablen bestimmte Werte zuweist.
Dieses Initialisierungsverhalten wird in einer oder mehreren Methoden beschrieben, welche man in der objektorientierten Methodik als Konstruktoren bezeichnet. Man kann in Java das Anlegen und das Initialisieren des Objektes
nicht voneinander trennen, insofern empfiehlt es sich davon zu sprechen, daß
der Konstruktor ein Objekt erzeugt und initialisiert.
Einem Konstruktor k¨
onnen auch zus¨atzliche Daten mitgegeben werden, die zur
Initialisierung verwendet werden.
Definition: Konstruktor
Ein Konstruktor ist eine Methode, welche ein Objekt erzeugt und dabei seinen anf¨
anglichen Zustand initialisiert. Eine Klasse kann mehrere Konstruktoren f¨
ur ihre Objekte definieren.
Jede Klasse erh¨
alt automatisch einen Standardkonstruktor. Daher ist es nicht
unbedingt n¨
otig, in eigenen Klassen einen Konstruktor vorzusehen.
Welcher Konstruktor in einer Klasse mit mehreren Konstruktoren beim Erzeugen eines Objektes benutzt wird, wird durch die Art der Objekterzeugung
festgelegt (genaueres dazu werden Sie in Kapitel 12 erfahren).
Der Standardkonstruktor einer Klasse kann man aufrufen, indem man den Ausdruck
new Klasse()
verwendet. Allgemeiner geschieht die Erzeugung eines Objektes durch einen
Ausdruck der Form
new Klasse( argument1, argument2, ..., argumentN )
79
Konstruktor
Dabei sind argument1 bis argumentN Argumente, die zur Initialisierung des
Objektes benutzt werden. Als Konstruktor wird ein solcher gew¨ahlt, der zu den
angegebenen Argumenten paßt (falls es in der Klasse keinen solchen Konstruktor
gibt, wird ein Kompilierfehler erzeugt). Was das genau bedeutet, erfahren Sie
in Kapitel 12.
Beispiel: Ein String kann außer per Literal auch per Konstruktor erzeugt
werden.
1
2
3
4
String
String
String
String
string1
string2
string3
string4
=
=
=
=
new String();
"Andreas";
new String("Hallo, Welt");
new String( string3 );
Im obigen Beispiel wird in Zeile 1 ein String durch den Standardkonstruktor erzeugt (new String()). In der String–Klasse ist festgelegt, daß dieser
Konstruktor den leeren String "" erzeugt.
Der durch string2 bezeichnete String wird durch das String–Literal automatisch erzeugt — immer wenn der Java–Compiler ein String–Literal entdeckt, f¨
ugt er automatisch einen entsprechenden Konstruktoraufruf zum
Erzeugen eines String–Objektes ein.
In Zeile 3 wird ein String durch einen Konstruktor erzeugt, der als Argument einen String entgegennimmt. Es wird daraufhin ein neuer String
erzeugt, der eine Kopie des Strings "Hallo, Welt" enth¨
alt.
In Zeile 4 wird ein String erzeugt, der eine Kopie des Strings string3
enth¨
alt.
Hier noch ein Beispiel mit einer selbstgebauten Klasse:
Beispiel: Angenommen, es sei eine Klasse Kind gegeben, welche ein Kind
durch seinen Namen und sein Alter repr¨
asentiert. Die Klasse k¨
onnte wie
folgt definiert sein:
1
2
3
4
5
6
7
8
9
10
11
12
class Kind {
// Instanzvariablen eines Kindes:
String name;
int alter;
// Konstruktor:
public Kind( String neuerName, int neuesAlter ) {
name = neuerName;
alter = neuesAlter;
}
}
In Zeile 4 und 5 sind zwei Attribute, name und alter definiert. In Zeile
8–11 ist eine Methode definiert, deren Name gleich dem Klassennamen
ist. Dies ist ein Konstruktor.
Wenn ein neues Kind erzeugt werden soll, k¨
onnte der Konstruktor wie
folgt aufgerufen werden:
80
Kind einKind = new Kind( "Bart Simpson", 12 );
Hier wird zun¨
achst ein neues Kind–Objekt erzeugt, welches die beiden
Variablen name und alter enth¨
alt. Anschließend wird der Konstruktor der
Klasse (Zeile 8–11) ausgef¨
uhrt. Der Konstruktor initialisiert daraufhin das
Attribut name zu "Bart Simpson" und das Attribut alter zu 12.
Sie sehen an diesem Beispiel, daß ein Konstruktor eigentlich nichts anderes
als eine Methode mit besonderem Namen ist.
10.3
Die Identit¨
at eines Objektes
Nachdem wir ein Objekt erzeugt haben, wollen wir es verwenden. Dazu m¨
ussen
wir das Objekt identifizieren k¨onnen — wir ben¨otigen also einen Namen, unter
dem wir es ansprechen k¨
onnen.
Genau wie bei primitiven Datentypen verwenden wir auch f¨
ur Objekte Variablen. Es gibt dabei jedoch einen großen Unterschied: W¨ahrend primitive Daten
in einer Variable gespeichert werden, wird ein Objekt durch eine Variable
nur referenziert.
Um primitive Daten in einer Variable zu speichern, verwendet man den Zuweisungsoperator. Auch bei der Zuweisung von Objekten verwendet man den
Zuweisungsoperator. Er bewirkt hier aber nicht, daß eine Kopie angelegt wird
sondern daß die zugewiesene Variable danach das Objekt referenziert. Es ist
durchaus m¨
oglich, daß mehrere Variablen das gleiche Objekt referenzieren.
Bitte merken Sie sich: Durch die alleinige Verwendung des Zuweisungsoperators wird kein neues Objekt erzeugt.
Beispiel: Im folgendenProgrammfragment werden Zahlen in Variablen
gespeichert.
int i = 1;
// speichere die Zahl 1 in i
int j = i;
// speichere die Zahl 1 in j
double f = 3.141;// speichere 3.141 in f
Die Situation nach der Ausf¨
uhrung dieses Programmfragments k¨
onnte
man wie folgt darstellen: Zu jeder Variable geh¨
ort ein gewisser Teil des
Speichers, in welchem der Wert der Variable gespeichert ist:
int i
1
int j
1
double f
3.141
Beispiel: Nun soll demonstriert werden, wie Zuweisungsoperatoren auf
Objekte wirden. In Zeile 3 wird ein String–Objekt erzeugt und durch die
Variable name referenziert.
In Zeile 4 wird eine weitere String–Variable definiert und verweist nach
der Zuweisung auf das gleiche Objekt wie die Variable name. Durch die
Zuweisung in Zeile 4 wird kein neues Objekt erzeugt !
81
In Zeile 5 hingegegen wird durch Verwendung des new–Operators ein neues String–Objekt erzeugt, welches eine Kopie des durch Name name referenzierten Strings enth¨
alt. Dies Objekt wird dann durch n2 referenziert.
Klasse ReferenzDemo
1
2
3
4
5
6
7
8
9
class ReferenzDemo {
public static void main( String[] args ) {
String name = "Hallo, Welt";
String n1 = name;
String n2 = new String( name );
System.out.println( n1 == name );
System.out.println( n2 == name );
}
}
Die Situation nach Ausf¨
uhrung der Zuweisungen kann hier wie folgt graphisch dargestellt werden: Es gibt zwei String–Objekte mit gleichem Inhalt. Eines wird durch die Variablen name und n1 referenziert, und das
andere wird durch die Variable n2 referenziert:
String name
referenziert
referenziert
String−Objekt
Zustand: "Hallo, Welt"
String n1
String−Objekt
String n2
referenziert
Zustand: "Hallo, Welt"
In Zeilen 6 und 7 des vorigen Beispiels verwenden wir den == Operator. Bisher haben Sie vermutlich gedacht, daß der ==–Operator zwei Ausdr¨
ucke auf
Gleichheit pr¨
ufe. Das stimmt aber so nicht ganz: der ==–Operator pr¨
uft, ob
zwei Ausdr¨
ucke identisch sind.
Zwei primitive Daten mit gleichem Wert sind auch identisch. Zwei Variablen,
die Objekte referenzieren, sind hingegen nur dann identisch, wenn sie beide auf
das gleiche Objekt verweisen. In Zeile 6 des vorigen Beispiels wurde daher true
und in Zeile 7 false ausgegeben.
Um das nochmal ganz deutlich zu machen: Wir unterscheiden zwischen Gleichheit und Identit¨
at. Zwei Objekte sind gleich, wenn sie den gleichen Zustand
haben. Zwei Objekte sind identisch, wenn sie beide das gleiche Objekt sind.
Im obigen Beispiel sind die durch name und n2 referenzierten Objekte gleich,
da sie beide den Text "Hallo,Welt" enthalten. Hingegen sind name und n2
nicht identisch, da sie auf unterschiedliche Objekte verweisen.
Die Identit¨
at eines Objektes ist unabh¨angig von seinem Zustand. Auch wenn
sich der Zustand eines Objektes ¨andert, bleibt seine Identit¨at erhalten.
Es ist m¨
oglich, daß zwei nicht–identische Objekte den gleichen Zustand haben.
Es ist aber nicht m¨
oglich, daß zwei identische Objekte unterschiedliche Zust¨ande
haben.
Bitte merken Sie sich folgendes:
82
Gleichheit
und
Identit¨at
Identit¨
at
Identit¨
at:
Jedes Objekt hat Identit¨at — die Identit¨at existiert unabh¨angig
von Namen, mit denen wir das Objekt bezeichnen. Die Identit¨at
eines Objektes ¨
andert sich vom Beginn seines Lebenszyklus bis zur
Zerst¨
orung nicht. Sie ¨
andert sich auch nicht, wenn wir den Zustand
des Objektes ver¨
andern.
¨
Ubrigens
bestimmt Java, ob zwei Objekte identisch sind, indem es nachschaut,
an welcher Stelle im Speicher sie gespeichert sind. Identisch sind genau diejenigen Objekte, die an gleichen Speicherstellen liegen.
Oft m¨
ochte man verhindern, daß zwei nicht–identische Objekte mit gleichem
Zustand existieren. In diesem Fall muß man das durch einen geeigneten Mechanismus sicherstellen.
Beispiel: Es ist denkbar, daß zwei verschiedene Studenten den gleichen
Namen haben. Will eine Universit¨
at verschiedene Studenten auseinanderhalten, eignet sich der Name also nicht zur Identifizierung.
Aus diesem Grund wird jedem Studenten eine eindeutige Immatrikulationsnummer gegeben. Eine Verfahrensvorschrift bei der Immatrikulation
stellt sicher, daß jede Immatrikulationsnummer nur einmal vergeben wird.
Die Immatrikulationsnummer ist aus Sicht der Universit¨
at also die Identit¨
at des Studenten.
10.4
Die Kommunikation mit Objekten
Nachdem ein Objekt erzeugt wurde, k¨onnen wir mit ihm kommunizieren. Wir
k¨
onnen es durch das Zusenden von Botschaften zu einem gewissen Verhalten
veranlassen, und es kann selbst Botschaften an andere Objekte verschicken.
Um einem Objekt eine Botschaft zu schicken, m¨
ussen wir eine Referenz auf
das Objekt kennen. Wir k¨
onnen ihm nur Botschaften schicken, die es versteht.
Welche Botschaften ein Objekt versteht und wie diese Botschaften aufgebaut
sein m¨
ussen, ist in seiner Klasse beschrieben.
Nochmal zur Erinnerung: Den Aufbau von Methoden definieren wir in der
Schnittstelle der Klasse wie folgt:
Klasse r methodenName(Klasse 1 e 1 , . . . , Klasse n e n )
Wenn wir nun ein Objekt auffordern wollen, eine Methode auszuf¨
uhren, schicken
wir ihm eine Botschaft, deren Aufbau genau dieser Beschreibung entspricht.
Wenn eine Methode kein Ergebnis zur¨
uckliefert, schreiben wir die Aufforderung
an das Objekt einObjekt die Methode methodenName auszuf¨
uhren, wie folgt:
einAusfuehrer.methodenName(eingabe 1 , . . . , eingabe n )
Dabei u
¨bergeben wir wir soviele zus¨atzliche Objekte eingabe 1 , . . . , eingabe n , wie
in der Schnittstelle beschrieben. Das Objekt eingabe i muß dabei Mitglied der
Klasse Klasse i sein.
Wenn die Methode ein Ergebnis liefert, k¨onnen wir dieses Ergebnis abholen.
Dazu ben¨
otigen wir ein Objekt ergebnis der Klasse Klasse r . Wir schreiben in
diesem Fall:
83
ergebnis = einAusfuehrer.methodenName(eingabe 1 , . . . , eingabe n ).
Beispiel: Zum Beispiel ist in der String–Klasse die Methode length definiert, welche die L¨
ange eines Strings zur¨
uckgibt. Sie nimmt keinen weiteren Parameter entgegen und liefert eine int–Zahl zur¨
uck.
Die L¨
ange eines Strings kann man daher ermitteln, indem man dem String
die Botschaft length() schickt:
String meinString = "Ein Text";
int laenge = meinString.length();
int laenge1 = "Noch ein Text".length();
Bisher haben wir uns das Bild gemacht, daß wir mit Objekten kommunizieren,
indem wir ihnen Botschaften schicken. Dieses Bild ist auch weiterhin richtig,
und es entspricht hervorragend der objektorientierten Denkweise. Es ist nun
leider an der Zeit, diese Denkweise ein wenig an Java anzupassen.
In Java bedeutet der Punkt nach dem Objektnamen eigentlich nicht das Verschicken einer Botschaft. Vielmehr hat man in Java die Vorstellung, daß ein
Objekt ein Container ist, welcher Instanzvariablen und Methoden enth¨alt. Auf
die in einem Objekt enthaltenen Dinge kann man dann zugreifen, indem man
objekt.enthaltenesDing
schreibt. Der Punkt dient also sozusagen dazu, das Objekt aufzumachjen und auf
die enthaltenen Dinge zuzugreifen. Insofern paßt die Vorstellung, man schicke einem Objekt Botschaften nicht hundertprozentig zu Java. Man kann die Schreibweise
objekt.methode()
auch als den Aufruf einer im Objekt enthaltenen Methode verstehen. Nat¨
urlich
ist diese Vorstellung ¨
aquivalent zur Vorstellung, daß wir dem Objekt eine Botschaft schicken.
Man kann den Punkt auch verwenden, um auf Instanzvariablen eines Objektes
zuzugreifen, und hier h¨
ort die Analogie zum Verschicken von Botschaften auf.
Weiter oben hatten wir ein Beispiel mit einer Kind–Klasse. Wir k¨onnen auf die
Attribute eines Kindes dieser Klasse wie folgt zugreifen:
Kind einKind;
einKind.name = "Bart Simpson";
einKind.alter = 12;
Hier dient der Punkt also tats¨
achlich dazu, auf die in einem Objekt enthaltenen
Variablen zuzugreifen.
Vielleicht sollten wir daher in Zukunft lieber davon reden, daß ein Objekt Methoden enthalte, und daß wir, statt dem Objekt eine Botschaft zu schicken, eine
im Objekt enthaltene Methode aufrufen. Vermutlich werden wir in Zukunft beide Sprechweisen verwenden — sie sind schließlich ¨aquivalent. Das Schicken von
Botschaften scheint dabei auch immer noch anschaulicher, auch wenn es nicht
so hundertprozentig zur Java–Philosophie paßt.
84
10.5
Welche Botschaften versteht ein Objekt ?
Wenn Sie selbst eine Klasse schreiben, wissen Sie nat¨
urlich, welche Methoden
enthalten sind. Wie aber k¨
onnen Sie erfahren, welche Methoden fremde Klassen
enthalten ?
Zu Java geh¨
ort eine ganze Menge an Dokumentation. S¨amtliche in Java enthaltenen Klassen sind in der Dokumentation des JDK (Java Development Kit)
beschrieben. Wie diese Dokumentation genau aufgebaut ist, erkl¨aren wir Ihnen,
sobald wir Klassen (Kapitel 12) und Pakete (Kapitel 15) besprochen haben.
In den folgenden Hausaufgaben und in den weiteren Texten werden Sie h¨aufig
mit dieser Dokumentation arbeiten m¨
ussen. Hier soll Ihnen die Verwendung der
JDK–Dokumentation anhand eines Beispiels demonstriert werden:
Beispiel: Die Dokumentation zur String–Klasse finden Sie auf der
WWW–Seite
http://www.tu-bs.de:82/wir/EIP/jdk1.1.8/docs/api/java.lang.String.html
Bitte klicken Sie sich dorthin, indem Sie von unseren WWW–Seiten unter den Litaturlinks zu Java auf den relativ weit unten stehenden Link
“Java–Api (lokal)” klicken. Sie gelangen dann auf eine Seite, auf der einige sogenannte “packages” aufgelistet sind.
Dort klicken Sie bitte auf den als java.lang bezeichneten Link. Anschließend erhalten Sie eine Liste von Klassen, unter denen sich auch die Klasse
String befindet.
Wenn Sie nun auf den String betitelten Link klicken, gelangen Sie zu der
oben genannten WWW–Seite.
Auf dieser WWW–Seite sind alle Methoden beschrieben, die String–
Objekte verstehen. Bitte lesen Sie den kompletten ersten Teil der Seite
¨
durch und schauen Sie sich unter der Uberschrift
“Method Index” die
Methode charAt an. Wenn Sie darauf klicken, sollten Sie die folgende
Erl¨
auterung erhalten:
public char charAt(int index)
Returns the character at the specified index. An index
ranges from 0 to length() - 1.
Parameters:
index - the index of the character.
Returns:
the character at the specified index of this string.
The first character is at index 0.
Throws: StringIndexOutOfBoundsException
if the index is out of range.
Sie erkennen, daß hier eine Methode namens charAt beschrieben wird,
mit der man einen String auffordern kann, ein in ihm enthaltenes Zeichen
zur¨
uckliefern.
Beachten Sie, daß die Zeichen eines Strings mit 0 beginnend durchnumeriert werden. Das erste Zeichen eines Strings erh¨
alt man, also indem man
ihm die Botschaft charAt( 0 ) schickt.
85
Mit Hilfe der obigen Beschreibung k¨
onnen wir ein Programm schreiben,
das alle Zeichen eines Strings einzeln ausgibt. Dazu rufen wir in einer
Schleife wiederholt die String–Methode charAt auf und geben das durch
den String zur¨
uckgegebene Zeichen mit der Methode System.out.println
aus. Um die L¨
ange des Strings zu ermitteln, schicken wir ihm die Botschaft
length(). Bitte sehen Sie sich auch die Dokumentation zur length–Methode
an.
Klasse DemoStringMethods
1
2
3
4
5
6
7
8
10.6
class DemoStringMethods {
static public void main( String[] args ) {
String text = "Hallo, Welt";
for( int i = 0; i < text.length(); i++ ) {
System.out.println( text.charAt( i ) );
}
}
}
Die Zerst¨
orung von Objekten
Wenn ein Objekt nicht mehr ben¨otigt wird, wird es zerst¨ort und entsorgt. Dabei wird der gesamte Speicherplatz, den das Objekt im Speicher belegt hatte,
freigegeben.
Gl¨
ucklicherweise muß man sich als Programmierer in Java u
¨berhaupt nicht um
die Objektentsorgung k¨
ummern. Java kann n¨amlich selbst erkennen, ob ein Objekt noch ben¨
otigt wird oder nicht. Dazu enth¨alt Java eine Funktion, die zwischendurch immer wieder mal automatisch gestartet wird und sich als “M¨
ullsammler” bet¨
atigt — diese Funktion heißt sehr treffend der Garbage Collector14
Der Garbage Collector kann feststellen, auf welche der momentan existierenden
Objekte Variablenreferenzen existieren. Immer wenn er ein Objekt entdeckt, das
durch keine einzige Variable referenziert wird, sammelt er es ein und gibt den belegten Speicherplatz f¨
ur andere Objekte frei — er betreibt also Recycling, denn
der so freigegebene Speicherplatz schafft Lebensraum im Speicher des Computers f¨
ur neue Objekte. Das tollste daran ist, daß Sie selbst sich u
¨berhaupt nicht
um Ihre M¨
ullentsorgung k¨
ummern m¨
ussen — alle nicht mehr Objekte werden
volautomatisch entsorgt.
Vielleicht fragen Sie sich, ob das nicht etwas unsicher sei: Da kommt irgendso
ein dahergelaufenes Programm und l¨oscht einfach Ihre Objekte.
Bedenken Sie aber, daß Sie ohnehin keine M¨oglichkeit haben, ein Objekt, das
Sie nicht durch eine Variable referenziert haben, zu benutzen. Insofern kann der
Garbage Collector keinen Schaden anrichten — er l¨oscht niemals Objekte, auf
die Sie noch zugreifen k¨
onnen.
Noch ein Hinweis: Es ist zwar m¨oglich, den Garbage Collector von Hand zu
starten. N¨
otig ist es aber meist nicht, da Java ihn ohnehin immer automatisch
startet, wenn mehr Platz ben¨
otigt wird.
Und noch ein Hinweis: Selbstverst¨andlich werden nur Objekte gel¨oscht — bei
primitiven Daten ist das nicht n¨otig, da sie in einer Variable enthalten sind
14 Garbage Collectoren sind ubrigens keine Erfindung von Java — das in den 70ern ent¨
wickelte Smalltalk besaß bereits einen Garbage Collector.
86
und ihre Lebensdauer daher durch die Lebensdauer der Variablen begrenzt ist.
Objekte m¨
ussen nur deshalb gel¨oscht werden, da sie unabh¨angig von Variablen
existieren.
Beispiel: Im folgenden Programm werden in Zeile 3 und 4 zwei String–
Objekte erzeugt.
Die Zuweisung in Zeile 5 bewirkt, daß auf "String1" keine Referenz mehr
existiert. Daher kann "String1" nach Ausf¨
uhrung von Zeile 5 vom Garbage Collector gel¨
oscht werden.
In Zeile 6 wird a2 ein neues Objekt zugewiesen. Das Objekt "String2"
darf jedoch noch nicht gel¨
oscht werden, da es noch durch die Variable a1
referenziert wird.
Erst nach Ausf¨
uhrung von Zeile 7 wird das Objekt "String2" von keiner Variable mehr referenziert und kann vom Garbage Collector entsorgt
werden.
Klasse DemoGarbage
1
2
3
4
5
6
7
8
9
10.7
class DemoGarbage {
public static void main() {
String a1 = "String 1";
String a2 = "String 2";
a1 = a2;
a2 = "String 3";
a1 = "";
}
}
Zusammenfassung
Sie sollten nun die folgenden Fragen beantworten k¨onnen:
87
B Welche primitiven Datentypen kennt Java ?
B Nennen Sie einige Unterschiede zwischen primitiven Datentypen und
Klassen !
B Wie erzeugt man ein Objekt ?
B Was ist ein Konstruktor ?
B Beschreiben Sie, was Sie unter der Identit¨at eines Objektes verstehen !
B Enthalten Variablen Objekte ?
B Was bewirkt der Zuweisungsoperator bei primitiven Daten ?
B Was bewirkt der Zuweisungsoperator bei primitiven Objekten ?
B Was bewirkt der ==–Operator bei primitiven Daten ?
B Was bewirkt der ==–Operator bei Objekten ?
B Was ist der Unterschied zwischen Gleichheit und Identit¨at ?
B Wie schickt man einem Objekt eine Botschaft ?
B Wie kann man herausfinden, welche Methoden die Klasse String
enth¨
alt ?
B Wie werden Objekte zerst¨ort ?
B Wie arbeitet der Garbage–Collector ?
B Warum kann der Garbage–Collector keinen Schaden anrichten ?
88
11
Arrays
Bevor Sie lernen, selbst Klassen zu definieren, m¨ochten wir Ihnen noch einen
weiteren Typ von sehr wichtigen Objekten vorstellen: Arrays.
Arrays sind Objekte, welche eine gr¨oßere Anzahl von Zahlenwerten oder anderen
Daten verwalten k¨
onnen. Sie k¨onnen sich ein Array als eine durchnumerierte
Sammlung von Variablen vorstellen. Arrays sind die einfachste M¨oglichkeit, die
Java zur Verwaltung mehrerer gleichartiger Daten oder Objekte bietet.
Arrays unterscheiden sich von allen anderen Objekten und Klassen in Java: Die
Array–Klassen haben einen recht eigenartigen Namen, und auch die Konstruktor–
Aufrufe von Arrays unterscheiden sich von normalen Konstruktor–Aufrufen.
11.1
Definition von Arrays
Ein Array ist ein Vektor gleichartiger Variablen. Die Klasse eines Arrays, in
welchem Variablen des Datentyps typ gespeichert werden k¨onnen, ist typ[].
Beispielsweise speichern Objekte der Klasse int[] einen Vektor von int–Werten,
w¨
ahrend Objekte der Klasse String[] einen Vektor String–Werten verwalten
k¨
onnen.
Eine Array–Klasse erh¨
alt man also, indem man hinter einen anderen Klassennamen zwei eckige Klammern setzt. Diese merkw¨
urdige Schreibweise ist praktisch,
da sie sehr pr¨
agnant ist und f¨
ur jede beliebige Klasse — auch f¨
ur selbstentwickelte Klassen — verwendbar ist.
Eine Variable, welche ein Array–Objekt referenzieren kann, definiert man also
so:
typ[] arrayVariable;
Da Arrays Objekte sind, m¨
ussen sie durch einen Konstruktor erzeugt werden.
Dem Erzeugen eines Arrays muß man angebeben, wie groß das Array sein soll,
d.h. f¨
ur wie viele Werte oder Objekte im Array Platz sein soll.
Ein Array–Objekt der Gr¨
oße n eines Datentyps typ wird wie folgt erzeugt:
new typ[ n ]
Auch hier wird eine etwas eigenartige Schreibweise verwendet — normalerweise
wird der Konstruktor einer Klasse ja geschrieben als Klasse( argumente ).
F¨
ur ein Array w¨
urde man also eigentlich erwarten, daß der Konstruktor–Aufruf
als Datentyp[](n) geschrieben wird. Auch hier haben Arrays also eine Sonderstellung.
Beispiel: Im folgenden Beispiel wird in Zeile 3 eine Array–Variable namens einIntArray definiert, welche int–Werte speichern kann. In Zeile 4
wird ein Array–Objekt der Gr¨
oße 100 angelegt und durch einIntArray
referenziert.
In Zeile 6 wird eine Variable zur Referenzierung eines double–Arrays erzeugt und mit einem Array der Gr¨
oße 10 initialisiert.
Zeile 8–9 zeigen, daß man nicht nur Arrays von primitiven Datentypen
sondern auch Arrays von Objekten definieren kann.
89
Klasse ArrayDefinitionen
1
2
3
4
5
6
7
8
9
10
11
class ArrayDefinitionen {
public static void main( String[] args ) {
int[] einIntArray;
einIntArray = new int[ 100 ];
double[] einDoubleArray = new double[ 10 ];
String[] einStringArray;
einStringArray = new String[ 50 ];
}
}
Bevor wir darauf eingehen, wie man Objekte benutzt, sollen noch ein paar Eigenarten von Arrays erw¨
ahnt werden:
• Die Gr¨
oße eines Arrays kann im Nachhinen nicht mehr ver¨andert werden.
Wenn Sie nach der Erzeugung eines Arrays feststellen, daß es zu klein ist,
k¨
onnen Sie es also nicht mehr wachsen lassen.
Statt dessen m¨
ussten Sie in diesem Fall ein weiteres, hinreichend großes
Array erzeugen und den Inhalt des alten Arrays in das neue Array hineinkopieren.
• Es ist nicht m¨
oglich, in einem Array Daten verschiedener Klassen oder
Datentypen zu speichern. Beispielsweise k¨onnen Sie kein Array erzeugen,
das sowohl int– als auch String–Werte speichern kann15
• Beachten Sie bitte den Unterschied zwischen Arrays primitiver Datentypen
wie int[], double[] und Arrays von Objekten wie String[].
Arrays primitiver Datentypen der Gr¨oße n bieten n Positionen, an denen
Daten gespeichert werden, w¨ahrend Arrays von Objekten der Gr¨oße n
nicht die Objekte selbst sondern nur Referenzen auf Objekte speichern.
Wir besprechen das weiter unten noch an einem Beispiel.
11.2
Verwendung von Arrays
Jedes Array der Gr¨
oße n enth¨
alt n von 0 bis n − 1 durchnumerierte Variablen.
Wenn array eine Array–Variable ist und wenn i eine int–Zahl ist, so kann man
auf den i–ten Arrayinhalt wie folgt zugreifen.
array[i]
Einen Kompilierfehler erh¨
alt man, wenn man versucht mit einem Wert, welcher
nicht automatisch in einen int gewandelt werden kann, auf ein Array zuzugreifen.
W¨
are i eine long oder eine double–Variable, so w¨
urde der Zugriff array[i] also
einen Kompilierfehler erzeugen.
15 Das stimmt nicht v¨
ollig — ein Array kann schon Objekte verschiedener Klassen speichern.
Das verstehen Sie aber erst, wenn Sie wissen, was Vererbung ist. F¨
ur die fortgeschrittenen Leser
ein Hinweis: Ein Array des Typs MeineKlasse[] kann selbstverst¨
andlich auch Instanzen von
Extensionen von MeineKlasse speichern. Insbesondere kann ein Array vom Typ Object[] alle
Objekte aufnehmen.
90
Wenn man versucht, auf eine negative oder auf eine sonstige nicht–vorhandene
Array–Position zuzugreifen, erh¨alt man w¨ahrend der Laufzeit seines Programms
eine Fehlermeldung. Das Programm wird dann beendet.
Beispiel: Das folgende Programm definiert ein int–Array der Gr¨oße 10.
In diesem Array speichert es die Zahlen 1 bis 10.
Anschließend wird versucht, die Zahlen auszugeben. Dabei wird ausversehen auf eine Array–Position zugegriffen, die nicht existiert. An der Stelle
bricht das Programm mit einer Fehlermeldung ab.
Klasse ArrayZugriff
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class ArrayZugriff {
public static void main( String[] args ) {
int[] intArr = new int[ 10 ];
// Array mit Zahlen von 1 bis 10 fuellen
for( int i = 0; i < 10 ; i++ ) {
intArr[ i ] = i + 1;
}
// Arrayinhalt ausgeben. Dabei wird ein Fehler gemacht:
// Statt von 0..9 wird auf Array-Position 1..10 zugegriffen
for( int i = 1; i <= 10; i++ ) {
System.out.println( intArr[i]);
}
}
}
Man kann bei einem gegebenen Array herausfinden, wie groß es ist. Dazu greift
man auf sein L¨
angen–Attribut zu, indem man schreibt:
array.length
Wannimmer Sie die Gr¨
oße eines Arrays verwenden, sollten Sie das L¨angen–
Attribut des Arrays verwenden. Sie sollten die Gr¨oße niemals direkt als Literal
im Programm benutzen.
Beispiel: Angenommen, Sie wissen, daß ein Array namens meinArray
10 Elemente enth¨
alt, welche Sie ausgeben wollen, so sollten Sie dennoch
nicht schreiben
for( int i = 0 ; i < 10 ; i++ ) {
System.out.println( meinArray[i] )
}
Verwenden Sie statt dessen das L¨
angen–Attribut:
for( int i = 0 ; i < meinArray.length ; i++ ) {
System.out.println( meinArray[i] )
}
Sie fragen sich vielleicht, warum man in seinem Programm die Gr¨oße eines Arrays nicht explizit verdrahten soll. Die Antwort ist einfach: Programme ¨andern
sich. Wenn Sie sp¨
ater die Gr¨
oße des Arrays ver¨andern, so m¨
ussten Sie alle Stellen, an denen Sie die Gr¨
oße als Zahl geschrieben haben, manuell nachbessern.
Das f¨
uhrt dann meist zu Fehlern.
91
Wenn Sie von vorneherein darauf achten, daß Ihre Programmteile m¨oglichst we¨
nig Annahmen u
¨ber die zu bearbeitenden Objekte machen, werden Anderungen
einfacher.
11.3
Array–Literale
Auch Arrays kann man als Literale schreiben. Ein Array–Literal hat die Form
{ wert, wert, ...., wert }.
Zum Beispiel wird hier ein durch Verwendung eines Array–Literales ein int–
Array der Gr¨
oße 5 erzeugt und mit den Zahlen 1, 4, 9, 16, 25 initialisiert:
int[] quadrate = {1, 4, 9, 16, 25};
¨
Ahnlich
kann man auch andere Arrays initialisieren:
double[] werte = {1.1, 1.3, 1.4};
String[] tage = {"Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"};
11.4
Primitive Arrays und Objektarrays
Wir m¨
ochten Ihnen hier anhand eines Beispiels nochmal den Unterschied zwischen Arrays von primitiven Daten und zwischen Arrays von Objekten zeigen.
Dazu betrachten wir ein Programm, welches ein int– und ein String–Array verwendet. Beide Arrays haben 4 Eintr¨age.
Klasse ArrayPrimObj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class ArrayPrimObj {
public static void main( String[] args ) {
int[] daten = {100, 200, 300, 400};
for( int i = 0; i < daten.length; i ++ ) {
System.out.println( daten[i] );
}
String[] sDaten = new String [ 4 ];
sDaten[0] = "String 1";
sDaten[1] = "String 2";
sDaten[2] = sDaten[3] = "String 3";
for( int i = 0; i < sDaten.length; i++ ) {
System.out.println( sDaten[i] );
}
}
}
Um Ihnen deutlich zu machen, wie die beiden Arrays von Java gehandhabt
werden, finden Sie hier zwei Zeichnungen:
92
int−Array
length=4
Inhalt=:
int[] daten;
referenziert
0
100
1
200
2
300
4
400
Das int–Array ist ein Objekt, welches von der Variable daten referenziert wird.
Als Array der Gr¨
oße 4 enth¨
alt es vier von 0 bis 3 durchnumerierte Variablen, in
welchen int–Werte gespeichert werden. Das Array enth¨alt außerdem ein Attribut
namens length, in welchem gespeichert wird, wie viele Speicherpl¨atze das Array
enth¨
alt.
String−Objekt
String−Array
referenziert
length=4
Inhalt=:
0
referenziert
String[] sDaten;
1
referenziert
2
Inhalt: String 1
String−Objekt
Inhalt: String 2
referenziert
String−Objekt
4
referenziert
Inhalt: String 3
Auch das String–Array ist ein Objekt. Es wird von der Variable sDaten referenziert. Auch das String–Array enth¨alt vier String–Variablen. Da String–
Variablen aber Objekte nicht speichern sondern nur referenzieren, enth¨alt das
String–Array selbst lediglich vier Verweise auf andere Objekte.
11.5
Referenztypen am Array-Beispiel
Nun m¨
ochten wir Ihnen noch am Beispiel der Arrays demonstrieren, was es
f¨
ur die Praxis bedeutet, mit Referenztypen zu arbeiten. Sie sollten dieses Programm auf jeden Fall ausprobieren und danach ein wenig mit dem Programm
herumspielen !
93
Klasse ArrayReferenz
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class ArrayReferenz {
public static void main( String[] args ) {
int[] daten = {100, 200, 300, 400};
int[] datenNeu = daten;
datenNeu[1] = 1;
datenNeu[3] = 3;
datenNeu = new int[4];
for(int i = 0; i < daten.length; i++ ) {
System.out.println( daten[i] );
}
}
}
Im obigen Programm wird ein Array–Objekt erzeugt, welches durch die Variable
daten referenziert wird. In Zeile 5 wird eine weitere Variable, datenNeu definiert,
welche das selbe Objekt referenziert.
Dann wird in Zeile 6 und 7 die Variable datenNeu verwendet, um das Array–
Objekt zu ver¨
andern.
Obwohl wir die Variable daten nicht benutzt haben, um das Array zu ver¨andern,
hat sich das durch die Variable daten referenzierte Array hierdurch ver¨andert,
wie die Ausgabe in Zeile 10–12 beweist.
¨
Sie sehen also, daß Anderungen
an Objekten alle Variablen beeinflussen, welche das Objekt referenzieren. Wenn man im Umgang mit Referenztypen nicht
aufpasst und sich nicht immer wieder klarmacht, daß die Zuweisung keine Ko¨
pie sondern nur eine Referenz erzeugt, kann man daher große Uberraschungen
erleben.
11.6
Mehrdimensionale Arrays
Man kann auch Arrays erzeugen, die Arrays enthalten. Beispielsweise wird hier
ein Array erzeugt, das int–Arrays enth¨alt:
int[][] einZweiDIntArray;
Arrays, die Arrays enthalten, nennt man mehrdimensionale Arrays. Unter “Dimension” versteht man dabei die Schachtelungstiefe der Arrays. Ein vierdimensionales Array k¨
onnte man so definieren:
int[][][][] einVierdimArray;
Mehrdimensionale Arrays eignen sich zur Darstellung von Daten wie Matrizen,
Tensoren, Spreadsheets, und so weiter.
Ein zweidimensionales Array wird wie folgt erzeugt:
int[][] zweiDArray = new int[ zeilen ][ spalten ];
Beachten Sie aber den Unterschied zwischen einem zweidimensionalen Array
und einer Matrix: In einer Matrix hat jede Zeile und jede Spalte die gleiche
L¨
ange. Ein zweidimensionales Array ist hingegen einfach ein Array, das andere
¨
die L¨
angen der enthaltenen Arrays ist damit nichts gesagt.
Arrays enth¨
alt. Uber
94
Die obige Initialisierung eines zweidimensionalen Arrays stellt allerdings sicher,
daß alle Zeilen des erzeugten Arrays die gleiche L¨ange haben.
Beispiel: Das folgende Programm demonstriert die Erzeugung eines matrixf¨
ormigen Arrays und eines zweidimensionalen Arrays einDreieck, in
welchem die Zeilen unterschiedliche L¨
angen besitzen:
Klasse Array2D
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Array2D {
public static void main( String[] args ) {
int[][] eineMatrix = new int[10][10];
for( int i=0; i < eineMatrix.length; i++) {
for( int j=0; j < eineMatrix.length; j++) {
eineMatrix[i][j] = i-j;
}
}
int[][] einDreieck = new int[10][1];
for( int i=0; i < einDreieck.length; i++) {
einDreieck[i] = new int[i+1];
}
for( int i=0; i < einDreieck.length; i++) {
for( int j=0; j < einDreieck[i].length; j++ ) {
einDreieck[i][j]=i-j;
System.out.print( einDreieck[i][j] );
System.out.print(" ");
}
System.out.println();
}
}
}
Beispiel: Schließlich demonstriert das folgende Programm demonstriert
den Umgang mit 2–dimensionalen Arrays und 2–dimensionalen Array–
Literalen.
Zuerst wird ein matrixf¨
ormiges double–Array definiert und ausgegeben,
und anschließend wird ein dreieckf¨
ormiges int–Array definiert und ausgegeben.
95
Klasse Array2D
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
11.7
class Array2D {
public static void main( String[] args ) {
// matrixf¨
ormiges double--array
double[][] matrix = { {1.1, 1.2}, {1.2, 1.3} };
for( int i = 0; i < matrix.length ; i++ ) {
for( int j= 0; j < matrix[i].length; j++ ) {
System.out.print( matrix[i][j] );
System.out.print(" ");
}
System.out.println();
}
// dreieckf¨
ormiges int--Array
int[][] zweiDInt = { {1}, {2,3}, {4,5,6}, {7,8,9,10} };
for( int i = 0; i < zweiDInt.length ; i++ ) {
for( int j= 0; j < zweiDInt[i].length; j++ ) {
System.out.print( zweiDInt[i][j] );
System.out.print(" ");
}
System.out.println();
}
}
}
Zusammenfassung
Sie sollten nun die folgenden Fragen beantworten k¨onnen:
B Was ist ein Array ?
B Wie definiert man ein boolean–Array ?
B Wie erzeugt man ein Array–Objekt ?
B Wie kann man im Nachhinein die Gr¨oße eines Arrays a¨ndern ?
B Erl¨
autern Sie den Unterschied zwischen Arrays primitiver Datentypen
und Arrays von Objekten !
B Wie greift man auf Array–Elemente zu ?
B Wie sind die Variablen in einem Array der L¨ange 4 durchnumeriert ?
B Wie kann man auf das letzte Element eines Arrays der L¨ange 4 zugreifen ?
B Wie kann man auf das erste Element eines Arrays zugreifen ? Wie auf
das zweite ?
B Wie kann man die Gr¨
oße eines gegebenen Arrays bestimmen ?
B Was ist ein Array–Literal, und wie benutzt man es ?
B Welche Gefahr besteht bei der Verwendung von Referenz–Typen ?
B Was ist ein Mehrdimensionales Array ?
B Wie erzeugt man ein mehrdimensionales Array ?
B Wie unterscheiden sich 2–dimensionale Arrays von Matrizen ?
96
12
Klassen in Java
Nun soll es endlich daran gehen, in Java eigene Klassen zu erstellen. Wir schließen hier an der in in Kapitel ?? begonnenen Diskussion u
¨ber Klassen und Objekte an. Wenn Ihnen die Inhalte von Kapitel ?? nicht mehr gel¨aufig sind, sollten
Sie es nun noch einmal lesen.
12.1
Instanz- und Klassenbestandteile
Bevor wir beginnen, Klassen zu programmieren, m¨
ussen wir unsere Vorstellung
von Klassen etwas revidieren — Sie wissen bisher, daß eine Klasse ein Bauplan
f¨
ur Objekte ist. Sie ist aber noch mehr — genau wie Objekte kann auch eine
Klasse Variablen und Methoden enthalten (eine Java–Klasse ist aber selbst kein
Objekt).
Die Klasse hat also zwei Funktionen: Zum einen ist die Klasse der Bauplan von
Objekten — in der Klasse ist beschrieben, welche Attribute die Objekte tragen
sollen, und auch das Verhalten der Objekte ist in der Klasse durch Methoden
beschrieben.
Zur Erinnerung: Die Methoden und Attribute der Objekte nennt man Instanzmethoden und Instanzvariablen. Von jeder Instanzvariable hat jedes Objekt
seine eigene, private Kopie. Instanzmethoden k¨onnen nur im Zusammenhang
mit einem Objekt verwendet werden, und aus Instanzmethoden ist der direkte
Zugriff auf die Instanzvariablen des Objektes m¨oglich.
Zum anderen kann auch eine Klasse selbst Methoden und Attribute besitzen. Die
der Klasse zugeordneten Methoden und Attribute nennt man Klassenmethoden und Klassenvariablen oder auch statische Methoden und statische
Variablen.
Von jeder Klassenvariable gibt es nur eine einzige Kopie pro Klasse, und jedes
Objekt der Klasse kann darauf zugreifen. Insofern teilen sich alle Objekte der
Klasse die Klassenvariablen. Da von jeder Klassenvariable nur ein Exemplar pro
Klasse vorhanden ist, greifen alle Objekte auf dieselben Klassenvariablen zu —
¨
die Anderung
einer Klassenvariable wirkt sich also auf alle Objekte aus, welche
diese Variable verwenden.
Ihnen ist sicher nicht klar, was wir mit dieser Unterscheidung in Instanzmethoden und Instanzvariablen auf der einen Seite und Klassenmethoden und Klassenvariablen auf der anderen Seite meinen. Nun, daß ein Objekt Methoden und
Variablen besitzt, sollte schon im vorigen Text hinreichend klar geworden sein.
Wieso aber sollte auch die Klasse Methoden und Variablen besitzen ? Daf¨
ur
gibt es einige Gr¨
unde:
• Eine Instanzmethode kann nur im Zusammenhang mit einem Objekt ausgef¨
uhrt werden. Manchmal ben¨otigt man aber Methoden, die ohne die
Existenz eines Objektes ausgef¨
uhrt werden k¨onnen. Ein Beispiel hierf¨
ur
ist die Ihnen schon bekannte main–Methode. Sie erst erm¨oglicht das Starten von Anwendungen, denn sie kann auch ausgef¨
uhrt werden, ohne daß
bereits Objekte angelegt wurden.
• Oft ben¨
otigen alle Objekte einer Klasse Zugriff auf gemeinsame, von den
Objekten unabh¨
angige Daten. Derartige Daten m¨ochte man nicht in den
97
Objekten selbst sondern an einer zentralen Stelle speichern, die irgendwie
zu den Objekten geh¨
ort. Hierzu verwendet man Klassenvariablen.
• Oft ben¨
otigt man in Objekten eine Funktionalit¨at, die zwar logisch zum
Objekt geh¨
ort, aber nicht auf den Zustand des Objektes angewiesen ist,
und die man daher nicht den Objekten sondern woanders zuordnen m¨ochte.
Hierzu verwendet man Klassenmethoden.
Wir werden Ihnen gleich erz¨
ahlen, wie man diese verschiedenen Arten von Methoden und Variablen definieren kann. Vorher aber ein Beispiel, das Sie jetzt
schon verstehen k¨
onnen m¨
ussten:
Beispiel:
Klasse Auto a
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/KapKlassen/Auto.java
Die obige Klasse definiert eine Klasse namens Auto.
In der Klasse sind drei Attribute definiert:
In Zeile 6 und 7 ist ein String–Attribut namens hersteller und ein int–
Attribut namens ps definiert. Da diese Variablen nicht als statisch deklariert sind, sind sie Instanzvariablen. Jedes Auto–Objekt hat eine eigene
Kopie dieser beiden Attribute. Dabei legen die Attribute den Hersteller
des Autos und die PS–Zahl fest.
In Zeile 4 ist eine als static ausgezeichnete double–Variable namens
kwpsfaktor definiert. Diese Variable wird in Zeile 4 mit einem Zahlenwert
initialisiert, welcher zur Umrechnung von Pferdest¨
arken in Kilowatt verwendet werden kann. Die Kennzeichnung als static macht aus kwpsfaktor
eine statische Variable bzw. eine Klassenvariable.
Der Grund, warum die Variablen hersteller und ps als Instanzvariablen
deklariert sind, ist klar — jedes Auto hat einen Hersteller und eine gewisse
PS–Zahl und braucht somit seine eigene Kopie dieser beiden Variablen.
Man kann diese Attribute von einem Auto nicht trennen.
Der Umrechnungsfaktor von PS–Werten zu Kilowatt hingegen ist f¨
ur jedes
Auto gleich. Er existiert unabh¨
angig von irgendwelchen Auto–Objekten,
und es w¨
are daher nicht angemessen, ihn zu einer Instanzvariable zu machen. Aus diesem Grund haben wir den Umrechnungsfaktor zu einer Klassenvariable gemacht. Damit k¨
onnen alle Auto–Objekte auf den Umrechnungsfaktor zugreifen, und es ist sichergestellt, daß alle Auto–Objekte
denselben Umrechnungsfaktor verwenden.
In Zeilen 9–12 sehen Sie eine Methode namens Auto. Ihnen sollte auffallen,
daß der Name dieser Methode mit dem Namen der Klasse u
¨bereinstimmt.
Derartige Methoden nennt man Konstruktoren (was das W¨
ortchen public
bedeutet, erz¨
ahlen wir Ihnen sp¨
ater noch). Ein Konstruktor wird beim
Erzeugen eines Objektes benutzt, um die Instanzvariablen des Objektes
zu initialisieren.
Wir verwenden den Konstruktor zweimal — in der main–Methode in Zeile
29 wird ein Auto–Objekt namens mercedes erzeugt, und in Zeile 32 wird
98
ein Auto–Objekt namens ferrari erzeugt. Beide Male wird der Konstruktor
verwendet. Im Kapitel 10 haben wir bereits erl¨
autert, wozu Konstruktoren
da sind. Vielleicht sollten Sie sich dieses Kapitel nochmal anschauen ?
In Zeilen 14–20 wird eine Methode void anzeigen() definiert. Dies ist eine Instanzmethode, welche ein Auto–Objekt anzeigen kann. Als Instanzmethode kann sie direkt auf die Attribute des Objektes zugreifen. Beim
Aufruf muß ihr daher ein Objekt angegeben werden.
Die anzeigen()–Methode wird in Zeile 30 und in Zeile 33 aufgerufen. Dabei wird sie einmal im Objekt mercedes und einmal im Objekt ferrari
aktiviert.
Beachten Sie bitte auch, daß die anzeigen()–Methode auf die Klassenvariable kwpsfaktor zugreifen kann — dies geschieht in Zeile 19.
In der anzeigen()–Methode wird in Zeile 17 eine Methode namens
wandlePSzuKW aufgerufen, um die PS–Zahl des Autos in den Kilowatt–
Wert umzuwandeln. Die wandlePSzuKW –Methode ist in Zeilen 22–25
definiert. Da sie als static ausgezeichnet ist, ist sie keine Instanz– sondern
eine Klassenmethode. Als solche kann sie auch auf alle Klassenvariablen
(in diesem Fall nur kwpsfaktor), nicht aber auf Instanzvariablen zugreifen.
Der Grund, daß die wandlePSzuKW –Methode als Klassenmethode und
nicht als Instanzmethode definiert wurde, sollte klar sein: Die Wandlung
eines Pferdest¨
arken in einen Kilowatt–Wert ist nicht von einem konkreten
Auto–Objekt abh¨
angig. Es w¨
are daher nicht angemessen, diese Methode
als Instanzmethode zu definieren.
Schließlich noch ein Wort zur main–Methode auf Zeile 27–34: Diese Methode ist durch die Definition als static eine Klassenmethode, ist also
nicht mit einem Objekt assoziiert. Die main–Methode macht die Klasse
ausf¨
uhrbar.
In der main–Methode werden zwei Objekte namens mercedes und ferrari
definiert, und es werden die anzeigen()–Methoden beider Objekte aufgerufen.
12.2
Zugriff auf Methoden und Attribute
In Kapitel 10.4 haben wir zwar bereits beschrieben, wie man auf die Bestandteile
von Objekten zugreift. Das soll hier nochmal rekapituliert werden:
Sie k¨
onnen sich die Java–Philosophie so vorstellen, daß ein Objekt Attribute und
Methoden enth¨
alt. Wenn man auf die Attribute zugreifen oder die Methoden
verwenden will, muß man in das Objekt hineingreifen. Dies geschieht in Java
durch den Punkt “.”. Will man auf etwas in einem Objekt zugreifen, schreibt
man hinter den Objektnamen einen Punkt und dahinter den Namen dessen,
worauf man zugreifen m¨
ochte.
Will man von außen auf ein Attribut attribut in einem Objekt einObjekt zugreifen oder eine Methode methode() aufrufen, schreibt man
einObjekt.attribut;
// oder
einObjekt.methode();
Genauso wie man auf die Inhalte eines Objektes zugreift, kann man auch auf
in einer Klasse enthaltene Klassenvariablen und Klassenmethoden zugreifen.
99
Auch hier benutzt man den Punkt. Wenn eine Klasse namens EineKlasse eine
Klassenvariable namens klassenVar oder eine Methode klassenMethode() kann
man hierauf von außen wie folgt zugreifen:
EineKlasse.klassenVar;
// oder
EineKlasse.klassenMethode();
Der Punkt dient also dazu, von außen auf die Inhalte eines Objektes oder einer
Klasse zuzugreifen. Ein Objekt kann auf seine Instanzvariablen, Klassenvariablen, Instanzmethoden und Klassenmethoden direkt zugreifen. Genauso k¨onnen
Sie in einer Klassenmethode direkt auf die Klassenattribute zugreifen.
Beispiel:
Die folgende Klasse verwendet die eben definierte Klasse Auto. Die
kompilierte Datei UseAuto.class sollte sich im gleichen Verzeichnis wie
Auto.class befinden, damit sie funktioniert:
Klasse UseAuto a
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/KapKlassen/UseAuto.java
In Zeile 5 wird ein Auto–Objekt namens vw erzeugt. Auf dessen Attribute
wird in Zeilen 7–8 zugegriffen.
In Zeile 10 und 11 werden die Attribute des Objektes von außen ge¨
andert.
Der dann erfolgte Aufruf der anzeigen()–Methode beweist, daß wir von
außen den Zustand des Objektes ver¨
andert haben.
In Zeile 15–16 greifen wir von außen auf die Klassenvariable kwpsfaktor
zu. In Zeile 18 erdreisten wir uns sogar, diese von außen auf einen falschen
Wert zu setzen.
Da die Umrechnungsroutine der Auto–Klasse auf diese Klassenvariable
angewiesen ist, haben wir damit auch unser vw–Objekt beeinflußt — ab
sofort funktioniert die Umrechnung von PS in KW nur noch fehlerhaft,
wie der Aufruf der anzeigen()–Instanzmethode in Zeile 21 beweist.
Sie sehen am vorigen Beispiel, daß man durch Eingriffe von außen die Funktionsf¨
ahigkeit einer Klasse beeintr¨achtigen kann. Java bietet Mechanismen an,
die derartige Eingriffe in das Innenleben von Klassen und Objekten abwehren.
In einem sp¨
ateren Kapitel werden Sie lernen, wie man “wehrhafte” Objekte und
Klassen entwickeln kann, die Eingriffe von außen abweisen.
12.3
Die Bestandteile einer Java–Klasse
Nun endlich zum Aufbau einer Java–Klasse: Jede Java–Klasse beginnt mit der
Definition der Klasseneigenschaften — dem Klassenkopf — gefolgt von einem
Block, in welchem die Klasse definiert wird — dem Klassenk¨orper. Vorerst sollten Sie davon ausgehen, daß jede Klasse in einer eigenen Datei enthalten ist.
Der Name dieser Datei ist der Klassenname gefolgt von der Endung “.java”.
Die Klassendefinition hat in ihrer einfachsten Form den Aufbau
100
class KlassenName // Klassenkopf
{
// Klassenk¨
orper
}
Die Beschreibung der Klasseneigenschaften im Klassenkopf werden wir sp¨ater
noch erg¨
anzen. Beispielsweise k¨onnte eine kompliziertere Klasse auch die folgende Eigenschaftsbeschreibung haben:
public class MyClass extends AnotherClass implements MyInterface
{
// Klassenk¨
orper
}
Eine Klassendefinition besteht also aus zwei Teilen: Dem Klassenkopf, in welchem gewisse sp¨
ater zu erl¨
auternde Eigenschaften der Klasse beschrieben werden
und dem Klassenk¨
orper, in welchem die Klasse selbst definiert wird.
In der einfachsten Version besteht der Klassenkopf aus dem Text
class KlassenName
Das Wort class weist den Java–Compiler darauf hin, daß nun die Definition
einer Klasse folgt. Durch den hinter dem Wort class stehenden Namen erf¨ahrt
der Compiler den Namen der zu definierenden Klasse. Der Klassenkopf veranlasst den Java–Compiler, den darauf folgenden Block als Definition der im Kopf
benannten Klasse zu interpretieren.
Beachten Sie, daß sich der Klassenk¨orper von den Ihnen bereits bekannten Anweisungsbl¨
ocken unterscheidet — der Klassenk¨orper selbst enth¨alt n¨amlich im
Gegensatz zu den Anweisungsbl¨ocken keine Anweisungen sondern eine Folge von
Definitionen.
Abstrakt ist ein Klassenk¨
orper wie folgt aufgabaut:
class KlassenName {
Definition_1
Definition_2
...
Definition_n
}
Die einzelnen Definitionen definieren dabei entweder Attribute oder Methoden.
Es ist auch m¨
oglich, in einer Klasse andere Klassen zu definieren, aber das soll
zun¨
achst außer acht gelassen werden.
Es gibt keine vorgeschriebene Reihenfolge f¨
ur die einzelnen Definitionen — es ist
also nicht n¨
otig, alle Attribute vor allen Methoden zu definieren oder umgekehrt.
Methoden– und Attribut–Definitionen d¨
urfen in beliebiger Reihenfolge stehen.
Es empfiehlt sich allerdings, die einzelnen Definitionen zusammenzulassen. Meist
definiert man zuerst alle Attribute und danach alle Methoden.
Wie die Methoden- und Attributdefinitionen aufgebaut sein m¨
ussen, wird in den
n¨
achsten Abschnitten erkl¨
art.
101
12.4
Attribute
Ein Attribut wird genauso definiert wie eine Variable: Es wird sein Datentyp
oder seine Klasse gefolgt von seinem Namen angegeben. Die Definition eines
Attributs muß mit einem Semikolon beendet werden.
Wenn die Objekte der Klasse eine Instanzvariable namens instanzVariable des
Datentyps Datentyp haben sollen, so wird diese wie folgt definiert (ein funktionierendes Beispiel folgt weiter unten):
Datentyp
instanzVariable;
Soll statt einer Instanzvariable eine Klassenvariable definiert werden, so schreibt
man vor die Definition das Wort “static”. Soll beispielsweise eine Klassenvariable
klassenVariable des Typs Datentyp definiert werden, so schreibt man
static Datentyp klassenVariable;
Genau wie alle Variablendefinitionen kann auch ein Klassenattribut oder ein
Instanzattribut schon bei der Definition initialisiert werden. Dazu schreibt man
hinter den Namen des Attributes direkt die Initialisierungszuweisung:
Datentyp
instanzVariable = Initialisierung;
static Datentyp klassenVariable = Initialisierung;
Wird auf diese Weise eine Instanzvariable initialisiert, so erh¨alt diese Instanzvariable bei jedem neu erzeugten Objekt den angegebenen Wert erh¨alt. Wird
hingegen eine Klassenvariable auf diese Weise initialisiert, so erh¨alt die Klassenvariable noch vor der ersten Verwendung der Klasse den angegebenen Wert.
Das folgende Beispiel demonstriert die Definition von Instanzvariablen:
Beispiel:
Hier wird eine Klasse Punkt3D definiert, deren Objekte Punkte im dreidimensionalen Raum repr¨
asentieren. Zun¨
achst definieren wir keine Methoden sondern nur die Attribute der Objekte.
Objekte dieser Klasse erhalten drei double–Instanzvariablen namens x,y
und z, welche die Koordinaten des Punktes beschreiben.
Die Klasse ist in dieser Form kompilierbar aber nicht ausf¨
uhrbar, da sie
keine main–Methode besitzt.
Klasse Punkt3Da
1
2
3
4
5
6
7
8
class Punkt3Da {
// Koordinaten:
double x;
double y;
double z;
}
Falls wir m¨
ochten, daß jeder neu angelegte Punkt einen vorher festgelegten Zustand erh¨
alt, k¨
onnen wir dies dadurch erreichen, daß wir jede
Instanzvariable schon bei der Definition initialisieren.
102
Wenn wir uns entscheiden, jeden neu angelegten Punkt auf die Koordinaten (0, 0, 0) zu initialisieren, k¨
onnen wir das so erreichen:
Klasse Punkt3D
1
2
3
4
5
6
7
8
class Punkt3D {
// Koordinaten:
double x = 0;
double y = 0;
double z = 0;
}
Beispiel: Hier noch ein Beispiel ohne tieferen Sinn, in welchem auch
Klassenvariable verwendet werden:
Klasse InstKlassVar
1
2
3
4
5
6
7
8
9
class InstKlassVar {
static double klassenVar1 = 0;
static double klassenVar2 = 1.3;
double instanzVar1 = 3;
double instanzVar2 = 5;
}
Und noch eine hoffentlich u
ussige Bemerkung: Als Attribute k¨onnen nat¨
urlich
¨berfl¨
nicht nur primitive Daten sondern auch Objekte anderer Klassen verwendet werden.
Beispiel:
Das folgende Beispiel verwendet die oben definierte Klasse Punkt3D, um
eine Linie von einem Punkt zum anderem im dreidimensionalen Raum zu
beschreiben.
Eine Linie ist dabei gegeben durch zwei Eckpunkte, welche wir punkt1
und punkt2 nennen, und welche Objekte der Klasse Punkt3D sind:
Klasse Linie
1
2
3
4
5
12.5
class Linie {
Punkt3D punkt1, punkt2;
}
Definition von Methoden
Auch eine Methodendefinition besteht aus zwei Teilen, n¨amlich dem Methodenkopf und dem Methodenk¨
orper. Abstrakt hat eine Methodendefinition folgenden
Aufbau:
Methodenkopf {
103
// Methodenk¨
orper
}
Dabei beschreibt der Methodenkopf mindestens
• den Namen der Methode,
• welche Werte die Methode zur Ausf¨
uhrung ben¨otigt,
• den Typ der von der Methode zur¨
uckgelieferten Daten
• und ob es sich um eine Instanzmethode oder eine Klassenmethode handelt.
Der Methodenk¨
orper besteht aus einer Folge von Anweisungen, welche bei jeder
Ausf¨
uhrung der Methode abgearbeitet werden.
Beispiel: Die wohl einfachste Methode wird so definiert:
void nichtsTun() {
}
Diese Methode hat einen leeren Methodenk¨
orper und tut daher beim Aufruf gar nichts.
Der Methodenkopf legt fest, daß die Methode den Namen “nichtsTun”
hat. Das Wort “void” bedeutet, daß sie keinen Wert zur¨
uckliefert.
Der Methodenkopf ist in Java genauso wie in Kapitel ?? beschrieben aufgebaut. Eine Instanzmethode namens methodenName, welche einen Wert des Typs
RueckgabeTyp zur¨
uckliefert und zur Ausf¨
uhrung Werte der Datentypen Typ1
bis TypN ben¨
otigt, welche dann in der Methode unter den Bezeichnern wert1
bis wertN angesprochen werden k¨onnen, wird wie folgt definiert:
RueckgabeTyp instanzMethode( Typ1 wert1, ... TypN wertN )
Falls eine Methode keinen Wert zur¨
uckliefern soll, wird als R¨
uckgabe–Typ der
Typ void verwendet (eng. void: leer, ung¨
ultig). Die zur Ausf¨
uhrung der Methode
ben¨
otigten Werte wert1 bis wertN nennt man auch formale Parameter der
Methode.
Soll eine Methode nicht als Instanz– sondern als Klassenmethode definiert werden, so ist vor die Methodendefinition noch das Wort “static” zu setzen:
static RueckgabeTyp klassenMethode( Typ1 wert1, ... TypN wertN )
Vom Aufbau unterscheiden sich Klassen– und Instanzmethoden ansonsten nicht
— der Unterschied ist der, daß innerhalb von Instanzmethoden auf alle Instanzvariablen eines Objektes sowie auf alle Klassenvariablen der Klasse zugegriffen
werden kann, w¨
ahrend aus Klassenmethoden nur auf die Klassenvariablen, nicht
aber auf Instanzvariablen zugegriffen werden kann.
Beispiel: Hier einige Beispiele f¨ur g¨ultige Methodenk¨opfe:
104
1
2
3
4
5
void anzeigen()
static void zeilenVorschub( int anzahlZeilen )
static double summiere( double[] werte )
double[] sortiere( double[] werte )
static int searchIndex( String[] stringListe, String s2 )
Beschreibung der obigen Methodenk¨
opfen:
1. Eine Instanzmethode namens anzeigen, welche keine Argumente entgegennimmt und keinen Wert zur¨
uckliefert.
2. Eine Klassenmethode namens zeilenVorschub, welche einen Parameter vom Typ int entgegennimmt, welcher in der Methode unter dem
Namen anzahlZeilen angesprochen werden kann. Diese Methode liefert keinen Wert zur¨
uck.
3. Eine Klassenmethode namens summiere, welche ein Array von
double–Werten entgegennimmt und einen double–Wert zur¨
uckliefert. Das Array kann im Methodenk¨
orper unter der Variabel werte
angesprochen werden.
4. Eine Instanzmethode namens sortiere, welche ein double–Array
zur¨
uckliefert und ein Array aus double–Werten namens werte entgegennimmt.
5. Eine Klassenmethode namens searchIndex, welche ein String–Array
namens stringListe und einen String namens s2 entgegennimmt und
eine int–Wert zur¨
uckliefert.
Der Methodenk¨
orper besteht aus einer Folge von Anweisungen. Wenn im Metho¨
denkopf Ubergabeparameter
beschrieben sind, so kann im Methodenk¨orper auf
diese genauso wie auf alle anderen Variablen zugegriffen werden. Dabei werden
die formalen Parameter bei jedem Aufruf der Methode durch die Aufrufsparameter ersetzt.
Ist eine Methode nicht als void gekennzeichnet, so muß sie ein Ergebnis an
den Aufrufer zur¨
uck¨
ubermitteln. Dazu wird der return–Befehl verwendet. Der
return–Befehl gibt einen Wert zur¨
uck und beendet dabei die Bearbeitung der
Methode.
In einer Methode darf der return–Befehl beliebig oft verwendet werden. Es dient
¨
aber normalerweise nicht der Ubersichtlichkeit
und dem besseren Verst¨andnis
des Programmes, wenn man viele return–Befehle in einer Methode benutzt.
Abstrakt wird der return–Befehl wie folgt benutzt:
return Wert;
Der zur¨
uckgegebene Wert muß dabei den Datentyp haben, welchen die Methode
zur¨
uckliefern soll.
Beispiel: Die folgende Methode w¨urde zu einem Kompilierfehler f¨uhren,
denn die Methode ist als int deklariert. Der Wert, der im return–Befehl
zur¨
uckgegeben wird, ist aber ein double–Wert.
int rechne( int wert ) {
return wert * 1.355;
}
105
So w¨
are die Methode korrekt:
int rechne( int wert ) {
return (int) (wert * 1.355);
}
Der return–Befehl darf auch in einer als void deklarierten Methode verwendet
werden, um die Methodenbearbeitung abzubrechen und zum Aufrufer der Methode zur¨
uckzukehren. In einer void–Methode muß allerdings nicht unbedingt
ein return stehen, denn am Ende eines jeden Methodenk¨orpers wird vom Java–
Compiler automatisch ein return eingef¨
ugt.
In einer void–Methode darf kein Wert zur¨
uckgegeben werden. Die Verwendung
des return–Befehles in einer void–Methode sieht so aus:
return;
Beispiel: Hier ein Beispiel f¨ur eine void–Methode, in der mehrere return–
Befehle auftauchen. Wenn man sehr viele Fallunterscheidungen hat, kann
es Sinn machen, mehrere return–Befehle in einer Methode zu haben. Normalerweise sollten Sie das jedoch vermeiden. In dieser einfachen Methode
ist die Verwendung mehrerer return–Befehle kein guter Programmierstil:
/** Beispiel f¨
ur unsch¨
one Verwendung mehrerer return--Befehle
*/
void intervallausgabe( int i ) {
if( i < 0) return;
System.out.println("i ist >= 0");
if( i < 10) return;
System.out.println("i ist >= 10");
if( i < 20) return;
System.out.println("i ist >= 20");
}
Hier folgt die Methode nochmal ohne Verwendung von return–Befehlen.
Durch die Verwendung geschachtelter if –Befehle wird die Struktur der
Methode im Gegensatz zur ersten Version auf den ersten Blick klar.
void intervallausgabe( int i ) {
if( i >= 0) {
System.out.println("i ist >= 0");
if( i >= 10) {
System.out.println("i ist >= 10");
if( i >= 20) {
System.out.println("i ist >= 20")
}
}
}
}
106
Nun sollen noch einige Beispiel–Methoden gezeigt und besprochen werden.
Beispiel: Die folgende Klasse enth¨alt eine Klassenmethode namens
quadriereWerte und eine Klassenmethode namens durchschnitt:
Klasse DemoMethode a
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/KapKlassen/DemoMethode.java
Die Klassenmethode quadriereWerte ist als void deklariert und hat einen
formalen Parameter, der innerhalb der Methode als werte angesprochen
werden kann und vom Typ double[] ist. Da die Methode als void deklariert ist, muß in ihr kein return–Befehl stehen. Auf den formalen Parameter kann in der Methode (z.B. in Zeile 5) zugegriffen werden wie auf jede
andere Variable auch.
In Zeilen 25 und 26 wird die Methode verwendet. Dabei wird anstelle des
formalen Parameters werte einmal die Variable w1 und einmal die Variable w2 eingesetzt. In der Methode wird dann der Inhalt der Parameter
ver¨
andert.
Auch die durchschnitt–Methode hat einen formalen Parameter namens
werte. Da sie keine void–Methode ist, muß sie mit return einen Wert
zur¨
uckliefern.
Beispiel: Nun noch ein Beispiel mit einigen Instanzmethoden:
Wir erweitern die Punkt3D–Klasse aus dem vorigen Abschnitt um zwei
Methoden: Die Methode show zeigt einen Punkt auf dem Bildschirm an,
die Methode norm berechnet die Euklidische Norm des Punktes, die Methode move verschiebt den Punkt um eine gewisse Distanz, indem die
Koordinaten eines anderen Punktes addiert werden:
Klasse Punkt3D a
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/KapKlassen/Punkt3D.java
Beispiel: Und hier noch ein Beispiel, welches das vorige Beispiel verwendet. Die Klasse Linie3D muß im selben Verzeichnis gespeichert sein wie
die Klasse Punkt3D.
Klasse Linie3D a
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/KapKlassen/Linie3D.java
12.6
Mehrere Methoden mit gleichem Namen
Java hat eine F¨
ahigkeit, die nur wenige andere Sprachen bieten: Es ist m¨oglich,
mehrere Methoden mit gleichem Namen zu benutzen. Damit Java weiß, welche
107
Methode bei einem Aufruf gemeint ist, m¨
ussen sich die Methoden dann durch
die geforderten Parameter unterscheiden.
Beispiel: In der folgenden Klasse ist die Methode max zweimal definiert
— einmal als Methode, welche zwei int–Werte akzeptiert und einmal als
Methode, welche zwei double–Werte akzeptiert.
Klasse MethodenDemo a
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/KapKlassen/MethodenDemo.java
Immer wenn Java einen Methodenaufruf sieht, geht es folgendermaßen vor:
• Zuerst schaut es nach, ob in der Klasse eine Methode definiert ist, die zu
den aktuellen Parametern im Methodenaufruf stehen.
Im obigen Beispiel geschieht der Aufruf in Zeile 20 mit zwei int–Werten
und in Zeile 21 mit zwei double–Werten. Dem Compiler ist also sofort
klar, daß der Aufruf in Zeile 20 die erste max–Methode auf Zeile 3–9 und
der Aufruf in Zeile 21 die max–Methode auf Zeile 11–17 meint.
• Wenn auf die Parameter im Methodenaufruf keine Methode passt, so
schaut der Compiler nach, ob er die Argumente im Methodenaufruf in
einen anderen Typ verwandeln kann, so daß der Aufruf dann auf eine Definition paßt. Beim Wandeln der Typen werden die Regeln aus Kapitel 8
verwendet.
Im obigen Beispiel werden im Aufruf in Zeile 22 ein int und ein double–
Wert verwendet. Eine solche Methode kennt der Compiler nicht. Er kann
aber den int–Wert in einen double–Wert verwandeln und dann die double–
Methode verwenden.
Kurz gesagt: Sie k¨
onnen beliebig viele Methoden des gleichen Namens schreiben,
wenn es eine eindeutige M¨
oglichkeit gibt, anhand der u
¨bergebenen Parameter
herauszufinden, welche Methode gemeint ist.
Sie k¨
onnen insbesondere nicht zwei Methoden schreiben, die sich nur durch den
R¨
uckgabewert unterscheiden. Warum ist das so ? Nun, man muß ja den R¨
uckgabewert einer Methode nicht unbedingt verwenden. Zum Beispiel ist folgender
Programmtext legal:
int rechne(int i) {
return i * 2;
}
public static void main(String[] args) {
rechne(5);
reche(3);
}
Wenn Sie nun mehrere Methoden definieren, die die gleichen Parameter definieren aber unterschiedliche R¨
uckgabewerte haben, h¨atte Java keine M¨oglichkeit
herauszufinden, welche Methode Sie meinen.
108
Beispiel: Das folgende Beispiel w¨urde nicht kompilieren, da sich die beiden Methoden nur durch den R¨
uckgabewert unterscheiden.
public class MethodenDemo2 {
int rechne( int i ) {
return i*2;
}
double rechne( int i ) { // Fehler
return i*3.0;
}
}
12.7
Konstruktoren
Konstruktoren sind besondere Methoden, welche beim Erzeugen von Objekten benutzt werden. Jedesmal wenn ein Objekt erzeugt wird, wird vom Java–
Laufzeitsystem zun¨
achst Speicher f¨
ur das Objekt bereitgestellt, d.h. f¨
ur jede
Instanzvariable des Objektes wird Speicher belegt.
Anschließend wird ein Konstruktor aufgerufen. Der Konstruktor dient dazu,
die Instanzvariablen des Objektes zu initialisieren. Ein Konstruktor ist eine
Methode, deren Namen gleich dem Namen der Klasse ist.
Eine Klasse darf mehrere Konstruktoren besitzen. Welcher Konstruktor bei der
Erzeugung eines Objektes aufgerufen wird, richtet sich nach den beim Erzeugen
des Objektes durch den new–Operator u
¨bergebenen Parametern
Beispiel: Die folgende Klasse PunktND repr¨asentiert einen Punkt im
N –dimensionalen Raum. Sie enth¨
alt drei Konstruktoren:
Klasse PunktND a
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/KapKlassen/PunktND.java
Der erste Konstruktor, PunktND() wird in Zeilen 9–14 definiert. Er wird
in Zeile 39 aufgerufen. Beachten Sie bitte, daß in Zeile 39 zwar durch den
new–Operator ein neues Objekt angelegt wird; dieses wird aber keiner
Variable zugewiesen.
Der zweite Konstruktor, PunktND( double[] ) wird in Zeilen 15–20 definiert. Er wird in Zeile 42 verwendet.
In
Zeile
22–27
wird
ein
weiterer
Konstruktor,
PunktND(double,double,double) definiert, welcher auf Zeile 45 verwendet wird. Auch hier wird das durch den new–Operator erzeugte
Objekt keiner Variable zugewiesen.
12.8
Die Parameteru
¨ bergabe an eine Methode
Wie werden Parameter an eine Methode u
¨bergeben ? Das wollen wir am folgenden Beispielprogramm besprechen:
109
Beispiel:
Klasse ParamUebergabe a
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/KapKlassen/ParamUebergabe.java
Wenn Sie das Programm starten, erhalten Sie folgende Ausgabe:
a ist 3
a ist jetzt 5
In main ist i = 3
a[0] ist 2
a[0] ist jetzt 5
In main ist arr[0] = 5
¨
Wie Sie sehen, wirkt sich die Anderung
des Parameters a innerhalb der ersten
change–Methode nicht auf die Variable i im Hauptprogramm aus.
¨
Hingegen wirkt sich die Anderung
am Array in der zweiten change–Methode
auf das Hauptprogramm aus.
Den Grund f¨
ur dieses unterschiedliche Verhalten haben wir bereits in einem vorigen Kapitel erkl¨
art — in Kapitel 11.5 haben wir Ihnen den Unterschiend zwischen Referenztypen und primitiven Typen erl¨autert. Genau dieser Unterschied
ist es, der bewirkt, daß das Array in der Methode ver¨andert, die int–Variable
jedoch nicht.
Wenn Java einen Methodenaufruf sieht, so erzeugt es f¨
ur jeden Parameter der
Methode eine Variable und weist diesen die u
¨bergebenen Variablen zu.
Wenn Sie sich noch an die Ausf¨
uhrungen u
¨ber die Referenztypen erinnern, sollte
Ihnen nun sofort einleuchten, warum die Variable des primitiven Datentyps nicht
ver¨
andert und die Array-Variable ver¨andert wurde.
12.9
Zusammenfassung
Sie sollten nun die folgenden Fragen beantworten k¨onnen:
• Was ist der Unterschied zwischen Klassen- und Instanzvariablen ?
• Was ist der Unterschied zwischen Klassen- und Instanzmethoden ?
• Auf welche Variablen kann in einer Klassenmethode zugegriffen werden ?
Auf welche Variablen kann in einer Instanzmethode zugegriffen werden ?
• Wie greift man auf eine Klassenvariable zu, wie greift man auf eine Instanzvariable zu ?
• Wie unterscheidet sich ein Klassenk¨orper von einem Anweisungsblock ?
• Wie definiert man Klassenvariablen und Instanzvariablen ?
• Wie unterscheiden sich Klassenmethoden und Instanzmethoden ?
110
• Wie definiert man einen Konstruktor und was tut er ?
• Unter welchen Umst¨
anden d¨
urfen innerhalb einer Klasse mehrere gleichnamige Instanz- oder Klassenmethoden definiert sein ?
• Besprechen Sie die Unterschiede zwischen Primitiven Datentypen und Referenztypen bei der Wert¨
ubergabe an eine Methode.
111
13
Dokumentieren von Java–Programmen
Es ist sehr wichtig, seine Programme zu dokumentieren, denn wenn Sie Ihre Programme in einigen Monaten nochmal anschauen, wissen Sie meist nicht mehr,
wozu diese oder jene Klasse da ist und was welche Methode oder Variable tut.
¨
Ublicherweise
schreibt man seine Dokumentation in den Klassencode. Wenn
man dann sp¨
ater etwas u
¨ber eine Klasse in Erfahrung wissen will, kann man
den Quelltext ¨
offnen und lesen.
Zur Erzeugung u
ur Java das Werk¨bersichtlicher Dokumentation gibt gibt es f¨
zeug javadoc. Diese extrahiert die Dokumentation aus einer Klasse und schreibt
sie in Html–Dateien, welche dann mit einem WWW–Browser betrachtet werden
k¨
onnen. Auf diese Weise kann man seine eigenen Klassen mit einer Hypertext–
Dokumentation16 versehen.
Sie haben bereits die Dokumentation des Java–API kennengelernt. Sie wurde
mit diesem Werkzeug erzeugt. W¨are das Java–API nicht in Form von HTML–
Dateien oder einem anderen Hypertext–Format dokumentiert, w¨are es sicher
schwerer, sich darin zurechtzufinden.
13.1
Wie arbeitet javadoc ?
Javadoc nimmt eine oder mehrere Java–Quelltextdateien entgegen und erzeugt
aus diesen Dokumentation im HTML–Format. Die erzeugten Dateien dokumentieren die angegebenen Klassen, Methoden und Instanzvariablen. Zur Dokumentation werden dabei in den Quelltexten gefundene besondere Kommentare
verwendet, welche man auch javadoc–Komentare nennt.
Ein javadoc–Kommentar sieht aus wie ein normaler mehrzeiliger Kommentar,
nur beginnt er nicht mit den Zeichen /* sondern mit den Zeichen /**.
Ein Javadoc–Kommentar k¨
onnte so aussehen:
/**
*
*/
Ein Javadoc -- Komentar
Jeder gefundene Javadoc–Kommentar wird von Javadoc als Dokumentation der
nachfolgenden Klasse, Instanzvariable, Klassenvariable oder Methode interpretiert.
Um javadoc zu starten, geben Sie auf der Kommandozeile den Befehl “javadoc”
gefolgt von den Namen aller zu verarbeitenden Dateien ein:
> javadoc datei1.java datei2.java ...
Um alle Dateien in einem Verzeichnis zu dokumentieren k¨onnten Sie eingeben:
> javadoc *.java
Beispiel: Am besten schauen wir uns die Arbeit von javadoc an einem
Beispiel an. Die folgende Klasse tut keine sinnvollen Dinge, zeigt aber den
Einsatz von javadoc–Kommentaren:
16 Unter Hypertext versteht man einen Text, in welchem man sich mit Hilfe von Mausklicks
zwischen einzelnen Textteilen bewegen kann
112
Klasse DemoDoc a
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/KapJavaDoc/DemoDoc.java
Bitte speichern Sie die obige Datei in einem eigenen Verzeichnis, wechseln
in das Verzeichnis und geben dann ein:
> javadoc -private DemoDoc.java
Hieraufhin werden mehrere HTML–Dateien erzeugt, von denen uns
zun¨
achst nur die DemoDoc.html–Datei interessiert. Sie beschreibt die
Klasse. Am besten sehen Sie sich die Datei in einem WWW–Browser an.
Schauen Sie sich auch die anderen erzeugten Dateien an.
Die Option -private im obigen Befehl haben wir deshalb verwendet, weil
Sie sich vermutlich noch nicht mit dem Zugriffsschutz von Objektattributen auskennen — normalerweise l¨
aßt man das Wort -private weg. Dann
erscheinen in der Html–Ausgabe nur Methoden und Variablen, die entweder als “public” oder als “protected” definiert sind.
Im f¨
unften Hausaufgabenblock ist ein Beispiel mit mehreren Klassen enthalten,
welches mit javadoc dokumentiert ist. Nachdem Sie dieses Beispiel durchgearbeitet haben, sollte Ihnen der Umgang mit Javadoc sofort einsichtig werden.
Zu Javadoc gibt es in der von Sun bereitgestellten Dokumentation des Java–API
weitere Informationen, die hier nicht wiederholt werden sollen. Wenn Sie selber
Javadoc–Kommentare schreiben wollen, schauen Sie dabei am besten entweder
auf der lokalen WWW–Seite
http://www.tu-bs.de:82/wir/EIP/jdk1.1.8/docs/tooldocs/solaris/javadoc.html
oder auf der WWW–Seite von Sun auf
http://www.javasoft.com/products/jdk/1.1/docs/tooldocs/solaris/javadoc.html
nach.
13.2
Zusammenfassung
Nach Lesen dieses Kapitels
• sollten Sie wissen, was javadoc tut,
• wie man javadoc einsetzt,
• wie es prinzipiell arbeitet und
• wo Sie weitere Informationen zu javadoc erhalten k¨onnen.
113
14
Vererbung — Extensionen von Klassen
Sie haben bisher Klassen als Mittel kennengelernt, um Konzepte im Computer
als Einheit von Daten und Verhalten zu repr¨asentieren. Die objektorientierte
Methodik w¨
are heute nicht so verbreitet, wenn das Zusammenf¨
ugen von Daten
und Methoden der einzige Zweck von Klassen w¨are.
Die objektorientierte Methodik wird erst dadurch zu einem m¨achtigen Werkzeug, daß wir gleichartiges Verhalten verschiedener Klassen in einer abstrakteren
Klasse zusammenfassen k¨
onnen.
Um mehrere Klassen zu einer abstrakteren zusammenzufassen, suchen wir nach
Klassen, die im Rahmen unserer Anwendung a¨hnliche Aufgaben haben oder
a
¨hnliche Dinge beschreiben. Dann abstrahieren wir von diesen Klassen und erarbeiten ein gedankliches Konzept, welches die entscheidenden Merkmale und
das gemeinsame Verhalten dieser Klassen enth¨alt.
Dieses Konzept implementieren wir dann als Klasse. Dabei wird alles Verhalten, das den urspr¨
unglichen Klassen gemeinsam ist, in der abstrakteren, u
¨bergeordneten Klasse implementiert. Das gemeinsame Verhalten muß dann nicht
mehrfach in jeder der spezielleren Klassen implementiert werden sondern kann
von der abstrakteren Klasse u
¨bernommen (man sagt auch “vererbt”) werden.
Indem man Verhalten vererbt, spart man sich nicht nur viel Programmierarbeit;
die Programme werden auch u
¨bersichtlicher und leichter wartbar.
14.1
Ein einfu
¨ hrendes Beispiel
Angenommen, wir schreiben ein Spiel, in welchem die Hauptfigur verschiedene
Fahrzeuge wie Auto, LKW, Fahrrad oder Kutsche verwenden soll. Jedes dieser Fahrzeuge kann von der Hauptfigur des Spiels gefahren werden, und die
motorisierten Fahrzeuge m¨
ussen tanken, sobald der Tank leer ist.
Wir k¨
onnten nun 4 verschiedene Klassen bilden:
Auto
Fahrrad
LKW
Kutsche
double geschwindigkeit
double tankFuellstand
double geschwindigkeit
double tankFuellstand
double geschwindigkeit
double geschwindigkeit
void fahren()
void tanken()
void fahren()
void tanken()
void fahren()
void fahren()
All diese Klassen beschreiben ein Fahrzeug, und vermutlich w¨are die fahren()–
Methode in allen Klassen ¨
ahnlich. Wir k¨onnen daher diese vier Klassen in
einer abstrakteren Klasse zusammenfassen, welche wir Fahrzeug nennen. Die
Fahrzeug–Klasse w¨
urde dabei alle Attribute und Methoden enthalten, welche
mit dem eigentlichen Vorgang des fahrens zusammenh¨angen. Sie enthielte also
das Attribut geschwindigkeit und die Methode fahren().
Auch die tanken()–Methode in den Klassen Auto und LKW w¨aren ann¨ahernd
identisch (sie w¨
urde den F¨
ullstand des Tanks hochsetzen). Wir k¨onnen daher
die Attribute und Methoden, die mit dem Tanken zusammenh¨angen in einer
gemeinsamen Klasse namens Motorisiertes Fahrzeug zusammenfassen.
Eine Klasse Unmotorisiertes Fahrzeug w¨
urden wir u
uhren, da
¨brigens nicht einf¨
wir in den Klassen Fahrrad und Kutsche kein eigenst¨andiges Verhalten beschrieben haben, das man allen unmotorisierten Fahrzeugen zuschreiben k¨onnte.
114
Das Verh¨
altnis zwischen all diesen Klassen sieht nun wie folgt aus: Jedes Auto
und jeder LKW ist ein Motorisiertes Fahrzeug. Jedes Motorisierte Fahrzeug
ist auch ein Fahrzeug. Wir meinen hiermit, daß jede Instanz der Klasse Auto
und jedes LKW –Objekt gleichzeitig auch eine Instanz der Klasse Motorisiertes
Fahrzeug ist. Jedes Objekt der Klasse Motorisiertes Fahrzeug ist gleichzeitig ein
Objekt der Klasse Fahrzeug.
Auch jedes Objekt der Klasse Fahrrad oder Kutsche ist gleichzeitig ein Objekt
der Klasse Fahrzeug.
Wir k¨
onnen diese Beziehungen in einem Diagramm darstellen. Wenn jede Instanz der Klasse B auch eine Instanz der Klasse A ist, machen wir einen Pfeil
von Klasse B zu Klasse A. Das resultierende Klassendiagramm sieht wie folgt
aus:
Fahrzeug
double geschwindigkeit
void fahren()
Motorisiertes Fahrzeug
Fahrrad
double tankFuellstand
Kutsche
void tanken()
Auto
LKW
Wir verwenden im weiteren folgende Begriffe:
Elternklasse/Vorfahre Eine Klasse A ist eine Elternklasse oder ein Vorfahre von
Klasse B, wenn jedes Objekt von Klasse B gleichzeitig
Objekt von Klasse A ist.
Im vorigen Beispiel ist die Klasse Motorisiertes Fahrzeug
Elternklasse von Auto und LKW . Die Klasse Fahrzeug
ist eine Elternklasse der Klassen Motorisiertes Fahrzeug,
Fahrrad Kutsche, Auto und LKW .
Dabei ist die Fahrzeug–Klasse nur indirekter Vorfahre
der Klassen Auto und LKW aber direkter Vorfahre der
Klassen Fahrrad, Kutsche und Motorisiertes Fahrzeug.
Super–Klasse
Ein anderer Begriff f¨
ur Elternklasse oder Vorfahre.
Erweiterung
Eine Klasse B ist eine Erweiterung von Klasse A, wenn
Klasse A eine Elternklasse f¨
ur Klasse B ist.
Im obigen Beispiel ist die Fahrrad–Klasse eine Erweiterung der Fahrzeug–Klasse; die Auto–Klasse ist eine
Erweiterung der Klasse Motorisiertes Fahrzeug und eine Erweiterung der Klasse Fahrzeug.
Kindklasse
Ein anderer Begriff f¨
ur Erweiterung.
115
Extension
Ein anderer Begriff f¨
ur Erweiterung.
erben
Eine Klasse B erbt von Klasse A, wenn sie eine Erweiterung von Klasse A ist. Wenn eine Kindklasse von einer
Elternklasse erbt, so u
¨bernimmt sie alles in der Elternklasse beschriebene Verhalten.
Wir haben ein Java–Programm vorbereitet, welches die obige Klassenhierarchie
implementiert. Es ist allerdings zum Abdrucken etwas lang — wir besprechen
es mit weiteren Beispielen in Kapitel 14.6.
14.2
Erweitern von Klassen
Eine Erweiterung einer Klasse Elternklasse wird definiert, indem im Klassenkopf
der Text extends Elternklasse angef¨
ugt wird:
class Kindklasse extends Elternklasse {
// Klassenkoerper
}
Der Effekt ist ungef¨
ahr der gleiche als h¨atten Sie alle Definitionen aus dem
Klassenk¨
orper der Elternklasse in Ihre neue Klasse Kindklasse kopiert.
Die Kindklasse enth¨
alt dann alle in der Elternklasse vorgenommenen Definitionen plus die im Klassenk¨
orper der Kindklasse get¨atigten Definitionen.
Beispiel:
Beispiel: Erweitern einer Klasse
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Point {
double x, y;
void move(double dx, double dy) {
x+=dx;
y+=dy;
}
}
class LabeledPoint extends Point {
String label;
void setLabel(String newLabel) {
label = newLabel;
}
}
Die Klasse Point stellt einen Punkt mit Koordinaten (x, y) dar, welcher
durch die move–Methode verschoben werden kann.
Die Klasse LabeledPoint stellt einen Punkt dar, welcher zus¨
atzlich eine
Beschriftung tr¨
agt. Indem in Zeile 9 die Klasse LabeledPoint als Erweiterung der Klasse Point definiert wird, u
¨bernimmt LabeledPoint alle Definitionen der Klasse Point. Ein ¨
ahnliches Ergebnis h¨
atten wir also erhalten,
wenn wir die Klasse LabeledPoint wie folgt definiert h¨
atten:
116
Beispiel: Erweiterte Klasse
1
2
3
4
5
6
7
8
9
10
11
12
13
class LabeledPoint2 {
double x, y;
String label;
void move(double dx, double dy) {
x+=dx;
y+=dy;
}
void setLabel(String newLabel) {
label = newLabel;
}
}
¨
Die Ubernahme
von Definitionen aus der Elternklasse geschieht nach folgenden
Regeln:
• Wenn eine Kindklasse eine Elternklasse erweitert, so werden bis auf Konstruktordefinitionen alle Definitionen der Elternklasse u
¨bernommen (Sie
k¨
onnen davon jedoch einige Definitionen ausnehmen — wie das geht, erfahren Sie im Kapitel u
¨ber “Zugriffsschutz: private, protected, public”).
Die Kindklasse erh¨
alt somit alle Instanzvariablen, Klassenvariablen, Instanzmethoden und Klassenmethoden der Elternklasse. All diese Dinge
haben in der Kindklasse die gleiche Definition wie in der Elternklasse.
• Definitionen der Elternklasse k¨onnen in der Kindklasse verdeckt werden,
indem eine Definition gleicher Signatur vorgenommen wird (das wird im
n¨
achsten Beispiel klar). Man sagt hierzu auch, daß eine Definition “¨
uberschrieben” wird.
Zwei Variablen haben dabei die gleiche Signatur, wenn sie den gleichen
Namen haben. Zwei Methoden besitzen die gleiche Signatur, wenn sie den
gleichen Namen haben und die gleichen formalen Parameter entgegennehmen.
• Beim Vererben werden alle Definitionen, die die Elternklasse selbst geerbt hat, weiter vererbt. Hat die Elternklasse eine Methode oder Variable
mit einer neuen Definition u
¨berschrieben, so wird nur die u
¨berschriebene
Version der Elternklasse weitervererbt.
14.3
¨
Uberschreiben
von Methoden
Wie schon erw¨
ahnt, k¨
onnen Methoden und Variablen in der Unterklasse u
¨berschrieben werden — da es sich in der Regel nicht empfiehlt, Variablen eines
gewissen Typs mit einer Variable eines anderen Typs zu u
¨berschreiben, gehen
¨
wir hier nur auf das Uberschreiben
von Methoden ein.
Eine Methode in der Oberklasse wird immer dann u
¨berschrieben, wenn in der
Unterklasse eine Methode mit gleicher Signatur definiert wird. Dabei haben
zwei Methoden die gleiche Signatur, wenn sie den gleichen Namen haben und
die gleichen Parameter entgegennehmen.
117
Beispiel:
Beispiel: Methoden u
¨berschreiben
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Point {
double x, y;
String getDescription() {
return "Punkt";
}
}
class LabeledPoint extends Point {
String label;
String getDescription() {
return "Punkt mit Label";
}
}
Hier wird die getDescription()–Methode u
¨berschrieben. Objekte der Klasse LabeledPoint liefern daher beim Aufruf der getDescription()–Methode
immer den Text “Punkt mit Label” zur¨
uck.
Beispiel: Methoden u
¨berladen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Point {
double x, y;
String getDescription() {
return "Punkt";
}
}
class LabeledPoint extends Point {
String label;
String getDescription(boolean outputFlag) {
if( outputFlag ) {
return "Punkt mit Label";
} else {
return "";
}
}
}
Hier wird die getDescription()–Methode nicht u
¨berschrieben, da die
getDescription–Methode in der Klasse LabeledPoint nicht die gleiche Signatur wie in der Klasse Point hat.
Die objektorientierte Methodik ist zum Teil deshalb so m¨achtig, weil ererbte
Routinen in der Lage sind, u
¨berschriebene Routinen aufzurufen. Um dies zu
verstehen, stellen Sie sich den Methodenaufruf am besten als das Verschicken
von Botschaften vor:
Immer wenn ein Objekt eine Botschaft erh¨alt, schaut es nach, ob in seiner Klasse
eine Methode f¨
ur diese Botschaft definiert ist. Wenn ja, benutzt es diese Methode. Wenn nein, schaut es in seiner Oberklasse nach und benutzt gegebenenfalls
die dort definierte Methode. Wenn auch in seiner Oberklasse keine passende
Methode definiert ist, so schaut es in der Oberklasse seiner Oberklasse nach
und so weiter.
Entscheidend ist hier, daß das Objekt dies f¨
ur jede Botschaft tut, die es erh¨alt,
118
selbst wenn die Botschaft in seiner Oberklasse abgesetzt wurde. Schauen Sie
sich das folgende Programmfragment an:
¨
Beispiel: Uberschreiben
und Botschaften
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Point {
double x, y;
String getDescription() {
return "Punkt (" + x + "," + y + ")";
}
void output() {
System.out.println( getDescription() );
}
}
class LabeledPoint extends Point {
String label;
String getDescription() {
return "Punkt mit Label:" + label;
}
}
Was passiert in diesem Beispiel, wenn man einem Objekt der Klasse LabeledPoint
die Botschaft “output()” schickt ? Sie m¨
ussten die Frage bereits beantworten
k¨
onnen.
Hier die Antwort: Zuerst schaut das LabeledPoint–Objekt in seiner Klasse nach,
ob es dort eine Methode namens output() findet. Da das nicht der Fall ist,
schaut es dann in seiner Oberklasse Point nach und findet dort die output()–
Methode von Zeile 8–10. Nun wird der Programmtext dieser output()–Methode
ausgef¨
uhrt.
Bei der Ausf¨
uhrung des Programmtextes wird in Zeile 9 an das Objekt die
Botschaft getDescription() geschickt. Obwohl dies im Programmtext der Klasse
Point geschieht, ist unser Objekt immer noch ein Objekt der Klasse LabeledPoint.
Es schaut daher zuerst in der LabeledPoint–Klasse nach und findet dort die
getDescription()–Methode aus Zeilen 16–17. Nun f¨
uhrt es den in der LabeledPoint
enthaltenen Programmtext der getDescription()–Methode aus.
Es ist also ohne weiteres m¨
oglich, daß in der Oberklasse definierte Methoden
Programmtext aufrufen, der in den Unterklassen u
¨berschrieben wurde. Es ist
daher m¨
oglich, in einer Oberklasse sehr abstrakt formulierten Programmtext zu
schreiben und die Details erst in den Unterklassen einzusetzen.
14.4
Die super–Variable
¨
Das Uberschreiben
von Methoden wird dann zum Problem, wenn man die u
¨berschriebene Methode weiterverwenden m¨ochte — oft ver¨andert die u
¨berschreibende Methode das Verhalten der u
¨berschriebenen Methode nur minimal, und so
m¨
ochte man die M¨
oglichkeit haben, die u
¨berschriebene Methode zu verwenden
und vielleicht ihr Resultat zu modifizieren.
Um dies zu erm¨
oglichen, tr¨
agt jedes Objekt in sich eine Variable namens super.
Immer wenn man innerhalb des Objektes diese Variable anspricht, spricht man
das Objekt als Instanz seiner Oberklasse an.
119
Beispiel: Verwendung von super
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Point {
double x, y;
String getDescription() {
return "Punkt (" + x + "," + y + ")";
}
void output() {
System.out.println( getDescription() );
}
}
class LabeledPoint extends Point {
String label;
String getDescription() {
return super.getDescription() + " mit Label: " + label;
}
}
Im obigen Programmtext benutzen wir in der u
¨berschreibenden getDescription()–
Routine die super–Variable, um die u
¨berschriebene getDescription()–Methode
von Zeile 4–6 aufzurufen.
Die super–Variable erm¨
oglicht also den Zugriff auf u
¨berschriebene Methoden.
14.5
Vererbung und Konstruktoren
Wie schon erw¨
ahnt, erbt ein Objekt von seiner Oberklasse alle Definitionen,
nicht aber Konstruktoren. Es ist aber h¨aufig n¨otig, Konstruktoren der Oberklasse aufzurufen.
Hier ein Beispiel:
Beispiel: Vererbung und Konstruktoren
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Point {
double x, y;
Point( double newX, double newY ) {
x = newX;
y = newY;
}
}
class LabeledPoint extends Point {
String label;
LabeledPoint( double newX, double newY, String newLabel ) {
x = newX;
y = newY;
label = newLabel;
}
}
Dieser Programmtext ist nicht besonders gut. Wir haben im LabeledPoint–
Konstruktor n¨
amlich Programmtext wiederholt, der eigentlich in die Klase Point
geh¨
ort. Damit greifen wir auf Interna der Klasse Point zu, die die vererbte
Klasse nicht zu interessieren haben (Kinder sollten nicht alle Geheimnisse der
Eltern kennen, das gilt auch beim objektorientierten Programmieren). Wenn
120
sich sp¨
ater irgendwann der Konstruktor f¨
ur Point a¨ndert, m¨
ussten wir auch den
Programmtext f¨
ur den LabeledPoint–Konstruktor a¨ndern.
Statt dessen sollten wir den Konstruktor der Oberklasse direkt verwenden. Dies
geschieht auch mit Hilfe des super–Schl¨
usselwortes, allerdings wird es jetzt nicht
wie vorhin als Variable sondern als Methode verwendet:
Beispiel: Konstruktoren der Oberklasse aufrufen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Point { double x, y;
Point( double newX, double newY ) {
x = newX;
y = newY;
}
}
class LabeledPoint extends Point {
String label;
LabeledPoint( double newX, double newY, String newLabel ) {
super(x,y);
label = newLabel;
}
}
Immer wenn in einem Konstruktor einer Unterklasse ein Methodenaufruf namens super(...) steht, so wird der zu den angegebenen Parametern passende
Konstruktor der Oberklasse aufgerufen.
Sie k¨
onnen diese Art des Konstruktor–Aufrufes u
¨brigens nur in Konstruktoren
verwenden.
14.6
Beispiele
Beispiel: Point und LabeledPoint
Das folgende Beispiel besteht aus drei Klassen, welche sich alle in einer
gemeinsamen Datei befinden. Sie k¨
onnen das Beispiel ausf¨
uhren, indem
Sie auf der Kommandozeile javac Points.java und dann java Demo eingeben.
¨
Beispiel: Uberschreiben
von Methodena
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/Vererbung/ex1/Points.java
Die Klasse Point repr¨
asentiert einen Punkt im zweidimensionalen Raum.
Sie besitzt unter anderem eine Methode output(), welche eine Beschreibung des Punktes ausgibt. Um die Beschreibung zu ermitteln, wird auf
die Methode getDescription() zur¨
uckgegriffen.
Von der Klasse Point wird in Zeilen 23–38 eine Klasse namens
LabeledPoint abgeleitet. Objekte dieser Klasse haben durch Vererbung
alle Variablen und Methoden, die auch die Objekte der Point–Klasse
besitzen. Dabei werden alle Definitionen der Oberklasse bis auf die
getDescription()–Methode direkt u
¨bernommen.
Die getDescription()–Methode wird in Zeilen 35–37 erneut definiert
121
und u
¨berschreibt so die getDescription()–Methode der Oberklasse Point.
Immer wenn nun in einem Objekt der Klasse LabeledPoint die
getDescription()–Methode aufgerufen wird, wird diese u
¨berschriebene Methode benutzt.
Beispiel: Das Beispiel vom Anfang
Am Anfang des Kapitels haben wir Klassenhierarchien am Beispiel verschiedener Fahrzeuge eingef¨
uhrt. Wir haben ein dazu passendes Java–
Programm entwickelt, welches zum Abdrucken leider zu groß ist.
Sie sollten es austesten, sich ausdrucken und lesen. Sie starten das
Programm, indem Sie die Klasse FahrzeugDemo.java kompilieren und
ausf¨
uhren. Sie finden das Programm unter
http://www.wire.tu-bs.de/lehre/eipclasses/Vererbung/Fahrzeug/.
Dieses Beispiel besteht aus den Klassen Fahrzeug, MotorisiertesFahrzeug,
Auto, Fahrrad, LKW und Kutsche. Vergleichen Sie die Klassenstruktur
der Programme bitte mit dem Diagramm am Anfang des Kapitels.
Beispiel: Geometrische Objekte
Hier eine kleine Klassenhierarchie zur Modellierung geometrischer Objekte. Starten Sie das Beispiel, indem Sie javac Demo.java und dann java
Demo eingeben.
Beachten Sie, wie wir die Interna der Objekte kapseln — wir vermeiden
direkte Zugriffe auf die Instanzvariablen von GeomObjekt und verwenden
statt dessen die getX() und getY()–Methoden. Hierdurch erhalten wir
Flexibilit¨
at, wenn wir irgendwann mal in einer Unterklasse die Art und
Weise ver¨
andern m¨
ochten, wie die Position berechnet wird.
Es empfiehlt sich sehr, Zugriffe auf Instanzvariablen durch entsprechende
get– bzw. set– Methoden zu erledigen. Beachten Sie auch die Klasse Linie
— sie erbt nicht nur von GeomObjekt sondern verwendet auch noch im
Konstruktor ein zweites GeomObjekt, um den Linienvektor zu bestimmen.
Beispiel: Geometrisches Objekta
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/Vererbung/Geom/GeomObjekt.java
Beispiel: Punkta
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/Vererbung/Geom/Punkt.java
Beispiel: Liniea
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/Vererbung/Geom/Linie.java
122
Beispiel: Demoa
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/Vererbung/Geom/Demo.java
14.7
Die Object-Klasse
Zu Java geh¨
ort eine spezielle Klasse namens Object. Wannimmer Sie eine Klasse
definieren, welche nicht explizit von einer anderen Klasse erbt, f¨
ugt der Java–
Compiler automatisch eine Vererbung von Object ein.
Wenn Sie eine Klasse
class MeineKlasse {
// Klassenk¨
orper
}
definieren, so wird das vom Java–Compiler u
¨bersetzt als
class MeineKlasse extends Object {
// Klassenk¨
orper
}
Es ist also unm¨
oglich, eine Klasse zu schreiben, die nicht zumindest indirekt von
Object erbt. Die meisten von Object geerbten Methoden sind nur mit gr¨oßerem
Wissen u
¨ber Java zu verstehen.
Schauen Sie sich bitte im Java–API die Klasse Object an. Sie erhalten die Dokumentation, indem Sie unter
http://www.tu-bs.de:82/wir/EIP/jdk1.1.8/docs/api/packages.html
auf das Paket java.lang und dann auf die Klasse Object klicken.
14.8
Abstrakte Klassen
H¨
aufig f¨
uhrt man Klassen ein, von denen man u
¨berhaupt nicht plant, Instanzen
zu erzeugen.
M¨
ochte man beispielsweise geometrische Objekte wie Linien, Rechtecke und
Punkte in einem gemeinsamen Konzept zusammenfassen, so k¨onnte man eine
Klasse GeometrischesObjekt definieren.
In der Klasse GeometrischesObjekt k¨onnte die Intelligenz zum Verschieben von
geometrischen Objekten enthalten sein. Dieses Verhalten w¨are f¨
ur alle geometrischen Objekte gleich und sollte daher in der Klasse GeometrischesObjekt
realisiert werden.
Allerdings w¨
urde man vermutlich niemals eine Instanz von GeometrischesObjekt
erzeugen, da dies nur ein abstraktes Konzept ist — Sie wissen, wie man eine Linie
oder ein Rechteck zeichnet, aber welches Verhalten soll das abstrakte Konzept
GeometrischesObjekt beim Zeichnen an den Tag legen ?
123
Java bietet spezielle Unterst¨
utzung f¨
ur abstrakte Klassen. Eine abstrakte Klasse ist eine Klasse, in der zwar alle n¨otigen Methodenk¨opfe definiert sind, in
der einige Methoden jedoch noch keinen Methodenk¨orper besitzen. Von einer
abstrakten Klasse weiß man somit zwar, wie man jede Methode aufruft und
welchen Zweck jede Methode hat, aber da die Klasse nur ein abstraktes Gedankenmodell ist, weiß man nicht, welches Verhalten die Methoden zeigen sollen.
Eine abstrakte Methode wird definiert, indem der Methodenk¨orper ausgelassen
und vor den Methodenname das Wort “abstract” gesetzt wird. Beispielsweise
k¨
onnte eine abstrakte Methode namens draw wie folgt definiert werden:
abstract void draw();
Sobald eine Klasse mindestens eine abstrakte Methode enth¨alt, muß auch die
Klasse als abstract definiert werden. Dies geschieht, indem in die Klassendefinition das Wort “abstract” gesetzt wird:
abstract class GeometrischesObjekt {
double x, y;
void move(double dx, dy) {
x+=dx; y+=dy;
}
abstract void draw();
}
Eine abstrakte Klasse ist zum Erweitern gedacht. F¨
ur die Extensionen einer
abstrakten Klasse gilt: Entweder f¨
ullt die Extension jede abstrakte Methode
der Oberklasse mit einem Methodenk¨orper, oder auch die Extension muß als
abstrakte Klasse definiert werden.
Es ist nicht m¨
oglich, Instanzen abstrakter Klassen zu erzeugen. Es ist jedoch
m¨
oglich, Variablen abstrakter Klassen zu definieren. Diese Variablen k¨onnen
dann Instanzen der Unterklassen aufnehmen.
Beispiel: Im folgenden Beispiel wird eine abstrakte Klasse namens
GeometrischesObjekt definiert, welche eine abstrakte draw–Methode sowie Verhalten zum Verschieben eines geometrischen Objektes enth¨
alt.
Von dieser abstrakten Klasse werden zwei konkrete Klassen, Punkt und
Linie vererbt. Die Klasse Demo zeigt, wie man Variablen abstrakter Klassen verwenden kann.
Beispiel: Geometrisches Objekta
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/Vererbung/Geom2/GeomObjekt.java
Beispiel: Punkta
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/Vererbung/Geom2/Punkt.java
124
Beispiel: Liniea
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/Vererbung/Geom2/Linie.java
Beispiel: Demoa
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/Vererbung/Geom2/Demo.java
14.9
Zuweisung an Variablen und Arrays
Jedes Objekt ist eine Instanz all seiner Oberklassen. Dies erschliesst vielf¨altige
M¨
oglichkeiten beim Umgang mit Variablen und Arrays. Angenommen, es seien
die folgenden drei Klassen definiert:
abstract class GeometrischesObjekt { ... }
class Linie extends GeometrischesObjekt { ... }
abstract class Flaeche extends GeometrischesObjekt { ... }
class Rechteck extends Flaeche { ... }
class Dreieck extends Flaeche { ... }
In diesem Fall ist jedes Rechteck–Objekt auch eine Flaeche– und eine GeometrischesObjekt–
Instanz. Da GeometrischesObjekt automatisch von der Object–Klasse erbt, ist
jedes Rechteck–Objekt auch eine Instanz der Klasse Object.
Die folgenden Definitionen sind daher g¨
ultig:
Object
GeometrischesObjekt
Flaeche
Rechteck
einObj
einGeoObj
eineFlaeche
einRechteck
=
=
=
=
new
new
new
new
Rechteck();
Rechteck();
Rechteck();
Rechteck();
Sie d¨
urfen jedes Objekt an Variablen seiner Oberklassen zuweisen. Dies ist eigentlich logisch, denn jedes Objekt IST auch ein Objekt seiner Oberklassen:
Jedes Rechteck ist ein Object, jedes Rechteck ist ein GeometrischesObjekt, und
jedes Rechteck ist eine Flaeche; daher sind die obigen Definitionen g¨
ultig.
Die folgenden Definitionen hingegen sind allesamt ung¨
ultig:
Rechteck einRechteck1 = new Flaeche(); // Fehler
Rechteck einRechteck2 = new Object(); // Fehler
Rechteck einRechteck3 = new Dreieck(); // Fehler
Daß diese Definitionen ung¨
ultig sind, sollte einleuchten — denn in unserem
Vererbungsbaum ist zwar jedes Rechteck eine Flaeche, aber nicht jede Flaeche
ist ein Rechteck. Auch ist zwar jedes Rechteck ein Object aber nicht jedes Object
ein Rechteck.
Es ist nun ohne weiteres m¨
oglich, Arrays zu erzeugen, welche Objekte verschiedener Klassen aufnehmen (man spricht hier auch von “polymorphen Arrays”).
Das folgende Programmfragment ist korrekt:
125
Dreieck einDreieck
= new Dreieck();
Rechteck einRechteck = new Rechteck();
Flaeche eineFlaeche = new Rechteck();
GeometrischesObject[] meinArray = new GeometrischesObject[3];
meinArray[0] = einDreieck;
meinArray[1] = einRechteck;
meinArray[2] = eineFlaeche;
Was kann man nun mit den Objekten anstellen, die man in Variablen einer
Oberklasse gespeichert hat ? Man kann ihnen nat¨
urlich Botschaften schicken,
aber der Java–Compiler achtet sehr darauf, daß man jedem Objekt nur die
Botschaften schickt, die es sicher verstehen kann.
Angenommen, in der GeometrischesObjekt–Klasse sei eine Methode Methode
draw() definiert, welche ein geometrisches Objekt auf dem Bildschirm zeichnet,
und welche in den erbenden Klassen durch das spezielle ben¨otigte Verhalten
u
¨berschrieben wird.
In diesem Fall k¨
onnte der obige Programmtext wie folgt fortgesetzt werden:
for(int i=0; i < meinArray.length(); i++) {
meinArray[i].draw();
}
Hier w¨
urde das Array durchlaufen und jedes gespeicherte Objekt w¨
urde die
zugeh¨
orige draw()–Methode ausf¨
uhren. Auf diese Art und Weise k¨onnte man
eine ganze Sammlung von geometrischen Objekten zeichnen ohne sich darum zu
k¨
ummern, ob das gerade gezeichnete Objekt ein Dreieck, Rechteck oder sonstwas
ist — das jeweilige Objekt weiß ja immer selbst, wie es zu zeichnen ist.
Es ist hingegen nicht m¨
oglich, einem Objekt Botschaften zu schicken, die nur
eine Unterklasse versteht. Angenommen, ein Dreieck–Objekt besitze eine Methode Linie getHypothenuse(), welche in einem Linie–Objekt die Hypothenuse
zur¨
uckliefere. Die Klasse GeometrischesObjekt besitze keine solche Methode.
In diesem Fall w¨
are der folgende Programmtext ung¨
ultig:
GeometrischesObjekt einGeomObj = new Dreieck();
Linie eineLinie = einGeomObj.getHypothenuse(); // Fehler
K¨
onnen Sie schon beantworten, warum der Programmtext ung¨
ultig ist ? In
der Variable “einGeomObj” ist ein Dreieck gespeichert, und da ein Dreieck die
Botschaft “getHypothenuse()” versteht, sollte doch alles in Ordnung sein.
Ber¨
ucksichtigen Sie aber, daß wir “einGeomObj” als GeometrischesObjekt definiert haben. Der Compiler weiß in der zweiten Zeile daher lediglich, daß wir an
eine Variable des Typs GeometrischesObjekt die Botschaft “getHypothenuse()”
schicken, und da in der Klasse GeometrischesObjekt keine derartige Methode
existiert, l¨
aßt er das nicht zu.
Wenn Sie wissen, daß ein Objekt ein Objekt einer gewissen Klasse enth¨alt,
k¨
onnen Sie einen Type–Cast verwenden. Der folgende Programmtext ist g¨
ultig:
GeometrischesObjekt einGeomObj = new Dreieck();
Linie eineLinie = (Dreieck)einGeomObj.getHypothenuse(); // ok
126
Durch den Type–Cast “(Dreieck)” sagen wir dem Compiler: Compiler, wir machen hier schon das korrekte, gehe davon aus, daß in einGeomObj ein Objekt
der Klasse Dreieck steht.
Der Compiler w¨
urde in solch einem Fall keinen Fehler melden. Wenn wir hierbei
aber einen Fehler machen, entsteht w¨ahrend der Laufzeit des Programms ein
Fehler:
GeometrischesObjekt einGeomObj = new Quadrat();
Linie eineLinie = (Dreieck)einGeomObj.getHypothenuse();
¨
Das obige Fragment ist so kompilierbar, da wir durch den Type–Cast die Uberpr¨
ufung durch den Compiler ausgeschaltet haben. Wenn das Programm abl¨auft,
merkt das Laufzeitsystem aber, daß hier versucht wird, ein Quadrat als Dreieck zu betrachten. Das Programm w¨
urde dann einen Fehler ausgeben und sich
beenden.
Beispiel: Das folgende Beispiel demonstriert eine Verwendung des Type–
Cast–Operators
Beispiel: TypeCast–Demoa
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/Vererbung/Cast/CastDemo.java
Hier wird in Zeile 36–39 ein Object[]–Array definiert, in welchem verschiedene Objekte gespeichert werden. Außerdem wird in Zeile 35 ein Array definiert, in welchem beschrieben ist, was f¨
ur ein Objekt an welcher
Position des Object–Arrays liegt.
Beide Arrays werden an die Methode output u
¨bergeben. Diese Methode
durchl¨
auft das Object[]–Array und f¨
uhrt f¨
ur jeden Eintrag basierend auf
der Information im desc–Array einen Type–Cast aus. Dann f¨
uhrt es in
jedem gecasteten Objekt eine spezielle Methode aus.
14.10
Zusammenfassung
Nach Lesen dieses Kapitels sollten Sie
• erkl¨
aren k¨
onnen, welche Vorteile Vererbung bietet.
• anhand von Beispielen die Vorz¨
uge von Vererbung demonstrieren k¨onnen.
• die Begriffe, Extension, erben, Super–Klasse, Elternklasse, Erweiterung,
direkter/indirekter Nachkomme erkl¨aren k¨onnen.
• beschreiben k¨
onnen, wie man in Java Extensionen schreibt.
• erkl¨
aren k¨
onnen, was beim Vererben in Java passiert.
• beschreiben k¨
onnen, was es bedeutet, eine Methode zu u
¨berschreiben, und
wie man u
¨berschriebene Methoden der Oberklasse aufrufen kann.
• sich Gedanken dar¨
uber gemacht haben, was es bedeutet, daß Konstruktoren nicht vererbt werden.
127
• erkl¨
aren k¨
onnen, wie man Konstruktoren einer Kindklasse schreiben sollte.
• die Beispiele in Kapitel 14.6 verstanden haben und Fragen dazu beantworten k¨
onnen.
• beschreiben k¨
onnen, an Variablen welchen Typs man Objekte zuweisen
kann.
• erkl¨
aren k¨
onnen, warum man nicht direkt alle Methoden von Objekten
aufrufen kann, die in einer Variable der Oberklasse gespeichert sind.
• beschreiben k¨
onnen, wie und warum man im Rahmen von Vererbung den
Type–Cast–Operator einsetzen kann.
• sich u
¨berlegen, ob und warum der Einsatz von Type–Cast–Operatoren
gef¨
ahrlich sein kann.
128
15
Packages
¨
Zu Java geh¨
oren sehr viele verschiedene Klassen. Um nicht die Ubersicht
zu
verlieren, werden Klassen mit a
hnlichem
Zweck
gemeinsam
zu
gr¨
o
ßeren
Paketen,
¨
sogenannten “packages” zusammengeschn¨
urt.
Um einen weiteren Zweck von Paketen klarzumachen, stellen Sie sich vor, Sie
h¨
atten eine Klasse namens Y2K erstellt, welche alle Millenium–Bugs vertreibt
und wollten diese Klasse verkaufen. Stellen Sie sich nun vor, ein potentieller
Kunde habe bereits eine eigene Klasse namens Y2K erstellt, benutze diese in
seinem Programm und wolle nicht auf sie verzichten.
Was tun ? Sie k¨
onnten nat¨
urlich ihre Klasse umbenennen. Aber selbst wenn sie
sie umbenennen, k¨
onnte es einen Namenskonflikt mit einem anderen Kunden
geben.
Eine L¨
osung, derartige Namenskonflikte gar nicht erst zum Problem werden zu
lassen, sind packages. Ein package oder Paket enth¨alt beliebig viele Klassen. Die
in einem package enthaltenen Klassen kann man ansprechen, indem man hinter
den Namen des packages einen Punkt und dann den Namen der Klasse setzt.
Wenn zum Beispiel ein Paket den Namen de.tubs.cs.sc.akeese hat und in dem
Paket eine Klasse namens Y2K enthalten ist, so k¨onnte man die Klasse als
de.tubs.cs.sc.akeese.Y2K ansprechen.
Es ist u
¨blich, Paket–Namen aus der Internet–Adressierung einer Firma zu generieren. Wenn die Firma SUN (www.sun.com) eine Erweiterung zu Java entwickelt, welche nicht im Java–Kern enthalten ist, so w¨
urde diese Erweiterung in
einem Paket namens com.sun.paketname enthalten sein.
Da Internet–Adressen eindeutig sind, wird so sichergestellt, daß Pakete unterschiedlicher Firmen unterschiedliche Namen enthalten. Somit haben auch in der
Regel Klassen verschiedener Firmen verschiedene Namen und sind deshalb gemeinsam einsetzbar.
Alle zu Java geh¨
origen Klassen sind in Paketen enthalten, die mit dem Text
“java” beginnen. So gibt es ein Paket java.awt, in welchem alle Bestandteile des
“Abstract Windowing Toolkits” enthalten sind, es gibt ein Paket java.math,
in welchem spezielle mathematische Klassen enthalten sind, und viele mehr.
Das wichtigste Paket ist das Paket java.lang, in welchem die wichtigsten Java–
Klassen wie z.B. String oder Object enthalten sind.
Wir wollen hier nicht weiter darauf eingehen, wie man packages erstellt, m¨ochten
Ihnen jedoch erl¨
autern, wie man packages benutzt.
Um eine in einem Paket enthaltene Klasse anzusprechen, m¨
ussen Sie vor den
Namen der Klasse jedesmal den Namen des Paketes setzen. Wenn der Paketname
lang ist, wird das eine ganze Menge Schreibarbeit. Stellen Sie sich vor, statt
String m¨
ussten Sie jedesmal java.lang.String schreiben.
Man kann daher die in einem Paket enthaltenen Namen in ein Programm importieren. Dazu setzt man an den Anfang der Datei den Text
import paketname.*;
Hierdurch werden alle in dem jeweiligen Paket enthaltenen Klassen importiert
und sind in Zukunft ansprechbar, ohne daß Sie den Paketnamen vor den Klassennamen setzen m¨
ussen.
129
Wenn Sie nur einzelne Klassen eines Paketes importieren wollen, k¨onnen Sie
statt des Sterns auch f¨
ur jede zu importierende Klasse einen einzelnen import–
Befehl angeben, etwa so:
import paketname.klasse1;
import paketname.klasse2;
Beispielsweise enth¨
alt das Paket java.util die Klasse Dictionary. Wenn Sie diese
Klasse benutzen wollen, haben Sie zwei M¨oglichkeiten:
Sie k¨
onnen die Klasse direkt verwenden:
class MeineKlasse {
java.util.Dictionary einDict;
public static void main( String[] args ) {
java.util.Dictionary aDict = new java.util.Dictionary();
}
}
Alternativ k¨
onnen Sie die Klasse auch importieren und k¨onnen dann eine k¨
urzere
Schreibweise w¨
ahlen:
import java.util.*;
// oder import java.util.Dictionary
class MeineKlasse {
Dictionary einDict;
public static void main( String[] args ) {
Dictionary aDict = new Dictionary();
}
}
Es ist u
otig, Inhalte des java.lang–Paketes zu importieren. Da die
¨brigens nicht n¨
dort enthaltenen Klassen so h¨aufig benutzt werden, wird es automatisch in jedes
Programm exportiert. Der Java–Compiler f¨
ugt automatisch in jedes kompilierte
Programm den Befehl
import java.lang.*;
ein.
¨
Eine Ubersicht
u
¨ber die zu Java geh¨origen Pakete finden Sie in der Dokumentation zum Java API unter
http://www.tu-bs.de:82/wir/EIP/jdk1.1.8/docs/api/packages.html
Bitte klicken Sie sich dorthin, indem Sie von unseren WWW–Seiten unter den
Litaturlinks zu Java auf den relativ weit unten stehenden Link “Java–Api (lokal)” klicken.
Bitte schauen Sie sich auf dieser WWW–Seite um, klicken Sie dort auf das
Package java.lang und schauen Sie sich die Dokumentation der Object–Klasse
in java.lang an.
130
15.1
Zusammenfassung
Nach Lesen dieses Kapitels sollten Sie
• erkl¨
aren k¨
onnen, welche Vorteile ein Package bietet.
• den import–Befehl kennen.
• wissen, was am java.lang–Paket besonders ist.
¨
• eine Ubersicht
der zu Java geh¨orenden Packages aufrufen k¨onnen.
131
16
Exceptions
Zur Behandlung unerwarteter Situationen bietet Java Unterst¨
utzung in Form
von Exceptions oder Ausnahmen.
Den Sinn von Exceptions k¨
onnen wir Ihnen an einem kleinen Beispiel klarmachen. Nehmen Sie an, wir schreiben eine Methode, in welcher durch eine Zahl
geteilt werden muß — es k¨
onnte etwa aus der zur¨
uckgelegten Distanz und einem Zeitintervall berechnet werden, mit welcher Geschwindigkeit sich ein Auto
bewegt. Eine erste Fassung einer entsprechenden Methode k¨onnte wie folgt aussehen:
Beispiel: Methode mit einem Fehler
1
2
3
4
static int geschwindigkeit( int distanz, int dauer )
{
return distanz / dauer;
}
Diese Methode wird problematisch, wenn von außen eine Dauer von 0 Sekunden
angegeben wird, etwa im Aufruf
int speed = geschwindigkeit( 10, 0 );
Was soll in solch einem Fall geschehen — in der Methode w¨
urde nun offensichtlich versucht, durch 0 zu teilen. Dies wiederum w¨
urde einen Fehler erzeugen,
welcher das Programm beenden w¨
urde.
Nun ist es nicht besonders w¨
unschenswert, daß ein Programm sich einfach beendet, wenn irgendwo ein ung¨
ultiger Zahlenwert erscheint. Das Programm sollte
zumindest die noch nicht gespeicherten Daten speichern und Aufr¨aumarbeiten
durchf¨
uhren (etwa die vom Programm erzeugen 5 Gigabyte tempor¨arer Daten
von der Festplatte l¨
oschen).
Noch sch¨
oner w¨
are es nat¨
urlich, wenn das Programm einen Fehler melden w¨
urde.
Man k¨
onnte nun vor jedem Aufruf der Funktion u
ufen, ob die Eingabeda¨berpr¨
ten in Ordnung sind. Es ist aber durchaus denkbar, daß vor dem Aufruf einer
Methode nicht von außen pr¨
ufbar ist, ob sie durchf¨
uhrbar ist — stellen Sie sich
eine Methode vor, welche auf eine Diskette zugreifen will. Hier hinge es davon
ab, ob eine Diskette im Laufwerk liegt, ob die Methode funktioniert oder nicht.
Beim Programmieren kommt es immer wieder zu Situationen, in denen unerwartete Daten oder unerwartete Ereignisse (Ausnahmen) vorliegen. Wenn eine
Ausnahme auftritt, muß sie entweder dort, wo sie aufgetreten ist, behandelt
werden, oder sie muß weitergegeben werden, so daß ein anderer Programmteil
eine M¨
oglichkeit hat, auf die Ausnahme zu reagieren. Wird die Ausnahme nirgends behandelt, bleibt als einzige Alternative der Abbruch des Programms mit
einem Fehler.
16.1
Try–catch
Der try–catch–Block bietet eine M¨oglichkeit, Programmtext auszuf¨
uhren, in welchem Ausnahmen auftreten k¨
onnten. Dabei ist eine Ausnahme ein Objekt einer
gewissen Klasse, welches erzeugt wird, wenn eine unerwartete Situation aufgetreten ist.
132
Sie k¨
onnen sich das wie folgt vorstellen: An einer Stelle der Programmausf¨
uhrung
tritt eine unerwartete Situation auf (z.B. Teilen durch Null, Diskette fehlt im
Laufwerk). Als Reaktion auf diese Situation wird ein Ausnahme–Objekt erzeugt,
welches die aufgetretene Situation genauer beschreibt. Dieses Ausnahme–Objekt
wird nun weitergereicht (“geworfen”), bis irgendwo Programmtext gefunden
wird, welcher das Ausnahmeobjekt auff¨angt und dann die Ausnahme behandelt. Wird kein Programmtext gefunden, der das Ausnahmeobjekt f¨angt, wird
das Programm mit einer Fehlermeldung beendet.
Eine Ausnahme kann nur gefangen werden, wenn sie innerhalb eines try–catch–
Blocks auftritt. Die try–catch–Anweisung erm¨oglicht erst das Einfangen der
Ausnahmeobjekte und sch¨
utzt so einen gewissen Programmtextbereich vor einem ungewollten Programmabbruch. Man verwendet einen try–catch–Block, indem man den Programmtext, in welchem Ausnahmen–Objekte geworfen werden
k¨
onnten, in einen try–Block setzt und dann f¨
ur jeden Ausnahmetyp, der eingefangen werden soll, hinter den gesch¨
utzten Block eine catch–Anweisung setzt:
try {
geschuetzte Anweisungen;
...
} catch ( Ausnahmetyp1 x )
Ausnahme-Anweisungen f¨
ur
...
} catch ( Ausnahmetyp2 x )
Ausnahme-Anweisungen f¨
ur
...
}
{
Ausnahmetyp 1;
{
Ausnahmetyp 2;
Der Block zwischen dem try und dem ersten catch ist hierdurch vor jeder Ausnahme gesch¨
utzt, welche durch eine der catch–Anweisungen aufgefangen werden
kann.
Sofern im gesch¨
utzten Block eine Ausnahme auftritt (wie man selbst Ausnahmen
erzeugt, erz¨
ahlen wir gleich noch), wird die Ausf¨
uhrung des gesch¨
utzten Blocks
sofort abgebrochen, und die Ausf¨
uhrung wird in demjenigen Block ausgef¨
uhrt,
welcher auf das passende catch folgt.
Was da genau passiert, wird nach dem folgenden Beispiel sicher klarer:
Beispiel: Beispielsweise entsteht in Java beim ganzzahligen Teilen durch
0 eine Ausnahme des Typs ArithmeticException. Das folgende Programm
demonstriert, wie man einen Programmteil vor einer solchen Ausnahme
sch¨
utzen kann:
Beispiel: Exceptions 1a
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/Except/ExcDemo1.java
Der Programmtext von Zeile 6 bis Zeile 8 ist durch den umschliessenden try–Block und das folgende catch–Statement vor einem Programmabbruch durch alle Ausnahmen des Typs ArithmeticException gesch¨
utzt.
133
Beim Ablauf des Programmtextes entsteht in Zeile 7 beim Teilen durch
0 ein ArithmeticException–Objekt, welches dann als Ausnahme geworfen
wird. Dadurch bricht die Ausf¨
uhrung von Zeile 7 sofort ab. Da in Zeile 9 eine catch–Anweisung f¨
ur Ausnahmeobjekte des Typs ArithmeticException
steht, wird das Programm dann in Zeile 10 fortgesetzt.
Das gefangene ArithmeticException–Ausnahmeobjekt kann in Zeile 10 unter der Variable e angesprochen werden.
Durch die Aneinanderreihung mehrerer catch–Bl¨ocke kann man unterschiedliche
Ausnahmen fangen und auf die unterschiedlichen Ausnahmen auch unterschiedlich reagieren.
Beispiel:
Spricht man in einem Array nicht existierende Indizes an,
so
wird
eine
ArrayOutOfBoundsException
geworfen.
Die
Klasse
ArrayOutOfBoundsException
erbt
von
der
Klasse
IndexOutOfBoundsException.
Im folgenden Programm wird sowohl das Teilen durch 0 als auch das
Verwenden eines zu großen Index abgefangen:
Beispiel: Exceptions 2a
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/Except/ExcDemo2.java
16.2
Ausnahmen werfen
Ausnahmen entstehen entweder durch die mißbr¨auchliche Verwendung von Java–
Anweisungen oder dadurch, daß im Programmtext eine Ausnahme geworfen
wird. Die bei der fehlerhaften Verwendung von Java–Anweisungen entstehenden Ausnahmen sind im Java–API im java.lang–Paket beschrieben.
Alle Ausnahmen m¨
ussen indirekt oder direkt von der Klasse Throwable erben.
Von dieser Klasse vererben sich unter anderem die Klasse Exception, und von
Exception ist eine Klasse RuntimeException abgeleitet. Alle Ausnahmen, die
bei fehlerhafter Verwendung von Java–Anweisungen auftreten kommen, sind
von RuntimeException erbende Objekte.
Wenn Sie selbst Ausnahmen definieren wollen, so sollten Sie diese in der Regel
von der Klasse Exception oder einer ihrer Unterklassen ableiten (schauen Sie
sich die genannten Klassen bitte in der JDK–Dokumentation an).
Sie k¨
onnen selbst Ausnahmen werfen, indem Sie ein Objekt der Klasse Throwable
oder ein Objekt einer Unterklasse von Throwable erzeugen und dann mit der
throw–Anweisung die Ausnahme ausl¨osen.
Exception myException = new Exception("Meine Ausnahme");
throw myException;
oder k¨
urzer:
throw new Exception();
134
Beispiel: So k¨onnen Sie selbst eine Exception werfen und wieder fangen:
Beispiel: Throw 1a
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/Except/ThrowDemo1.java
16.3
Exceptions in Methoden
Wenn bei der Ausf¨
uhrung einer Methode A eine Ausnahme geworfen wird, welche nicht in der Methode selbst gefangen wird, so wird die Methode beendet. Die
Ausnahme wird dann an die Stelle u
¨bergeben, an der die Methode A aufgerufen
wurde. Dort wird die Ausnahme erneut geworfen.
Wurde die Methode A aus einer anderen Methode B aufgerufen, so gibt es
zwei M¨
oglichkeiten: Wenn der Aufruf der Methode A in Methode B von einem
try–catch–Block gesch¨
utzt wurde, welcher die erzeugte Ausnahme fangen kann,
so wird das Programm im zugeh¨origen catch–Block fortgesetzt. Wurde der Methode A jedoch ungesch¨
utzt aufgerufen, so wird Methode B abgebrochen, und
die Ausnahme wird dort erneut geworfen, wo Methode B aufgerufen wurde.
Dies setzt sich fort, bis ein try–catch–Block gefunden wird, welcher die geworfene Ausnahme f¨
angt. Wird kein try–catch–Block gefunden, der die Ausnahme
behandelt, so wird das Programm mit einer Fehlermeldung abgebrochen.
Beispiel: Im folgenden Beispiel wird in der geschwindigkeit–Routine eine
Exception geworfen, wenn versucht wird durch 0 zu teilen. Die Ausnahme
wird in der main–Methode gefangen.
Beispiel: Exceptions in Methodena
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/Except/MethodsDemo1.java
Wenn eine Methode eine Ausnahme zur¨
uckgibt, welche nicht zu den Java–
Laufzeitausnahmen geh¨
ort, muß dies im Kopf der Methode vermerkt werden.
Die Java–Laufzeitausnahmen (dies sind alle Ausnahmen, welche von der Klasse
java.lang.RuntimeException vererbt sind) wird dies deshalb nicht gefordert, weil
diese Ausnahmen fast jederzeit auftreten k¨onnen und daher fast jede Methode
eine RuntimeException werfen kann. W¨are aber im Kopf einer jeden Methoden
vermerkt, daß sie eine Java–Laufzeitausnahme werfen kann, so w¨are das kein
besonderer Informationsgewinn.
In einer Methode wird im Methodenkopf beschrieben, welche Ausnahmen ihre
Ausf¨
uhrung erzeugen kann, indem hinter der Kopfdeklaration der Text
throws Ausnahmetyp1, ... , AusnahmetypN
folgt.
Beispiel:
135
Im folgenden Beispiel wirft die geschwindigkeit–Methode eine eigene Ausnahme. Da diese Ausnahme keine Java–Laufzeitausnahme ist, muß sie im
Methodenkopf durch die throws–Klausel deklariert werden. Wir deklarieren hier auch u
ussigerweise, daß die Routine eine ArithmeticExcep¨berfl¨
tion (dies ist eine Java–Laufzeitausnahme) werfen kann.
Beispiel: Eigene Exceptions in Methodena
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/Except/MethodsDemo2.java
Wenn in einer Methode A eine Methode B aufgerufen wird, welche eine Ausnahme von Typ X (abgesehen von RuntimeExceptions) erzeugen kann, so muß die
von B erzeugte Ausnahme entweder innerhalb der Methode A gefangen werden,
oder im Methodenkopf von A muß vermerkt sein, daß Methode A eine Ausnahme vom Typ X werfen kann. Andernfalls wird ein Kompilierfehler erzeugt.
So wird sichergestellt, daß jede Methode, welche Ausnahmen werfen kann, dies
im Methodenkopf vermerkt.
Beispiel:
Das folgende Beispiel kompiliert nicht korrekt. Der Compiler beschwert
sich, daß die Methode test eine DistanzException werfen kann, ohne daß
das im Kopf der Methode vermerkt ist.
Beispiel: Eigene Exceptions in Methodena
a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/Except/MethodsDemo3.java
Wir k¨
onnen das Beispiel reparieren, indem wir den Kopf der test–Methode
wie folgt ¨
andern:
public static void test() throws DistanzException
Wir k¨
onnten das Beispiel auch reparieren, indem wir in der test–Methode
einen try–catch–Block einf¨
ugen. Die test–Methode s¨
ahe dann so aus:
public static void test() {
try {
System.out.println( geschwindigkeit(11,3) );
} catch( DistanzException e ) {
System.out.println("Habe Ausnahme gefangen: " + e);
}
}
16.4
Ein paar abschließende Bemerkungen
Immer wenn Sie Klassen aus dem Java–API benutzen wollen, so m¨
ussen Sie
in der Dokumentation des Java–API nachsehen, ob die von Ihnen verwendeten
136
Methoden eine Ausnahme werfen k¨onnen. Wenn ja, so m¨
ussen Sie sich in Ihrem
Programm um diese Ausnahme k¨
ummern.
Wir haben nicht alle Aspekte von Ausnahmen besprochen. Wenn Sie sich daf¨
ur
interessieren, lesen Sie es bitte im Buch Go To Java 2 (Kapitel 9) oder sonst
irgendwo nach.
16.5
Zusammenfassung
Nach Lesen dieses Kapitels sollten Sie
• erkl¨
aren k¨
onnen, was eine Ausnahme ist.
• Beispielsituationen nennen k¨onnen, in denen Ausnahmen auftreten.
• erkl¨
aren k¨
onnen, wie Ausnahmen in Java repr¨asentiert werden.
• den try–catch–Befehl erkl¨aren k¨onnen.
• beschreiben k¨
onnen, wie Ausnahmen behandelt werden, die innerhalb einer Methodenausf¨
uhrung auftreten.
• beschreiben k¨
onnen, was man beim Schreiben von Methoden ber¨
ucksichtigen muß, in denen Ausnahmen entstehen k¨onnen.
• erl¨
autern k¨
onnen, was man beachten muß, wenn man JDK–Funktionen
verwendet, die Ausnahmen werfen k¨onnen.
137
A
Anhang: Unix, Editor und CIP-Pool
A.1
Hinweise zu weiterer Unix-Literatur
Sie sollten zuerst das Don’t Panic-Heftchen des Rechenzentrums lesen, um die
Grundlagen von Unix kennenzulernen.
Zus¨
atzlich empfiehlt es sich, auf die in den Seiten des Rechenzentrums angebotene Dokumentation zu schauen. Insbesondere m¨ochten wir Sie auf folgende
Dokumente hinweisen:
• Verzeichnis der vom Rechenzentrum angebotenen Dokumente17
• Don’t Panic online18
• Dokumentation zu Unix19
A.2
A.2.1
Editoren
Der nedit-Editor
Um Programmtexte zu schreiben, ben¨otigen Sie einen Editor. Wir empfehlen
Ihnen, den nedit Editor zu verwenden. Denn wenn Sie bereits mit Windows
oder DOS gearbeitet haben, werden Sie ihn sehr schnell verwenden k¨onnen.
Sie starten ihn, indem Sie auf einer Unix-Shell
> nc &
eingeben (Das Gr¨
oßerzeichen sollten Sie dabei nicht miteingeben; wir verwenden
es hier nur, um zu verdeutlichen, daß Sie etwas eingeben sollen). Nach dem
Start lesen Sie in seinem Hilfe-Men¨
u bitte die Abschnitte Getting Started und
Programming with NEdit (letzteres finden Sie im Untermen¨
u des Hilfe-Menus
Features for programming).
Weitere Dokumentation finden Sie auf folgenden Seiten:
• Dokumentation im Rechenzentrum20
• Dokumentation zu nedit (FU Berlin; engl.)21
• Regular Expressions in nedit (eher ein Thema f¨
ur Fortgeschrittene)22
A.2.2
Der Emacs-Editor
Der Editor Emacs ist vermutlich der m¨achtigste Editor u
¨berhaupt. Seine Bedienung ist f¨
ur MS Windows-Kenner nicht sofort einsichtig, und das Kennenlernen
des Emacs deutlich l¨
anger, als den Umgang mit nedit zu lernen.
17 http://www.tu-bs.de/rz/doku/
18 http://www.tu-bs.de/rz/doku/panic.html
19 http://www.tu-bs.de/rz/doku/unix-pages/kap0-unix.html
20 http://www.tu-bs.de/rz/software/nedit/neditdoc.htm
21 http://www.chemie.fu-berlin.de/chemnet/use/nedit.html
22 HTTP://www.iti.cs.tu-bs.de/soft/nedit/hlp11.html
138
Daf¨
ur werden Sie niemals wieder einen anderen Editor ben¨otigen, wenn Sie ihn
erstmal sch¨
atzen gelernt haben. Sie sollten sich mit dem Emacs allerdings nur
dann auseinandersetzen, wenn Sie etwas Zeit haben und wissen, daß Sie auch in
Zukunft Programme schreiben werden.
Starten Sie ihn von der Unix-Shell per
> emacs &
Anschließend sollten Sie das Emacs-Tutorial durcharbeiten. Sie starten es, indem
Sie nach dem Start des Emacs die Tastenkombination Strg+h (auf englischen
Tastaturen ist die “Strg”-Taste mit “Ctrl” bezeichnet) und danach die Taste
“t”dr¨
ucken .
Weitere Hilfe zum Emacs finden Sie hier:
http://www.cs.tu-bs.de/softech/info/
A.3
Das Hilfe-System
Unter Unix-Systemen sind mehrere Hilfesysteme verf¨
ugbar. Gerade beim Programmieren ist es wichtig, sich mit diesen Systemen auszukennen, da man des
ofteren die Aufrufkonventionen von Bibliotheksfunktionen nachschlagen muß.
¨
A.3.1
Der man-Befehl
Um Hilfe zu einem Unix-Befehl zu erhalten, k¨onnen Sie den man-Befehl verwenden. Hierzu geben Sie beim Aufruf des man-Befehls als Argument den Namen
des Unix-Kommandos, zu dem Sie Hilfe w¨
unschen.
Sie k¨
onnen auch zum man-Befehl Hilfe erhalten, und das wollen wir gleich mal
ausprobieren. Geben Sie ein:
> man man
Wenn Sie im CIP-Pool arbeiten, erscheint nun zuerst ein Hilfetext zum aproposBefehl (dieser ist ein Bestandteil des man-Befehls), aber wenn Sie mit der Leertaste weiterbl¨
attern, erscheint etwas weiter unten auch Hilfe zum man-Befehl.
Diese Hilfe verlassen Sie durch dr¨
ucken der Taste “q”.
Um den Umgang mit man zu trainieren, schauen Sie sich bitte auch die Hilfe zu
anderen Unix-Kommandos an. Wenn Sie Hilfe zum Kopier-Befehl cp oder zum
Directory-Anzeigekommando ls w¨
unschen, probieren Sie aus:
> man cp
> man ls
Es gibt u
¨brigens auch eine graphische Version des man-Befehls. Um diese zu
starten, geben Sie im CIP-Pool ein:
> xman &
139
A.3.2
Der Apropos-Befehl
Die oben beschriebene Form des man-Befehles hat einen Nachteil: Sie k¨onnen sich
nur Informationen zu einem Kommando geben lassen, wenn Sie schon dessen
Namen kennen.
Wenn Sie einen Befehl suchen, der eine gewisse Aufgabe erledigt, dessen Namen Sie aber nicht kennen, k¨
onnen Sie das apropos Kommando, gefolgt von
einem Schl¨
usselwort, verwenden. Daraufhin werden die Kurzbeschreibungen aller durch man dokumentierten Kommandos durchsucht. Alle Kommandos, in
deren Beschreibung dies Schl¨
usselwort auftaucht, werden aufgelistet.
Wenn Sie ganz allgemein nach Hilfe suchen, k¨onnten Sie zum Beispiel einen der
folgenden Befehle ausprobieren:
> apropos help
> apropos info
> apropos manual
Sie erhalten bei jedem apropos-Befehl eine Liste von zum Schl¨
usselwort passenden Kommandos mit einer Kurzerkl¨arung angezeigt.
Diese Beispiele demonstrieren, daß Sie je nach gew¨ahlten Schl¨
usselwort unterschiedliche Kommandos angezeigt bekommen. Selbst wenn ein Befehl sinngem¨aß
etwas mit dem Hilfesystem zu tun hat, nennt apropos help ihn nur dann, wenn
in seiner Kurzbeschreibung auch das Wort help vorkommt. Man muß daher
manchmal ein wenig mit unterschiedlichen Schl¨
usselw¨ortern rumprobieren, bis
man einen Befehl gefunden hat.
Außerdem zeigen diese Beispiele, daß apropos auch unerwartete Antworten liefert - so hat der Befehl apropos manual auch die Zeile
route(1) - Manually manipulates the routing tables
angezeigt. Das hat nichts mit manuals zu tun, aber weil in der Beschreibung
die Zeichenkette “manual” vorkommt, hat der apropos-Befehl auch dieses Kommando angezeigt.
Ein weiteres Beispiel: Angenommen, Sie suchen nach einem Befehl, der eine
Eingabe sortiert. Also geben Sie ein
> apropos sort
Irgendwo in der Anzeige findet sich die Zeile
sort(1) - Sorts files, merges files that are already sorted, and
checks files to determine if they have been sorted.
So haben wir also herausgefunden, daß man zum sortieren einer Textdatei den
sort-Befehl verwenden kann. Bevor man den Befehl verwenden kann, braucht
man nat¨
urlich noch detailliertere Hilfe:
> man sort
140
A.4
Ausgabeumleitung
Als n¨
achstes wollen wir Sie mit Ausgabeumleitung bekannt machen. Ausgabeumleitung ist ein Weg, mehrere Unix-Programme miteinander zu kombinieren. Eine f¨
ur das Programmieren hilfreiche Anwendung ist z.B. das Bl¨attern
oder Suchen in Ausgaben des Compilers oder anderer Werkzeuge.
Fast alle Unix-Shell-Programme arbeiten wie folgt: Sie nehmen Eingaben aus
Ihrem Eingabekanal entgegen, verarbeiten diese und geben Sie auf ihrem Ausgabekanal aus. Ein Eingabekanal kann z.B. eine Datei, Eingaben eines Benutzers
oder Ausgaben eines anderen Programms sein. Ein Ausgabekanal kann z.B. der
Bildschirm, eine Datei oder der Eingabekanal eines anderen Programms sein.
Normalerweise w¨
ahlt jedes Unix-Programm als Eingabekanal die Tastatur und
als Ausgabekanal den Bildschirm. Nehmen wir beispielsweise den Befehl cat. Er
kopiert alle Daten aus seinem Eingabekanal zu seinem Ausgabekanal. Probieren
wir das aus:
> cat
Sie erhalten nun einen blinkenden Cursor. Geben Sie irgend etwas ein und
dr¨
ucken die Return-Taste. Als Ergebnis wird Ihre Eingabe ein zweites Mal ausgeben. Als Sie Return gedr¨
uckt haben, hat cat Ihre Eingabe (von der Tastatur)
auf seinen Ausgabekanal (das Bildschirmfenster) kopiert. Die Tatsache, daß Ihre Eingabe schon beim Tippen auf dem Bildschirm erschien, darf Sie hier nicht
st¨
oren - damit hatte cat nichts zu tun.
Geben Sie noch ein paar Zeilen ein - jedesmal, wenn Sie Return dr¨
ucken, wird
ihre Eingabe abgeschickt und von cat auf die Ausgabe kopiert. Beenden Sie cat,
indem Sie die Tasten Strg und “d” gleichzeitig dr¨
ucken. Die Tastenkombination
“Strg+d” bedeutet f¨
ur cat, daß die Eingabe beendet ist und es zu arbeiten
aufh¨
oren kann — Hier gleich eine Warnung: Der Tastendruck Strg+d und das
EOF, das sie beim Programmieren mit C kennenlernen werden, sind nicht das
gleiche — allerdings erzeugt “Strg+d” ein EOF.
Jetzt wollen wir die Ausgabe von cat umleiten, d.h. wir wollen seinen Ausgabekanal ver¨
andern. Wenn man die Ausgabe in eine Datei leiten will, so gibt man
nach dem umzuleitenden Befehl den Text > dateiname an, wobei “dateiname”
f¨
ur den Namen der Zieldatei steht.
Probieren wir das aus:
> cat > testDatei
Geben Sie wieder ein paar Textzeilen, jedesmal gefolgt von Return, ein. Ihnen
f¨allt sicher auf, daß die Textzeilen nach dem Return nicht ein zweites Mal ausgeben werden. Das war erwartet, da der Ausgabekanal nicht mehr der Bildschirm
sondern die Datei testDatei ist. Schließen Sie nach ein paar Zeilen die Eingabe
ab, indem Sie wieder Strg+d dr¨
ucken.
Benutzen Sie bitte den ls-Befehl, um sich davon zu u
¨berzeugen, daß eine Datei
namens testDatei erzeugt wurde. Um den Inhalt dieser Datei am Bildschirm
anzuzeigen, benutzen wir wieder cat, leiten diesmal aber nicht die Ausgabe
sondern die Eingabe um. Dies geschieht, indem man hinter den Befehl den Text
> dateiname schreibt. Probieren Sie das bitte aus:
141
> cat < testDatei
Dies sollte Ihre vorhin angegebenen Zeilen vom Eingabekanal, also der Datei
testDatei auf den Ausgabekanal, also den Bildschirm, ausgeben.
Das man auch auch kombinieren, um eine eine Datei zu kopieren (in der Praxis
verwendet man daf¨
ur aber lieber den cp-Befehl):
> cat < testDatei > kopie
Dies liefert keine Ausgabe auf dem Bildschirm. Der Eingabekanal war die Datei
testDatei und der Ausgabekanal ging in die Datei kopie. Testen Sie, ob die
Datei auch wirklich kopiert wurde:
> cat < kopie
¨
Ubrigens
ist dies gleichwertig zu
> cat kopie
Das letzte Beispiel demonstriert eine Eigenschaft vieler Unix-Befehle — oft interpretieren Unix-Befehle Parameter als Dateinamen, welche dann zum Einoder Ausgabekanal werden.
Nun folgen noch ein paar Anwendungen und Beispiele zur Ausgabeumleitung.
Das Umleiten von Ein- und Ausgabekanal funktioniert bei fast allen UnixProgrammen. Probieren Sie einfach mal die folgenden Kommandos aus (Falls
Sie wissen wollen, was der sort-Befehl tut, k¨onnen Sie sich dar¨
uber mit dem
man-Befehl informieren):
> ls > verzeichnis
> cat verzeichnis
> sort -r < verzeichnis
Was ist hier passiert ? Wir haben die Ausgabe von ls in eine Datei geleitet und
anschließend diese Datei als Eingabekanal an den sort-Befehl gegeben. Dieser
hat den Inhalt der Datei absteigend sortiert wieder an seinen Ausgabekanal, den
Bildschirm, ausgegeben.
Mit Hilfe des |-Operators kann man mehrere Ausgabeumleitungen kombinieren.
Der |-Operator leitet den Ausgabekanal eines Programms in den Eingabekanal
eines anderen Programms um. Konstrukte der Form
> befehl1 | befehl2
arbeiten ungef¨
ahr wie folgende Sequenz von Befehlen, allerdings ohne eine tempor¨
are Datei anzulegen:
> befehl1 > tempor¨
areDatei
> befehl2 < tempor¨
areDatei
Wir k¨
onnen dies auf das Beispiel, in welchem wir das Verzeichnis sortiert haben,
anwenden — probieren Sie bitte folgende Befehle aus:
142
>
>
>
>
ls | sort -r
ls | sort -r > sorted
cat sorted
ls -lF /usr/bin | sort | less
Die letzte Zeile demonstriert, daß man beliebig viele Ausgabeumleitungen hintereinanderschalten kann. Hier wird die Ausgabe von ls an sort weitergeleitet,
dort sortiert und dann an den less-Befehl weitergegeben.
Der less-Befehl gibt die Eingabe seitenweise auf den Bildschirm aus. Sie verlassen ihn durch die Taste “q”. Wenn Sie etwas durch den less-Befehl anzeigen
lassen, k¨
onnen Sie sich mit den Cursor hoch und Cursor runter-Tasten in der
Ausgabe bewegen.
A.5
Trennung von Rechner und Bildschirm
Es ist in einigen Praktika aufgefallen, daß viele Studenten einen der genialsten
Mechanismen von Unix nicht kennen. Darum werden wir hier kurz darauf eingehen.
Der Monitor, auf dem Ihre Programme angezeigt werden, und der Rechner auf
dem Ihre Programme laufen, sind voneinander v¨ollig unabh¨angig. Auf Ihrem
Monitor hier in Braunschweig k¨onnte ein in M¨
unchen oder in Amerika laufendes
Programm angezeigt werden. Dazu m¨
usste man in M¨
unchen einem Programm
beim Start nur mitteilen, daß es f¨
ur Ausgaben bitte nicht den lokalen Monitor
sondern den Monitor in Braunschweig verwenden soll.
Nehmen wir an, Sie sitzen an einem Terminal namens lokalerRechner.rz.tu-bs.de
und wollen sich auf den Rechner fremderRechner.rz.tu-bs.de einloggen und
dort Programme ausf¨
uhren, deren Fenster dann bei Ihnen landen sollen.
Dazu m¨
ussen Sie drei Dinge tun:
1. Zuerst m¨
ussen Sie dem anderen Rechner gestatten, auf Ihren Monitor zuzugreifen. Die Methode, die wir hier beschreiben, ist zwar etwas unsicher,
da Sie dem fremden Rechner v¨olligen Zugriff auf Ihren Monitor gew¨ahrt,
aber sie demonstriert am besten die Trennung von Bildschirm und Rechner.
Um dem fremden Rechner Zugriff auf Ihren Monitor zu gestatten, geben
Sie in einer Unix-Shell auf dem lokalen Rechner ein:
> xhost fremderRechner.rz.tu-bs.de
Damit kann der fremde Rechner Ihren Monitor beschreiben und auch auslesen.
2. Anschließend m¨
ussen Sie in Erfahrung bringen, wie das Display heißt, auf
dem Ihr Rechner seine Fenster momentan ¨offnet. Dazu geben Sie auf der
Kommandozeile ein:
> echo $DISPLAY
Daraufhin sollte entweder der Text :0.0 oder ein Text der Form rechnername:0.0
ausgeben werden. Merken Sie sich diesen Namen.
143
3. Anschließend m¨
ussen Sie sich auf den fremden Rechner einloggen.
> telnet fremderRechner.rz.tu-bs.de
4. Nachdem Sie sich auf dem fremden Rechner angemeldet haben, m¨
ussen
noch das Display einstellen, auf das er seine Ausgabe leiten soll.
Wenn Sie bei der Ausgabe von
> echo $DISPLAY
vorhin den Text :0.0 erhalten haben, ist der Name des Displays lokalerRechner.rz.tu-bs.de:0.0.
Ansonsten ist der Name des Displays der vorhin angezeigte Name.
Geben Sie in der telnet-Sitzung23 folgendes ein, wenn vorhin der Name
:0.0 angezeigt wurde:
> export DISPLAY=lokalerRechner.rz.tu-bs.de:0.0
oder, wenn vorhin ein anderer Name als :0.0 angezeigt wurde, geben Sie
hinter dem Gleichheitszeichen den Namen des vorhin angezeigten Displays
an.
> export DISPLAY=name des displays
Anschließend starten Sie irgendein X-Programm, z.B. xclock, um zu testen, daß das Umlenken des Displays funktioniert hat:
> xclock
Und hier auch gleich eine Tip: Der Pool ist sehr stark ausgelastet. Wenn Sie
im Pool arbeiten und nebenher einen WWW-Browser wie Netscape verwenden
wollen, so k¨
onnen Sie sich mit Ihrer y-Nummer auf einem Rechner außerhalb
des Pools einloggen und dann das Display auf Ihren Arbeitsrechner im Pool
umlenken. Sie k¨
onnen dann Netscape auf dem entfernten Rechner starten.
Und noch ein Tip: Nat¨
urlich k¨onnen Sie sich auf diese Weise auch von anderen
Rechnern aus im CIP-Pool einloggen. Sie k¨onnen ohne weiteres in einem Institut
oder im Rechenzentrum sitzen, w¨ahrend Sie im Pool im Altbau arbeiten.
23 wir
gehen davon aus, daß Sie die bash–Shell benutzen
144
B
B.1
Anhang: Verwendung von Java unter Unix
Java-Compiler und Laufzeitumgebung
Die Programmierung mit Java geht folgendermaßen vor sich: Sie tippen Ihren
Programmtext mit einem Editor ein und speichern ihn irgendwo in Ihrem Homeverzeichnis. Der Programmtext kann von einem Computer aber nicht verstanden
und muß erst in eine vom Computer verstandene Form u
¨bersetzt (kompiliert)
werden.
Das Programm, welches Ihren Quelltext in eine vom Computer verstandene
Form u
¨bersetzt, nennt sich Compiler. Der Java-Compiler hat eine Besonderheit
— er erzeugt zwar f¨
ur einen Computer verst¨andlichen Code, aber der erzeugte
Code ist f¨
ur keinen existierenden Computer verst¨andlich.
Der Java-Compiler erzeugt Code — den sogenannten Java Bytecode, der von
einem sogenannten virtuellen Computer — der Java Virtual Machine
(JVM) — verstanden werden kann. Die Java Virtual Machine ¨ahnelt real existierenden Prozessoren, ber¨
ucksichtigt dabei aber die besonderen Eigenschaften
von Java.
Zum Java-System geh¨
ort nun ein Programm, welches diesen virtuellen Computer auf einem anderen Computer emuliert. Sie m¨
ussen also nach dem Kompilieren Ihres Quelltextes ein weiteres Programm starten, welches den Bytecode
entgegennimmt und interpretiert.
Nat¨
urlich kann Java damit nicht so schnell sein wie andere Programmiersprachen, die direkt Programmcode die jeweilige Zielplattform erzeugen, weil der
Prozessor niemals Java-Code direkt ausf¨
uhrt. Es gibt allerdings inzwischen Laufzeitsysteme, welche welche den Bytecode auf die jeweilige reale Hardware-Plattform
kompilieren und dort direkt ausf¨
uhren k¨onnen.
Die Tatsache, daß Java-Programme nicht auf eine spezielle Zielarchitektur kompiliert werden, bewirkt also daß Java-Programme nicht so schnell laufen wie in
anderen Sprachen entwickelte Software. Dennoch ist die Verwendung der virtuellen Maschine ein großer Vorteil und hat den Einsatz von Java im Internet erst
erm¨
oglicht — hierdurch wird n¨amlich Systemunabh¨angigkeit erreicht.
In der Theorie l¨
auft ein Java-Programm auf jeder Hardware und jedem Betriebssystem, f¨
ur das eine virtuelle Maschine existiert. Man muß sich also nicht
mehr entscheiden, ob man ein Programm f¨
ur Windows, Macintosh, Linux, HPUnix, Be-OS, OS/2 oder sonst ein Betriebssystem entwickelt. Sobald die virtuelle Java Maschine auf diesem Betriebssystem vorhanden ist, l¨auft hierauf jedes
Java-Programm identisch (wohlgemerkt: in der Theorie).
B.2
Kompilation
Wir wollen nun ein erstes Programm kompilieren. Bitte tippen Sie dazu folgendes Programm in einem Editor ein — beachten Sie dabei, Groß und Kleinschreibung exakt so wiederzugeben wie hier beschrieben.
public class HalloWelt
{
public static void main( String[] args )
{
145
System.out.println("Hallo, Welt");
}
}
Sie sollten immer darauf achten, daß der Name einer Programmdatei mit dem
Namen der enthaltenen Klasse beginnt und auf .java endet. Hier ist der Name
der Klasse “HalloWelt”. Speichern Sie daher den obigen Programmtext in der
Datei HalloWelt.java ab.
Um die Klasse zu kompilieren, gehen Sie bitte auf eine Unix-Shell und wechseln
in das Verzeichnis, in welchem Sie die Datei gespeichert haben. Hier geben Sie
dann ein
> javac HalloWelt.java
Wenn Sie beim Abtippen einen Fehler gemacht haben, meldet sich der Compiler
mit einer Fehlermeldung. Lesen Sie bitte die Fehlermeldung aufmerksam —
Sie sollten Fehlermeldungen immer aufmerksam lessen — und korrigieren ihn
bitte im Editor anschließend den in der Fehlermeldung beschriebenen Fehler.
Speichern Sie dann die Datei und kompilieren sie erneut.
Wenn Sie beim abtippen keine Fehler gemacht haben, sollte beim Kompilieren keine Ausgabe auf dem Bildschirm erzeugt worden sein aber die Datei
HalloWelt.class entstanden sein. Pr¨
ufen Sie dies bitte nach !
Die Datei HalloWelt.class enth¨alt den Bytecode unserer Klasse. Sie k¨onnen
das Programm nun laufen lassen, indem Sie die Java-Virtual-Machine mit unserem kompilierten Programm starten:
> java HalloWelt
Achten Sie bitte darauf, beim Aufruf von java nicht die Erweiterung .class
anzugeben. Dann wird ihre Klasse n¨amlich nicht gefunden.
Wenn Sie bis hierher alles nachgemacht haben, sollte der Text “Hallo, Welt” auf
dem Bildschirm ausgegeben werden.
B.3
Datentypen
Um dieses Kapitel zu verstehen, sollten Sie den Anhang ?? zum Aufbau eines
Computers gelesen haben.
Um die Attribute unserer Objekte zu definieren, haben wir bisher Wertebereiche
wie ganze Zahl, reelle Zahl, Zeichenkette oder Vektor verwendet. Wenn wir in
Java die Wertebereiche von Attributen beschreiben, kann man nicht einfach
einen umgangssprachlichen Ausdruck wie “ganze positivie Zahl” verwenden.
Der Java-Compiler muß schon etwas genauer wissen, was f¨
ur Zahlen zu verwenden sind. Letztendlich muß ja jede Zahl in einem Java-Programm im Speicher
des Computers gespeichert werden. Es sollte einleuchtend sein, daß große Zahlen
mehr Platz zur Speicherung ben¨otigen als kleine Zahlen — um eine 20 stellige
Zahl aufzuschreiben, ben¨
otigt man ja auch mehr Platz als f¨
ur eine 2 stellige
Zahl. Es w¨
are nun sehr unangebracht, wenn der Java-Compiler jede ganzzahlige
Zahl
146
C
C.1
Probleme
Probleme bei Verwendung von javac
Beim Kompilieren einer Klassendatei
> javac MeineKlasse.java
treten manchmal folgende Fehlermeldungen auf:
bash: MeineKlasse.java: command not found
Sie haben das “>” Zeichen mit eingegeben. Wenn wir
> javac MeineKlasse.java
schreiben, dann meinen wir damit, daß Sie auf der Kommandozeile den Text
javac MeineKlasse.java eingeben sollen. Das Gr¨oßerzeichen verbildlicht also
die Kommandozeile und darf nicht mit eingetippt werden.
error: Can’t read: MeineKlasse.java
1 error
Der Compiler findet die Datei nicht. M¨oglicherweise befinden Sie sich im falschen
Verzeichnis oder haben die Datei im Editor noch nicht oder unter einem anderen
Namen abgespeichert. Pr¨
ufen Sie, ob die Datei vorhanden ist:
> ll MeineKlasse.java
javac: invalid argument: MeineKlasse
use: javac [-g][-O][-debug][-depend][-nowarn][-verbose]
[-classpath path][-nowrite][-deprecation][-d dir]
[-J<runtime flag>] file.java...
Sie haben statt
> javac MeineKlasse.java
den Befehl
> javac MeineKlasse
eingegeben. Wenn der Name, den Sie an javac u
¨bergeben, nicht auf .java
endet, denkt javac, sie wollten ihm besondere Hinweise zur Kompilation geben.
Unable to initialize threads: cannot find class java/lang/Thread
Der Compiler findet die zum Java-System geh¨origen Klassen nicht. Ihr JavaProgrammiersystem ist nicht korrekt installiert. Sie sollten die Umgebungsvariable CLASSPATH pr¨
ufen. Siehe Anhang E
147
MeineKlasse.java:1: Class ak.Turtle.TurtleScreen not found in import.
import ak.Turtle.TurtleScreen;
^
MeineKlasse.java:2: Class ak.Turtle.Turtle not found in import.
import ak.Turtle.Turtle;
^
2 errors
Der Compiler findet die Turtle–Graphik nicht. Sie sollten die Turtle–Graphik
installieren und die Umgebungsvariable CLASSPATH pr¨
ufen. Siehe Anhang E
C.2
Probleme bei Verwendung von java
Beim Ausf¨
uhren einer Klasse
> java MeineKlasse
treten m¨
oglicherweise folgende Fehler auf:
Can’t find class MeineKlasse.class
Sie haben statt
> java MeineKlasse
das Kommando
> java MeineKlasse.class
verwendet.
In class MeineKlasse: void main(String argv[]) is not defined
Sie versuchen eine Klasse auszuf¨
uhren, in welcher die main-Methode fehlt. Jede
Klasse, die Sie ausf¨
uhren wollen, ben¨otigt eine solche Methode. (siehe ??).
In class MeineKlasse: void main(String argv[]) is not defined
Sie versuchen eine Klasse auszuf¨
uhren, in welcher die main-Methode fehlt. Jede
Klasse, die Sie ausf¨
uhren wollen, ben¨otigt eine solche Methode. (siehe ??).
Can’t find class MeineKlasse
Entweder existiert im aktuellen Verzeichnis keine Datei MeineKlasse.class,
oder die CLASSPATH–Variable enth¨alt nicht das aktuelle Verzeichnis. Siehe Anhang E
Can’t find class MeineKlasse
148
Entweder existiert im aktuellen Verzeichnis keine Datei MeineKlasse.class,
oder die CLASSPATH–Variable enth¨alt nicht das aktuelle Verzeichnis. Siehe Anhang E
java.lang.NoClassDefFoundError: ak/Turtle/TurtleScreen
at MeineKlasse.main(Compiled Code)
Die Turtle–Graphik ist nicht in u
¨ber die CLASSPATH–Umgebungsvariable zug¨anglich. Siehe Anhang E
149
D
Goldene Regeln fu
¨ rs Programmieren
Ihre erstellten Javaprogramme sollen nicht nur syntaktisch richtig, sondern auch
leicht zu lesen sein. Deshalb sollen Sie von der ersten Zeile an auf einen guten
Programmierstil achten. Dieser zeichnet sich vor allem dadurch aus, dass ihre
Programme gut gegliedert sind, Kommentare enthalten und von anderen leicht
verst¨
andlich zu lesen sind. Dadurch vermeiden Sie beim Programmieren Fehler
und ihre Programme sind besser wartbar. Um ihnen von Anfang an einen guten
Programmierstil beizubringen, m¨ochten wir ihnen folgende Richtlinien mit auf
den Weg geben, die f¨
ur diese Lehrveranstaltung verbindlivh sind.
D.1
Allgemeines
Hier nun ein paar allgemein Regeln auf die in den n¨achsten Abschnitten noch
n¨
aher eingegangen wird.
• Benutzen Sie maximal einen Befehl pro Zeile!
• Verwenden Sie sprechende Bezeichner.
• Folgen Sie bei der Benennung von Bezeichnern vorhersehbaren Konventionen.
• Kommentieren Sie die einzelnen Programmteile.
• Benutzen Sie eine sinnvolle und einheitliche Einr¨
uckung.
• Trennen Sie Programmteile durch Leerzeilen.
• Teilen Sie ein Programm sinnvoll auf.
• Vermeiden Sie “magic numbers”.
Benennen Sie Klassen nach ihrem Zweck, Variablen nach ihrem Inhalt, Methoden nach ihrer Aufgabe. Das Programm wird dadurch lesbarer, da es dann nicht
mehr notwendig ist, bei jedem Vorkommen eines Bezeichners zu u
¨berlegen, wozu
der Bezeichner da ist. Lassen Sie nach jedem Schl¨
usselwort und zwischen den
bin¨
aren Operatoren ein Leerzeichen frei.
D.2
Quelldateien
Die Quelldateien sollen folgendes Aussehen besitzen:
¨
• Ubungskommentar
• package Anweisung
• import Anweisung
• Die als public deklarierte Klasse
• andere Klassen, falls erforderlich
150
¨
Der Ubungskommentar
besteht aus dem Namen des Studenten, seiner Matrikelnummer, dem Datum und der Aufgabenstellung. Dieser Kommentar sollte
als spezieller “doc Kommentar” dargestellt werden, d. h. er beginnt mit einem
/** und endet mit */. Diese werden vom javadoc Programm speziell ausgewertet, um eine einfache Onlinedokumentation aus dem Javaquellcode zu erstellen.
Nutzen Sie auch die @author und @version tags. Hier ist kleines Beispiel:
/**
* Die Klasse Kreise mit ihren Daten und Methoden
* Hausaufgabe 3
* @author: Harry Hacker
* @version 1.2.3 31.04.00
*/
Benutzen Sie maximal einen Befehl pro Zeile! Tritt im Programm ein Fehler
auf, kann man seinen Ort h¨
aufig schnell auf eine Programmzeile einschr¨anken.
Wenn dort dann mehrere Anweisungen stehen, ist eine Lokalisierung des Fehlers
schwierig. Auch die Fehlermeldungen des Compilers beziehen sich auf eine Zeile.
D.3
Klassen
Es ist u
¨blich, Klassennamen mit einem Grossbuchstaben beginnen zu lassen.
Es empfiehlt sich, englischsprachige Bezeichner zu verwenden, da das gesamte
JDK englischsprachig ist. Wo m¨oglich, sollten Ihre Wahl von Bezeichnern den
im JDK verwendeten Konventionen folgen.
Schreiben Sie als erstes die Variablen und Methoden auf, die als public deklariert werden und dann die als private deklarierten.
D.4
Methoden
Jede Methode (ausser main) beginnt mit einem Kommentar im javadoc Format.
/**
* Berechnet die Fakultaet von n
*/
public int fakultaet(int fac)
{ . . .
}
Vermeiden Sie Programmzeilen, die mehr als 80 Zeichen enthalten und Metho¨
den mit mehr als 50 Zeilen, da beides die Ubersichtlichkeit
reduziert. Manchmal
l¨
asst es sich aber nicht vermeiden, wie z.B. bei langen if/else Anweisungen.
Sie sollten jedoch immer komplexe Probleme in kleinere und einfachere Teilprobleme unterteilen.
D.5
Variablen, Konstanten und Literale
Benutzen Sie f¨
ur Variablenbezeichner nur kleine Buchstaben. Setzt sich wegen
der besseren Lesbarkeit der Variablenbezeichner aus mehreren W¨ortern zusam151
men, so k¨
onnen Sie die n¨
achstfolgenden W¨orter auch mit einem grossen Buchstaben beginnen lassen, zum Beispiel firstPlayer. Definieren Sie nicht alle
Variablen sofort am Anfang eines Blockes,
public static double sqrt(double a)
{
double xold;
double xnew;
boolean more;
. . .
}
sondern dann wenn sie das erste mal gebraucht werden.
public static double sqrt(double a)
{
. . .
while(more)
{
double xnew = (xold + a / xold) / 2;
. . .
}
}
Benutzen Sie f¨
ur Konstantenbezeichner nur Grossbuchstaben. Werden sie nicht
von anderen Klassen ben¨
otigt sollten als private deklariert werden.
privat static final int DAYS_PER_YEAR = 365
Vermeiden Sie im Programmtext die Verwendung von “magic numbers”. Eine
“magic number” ist eine Zahl die sie ohne Erkl¨arung in ihrem Quellcode benutzen. Definieren Sie statt dessen an einer zentralen Stelle Konstanten und
verwenden Sie diese.
Wenn Sie ein Array der Gr¨
osse 100 dimensionieren, schreiben Sie nicht
int[] daten_Arr = new int[ 100 ]
sondern definieren Sie am Klassenanfang eine Konstante z.B. als
final int SIZE_DATEN_ARR = 100;
und dimensionieren das Array dann als
int[] daten_Arr = new int[ SIZE_DATEN_ARR ];
Wenn sich die Variable SIZE DATEN ARR sp¨ater a¨ndern sollte, sind Sie so auf
der sicheren Seite, ausserdem wird der Programmtext durch die Verwendung
sprechender Namen besser lesbar.
Wenn in einem Programmtext die Zahl π ben¨otigt wird, schreiben Sie diese
nicht als 3.14159265358979323846 sondern definieren Sie am Klassenanfang die
Konstante
152
final static double PI = 3.14159265358979323846;
D.6
Kontrollstrukturen
Benutzen Sie eine sinnvolle und einheitliche Einr¨
uckung. Die Einr¨
uckung soll
die Struktur des Programms deutlich machen. Die Lesbarkeit des Programms
wird deutlich erh¨
oht, wenn zusammengeh¨orige Programmteile auch optisch zusammen geh¨
oren. Achten Sie darauf, dass die Einr¨
uckung konsistent ist. Die
Einr¨
ucktiefe sollte jeweils drei Leerzeichen betragen. Benutzen Sie geschweifte
Klammern um die entsprechenden Zugeh¨origkeiten anzuzeigen, oder um sie zu
erzwingen. Bei diesem Beispiel ist nicht eindeutig wohin die else Anweisung
geh¨
ort.
if (n > 0)
if (a > b)
z = a;
else
z = b;
Abhilfe erh¨
alt man durch das Benutzen von geschweiften Klammern.
if (n > 0)
{ if (a > b)
z = a;
else
z = b;
} /* {...} sind hier nicht erforderlich */
if (n > 0)
{ if (a > b)
z = a;
}
else
z = b; /* hier sind {...} erforderlich */
Die offene geschweifte und die dazugeh¨orige geschlossene geschweifte Klammer
sollten entweder in der gleichen Spalte (vertikal) oder in der gleichen Zeile (ho¨
rizontal) angeordnet sein. Dies erleichtert die Uberpr¨
ufung ob alle Klammern
ordentlich gesetzt wurden.
while (i < n) { print(a[i]); i++; }
while (i < n)
{
print(a[i]);
i++;
}
153
E
E.1
Installation von Java und Turtle–Graphik
Installation von Java
Wenn Sie im CIP–Pool arbeiten, ist dies schon f¨
ur Sie erledigt. Falls Sie zuhause
arbeiten wollen, holen Sie sich bitte von Sun24 das JDK 1.2 f¨
ur Ihr Betriebssystem und folgen den Installationsanweisungen.
E.2
Der CLASSPATH
Das JDK besteht aus sehr vielen Programmbausteinen (Klassen), die in auf die
Zeichenfolge .class endenden Dateien enthalten sind. Der Name einer Java–
Klasse besteht aus einer Pfadangabe und einem Klassennamen. Beispielsweise
bezeichnet der Klassenname
eip.TurtleScreen
eine Klasse namens TurtleScreen, welche unter der Pfadangabe eip zu finden
ist. Da jeder Benutzer die Dateien auf seiner Festplatte unterschiedlich organisiert, muß Java irgendwie mitgeteilt werden, wo auf der Festplatte nach einer
derartigen Klasse gesucht werden soll.
Hierzu verwendet Java die CLASSPATH–Umgebungsvariable. Eine Umgebungsvariable ist ein Mittel des Betriebssystems zur Konfiguration von Programmen.
Die CLASSPATH–Variable enth¨alt beliebig viele, durch Doppelpunkte getrennte Pfadangaben:
CLASSPATH=pfad1:pfad2:pfad3:...:pfadN
Dabei ist eine Pfadangabe entweder ein Verzeichnispfad oder der Name eines
.zip–Archives. Ein .zip–Archiv ist eine Datei, welche viele andere Dateien
enth¨
alt.
Beispiel: Unter Windows k¨onnte eine CLASSPATH–Variable, welche auf
die Verzeichnissse c:\java\jdk\classes.zip, c:\java\turtle und . verweist, so ausssehen:
CLASSPATH=.:C:\java\jdk\classes.zip:C:\java\turtle
Unter Unix k¨
onnte die CLASSPATH–Variable so aussehen:
CLASSPATH=.:/usr/local/java/jdk/classes.zip:/usr/local/java/turtle/
Wenn Java nach der Klase eip.TurtleScreen sucht, geht es wie folgt vor: Es
schaut in jedem im CLASSPATH angegebene Verzeichnis nach, ob es eine Datei
ak/Turtle/TurtleScreen.class enth¨alt (unter Windows w¨
urde Java schauen,
ob es eine Datei ak\Turtle\TurtleScreen.class findet). Die erste derartige
Datei, die in einem Verzeichnis des CLASSPATH gefunden wird, wird dann benutzt.
24 http://www.java.sun.com/products/
154
Beispiel: Wenn unter Unix der CLASSPATH wie oben angegeben ist,
w¨
urde Java zuerst nachsehen, ob die Datei ak/Turtle/Turtle.class in
der .zip–Datei /usr/local/java/jdk/classes.zip enthalten ist.
Wenn dies nicht der Fall ist, w¨
urde Java nachschauen, ob die Datei
/usr/local/java/turtle/ak/Turtle/Turtle.class existiert.
Falls auch dies nicht der Fall ist, w¨
urde Java nachsehen, ob im aktuellen Verzeichnis das Unterverzeichnis ak/Turtle/ und darin die Datei
TurtleScreen.class existiert.
Der CLASSPATH wird immer benutzt, wenn Java eine Datei sucht. Ob Sie Java
innerhalb eines Programms durch den Befehl
import eip.TurtleScreen;
anweisen, die Klasse eip.TurtleScreen zu verwenden, oder ob Sie auf der Kommandozeile ein Javaprogramm per
> java MeineKlasse
starten — immer wird die Klasse mit Hilfe des CLASSPATH gesucht.
Dies kann zu verwirrenden Effekten f¨
uhren, wenn im CLASSPATH nicht auch das
aktuelle Arbeitsverzeichnis namens .“ enthalten ist. Denn dann kann Java Ihre
”
Klassendatei selbst dann nicht finden, wenn Sie im aktuellen Arbeitsverzeichnis
zu finden ist.
Wenn im Arbeitsverzeichnis die Datei MeineKlasse.java existiert und .“ nicht
”
im CLASSPATH enthalten ist, so erhalten Sie auf den Befehl java MeineKlasse
die Meldung
Can’t find class MeineKlasse
In solch einem Fall nehmen Sie das aktuelle Arbeitsverzeichnis in Ihren CLASSPATH
auf.
Weiter unten beschreiben wir, wie man den CLASSPATH erweitern kann.
E.3
Installation der Turtle–Graphik
• Um die Turtle–Graphik unter Unix zu installieren, holen Sie sich bitte
von unseren WWW–Seiten25 die Datei Turtle.tgz und legen Sie sie in
Ihr Homeverzeichnis.
Legen Sie dann bitte ein Verzeichnis f¨
ur die Turtle an (z.B. ~/turtle).
Dies geht auf der Unix–Kommandozeile per
> mkdir ~/turtle
Dann entpacken Sie die Datei Turtle.tgz wie folgt:
>
>
>
>
cd ~
gzip -d Turtle.tgz
cd ~/turtle
tar xvf ~/Turtle.tar
25 http://www.tu-bs.de/institute/wir/eip/
155
Pr¨
ufen Sie nun, ob die Datei ~/turtle/ak/Turtle/Turtle.java existiert.
Anschließend setzen Sie bitte, wie im n¨achsten Kapitel beschrieben, den
CLASSPATH und testen die Turtle durch den Befehl
> java eip.TurtleDemo
• Um die Turtle–Graphik unter Windows zu installieren, holen Sie sich bitte
von unseren WWW–Seiten26 die Datei Turtle.zip.
Sie ben¨
otigen außerdem den Entpacker unzip27 oder Winzip28 (Winzip ist
eine kommerzielle Software — es ist u
¨blich, daß fast alle unter Unix frei
erh¨
altliche Software unter Windows Geld kostet).
Legen Sie bitte ein Verzeichnis f¨
ur die Turtle an (z.B. C:\java\turtle).
Dies geht entweder mit Hilfe des Explorers oder auf der Windows–Kommandozeile
per
> mkdir C:\java\turtle
anschließend entpacken Sie bitte die Datei Turtle.zip hier hinein. Achten
Sie dabei darauf, daß beim Entpacken alle Pfade wiederhergestellt werden.
Wie das geht, ist in der Dokumentation des Entpackers beschrieben.
Sehen Sie nach dem Entpacken nach, ob die Datei
c:\java\turtle\ak\Turtle\Turtle.java
existiert — sonst ist irgendwas schief gegangen.
Anschließend setzen Sie bitte, wie im n¨achsten Abschnitt beschrieben, den
CLASSPATH und testen die Turtle durch den Befehl
> java eip.TurtleDemo
E.4
Erweitern des CLASSPATH
Sie k¨
onnen den CLASSPATH wie folgt um weitere Suchpfade erweitern.
• Wenn Sie unter Unix arbeiten und die bash–Shell verwenden, k¨onnen Sie
auf der Kommandozeile eingeben
export CLASSPATH=$CLASSPATH:neuerPfad
Angenommen, Sie haben die Turtle–Graphik im Verzeichnis ~/turtle installiert, so sollten Sie den CLASSPATH wie folgt erweitern:
export CLASSPATH=$CLASSPATH:~/turtle
26 http://www.tu-bs.de/institute/wir/eip/
27 http://www.cdrom.com/pub/infozip/UnZip.html
28 http://www.winzip.com/
156
Diesen Befehl m¨
ussen Sie jedesmal erneut eingeben, wenn Sie eine Kommandozeile o
ffnen.
Sie k¨onnen den Befehl statt dessen auch als letzte Zeile
¨
in die Datei ~/.profile aufnehmen. Dann wird der CLASSPATH bei jedem
anmelden automatisch gesetzt.
• Unter Windows k¨
onnen Sie dazu auf der Kommandozeile eingeben
set CLASSPATH=%CLASSPATH%:neuerPfad
Angenommen, Sie haben die Turtle–Graphik im Verzeichnis C:\java\turtle\
installiert, so sollten Sie den CLASSPATH wie folgt erweitern:
set CLASSPATH=%CLASSPATH%:C:\java\turtle
Diese Zeile m¨
ussen Sie jedesmal eingeben, wenn Sie eine Kommandozeile
offnen. Wenn Sie die obige Zeile als letzte Zeile in die Datei C:\autoexec.bat
¨
aufnhemen, wird der CLASSPATH bei jedem Neustart von Windows automatisch gesetzt.
157
Was this manual useful for you? yes no
Thank you for your participation!

* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project

Download PDF

advertisement