Grundlagen der Programmierung 1. Einführung

Werbung
Grundlagen der Programmierung
Ziel
Einfache Programme in Java schreiben können
Inhalte
•
•
•
•
•
Literatur
H.Mössenböck: Sprechen Sie Java? dpunkt.verlag, 2005
Klausur
Di 1.2.2011, 8:00 - 9:00
Prof. Dr. Hanspeter Mössenböck
© H.Mössenböck: Sprechen Sie Java? 3. erweiterte Auflage, dpunkt.verlag, 2005
Anweisungen und Datenstrukturen
Grundlagen der Objektorientierung
Programmentwurf
Rekursion
Fehlerbehandlung
2
Worum geht es?
Programmieren
Grundlagen der Programmierung
Prof. H. Mössenböck
ein Problem so exakt beschreiben, dass es ein Computer lösen kann
kreative Tätigkeit
Ingenieurtätigkeit
1. Einführung
Nur wenige Leute können gut programmieren
Programm = Daten + Befehle
4
Daten und Befehle
Daten
Programmerstellung
Problem
Menge adressierbarer Speicherzellen
x
17
y
4
z
21
Name
Wert
Daten sind binär gespeichert (z.B. 17 = 10001)
Binärspeicherung ist universell (Zahlen, Texte, Bilder, Ton, ...)
1 Byte = 8 Bit
1 Wort = 4 Byte (manchmal 2 Byte)
Befehle
Spezifikation
Aufgabenstellung
Algorithmus
Lösungsverfahren
Mensch
Programm
Operationen mit den Speicherzellen
Maschinensprache
Hochsprache
// Lade Zelle x
ACC x
ACC ACC + y // Addiere Zelle y
// Speichere Ergebnis in Zelle z
z ACC
z = x + y;
Compiler
Codiertes Lösungsverfahren
Maschinenprogramm
Lader
5
Algorithmus
Variablen
Sind benannte Behälter für Werte
Schrittweises, präzises Verfahren zur Lösung eines Problems
Name
x
y
99
3
Parameter
Können ihren Wert ändern
Summiere Zahlen von 1 bis max ( max, sum)
1. sum 0
2. zahl 1
3. Wiederhole, solange zahl
3.1 sum sum + zahl
3.2 zahl zahl + 1
6
x
x
max
100
x+1
Folge von Schritten
Haben einen Datentyp = Menge erlaubter Werte
Variablentyp
Programm = Beschreibung eines Algorithmus in einer Programmiersprache
Zahl
Zeichen
7
Werte
17
'a'
54
'x'
...
...
Typ Form
- in eine Zahlenvariable passen nur Zahlen
- in eine Zeichenvariable passen nur Zeichen
8
Anweisungen
Anweisungen
Wertzuweisung
Auswahl (auch Verzweigung, Abfrage, Selektion)
x
x+1
Variable
1. werte Ausdruck aus
2. weise seinen Wert der Variablen zu
j
x<y?
Ausdruck
min
x
n
x
min
Anweisungsfolge (auch Sequenz)
y
y
min = Minimum von x und y
"Ablaufdiagramm"
x
y
z
3
4
x+y
Assertion
x = 3, y = 4, z = 7
Assertion (Zusicherung)
Aussage über den Zustand des Algorithmus
an einer bestimmten Stelle
9
Anweisungen
Beispiel: Vertauschen zweier Variableninhalte
Wiederholung (auch Schleife, Iteration)
n
0
x>1?
j
x
n
Swap ( x, y)
n
n
x/2
n+1
10
0
h
x
y
x>1
x
n
x
y
h
Schreibtischtest
x
3
2
y
2
3
h
3
x/2
n+1
x 1
n = log2 x0
x 1
n = log2 x0
Alternative Darstellung
11
12
Beispiel: Maximum dreier Zahlen bestimmen
Max ( a,
b,
Beispiel: Euklidscher Algorithmus
Berechnet den größten gemeinsamen Teiler zweier Zahlen x und y
c, max)
GGT ( x,
j
j
max
a>c?
a
a>b?
n
j
c
max
Schreibtischtest
y, ggt)
n
b>c?
a>b
c
max
a b
n
b
a
max
b
rest
c
Rest von x / y
rest
c
0
x
y
rest
28
20
8
20
8
4
8
4
0
x y
y rest
Rest von x / y
rest
Warum funktioniert dieser Algorithmus?
(ggt teilt x) & (ggt teilt y)
x = i*ggt, y = j*ggt, (x-y) = (i-j)*ggt
ggt teilt (x - y)
ggt teilt (x - q*y)
ggt teilt rest
GGT(x, y) = GGT(y, rest)
rest = 0
ggt
y
13
Beschreibung von Programmiersprachen
Beispiel: Quadratwurzel von x berechnen
1.
2. Näherung:
root x(a/ +2 root) / 2
a x / root
0
a
root
a
root x / 2
a x / root
a
Syntax
x
Regeln, nach denen Sätze gebaut werden dürfen
z.B.: Zuweisung = Variable " " Ausdruck.
root
SquareRoot ( x, root)
a * root = x
root
a * root = x
root (root + a) / 2
a x / root
a * root = x
a * root = x & a = root
root * root = x
14
Schreibtischtest
x
root
10
5
3.5
3.17857
3.16232
3.16228
Semantik
a
Bedeutung der Sätze
z.B.: werte Ausdruck aus und weise ihn der Variablen zu
2
2.85714
3.14607
3.16223
3.16228
Grammatik
Menge von Syntaxregeln
z.B. Grammatik der ganzen Zahlen
Kommazahlen sind meist nicht exakt gleich
Ziffer = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9".
Zahl = Ziffer {Ziffer}.
daher besser
Ziffer
| a-root | > 0.0000001
0
Zahl
1
2
3
4
5
6
7
8
9
Ziffer
15
16
EBNF (Erweiterte Backus-Naur-Form)
Metazeichen
=
.
|
()
[]
{}
Bedeutung
Beispiel
trennt Regelseiten
schließt Regel ab
trennt Alternativen
klammert Alternativen
wahlweises Vorkommen
0..n-maliges Vorkommen
A=xyz.
beschreibt
Grundlagen der Programmierung
Prof. H. Mössenböck
x|y
(x | y) z
[x] y
{x} y
x, y
xz, yz
xy, y
y, xy, xxy, xxxy, ...
2. Einfache Programme
Beispiele
Grammatik der Gleitkommazahlen
Zahl = Ziffer {Ziffer}.
Gleitkommazahl = Zahl "." Zahl ["E" ["+" | "-"] Zahl].
Grammatik der If-Anweisung
IfAnweisung = "if" "(" Ausdruck ")" Anweisung ["else" Anweisung].
17
Grundsymbole
Variablendeklarationen
Namen
bezeichnen Variablen, Typen, ... in einem Programm
- bestehen aus Buchstaben, Ziffern und "_"
- beginnen mit Buchstaben
- beliebig lang
- Groß-/Kleinschreibung signifikant
Schlüsselwörter
- heben Programmteile hervor
- dürfen nicht als Namen verwendet werden
Zahlen
- ganze Zahlen (dezimal oder hexadezimal)
- Gleitkommazahlen
Zeichketten
- beliebige Zeichen zwischen Hochkommas
- dürfen nicht über Zeilengrenzen gehen
- " in der Zeichenkette wird als \" geschrieben
Jede Variable muss vor ihrer Verwendung deklariert werden
x
x17
myVar
my_var
- macht den Namen und den Typ der Variablen bekannt
- Compiler reserviert Speicherplatz für die Variable
if
while
Ganzzahlige Typen
376
0x1A5
3.14
int x;
short a, b;
dezimal
1*162+10*161+5*160
Gleitkommazahl
byte
short
int
long
deklariert eine Variable x vom Typ int (integer)
deklariert 2 Variablen a und b vom Typ short (short integer)
8-Bit-Zahl
16-Bit-Zahl
32-Bit-Zahl
64-Bit-Zahl
-27 .. 27-1
-215 .. 215-1
-231 .. 231-1
-263 .. 263-1
(-128 .. 127)
(-32768 .. 32767)
(-2 147 483 648 .. 2 147 483 647)
Initialisierungen
"a simple string"
"sie sagte \"Hallo\""
int x = 100;
short a = 0, b = 1;
2
deklariert int-Variable x; weist ihr den Anfangswert 100 zu
deklariert 2 short-Variablen a und b mit Anfangswerten
3
Konstantendeklarationen
Kommentare
Initialisierte "Variablen", deren Wert man nicht mehr ändern kann
Geben Erläuterungen zum Programm
Zeilenendekommentare
static final int max = 100;
- beginnen mit //
- gehen bis zum Zeilenende
Zweck
- bessere Lesbarkeit
Klammerkommentare
max ist lesbarer als 100
-
- bessere Wartbarkeit
wenn die Konstante mehrmals vorkommt und geändert werden muss,
dann muss das nur an 1 Stelle erfolgen
Konstantendeklaration muss auf Klassenebene stehen (s. später)
durch /* ... */ begrenzt
können über mehrere Zeilen gehen
dürfen nicht geschachtelt werden
oft zum "Auskommentieren" von
Programmteilen
int sum; // total sales
/* Das ist ein längerer
Kommentar, der über
mehrere Zeilen geht */
Sinnvoll kommentieren!
- alles kommentieren, was Erklärung bedarf
- statt unklares Programm mit Kommentar, besser klares Programm ohne Kommentar
- nicht kommentieren, was ohnehin schon im Programm steht;
folgendes ist z.B. unsinnig
int sum; // Summe
4
5
Sprache in Kommentaren und Namen
Namenswahl für Variablen und Konstanten
Deutsch
+ einfacher
Einige Tipps
• lesbare aber nicht zu lange Namen
z.B. sum, value
Englisch
+ meist kürzer
+ passt besser zu den englischen Schlüsselwörtern (if, while, ...)
+ Programm kann international verteilt werden (z.B. über das Web)
• Hilfsvariablen, die man nur über kurze Strecken braucht, eher kurz:
z.B. i, j, x
• Variablen, die man im ganzen Programm braucht, eher länger:
z.B. inputText
• mit Kleinbuchstaben beginnen,
Worttrennung durch Großbuchstaben oder "_"
Jedenfalls: Deutsch und Englisch nicht mischen!!
z.B. inputText, input_text
• Englisch oder Deutsch?
6
7
Zuweisungen
Arithmetische Ausdrücke
x = y+1 ;
Variable
Vereinfachte Grammatik
1. berechne den Ausdruck
2. speichere seinen Wert in der Variablen
Expr = Operand {BinaryOperator Operand}.
Operand = [UnaryOperator] ( identifier | number | "(" Expr ")" ).
Ausdruck
Binäre Operatoren
Bedingung: linke und rechte Seite müssen zuweisungskompatibel sein
- müssen dieselben Typen haben, oder
- Typ links Typ rechts
Hierarchie der ganzzahligen Typen
long
int
short
byte
Beispiele
int i, j; short s; byte b;
i = j;
// ok: derselbe Typ
i = 300; // ok (Zahlkonstanten sind int)
b = 300; // falsch: 300 passt nicht in byte
i = s;
// ok
s = i;
// falsch
+
*
/
%
- dass Variablen nur erlaubte Werte enthalten
- dass auf Werte nur erlaubte Operationen ausgeführt werden
4/3 = 1 (-4)/3 = -1 4/(-3) = -1 (-4)/(-3) = 1
4%3 = 1 (-4)%3 = -1 4%(-3) = 1 (-4)/(-3) = -1
Unäre Operatoren
+
-
Statische Typenprüfung: Compiler prüft:
Addition
Subtraktion
Multiplikation
Division, Ergebnis ganzzahlig
Modulo (Divisionsrest)
Identität (+x = x)
Vorzeichenumkehr
8
9
Typregeln in arithm. Ausdrücken
Increment und Decrement
Vorrangregeln
Variablenzugriff kombiniert mit Addition/Subtraktion
- Punktrechnung (*, /, %) vor Strichrechnung (+, -)
- Operatoren auf gleicher Stufe werden von links nach rechts ausgewertet (a + b + c)
- Unäre Operatoren binden stärker als binäre
z.B.: -2 * 4 + 3 ergibt -5
x++
++x
x---x
Typregeln
Operandentypen
Ergebnistyp
byte, short, int, long
- wenn mindestens 1 Operand long ist
- sonst int
Beispiele
short s; int i; long x;
x = x + i;
// long
i = s + 1;
// int (1 ist vom Typ int)
s = (short)(s + 1); // Typumwandlung nötig
nimmt den Wert von x und erhöht x anschließend um 1
erhöht x um 1 und nimmt anschließend den erhöhten Wert
nimmt den Wert von x und erniedrigt x anschließend um 1
erniedrigt x um 1 und nimmt anschließend den erniedrigten Wert
Beispiele
x = 1; y = x++ * 3;
x = 1; y = ++x * 3;
long
// x = 2, y = 3
// x = 2, y = 6
entspricht: y = x * 3; x = x + 1;
entspricht: x = x + 1; y = x * 3;
Kann auch als eigenständige Anweisung verwendet werden
Typumwandlung (type cast)
(type)expression
x = 1; x++;
- wandelt Typ von expression in type um
- dabei kann etwas abgeschnitten werden
i
// x = 2
entspricht: x = x + 1;
Darf nur auf Variablen angewendet werden (nicht auf Ausdrücke)
y = (x + 1)++;
// falsch!
(short)i
10
11
Zuweisungsoperatoren
Multiplikation/Division mit Zweierpotenzen
Mit Shift-Operationen effizient implementierbar
Multiplikation
x << 1
x << 2
x << 3
x << 4
...
x*2
x*4
x*8
x * 16
...
Arithmetischen Operationen lassen sich mit Zuweisung kombinieren
Division
x/2
x/4
x/8
x / 16
...
x >> 1
x >> 2
x >> 3
x >> 4
...
Division nur bei
positiven Zahlen
durch Shift ersetzbar
+=
-=
*=
/=
%=
Beispiele
x = 3;
x = -3;
0000 0011
x = x << 2; // 12
x = x + y;
x = x - y;
x = x * y;
x = x / y;
x = x % y;
0000 1111
x = x << 1; // -6
0000 1100
Langform
x += y;
x -= y;
x *= y;
x /= y;
x %= y;
Spart Schreibarbeit, ist aber nicht schneller als die Langform
x = 15;
1111 1101
Kurzform
1111 1010
x = x >> 2; // 3
0000 0011
12
Eingabe und Ausgabe von Werten
Eingabe von der Tastatur
Besonderheiten zur Eingabe
Eingabe von Tastatur
Eingabe von einer Datei
Eintippen von:
Programm
Ausgabe auf den Bildschirm
Ausgabe
http://ssw.jku.at/JavaBuch
Return-Taste
Programm:
// liest eine Zahl vom Eingabestrom
// liefert true oder false, je nachdem, ob Lesen erfolgreich war
// öffnet Datei als neuen Eingabestrom
// schließt Datei und kehrt zum alten Eingabestrom zurück
Out.print(x);
Out.println(x);
Out.open("MyFile.txt");
Out.close();
12 100
füllt Lesepuffer
Ausgabe auf eine Datei
Eingabe
int x = In.readInt();
if (In.done()) ...
In.open("MyFile.txt");
In.close();
13
// gibt x auf dem Ausgabestrom aus (x kann von bel. Typ sein)
// gibt x aus und beginnt eine neue Zeile
// öffnet Datei als neuen Ausgabestrom
// schließt Datei und kehrt zum alten Ausgabestrom zurück
14
int x = In.readInt(); // liest 12
int y = In.readInt(); // liest 100
int z = In.readInt(); // blockiert, bis Lesepuffer wieder gefüllt ist
Ende der Eingabe: Eingabe von Strg-Z in leere Zeile
Eingabe von Datei
kein Lesepuffer, In.readInt() liest direkt von der Datei
Ende der Eingabe wird automatisch erkannt (kein Strg-Z nötig)
15
Grundstruktur von Java-Programmen
class ProgramName {
public static void main (String[] arg) {
... // Deklarationen
... // Anweisungen
}
Übersetzen und Ausführen mit JDK
Text muss in einer Datei namens
ProgramName.java stehen
C:\> cd MySamples
C:\MySamples> javac Sample.java
wechselt ins Verzeichnis mit der Quelldatei
erzeugt Datei Sample.class
Ausführen
C:\MySamples> java Sample
Geben Sie 2 Zahlen ein: 3 4
Summe = 7
}
Beispiel
class Sample {
public static void main (String[] arg) {
Out.print("Geben Sie 2 Zahlen ein: ");
int a = In.readInt();
int b = In.readInt();
Out.print("Summe = ");
Out.println(a + b);
}
}
Übersetzen
ruft main-Methode der Klasse Sample auf
Eingabe mit Return-Taste abschließen
Text steht in Datei
Sample.java
16
17
If-Anweisung
Grundlagen der Programmierung
Prof. H. Mössenböck
n
n>0?
j
x
3. Verzweigungen
ohne else-Zweig
if (x > y)
max = x;
else
max = y;
mit else-Zweig
x/n
j
max
if (n > 0) x = x / n;
x
x>y?
n
max
y
Syntax
IfStatement = "if" "(" Expression ")" Statement ["else" Statement].
2
Anweisungsblöcke
Einrückungen
Wenn if-Zweig oder else-Zweig aus mehr als 1 Anweisung bestehen,
müssen sie durch { ... } geklammert werden.
• erhöhen die Lesbarkeit (machen Programmstruktur besser sichtbar)
• Einrückungstiefe: 1 Tabulator oder 2 Leerzeichen
if (n != 0)
x = x / n;
Statement = Assignment | IfStatement | Block | ... .
Block = "{" {Statement} "}".
if (x > y)
max = x;
else
max = y;
Beispiel
if (x < 0) {
negNumbers++; Out.print(-x);
} else {
posNumbers++; Out.print(x);
}
if (x < 0) {
negNumbers++;
Out.print(-x);
} else {
posNumbers++;
Out.print(x);
}
Kurze If-Anweisungen können auch in einer Zeile geschrieben werden
if (n != 0) x = x / n;
if (x > y) max = x; else max = y;
3
Dangling Else
if (a > b)
if (a != 0) max = a;
else
max = b;
4
Vergleichsoperatoren
Vergleich zweier Werte liefert wahr (true) oder falsch (false)
if (a > b)
if (a != 0) max = a; else max = b;
==
!=
>
<
>=
<=
Mehrdeutigkeit! Zu welchem if gehört das else?
Regel: else gehört immer zum unmittelbar vorausgegangenen if.
Wenn man das nicht will, muss man die Anweisung so schreiben:
Bedeutung
Beispiel
gleich
ungleich
größer
kleiner
größer oder gleich
kleiner oder gleich
x == 3
x != y
4>3
x+1 < 0
x <= y
x >= y
Wird z.B. in If-Anweisung verwendet
if (a > b) {
if (a != 0) max = a;
} else
max = b;
if (x == 0) Out.println("x is zero");
Achtung: "=" ist in Java kein Vergleich, sondern eine Zuweisung
if (x = 0) Out.println("x is zero"); // Compiler meldet einen Fehler!
5
6
Zusammengesetzte Vergleiche
&& Und-Verknüpfung
|| Oder-Verknüpfung
Kurzschlussauswertung
Zusammengesetzter Vergleich wird abgebrochen, sobald Ergebnis feststeht
! Nicht-Verknüpfung
x
y
x && y
x
y
x || y
x
!x
true
true
false
false
true
false
true
false
true
false
false
false
true
true
false
false
true
false
true
false
true
true
true
false
true
false
false
true
äquivalent zu
if ( a != 0 && b / a > 0 ) x = 0;
if (a != 0)
if (b / a > 0) x = 0;
wenn false, ist gesamter Ausdruck false
Beispiel
if ( a == 0 || b / a > 0 ) x = 1;
if (x >= 0 && x <= 10 || x >= 100 && x <= 110) y = x;
wenn true, ist gesamter Ausdruck true
if (a == 0)
x = 1;
else if (b / a > 0)
x = 1;
Vorrangregeln
! bindet stärker als &&
&& bindet stärker als ||
Vorrangregeln können durch Klammerung umgangen werden:
if (x > 0 && (y == 1 || y == 7)) ...
7
Datentyp boolean
Assertionen bei If-Anweisungen
nach George Boole: Mathematiker, 1815-1864
Datentyp wie int mit den beiden Werten true und false
Beispiele
boolean p, q;
p = false;
q = x > 0;
p = p || q && x < 10;
if (condition)
// condition
...
else
// ! condition
...
diese Assertion sollte man immer hinschreiben
oder zumindest im Kopf bilden
Beispiel: Maximum dreier Zahlen berechnen
int a, b, c, max;
a = In.readInt(); b = In.readInt(); c = In.readInt();
if (a > b) /* a>b */
if (a > c) /* a>b && a>c */ max = a;
else /* a>b && c>=a */ max = c;
else /* b>=a */
if (b > c) /* b>=a && b>c */ max = b;
else /* b>=a && c>=b */ max = c;
Out.println(max);
Beachte
•
•
•
•
8
Jeder Vergleich liefert einen Wert vom Typ boolean
Boolesche Werte können mit &&, || und ! verknüpft werden
Boolesche Werte können in boolean-Variablen abgespeichert werden ("flags")
Namen für boolean-Variablen sollten mit Adjektiv beginnen: equal, full
9
10
Programmvergleich
Negation zusammengesetzter Ausdrücke
Welches der beiden Programme ist besser?
if (a > b)
if (a > c)
max = a;
else
max = c;
else
if (b > c)
max = b;
else
max = c;
Regeln von DeMorgan
max = a;
if (b > max) max = b;
if (c > max) max = c;
! (a && b)
! (a || b)
Augustus De Morgan,
britischer Mathematiker, 1806-1871
! a || ! b
! a && ! b
Diese Regeln helfen beim Bilden von Assertionen
if (x >= 0 && x < 10) {
...
} else { // x < 0 || x >= 10
...
}
Was heißt "besser"?
• Kürze: das 2. Programm ist kürzer
• Effizienz:
- 1. Programm braucht immer 2 Vergleiche und 1 Zuweisung
- 2. Programm braucht immer 2 Vergleiche und im Schnitt 2 Zuweisungen
• Lesbarkeit?
• Erweiterbarkeit?
11
Switch-Anweisung
Semantik der Switch-Anweisung
Switch-Ausdruck
Mehrwegverzweigung
month
1,3,5,7,8,10,12
days
31
12
4,6,9,11
days
30
2
days
switch (month) {
case 1: case 3: case 5: case 7: case 8: case 10: case 12:
days = 31; break;
case 4: case 6: case 9: case 11:
days= 30; break;
case 2:
days = 28; break;
default:
Out.println("error");
}
sonst
28
error
In Java
switch (month) {
case 1: case 3: case 5: case 7: case 8: case 10: case 12:
days = 31; break;
case 4: case 6: case 9: case 11:
days= 30; break;
case 2:
days = 28; break;
default:
Out.println("error");
}
13
Break-Anweisung
• springt ans Ende der
Switch-Anweisung
• wenn break fehlt, läuft
Programm über nächste
case-Marke weiter
(häufige Fehlerursache!!)
Semantik
Bedingungen
1. berechne Switch-Ausdruck
2. springe zur passenden case-Marke
- wenn keine passt, springe zu default
- wenn kein default angegeben, springe
ans Ende der Switch-Anweisung
1. switch-Ausdruck muss ganzzahlig oder char sein
2. case-Marken müssen Konstanten sein
3. ihr Typ muss zum Typ des switch-Ausdrucks passen
4. case-Marken müssen voneinander verschieden sein
14
Syntax der Switch-Anweisung
Statement
Unterschied zwischen If und Switch
if (month==1 || month==3 || month==5
|| month==7 || month==8 || month==10
|| month==12)
days = 31;
else if (month==4 || month==6
|| month==9 || month==11)
days = 30;
else if (month==2)
days = 28;
else Out.println("error");
= Assignment | IfStatement | SwitchStatement | ... | Block.
SwitchStatement = "switch" "(" Expression ")" "{" {LabelSeq StatementSeq} "}".
LabelSeq
= Label {Label}.
StatementSeq
= Statement {Statement}.
Label
= "case" ConstantExpression ":" | "default" ":".
prüft Bedingungen sequentiell
benutzt Sprungtabelle
1
days = 31;
month
15
switch (month) {
case 1: case 3: case 5: case 7:
case 8: case 10: case 12:
days = 31; break;
case 4: case 6: case 9: case 11:
days= 30; break;
case 2:
days = 28; break;
default:
Out.println("error");
}
2
3
4
5
6
7
8
9
10
11
12
default
days = 28;
days = 30;
error
16
While-Schleife
Führt eine Anweisungsfolge aus, solange eine bestimmte Bedingung gilt
Grundlagen der Programmierung
i 1
sum 0
Prof. H. Mössenböck
i
4. Schleifen
sum
i
n
sum + i
i+1
i = 1;
sum = 0;
while ( i <= n ) {
sum = sum + i;
i = i + 1;
}
Schleifenbedingung
Schleifenrumpf
Syntax
Statement = Assignment | IfStatement | SwitchStatement | WhileStatement | ... | Block.
WhileStatement = "while" "(" Expression ")" Statement .
Wenn Schleifenrumpf aus mehreren Anweisungen besteht, muss er mit {...} geklammert werden.
2
Beispiel
Assertionen bei Schleifen
Aufgabe: Zahlenfolge lesen und Histogramm ausgeben
Triviale Assertionen
Eingabe: 3 2 5
Aussagen, die sich aus der Schleifenbedingung ergeben
Ausgabe: ***
**
*****
i = 1; sum = 0;
while (i <= n) { /* i <= n */
sum = sum + i;
i = i + 1;
sollte man immer hinschreiben oder zumindest
}
im Kopf bilden
/* i > n */
class Histogram {
public static void main (String[] arg) {
int i = In.readInt();
while (In.done()) {
... j = 1;
int
while (j <= i ) {Out.print("*"); j++;}
Out.println();
i = In.readInt();
}
}
liest die Zahlenfolge
Schleifeninvariante
gibt i Sterne aus
Aussage über das berechnete Ergebnis, die in jedem Schleifendurchlauf gleich bleibt
i = 1; sum = 0;
while (i <= n) { /* i <= n */
/* sum == Summe(1..i-1) */
sum = sum + i;
i = i + 1;
}
/* i > n */
}
3
4
Verifikation der Schleife
Do-While-Schleife
"Durchdrücken" der Invariante durch die Anweisungen des Schleifenrumpfs
Abbruchbedingung wird am Ende der Schleife geprüft
i = 1; sum = 0;
while (i <= n) {
/* sum == Summe(1..i-1) */
sum = sum + i;
/* sum == Summe(1..i) */
i = i + 1;
}
Beispiel: Ausgabe der Ziffern einer Zahl in umgekehrter Reihenfolge
n
sum' == sum + i
sum == sum' - i == Summe(1..i-1)
sum' == Summe(1..i)
Out.print(n % 10)
n n / 10
j n>0?
n
i' == i + 1
i == i' - 1
sum == Summe(1..i'-1)
int n = In.readInt();
do {
Out.print(n % 10);
n = n / 10;
} while ( n > 0 );
Schreibtischtest
n
n % 10
123
12
1
0
3
2
1
Syntax
/* sum == Summe(1..i-1) */
/* sum == Summe(1..i-1) && i == n+1
In.readInt()
Statement = Assignment | IfStatement | WhileStatement | DoWhileStatement | ... | Block.
sum == Summe(1..n) */
DoWhileStatement = "do" Statement "while" "(" Expression ")" ";".
Termination der Schleife muss auch noch bewiesen werden:
i wird in jedem Durchlauf erhöht und ist mit n beschränkt
Wenn Schleifenrumpf aus mehreren Anweisungen besteht, muss er mit {...} geklammert werden.
5
6
Do-While-Schleife
For-Schleife
Warum kann man dieses Beispiel nicht mit einer While-Schleife lösen?
Falls die Anzahl der Schleifendurchläufe im voraus bekannt ist
int n = In.readInt();
while (n > 0) {
Out.print(n % 10);
n = n / 10;
}
"Abweisschleife"
sum = 0;
for ( i = 1 ; i <= n ; i++ )
sum = sum + i;
Kurzform für
Weil das für n == 0 die falsche Ausgabe liefern würde.
Die Schleife muss mindestens einmal durchlaufen werden, daher :
int n = In.readInt();
do {
Out.print(n % 10);
n = n / 10;
} while (n > 0);
1) Initialisierung der Laufvariablen
2) Schleifenbedingung
3) Ändern der Laufvariablen
sum = 0;
i = 1;
while ( i <= n ) {
sum = sum + i;
i++;
}
"Durchlaufschleife"
7
Syntax der For-Schleife
ForStatement =
ForInit
=
|
ForUpdate =
8
Beispiel: Multiplikationstabelle drucken
class PrintMulTab {
"for" "(" [ForInit] ";" [Expression] ";" [ForUpdate] ")" Statement.
Assignment {"," Assignment}
Type VarDecl {"," VarDecl}.
Assignment {"," Assignment}.
public static void main (String[] arg) {
int n = In.readInt();
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++)
Out.print(i * j + " ");
Out.println();
}
}
Beispiele
for (i = 0; i < n; i++) ...
Schreibtischtest für n == 3
i
j
1
1
2
3
4
1
2
3
4
1
2
3
4
2
}
for (i = 10; i > 0; i--) ...
3
for (int i = 0; i <= n; i = i + 1) ...
1
2
3
for (int i = 0, j = 0; i < n && j < m; i = i + 1, j = j + 2) ...
2
4
6
for (;;) ...
3
6
9
9
1 2 3
2 4 6
3 6 9
4
10
Abbruch von Schleifen
Abbruch äußerer Schleifen
Beispiel: Summieren mit Fehlerabbruch
Beispiel
int sum = 0;
break verlässt die Schleife, in der
int x = In.readInt();
es enthalten ist (while, do, for)
while (In.done()) {
sum = sum + x;
if (sum > 1000) {Out.println("zu gross"); break;}
x = In.readInt();
}
outer:
for (;;) {
for (;;) {
...
if (...) break;
else break outer;
...
}
}
Schleifenabbruch mit break möglichst vermeiden: schwer zu verifizieren.
Meist lässt sich dasselbe mit while ebenfalls ausdrücken:
// Marke!
// Endlosschleife!
// verlässt innere Schleife
// verlässt äußere Schleife
Wann ist ein Schleifenabbruch mit break vertretbar?
int sum = 0;
int x = In.readInt();
while (In.done() && sum <= 1000) {
sum = sum + x;
if (sum <= 1000) x = In.readInt();
}
// ! In.done() || sum > 1000
if (sum > 1000) Out.println("zu gross");
• bei Abbruch wegen Fehlern
• bei mehreren Aussprüngen an verschiedenen Stellen der Schleife
• bei echten Endlosschleifen (z.B. in Echtzeitsystemen)
11
Vergleich der Schleifenarten
12
Programm-Muster
Wie denken Programmierexperten?
E?
S
Abweisschleife
S
E?
Durchlaufschleife
•
•
S1
E?
Muster = Schema zur Lösung häufiger Aufgaben
S2
allgemeine Schleife
Experten
•
while (E)
S
for (I; E; U)
S
do
S
while (E)
nicht in einzelnen Anweisungen
sondern in größeren Programm-Mustern
for (;;) {
S1;
if (E) break;
S2;
}
•
benutzen Muster intuitiv
- z.B. Stellungen im Schachspiel
- z.B. Redewendungen in Geschäftsbriefen
lösen Aufgaben in Analogie zu bekannten Aufgaben
Frage: Was sind typische Muster in der Programmierung?
13
14
Zusammensetzen der Muster
Zusammensetzen von Programmen aus Mustern
Beispiel: Berechnung des Mittelwerts einer Zahlenfolge
int x = In.readInt();
int sum = 0, n = 0;
while (In.done()) {
sum = sum + x;
n++;
x = In.readInt();
}
if (n != 0) {
float avg = (float)sum / n;
Out.println("avg = " + avg);
} else
Out.println("error");
Einlese-Muster
Zähl-Muster
int x = In.readInt();
while (In.done()) {
...
x = In.readInt();
}
int n = 0;
while (...) {
n++;
...
}
Summierungs-Muster
Prüf-Muster
int sum = 0;
while (...) {
sum = sum + x;
...
}
if (n != 0)
...
else
Out.println("error");
Mittelwert-Berechnung
Ausgabe
float avg = (float)sum / n;
Out.println("avg = " + avg);
Einlesen
Summieren
Zählen
Prüfen
Mittelwert
Ausgabe
15
16
Weiteres Beispiel
Schrittweise Eingabe von Ablaufstrukturen
Und-Verknüpfung von true- und false-Termen
Bei Schleifen
true true true
true false true
true
false
while (In.done()) {
}
Einlese-Muster
Und-Muster
boolean b = In.readBoolean();
while (In.done()) {
...
b = In.readBoolean();
}
boolean ok = true;
while (...) {
ok = ok && b;
...
}
boolean b = In.readBoolean();
boolean ok = true;
while (In.done()) {
ok = ok && b;
b = In.readBoolean();
}
Out.println(ok);
Ausgabe-Muster
Out.println(ok);
17
while (In.done()) {
x = In.readInt();
}
while (In.done()) {
...
x = In.readInt();
}
Bei Abfragen
if (n != 0) {
} else {
}
if (n != 0) {
avg = (float)sum / n;
Out.println("avg = " + avg);
} else {
}
if (n != 0) {
avg = (float)sum / n;
Out.println("avg = " + avg);
} else {
Out.println("error");
}
18
Die Typen float und double
Variablen
float x, y;
double z;
Grundlagen der Programmierung
Prof. H. Mössenböck
// 32 Bit groß
// 64 Bit groß
Konstanten
3.14
3.14f
3.14E0
0.314E1
31.4E-1
.23
1.E2
5. Gleitkommazahlen
// Typ double
// Typ float
// 3.14 * 100
// 0.314 * 101
// 31.4 * 10-1
// 100
Syntax der Gleitkommakonstanten
FloatConstant
Digits
Exponent
FloatSuffix
= [Digits] "." [Digits] [Exponent] [FloatSuffix].
= Digit {Digit}.
= ("e" | "E") ["+" | "-"] Digits.
= "f" | "F" | "d" | "D".
2
Zuweisungen und Operationen
Beispiel: Berechnung der harmonischen Reihe
sum = 1/1 + 1/2 + 1/3 + ... + 1/n
Zuweisungskompatibilität
double
class HarmonicSequence {
long
int
short
byte
float f; int i;
f = i;
// erlaubt
i = f;
// verboten
i = (int)f; // erlaubt: schneidet Nachkommastellen ab; falls zu groß: maxint, minint
f = 1.0;
// verboten, weil 1.0 vom Typ double ist
public static void main (String[] arg) {
float sum = 0;
int n = In.readInt();
for (int i = n; i > 0; i--)
sum += 1.0f / i
Out.println("sum = " + sum);
}
Erlaubte Operationen
}
• Arithmetische Operationen (+, -, *, /)
• Vergleiche (==, !=, <, <=, >, >=)
Achtung: Gleitkommazahlen sollte man nicht auf Gleichheit prüfen
Was würden statt 1.0f / i folgende Ausdrücke liefern?
1/i
1.0 / i
float
0 (weil ganzzahlige Division)
einen double-Wert
3
4
Typen von Gleitkommaausdrücken
Der "kleinere" Operandentyp wird in den "größeren" konvertiert,
zumindest aber in int.
double
float
long
int
short
Grundlagen der Programmierung
Prof. H. Mössenböck
byte
double d; float f; int i; short s;
...
d+i
// double
f+i
// float
s+s
// int
6. Methoden
Ein- / Ausgabe von Gleitkommazahlen
double d = In.readDouble();
float f = In.readFloat();
Out.println("d = " + d + ", f = " + f);
5
Parameterlose Methoden
Wie funktioniert ein Methodenaufruf?
Beispiel: Ausgabe einer Überschrift
class Sample {
static void printHeader() { // Methodenkopf
Out.println("Artikelliste"); // Methodenrumpf
Out.println("------------");
}
public static void main (String[] arg) {
printHeader();
// Aufruf
...
printHeader();
...
}
Zweck von Methoden
• Wiederverwendung häufig
benutzten Codes
static void P() {
...
Q();
...
}
static void Q() {
...
R();
...
}
static void R() {
...
...
...
}
• Definition benutzerspezifischer Operationen
• Strukturierung des Programms
Namenskonventionen für Methoden
Namen sollten mit Verb und Kleinbuchstaben beginnen
Beispiele: printHeader, findMaximum, traverseList, ...
}
2
3
Parameter
Funktionen
Werte, die vom Rufer an die Methode übergeben werden
Methoden, die einen Ergebniswert an den Rufer zurückliefern
class Sample {
static void printMax (int x, int y) {
if (x > y) Out.print(x); else Out.print(y);
}
public static void main (String[] arg) {
...
printMax(100, 2 * i);
}
class Sample {
formale Parameter
- im Methodenkopf (hier x, y)
- sind Variablen der Methode
static int max (int x, int y) {
if (x > y) return x; else return y;
}
public static void main (String[] arg) {
...
int result = 3 * max(100, i + j) + 1;
...
}
aktuelle Parameter
- an der Aufrufstelle (hier 100, 2*i)
- können Ausdrücke sein
}
• haben Funktionstyp (z.B. int)
statt void (= kein Typ)
• liefern Ergebnis mittels returnAnweisung an den Rufer zurück
(x muss zuweisungskompatibel mit int sein)
• Werden wie Operanden in einem
Ausdruck benutzt
}
Parameterübergabe
Aktuelle Parameter werden den entsprechenen formalen Parametern zugewiesen
x = 100; y = 2 * i;
aktuelle Parameter müssen mit formalen zuweisungskompatibel sein
formale Parameter enthalten Kopien der aktuellen Parameter
Funktionen
Prozeduren
Methoden mit Rückgabewert
Methoden ohne Rückgabewert
4
static int max (int x, int y) {...}
static void printMax (int x, int y) {...}
5
Weiteres Beispiel
Return in Prozeduren
Ganzzahliger Zweierlogarithmus
class ReturnDemo {
static void printLog2 (int n) {
if (n <= 0) return;
// kehrt zum Rufer zurück
int res = 0;
while (n > 1) {n = n / 2; res++;}
Out.println(res);
}
class Sample {
static int log2 (int n) { // assert: n > 0
int res = 0;
while (n > 1) {n = n / 2; res++;}
return res;
}
public static void main (String[] arg) {
int x = In.readInt();
if (!In.done()) return; // beendet das Programm
printLog2(x);
...
}
public static void main (String[] arg) {
int x = log2(17); // x == 4
...
}
}
}
Funktionen
Prozeduren
6
müssen mit return beendet werden
können mit return beendet werden
7
Lokale und statische Variablen
class C {
static int a, b;
static void P() {
int x, y;
...
}
...
Beispiel: Summe einer Zahlenfolge
Statische Variablen
auf Klassenebene mit static deklariert;
auch in Methoden dieser Klasse sichtbar
falsch!
richtig!
class Wrong {
class Correct {
static void add (int x) {
int sum = 0;
sum = sum + x;
}
Lokale Variablen
in einer Methode deklariert
(lokal zu dieser Methode; nur dort sichtbar)
static void add (int x) {
sum = sum + x;
}
public static void main(String[] arg) {
add(1); add(2); add(3);
Out.println("sum = " + sum);
}
}
Reservieren und Freigeben von Speicherplatz
Statische Variablen
am Programmbeginn angelegt
am Programmende wieder freigegeben
static int sum = 0;
}
Lokale Variablen
bei jedem Aufruf der Methode neu angelegt
am Ende der Methode wieder freigegeben
public static void main(String[] arg) {
add(1); add(2); add(3);
Out.println("sum = " + sum);
}
}
• sum ist in main nicht sichtbar
• sum wird bei jedem Aufruf von add
neu angelegt (alter Wert geht verloren)
8
Sichtbarkeitsbereich von Namen
9
Beispiel zu Sichtbarkeitsregeln
x
Programmstück, in dem auf diesen Namen zugegriffen werden kann
class Sample {
(auch Gültigkeitsbereich oder Scope des Namens genannt)
class Sample {
Regeln
x y
static void P() {
...
}
1. Ein Name darf in einem Block nicht
mehrmals deklariert werden (auch nicht
in geschachtelten Anweisungsblöcken).
static int x;
static int y;
2. Lokale Namen verdecken Namen,
die auf Klassenebene deklariert sind.
static void Q(int z) {
int x;
...
}
}
static void P() {
Out.println(x);
}
z
x
3. Der Sichtbarkeitsbereich eines lokalen
Namens beginnt bei seiner Deklaration
und geht bis zum Ende der Methode.
4. Auf Klassenebene deklarierte Namen
sind in allen Methoden der Klasse
sichtbar.
10
// gibt 0 aus
static int x = 0;
x
y
y'
}
public static void main(String[] arg) {
Out.println(x);
// gibt 0 aus
int x = 1;
// verdeckt statisches x
Out.println(x);
// gibt 1 aus
P();
if (x > 0) {
int x;
// Fehler: x ist in main bereits deklariert
int y;
...
} else {
int y;
// ok, kein Konflikt mit y im then-Zweig
...
}
for (int i = 0; ...) {...}
for (int i = 1; ...) {...} // ok, kein Konflikt mit i aus letzter Schleife
}
11
Lebensdauer von Variablen
Lokalität
class LifenessDemo {
Variablen möglichst lokal deklarieren, nicht als statische Variablen.
static int g;
static void A() {
int a;
...
}
Vorteile
• Übersichtlichkeit
Deklaration und Benutzung nahe beisammen
static void B() {
int b;
... A(); ... A(); ...
}
}
• Sicherheit
Lokale Variablen können nicht durch andere Methoden zerstört werden
public static void main(String[] arg) {
int m;
... B(); ...
}
lokale Variablen
(Methodenkeller)
statische Var.
g
• Effizienz
Zugriff auf lokale Variable ist oft schneller als auf statische Variable
m
b
m
a
b
m
b
m
a'
b
m
b
m
m
g
g
g
g
g
g
g
12
13
Überladen von Methoden
Beispiele
Methoden mit gleichem Namen aber verschiedenen Parameterlisten
können in derselben Klasse deklariert werden
Größter gemeinsamer Teiler nach Euklid
static int ggt (int x, int y) {
int rest = x % y;
while (rest != 0) {
x = y; y = rest; rest = x % y;
}
return y;
}
static void write (int i) {...}
static void write (float f) {...}
static void write (int i, int width) {...}
Beim Aufruf wird diejenige Methode gewählt, die am besten zu den
aktuellen Parametern passt
write(100);
write(3.14f);
write(100, 5);
short s = 17;
write(s);
Kürzen eines Bruchs
write (int i)
write (float f)
write (int i, int width)
static void reduce (int z, int n) {
int x = ggt(z, n);
Out.print(z/x); Out.print("/"); Out.print(n/x);
}
write (int i);
14
15
Beispiele
Beispiele
Prüfe, ob x eine Primzahl ist
Berechne xn
static boolean isPrime (int x) {
if (x ==1 || x == 2) return true;
if (x % 2 == 0) return false;
int i = 3;
while (i * i <= x) {
if (x % i == 0) return false;
i = i + 2;
}
// i > x und x ist durch keine Zahl
// zwischen 1 und i teilbar
return true;
}
static long power (int x, int n) {
long res = 1;
for (int i = 1; i <= n; i++) res = res * x;
return res;
}
for (int i = 3; i * i <= x; i += 2)
if (x % i == 0) return false;
Dasselbe effizienter
static long power (int x, int n) {
long res = 1;
while (n > 0) {
if (n % 2 == 0) {
x = x * x; n = n / 2; // x2n = (x*x)n
} else {
res = res * x; n--;
// xn+1 = x*xn
}
}
return res;
}
x
n
res
2
5
4
2
1
1
2
0
32
4
16
16
17
Eindimensionale Arrays
Array = Tabelle gleichartiger Elemente
Grundlagen der Programmierung
Prof. H. Mössenböck
7. Arrays
a[0] a[1] a[2] a[3]
...
a
•
•
•
•
Name a bezeichnet das gesamte Array
Elemente werden über Indizes angesprochen (z.B. a[3])
Indizierung beginnt bei 0
Elemente sind "namenlose" Variablen
Deklaration
int[] a;
float[] b;
• deklariert ein Array namens a (bzw. b)
• seine Elemente sind vom Typ int (bzw. float)
• seine Länge ist noch unbekannt
Erzeugung
a = new int[5];
b = new float[10];
a
a[0] a[1] a[2] a[3] a[4]
• legt ein neues int-Array mit 5 Elementen an
(aus dem Heap-Speicher)
• weist seine Adresse a zu
Array-Variablen enthalten in Java Zeiger auf Arrays!
(Zeiger = Speicheradresse)
2
Arbeiten mit Arrays
Arrayzuweisung
Zugriff auf Arrayelemente
•
•
•
•
a[3] = 0;
a[2*i+1] = a[i] * 3;
Arrayelemente werden wie Variablen benutzt
Index kann ein ganzzahliger Ausdruck sein
Laufzeitfehler, falls Array noch nicht erzeugt wurde
Laufzeitfehler, falls Index < 0 oder Arraylänge
int[] a, b;
a = new int[3];
a
a
b = a;
b
int len = a.length;
Beispiele
// Array einlesen
int sum = 0;
for (int i = 0; i < a.length; i++)
sum += a[i];
// Elemente aufaddieren
a[0] a[1] a[2]
b bekommt denselben Wert wie a.
Arrayzuweisung ist in Java Zeigerzuweisung!
0
0
0
b[0] b[1] b[2]
a[0] a[1] a[2]
a[0] = 17;
a
b
a = new int[4];
for (int i = 0; i < a.length; i++)
a[i] = In.readInt();
Arrayelemente werden in Java
standardmäßig mit 0 initialisiert
b
Arraylänge abfragen
• length ist ein Standardoperator, der auf alle Arrays
angewendet werden kann.
• Liefert Anzahl der Elemente (hier 5).
a[0] a[1] a[2]
0
0
0
b = null;
ändert in diesem Fall auch b[0]
17
0
0
b[0] b[1] b[2]
a
0
0
0
b
17
0
0
a
0
0
0
b
17
0
0
0
0
a zeigt jetzt auf neues Array.
null: Spezialwert, der auf kein Objekt zeigt;
kann jeder Arrayvariablen zugewiesen werden
3
Freigeben von Arrayspeicher
4
Initialisieren von Arrays
Garbage Collection (Automatische Speicherbereinigung)
int[] primes = {2, 3, 5, 7, 11};
Objekte, auf die kein Zeiger mehr verweist, werden automatisch eingesammelt.
Ihr Speicher steht für neue Objekte zur Verfügung
static void P() {
int[] a = new int[3];
int[] b = new int[4];
int[] c = new int[2];
...
...
b = a;
...
...
c = null;
...
...
...
}
primes
2
3
5
7
11
identisch zu
int[] primes = new int[5];
primes[0] = 2;
primes[1] = 3;
primes[2] = 5;
primes[3] = 7;
primes[4] = 11;
a
b
c
a
b
c
kein Zeiger mehr auf dieses Objekt
wird eingesammelt
Initialisierung kann auch bei der Erzeugung erfolgen
a
b
c
kein Zeiger mehr auf dieses Objekt
wird eingesammelt
a
b
c
Am Methodenende werden lokale Variablen
freigegeben Zeiger a, b, c fallen weg
Objekt wird eingesammelt
int[] primes;
...
primes = new int[] {2, 3, 5, 7, 11};
5
6
Kopieren von Arrays
int[] a = {1, 2, 3, 4, 5};
int[] b;
Kommandozeilenparameter
Programmaufruf mit Parametern
a
1
2
3
4
5
java Programmname par1 par2 ... parn
b
Parameter werden als String-Array an main-Methode übergeben
b = (int[]) a.clone();
a
1
2
3
4
5
b
1
2
3
4
5
class Sample {
public static void main (String[] arg) {
for (int i = 0; i < arg.length; i++)
Out.println(arg[i]);
...
}
Typumwandlung nötig, da clone etwas vom Typ Object[] liefert
}
Aufruf z.B.
java Sample Anton /a 10
Ausgabe:
Anton
/a
10
7
8
Beispiel: sequentielles Suchen
Beispiel: binäres Suchen
Suchen eines Werts x in einem Array
• schneller als sequentielles Suchen
• Array muss allerdings sortiert sein
falls gefunden
0
17
x
pos = 17
falls nicht gefunden
0
99
99
z.B. Suche von 13
pos = -1
static int search (int[] a, int x) {
int pos = a.length - 1;
while ( pos >= 0 && a[pos] != x ) pos--;
// pos == -1 || a[pos] == x
return pos;
}
a
0
2
1
3
2
5
low
3 4 5 6 7
7 11 13 17 19
m
high
• Index des mittleren Element bestimmen ( m = (low + high) / 2 )
• 13 > a[m] zwischen a[m+1] und a[high] weitersuchen
gewünschtes Ergebnis
a
0
1
2
2
3
5
4 5 6 7
7 11 13 17 19
3
low m
Achtung: int[] a wird nur als Zeiger übergeben.
Würde search etwas in a ändern (z.B. a[3] = 0;), würde sich diese Änderung auch auf das
Array im Rufer auswirken.
9
high
10
Binäres Suchen
Primzahlenberechnung: Sieb des Erathostenes
static int binarySearch (int[] a, int x) {
int low = 0;
int high = a.length - 1;
while (low <= high) {
int m = (low + high) / 2;
if (a[m] == x) return m;
else if (x > a[m]) low = m + 1;
else /* x < a[m] */ high = m - 1;
}
/* low > high*/
return -1;
}
1. "Sieb" wird mit den natürlichen Zahlen ab 2 gefüllt
2 3 3 5 7 7 8 10 11 15 16 17
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, ...
2 3 3 5 7 7 8 10 11 15 16 17
2. Erste Zahl im Sieb ist Primzahl. Entferne sie und alle ihre Vielfachen
2 3 3 5 7 7 8 10 11 15 16 17
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, ...
3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, ...
2 3 3 5 7 7 8 10 11 15 16 17
3. Wiederhole Schritt 2
3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, ...
5, 7, 11, 13, 17, 19, 23, 25, ...
• Suchraum wird in jedem Schritt halbiert
• bei n Arrayelementen sind höchstens log2(n) Schritte nötig, um jedes Element zu finden
n
seq.Suchen
10
100
1000
10000
... Wiederhole Schritt 2
bin.Suchen
5, 7, 11, 13, 17, 19, 23, 25, ...
4
7
10
14
10
100
1000
10000
7, 11, 13, 17, 19, 23, ...
11
Implementierung
Beispiel: Monatstage berechnen
Sieb = boolean-Array, Zahl i im Sieb
3
4
5
6
7
8
9
false false true
true
true
true
true
true
true
true
8
9
1
Bisher mit Switch-Anweisung gelöst
sieve[i] == true
2
0
switch (month) {
case 1: case 3: case 5: case 7: case 8: case 10: case 12:
days = 31; break;
case 4: case 6: case 9: case 11:
days = 30; break;
case 2:
days = 28;
}
...
Zahl i entfernen: sieve[i] = false
0
1
2
3
false false false true
4
12
5
false true
6
7
false true
false true
...
static void printPrimes (int max) {
boolean[] sieve = new boolean[max + 1];
for (int i = 2; i <= max; i++) sieve[i] = true;
for (int i = 2; i <= max; ) {
Out.print(i + " "); // i is prime
for (int j = i; j <= max; j = j + i) sieve[j] = false;
while (i <= max && !sieve[i]) i++;
}
}
Besser mit Tabelle
int[] days = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
...
int d = days[month];
13
14
Mehrdimensionale Arrays
Mehrdimensionale Arrays
Zweidimensionales Array
Zeilen können unterschiedlich lang sein (das ist aber selten sinnvoll)
0
1
2
3
0
1
2
a[0][0]
a[0][1]
a[0][2]
a[1][0]
a[1][1]
a[1][2]
a[2][0]
a[2][1]
a[2][2]
a[3][0]
a[3][1]
a[3][2]
Matrix
a
a[0][0]
a[0][1]
a[1][0]
a[1][1]
a[2][0]
a[2][1]
a[0][2]
a[0][3]
a[0]
a[1]
a[2][2]
int[][] a = new int[3][];
a[0] = new int[4];
a[1] = new int[2];
a[2] = new int[3];
a[2]
In Java als Array von Arrays implementiert
a
a[0][0]
a[0][1]
a[0][2]
a[1][0]
a[1][1]
a[1][2]
Initialisierung
Deklaration und Erzeugung
a[0]
a[1]
a[2][0]
a[2][1]
a[2][2]
a[3][0]
a[3][1]
a[3][2]
a
Zugriff
a[2]
a[3]
int[][] a = {{1, 2, 3},{4, 5, 6}};
int[][] a;
a = new int[4][3];
a[i][j] = a[i][j+1];
1
2
3
4
5
6
15
16
Beispiel: Matrixmultiplikation
a
i
b
× k
c
= i
j
Grundlagen der Programmierung
j
Prof. H. Mössenböck
c0,0 = a0,0*b0,0 + a0,1*b1,0 + a0,2*b2,0
8. Zeichen
static float[][] matrixMult (float[][] a, float[][] b) {
float[][] c = new float[a.length][b[0].length];
for (int i = 0; i < a.length; i++)
for (int j = 0; j < b[0].length; j++) {
float sum = 0;
for (int k = 0; k < b.length; k++)
sum += a[i][k] * b[k][j];
c[i][j] = sum;
}
return c;
}
17
Datentyp char
ASCII
char ch = 'x';
Zeichenvariable
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Zeichenkonstante
(unter einfachen Hochkommas)
Zeichen braucht man zur Verarbeitung von Texten.
Zeichencodes
ASCII (American Standard Code for Information Interchange)
• 1 Zeichen = 1 Byte (128 bzw. 256 Zeichen darstellbar)
• z.B. in Pascal oder C verwendet
Unicode (www.unicode.org)
• 1 Zeichen = 2 Bytes (65536 Zeichen darstellbar)
• auch Umlaute, griechische, arabische Zeichen etc.
• z.B. in Java und C# verwendet
• ASCII ist Teilmenge von Unicode
NUL
SOH
STX
ETX
EOT
ENQ
ACK
BEL
BS
HT
LF
VT
FF
CR
SO
SI
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
DLE
DC1
DC2
DC3
DC4
NAK
SYN
ETB
CAN
EM
SUB
ESC
FS
GS
RS
US
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
space
!
"
#
$
%
&
'
(
)
*
+
,
.
/
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
0
1
2
3
4
5
6
7
8
9
:
;
<
=
>
?
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
@
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
P
Q
R
S
T
U
V
W
X
Y
Z
[
\
]
^
_
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
´
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
Wichtige Steuerzeichen
löscht Zeichen vor Cursor
BS backspace
HT horizontal tab Tabulatorsprung
ESC escape
CR
LF
FF
carriage return Zeilenvorschub
folgt auf CR
line feed
Seitenvorschub
form feed
2
Unicode
0000 - 007F
0080 - 024F
0370 - 03FF
0400 - 04FF
0530 - 058F
0590 - 05FF
0600 - 06FF
...
3
Unicode-Zeichenkonstanten
ASCII-Zeichen
Umlaute, Akzente, Sonderzeichen
griechische Zeichen
cyrillische Zeichen
armenische Zeichen
hebräische Zeichen
arabische Zeichen
...
Alle Zeichen können auch mit ihrem Unicode-Wert angegeben werden:
'\udddd'
Beispiele:
'\u0041'
'\u000d'
'\u0009'
'\u03c0'
Details siehe
http://www.unicode.org
Deutsche Umlaute
00E4 ä
00F6 ö
00FC ü
p
q
r
s
t
u
v
w
x
y
z
{
|
}
~
DEL
00C4 Ä
00D6 Ö
00DC Ü
'A'
CR (carriage return)
TAB
Spezielle Zeichen
00DF ß
'\n'
'\r'
'\t'
'\\'
'\''
'\ddd'
4
LF (line feed, newline)
CR (carriage return)
TAB
\
'
Zeichenwert als Oktalzahl
5
Zeichen-Operationen
Beispiel: Textsuche
Zuweisungen
geg.: Text t, Muster pat
ges.: erstes Vorkommen von pat in t
char ch1, ch2 = 'a';
ch1 = ch2;
// ok, gleicher Typ
int i = ch2;
// ok, char kann int zugewiesen werden
ch1 = (char)i;
// Zuweisung nach Typumwandlung möglich
double
float
long
int
short
char
Vergleiche (==, !=, <, <=, >, >=)
t
Einfuehrung in die Programmierung
ung
byte
if ('a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z') ...
Arithmetische Operationen (+, -, *, /, %)
// Ergebnistyp: int
Zeichenarrays
char[] s = new char[20];
char[] t = {'a', 'b', 'c'};
ung
Ergebnis: pos 0: pat in t an Stelle pos
pos < 0: pat kommt nicht in t vor
Zeichen sind nach Unicode-Wert geordnet;
Buchstaben und Ziffern liegen aufeinanderfolgend
10 + (ch - 'A')
pat
ung
// initialisiert alle Elemente mit '\u0000'
6
Beispiel: Nachbauen von readInt
static int search (char[] t, char[] pat) {
int last = t.length - pat.length;
for (int i = 0; i <= last; i++) {
if (t[i] == pat[0]) {
int j = 1;
while (j < pat.length && pat[j] == t[i+j]) j++;
// j == pat.length || pat[j] != t[i+j]
if (j == pat.length) return i;
}
}
return -1;
}
i
t
0 1 2 3 4 5 6 7 8 9 10
pat
0 1 2
j
7
Standardfunktionen mit Zeichen
static int readInt() {
int val = 0;
char ch = In.read(); // liest ein einzelnes Zeichen
while (In.done() && '0' <= ch && ch <= '9') {
val = 10 * val + (ch - '0');
ch = In.read();
}
// ! In.done() || ch < '0' || ch > '9'
return val;
}
if (Character.isLetter(ch)) ...
if (Character.isDigit(ch)) ...
if (Character.isLetterOrDigit(ch)) ...
true, wenn ch ein Unicode-Buchstabe ist
true, wenn ch eine Ziffer ist
if (Character.isLowerCase(ch)) ...
if (Character.isUpperCase(ch)) ...
true, wenn ch ein Kleinbuchstabe ist
true, wenn ch ein Großbuchstabe ist
ch1 = Character.toUpperCase(ch2);
ch1 = Character.toLowerCase(ch2);
wandelt ch2 in einen Großbuchstaben um
wandelt ch2 in einen Kleinbuchstaben um
Schreibtischtest: Eingabe 123+
val
0
1
12
123
ch
'1'
'2'
'3'
'+'
(49)
(50)
(51)
(43)
'0' = 48
"Horner-Schema"
8
9
Datentyp String
Grundlagen der Programmierung
Prof. H. Mössenböck
String a, b;
Bibliothekstyp für Zeichenketten ("Strings")
a = "Hello";
Stringkonstante (unter doppelten Hochkommas)
b = a;
a
b
9. Strings
Hello
Stringvariablen sind Zeiger auf Stringobjekte
Stringzuweisung ist eine Zeigerzuweisung
Stringobjekte sind nicht als Arrays ansprechbar
Stringobjekte sind nicht veränderbar
b = a + " World";
a
Hello
b
Hello World
Verkettung mit "+" erzeugt neues Stringobjekt (relativ teure Operation)
2
Stringvergleiche
Stringoperationen
String s = "a long string";
String s = In.readWord();
// liest ein Wort, z.B. Hello
if (s == "Hello") ...
// liefert false! (Zeigervergleich)
s
Hello
Hello
if (s.equals("Hello")) ...
weil zwei verschiedene Objekte,
trotz gleichem Inhalt
// liefert true! (Wertvergleich)
aber:
String s = "Hello";
if (s == "Hello") ..
// liefert true, weil gleichlautende Stringkonstanten
// nur einmal als Objekt abgespeichert werden.
trotzdem Wertvergleich immer mit equals durchführen
3
0 1
2 3 4
a
l o n g
5 6
7 8
9 10 11 12
S t r i n g
int len = s.length();
liefert Anzahl der Zeichen in s
(im Gegensatz zu arr.length sind Klammern nötig!)
char ch = s.charAt(3);
liefert das Zeichen mit Index 3 (hier 'o')
int i = s.indexOf("ng");
i = s.indexOf("ng", 5);
i = s.indexOf('n');
i = s.lastIndexOf("ng");
liefert Index des 1. Vorkommens von "ng" in s (hier 4) oder -1
liefert Index des 1. Vorkommens von "ng" ab Index 5 (hier 11)
geht auch mit char
liefert Index des letzten Vorkommens von "ng" (Varianten wie oben)
String x;
x = s.substring(2);
x = s.substring(2, 6);
liefert Teilstring ab Index 2 (hier "long string")
liefert Teilstring s[2..6[, d.h. s[2..5] (hier "long")
if (s.startsWith("abc")) ...
if (s.endsWith("abc")) ...
liefert true, falls s mit "abc" beginnt
liefert true, falls s mit "abc" ended
4
Aufbauen von Strings
Aufbauen von Strings aus StringBuilder
aus Stringkonstante
aus StringBuilder (Bibliothekstyp wie String, aber modifizierbar)
String s = "very simple";
StringBuilder b;
b = new StringBuilder();
// erzeugt leeren StringBuilder der Länge 0
aus char-Array
char[] a = {'a', 'b', 'c', 'd', 'e'};
len = b.length();
b.append(x);
String s1 = new String(a);
// s1 enthält Kopie der Zeichen in a
String s2 = new String(a, 2, len); // s2 enthält Kopie von a[2..2+len-1]
b.insert(pos, x);
b.delete(from, to);
b.replace(from, to, "abc");
// hängt x an b an. x kann beliebigen Typ haben:
// short, int, long, float, double, char, char[], String, boolean
// fügt x an der Stelle pos ein (Typ von x beliebig)
// löscht [from..to[ aus b
// ersetzt b[from, to[ durch "abc"
ch = b.charAt(i);
s = b.substring(from, to);
// wie bei String
b.setCharAt(pos, 'x');
// setzt b[pos] auf 'x'
s = b.toString();
// liefert Pufferinhalt als String
5
Stringkonversionen
6
Beispiel: Manipulation von Dateipfaden
int i = new Integer("123").intValue();
float f = new Float("3.14").floatValue();
// String
// String
String s;
s = String.valueOf(123);
s = String.valueOf(3.14);
// int String
// float String
char[] a = s.toCharArray();
String s = new String(a);
// String
// char[]
dir1\dir2\name.java
int
float
name.class
- Verzeichnisse entfernen
- ".java" auf ".class" ändern (bzw. ".class" anhängen)
static String strip (String path) {
StringBuilder b = new StringBuilder(path); // erzeugt StringBuilder mit path als Inhalt
if (path.endsWith(".java")) {
int len = path.length();
b.delete(len-5, len);
}
b.append(".class");
int i = path.lastIndexOf('\\');
if (i >= 0) b.delete(0, i+1);
return b.toString();
}
char[]
String
7
8
Beispiel: Wörter aus einem Text lösen
Beispiel: Zahl in String konvertieren
Eingabe: "Ein Text aus Woertern ..."
Ausgabe:
Ein
Text
aus
Woertern
Idee: Ziffern mit n % 10 abspalten und in char-Array sammeln
static void printWords (String text) {
int i = 0, last = text.length() - 1;
while (i <= last) {
//--- skip nonletters
while (i <= last && !Character.isLetter(text.charAt(i))) i++;
// end of text or text[i] is a letter
//--- read word
int beg = i;
while (i <= last && Character.isLetter(text.charAt(i))) i++;
// end of text or text[i] is not a letter
//--- print word
if (i > beg) Out.println(text.substring(beg, i));
}
}
a
n
0 1
2
a b c
i
3
4
5
static String valueOf (int n) {
char[] a = new char[20];
int i = 0;
do {
a[i] = (char) (n % 10 + '0');
n = n / 10;
i++;
} while (n > 0);
StringBuilder b = new StringBuilder();
do {
i--;
b.append(a[i]);
} while (i > 0);
return b.toString();
}
6
d e f
last
154
15
i
"4"
1
i
"4" "5"
0
i
"4" "5" "1"
i
"4" "5" "1"
i
"4" "5"
i
"4"
i
b
"1"
"15"
"154"
9
10
Motivation
Wie würde man ein Datum speichern (z.B. 13. November 2004)?
Grundlagen der Programmierung
Prof. H. Mössenböck
10. Klassen
3 Variablen
int day;
String month;
int year;
Unbequem, wenn man mehrere Exemplare davon braucht:
int day1;
String month1;
int year1;
int day2;
String month2;
int year2;
...
Idee: die 3 Variablen zu einem eigenen Datentyp zusammenfassen
2
Datentyp Klasse
Objekte
Speicherung verschiedenartiger Werte unter einem gemeinsamen Namen
Objekte einer Klasse müssen vor ihrer ersten Benutzung erzeugt werden
Deklaration
Date x, y;
class Date {
int day;
String month;
int year;
}
reserviert nur Speicher für die Zeigervariablen
x
Felder der Klasse Date
y
Erzeugung
x = new Date();
erzeugt ein Date-Objekt und weist seine Adresse x zu
x
Verwendung als Typ
0
null
0
y
Date x, y;
x
Zugriff
Date-Objekt
neu erzeugte Objekte werden
mit 0, null, false, '\u0000'
initialisiert
Eine Klasse ist wie eine Schablone, von der beliebig viele Objekte
erzeugt werden können.
13
day
"November" month
2004
year
x.day = 13;
x.month = "November";
x.year = 2004;
day
month
year
Benutzung
x.day = 13;
x.month = "November";
x.year = 2004;
Date-Variablen sind Zeiger auf Objekte
x
y
13
"November"
2004
day
month
year
3
Zuweisungen
y = x;
x
y
y.day = 20;
x
y
Vergleiche
13
"November"
2004
day
month
year
Zeigerzuweisung!
20
"November"
2004
day
month
year
ändert auch x.day!
Zeigervergleich
Zuweisungen sind erlaubt, wenn die Typen gleich sind
class Date {
int day;
String month;
int year;
}
d1 = d2;
a1 = a2;
d1 = a2;
4
class Address {
int number;
String street;
int zipCode;
}
x == y
x != y
vergleicht nur Zeiger
x<y
x <= y
x>y
x >= y
nicht erlaubt
Wertvergleich muss mittels Vergleichsmethode selbst implementiert werden
Date d1, d2 = new Date();
Address a1, a2 = new Address();
static boolean equalDate (Date x, y) {
return
x.day == y.day &&
x.month.equals(y.month) &&
x.year == y.year;
}
// ok, gleiche Typen
// ok, gleiche Typen
// verboten: verschiedene Typen trotz gleicher Struktur!
5
6
Wo werden Klassen deklariert
In einer einzigen Datei (auf äußerster Ebene)
Beispiel: Polygone
In getrennten Dateien
MainProgram.java
class Point {
int x, y;
}
C1.java
class C1 {
...
}
class C1 {
...
}
class C2 {
...
}
class C2 {
...
}
}
class MainProgram {
Polygon
p
pt
color
public static void main (String[] arg) {
...
}
20
Point
20
40
Point
p = new Point(); p.x = 60; p.y = 40;
poly.pt[2] = p;
}
p = new Point(); p.x = 70; p.y = 20;
poly.pt[3] = p;
...
Zugriff z.B.
60
poly.pt[0].x = ...;
40
Point
70
20
javac MainProgram.java C1.java C2.java
7
8
Kombination von Klassen mit Arrays
Methoden mit mehreren Rückgabewerten
Beispiel: Telefonbuch
Java-Funktionen haben nur 1 Rückgabewert
Will man mehrere Rückgabewerte, muss man sie zu einer Klasse zusammenfassen
Beispiel: Umrechnung von Sekunden auf Std, Min, Sek
Point
10
p = new Point(); p.x = 20; p.y = 40;
poly.pt[1] = p;
Übersetzung
javac MainProgram.java
poly
(70,20)
Point p = new Point(); p.x = 10; p.y = 20;
poly.pt[0] = p;
MainProgram.java
public static void main (String[] arg) {
...
}
(60,40)
(10,20)
...
Polygon poly = new Polygon();
poly.pt = new Point[4];
poly.color = RED;
C2.java
class MainProgram {
(20,40)
class Polygon {
Point[] pt;
int color;
}
sec
0
h
m
s
convert
class Time {
int h, m, s;
}
Time
name
Maier
Mayr
Meier
phone
876 8878
543 2343
656 2332
zweidimensionales Array
kann hier nicht verwendet werden
99
Objekt bestehend aus 2 Arrays
Array von Objekten
class Person {
String name;
int phone;
}
Person[] book = new Person[100];
class Program {
static Time convert (int sec) {
Time t = new Time();
t.h = sec / 3600; t.m = (sec % 3600) / 60; t.s = sec % 60;
return t;
}
public static void main (String[] arg) {
Time t = convert(10000);
Out.println(t.h + ":" + t.m + ":" + t.s);
}
}
class PhoneBook {
String[] name;
int[] phone;
}
PhoneBook book = new PhoneBook();
book.name = new String[100];
book.phone = new int[100];
book
book
Maier
876 8878
name
phone
Maier
9
Diese Lösung ist aus logischer Sicht besser
876 8878
10
Implementierung
Implementierung (Fortsetzung)
class Person {
String name;
int phone;
}
class PhoneBookSample {
static Person[] book;
static int nEntries = 0; // current number of entries in book
static void enter (String name, int phone) {
if (nEntries >= book.length) Out.println("--- phone book full");
else {
book[nEntries] = new Person();
book[nEntries].name = name;
book[nEntries].phone = phone;
nEntries++;
}
}
static int lookup (String name) {
int i = 0;
while (i < nEntries && !name.equals(book[i].name)) i++;
// i >= nEntries || name.equals(book[i].name)
if (i < nEntries) return book[i].phone; else return -1;
}
...
public static void main (String[] arg) {
book = new Person[1000];
//----- read the phone book from a file
In.open("phonebook.txt");
String name = In.readName();
int phone;
while (In.done()) {
phone = In.readInt();
enter(name, phone);
name = In.readName();
}
In.close();
//----- search in the phone book
for (;;) {
Out.print("Name: "); name = In.readName();
if (!In.done()) break;
phone = lookup(name);
if (phone >= 0) Out.println("phone number = " + phone);
else Out.println(name + " unknown");
}
}
} // end PhoneBookSample
book
0
Maier 876 8878
1
2
3
nEntries = 3
11
12
Klasse = Daten + Methoden
Beispiel: Positionsklasse
Grundlagen der Programmierung
class Position {
private int x;
private int y;
Prof. H. Mössenböck
void goLeft()
void goRight()
void goUp()
void goDown()
11. Objektorientierung
{ x = x - 1; }
{ x = x + 1; }
{ y = y - 1; }
{ y = y + 1; }
}
Methoden sind
• lokal zu Position
• ohne static deklariert (siehe später)
Black-Box
Position pos = new Position();
pos.goRight(); // pos.x == 1, pos.y == 0
pos.goDown(); // pos.x == 1, pos.y == 1
pos.goDown(); // pos.x == 1, pos.y == 2
...
ruft goDown-Methode von pos auf
Jedes Objekt hat seinen eigenen Zustand
Position pos2 = new Position();
pos2.goUp(); // pos2.x == 0, pos2.y == -1
pos2.goLeft(); // pos2.x == -1, pos2.y == -1
...
pos.goRight();
goLeft goRight goUp goDown
x
Benutzung
y
Man sagt:
• pos bekommt die Nachricht (message) goRight
• pos ist der Empfänger der Nachricht goRight
2
Schlüsselwort this
Beispiel: Bruchzahlenklasse
Methoden können Parameter haben
class Fraction {
Position pos = new Position();
pos.goLeft(3); // pos.x == -3, pos.y == 0
...
class Position {
private int x;
private int y;
int z; // Zähler
int n; // Nenner
void mult (Fraction f) {
z = z * f.z;
n = n * f.n;
}
void goLeft(int n) { x = x - n; }
...
}
void add (Fraction f) {
z = z * f.n + f.z * n;
n = n * f.n;
}
Schlüsselwort this
class Position {
private int x;
private int y;
Fraction a = new Fraction(); a.z = 1; a.n = 2;
Fraction b = new Fraction(); b.z = 3; b.n = 5;
a
1
2
z
n
b
3
5
a
a.mult(b);
a
b.mult(a);
z
n
3 z
10 n
1
2
z
n
b
b
3
5
z
n
3
10
z
n
}
void goLeft(int x) { this.x = this.x - x; }
...
}
this bezeichnet das Objekt,
auf das goLeft angewendet wird
(nötig, um Feld x vom
Parameter x zu unterscheiden)
Es wird immer der Zustand des Empfängers verändert!
3
4
Grafische Notation für Klassen
Konstruktoren
UML-Notation (Unified Modeling Language)
Spezielle Methoden, die beim Erzeugen eines Objekts automatisch aufgerufen werden
Fraction
Klassenname
int z
int n
Felder
void mult (Fraction f)
void add (Fraction f)
Methoden
class Fraction {
int z, n;
Fraction (int z, int n) {
this.z = z; this.n = n;
}
dienen zur Initialisierung eines Objekts
heißen wie die Klasse
ohne Funktionstyp und ohne void
können Parameter haben
können überladen werden
Fraction () {
z = 0; n = 1;
}
Vereinfachte Form
void mult (Fraction f) {...}
void add (Fraction f) {...}
}
Fraction
z
n
•
•
•
•
•
falls weniger Details gewünscht oder nötig
Aufruf
Fraction f = new Fraction();
Fraction g = new Fraction(3, 5);
mult(f)
add(f)
5
1. legt neues Fraction-Objekt an
2. ruft für dieses Objekt den Konstruktor auf
6
Bsp: Polygon-Aufbau mit Konstruktoren
class Point {
int x, y;
Point (int x, int y) { this.x = x; this.y = y; }
}
class Polygon {
Point[] pt;
int color;
Polygon (Point[] pt, int color) {
this.pt = pt; this.color = color;
}
}
(20,40)
(10,20)
static
class Window {
int x, y, w, h;
static int border;
(60,40)
(70,20)
// Objektfelder (in jedem Window-Objekt vorhanden)
// Klassenfeld (nur einmal pro Klasse vorhanden)
void redraw () {...}
// Objektmethode (auf Objekte anwendbar)
static void setBorder (int n) {border = n;} // Klassenmethode (auf Klasse Window anwendbar)
poly
class Program {
...
Polygon poly = new Polygon(
new Point[] {
new Point(10, 20),
new Point(20, 40),
new Point(60, 40),
new Point(70, 20)
},
RED
);
...
}
Polygon
Point
pt
color
10
Window(int w, int h) {...}
static { ... }
// Objektkonstruktor (zur Initialisierung von Objekten)
// Klassenkonstruktor (zur Initialisierung der Klasse)
}
20
Klasse Window
20
border
40
setBorder()
Klassenkonstruktor
60
Window-Objekt
x
y
w
h
redraw()
Window()
40
70
Window-Objekt
x
y
w
h
redraw()
Window()
Window-Objekt
x
y
w
h
redraw()
Window()
• Objektmethoden haben Zugriff auf Klassenfelder (redraw kann auf border zugreifen)
• Klassenmethoden haben keinen direkten Zugriff auf Objektfelder
(setBorder kann nicht auf x zugreifen)
20
7
static (Forts.)
static (Forts.)
Was geschieht wann?
class Window {
Beim Laden der Klasse Window
- Klassenfelder werden angelegt (border)
- Klassenkonstruktor wird aufgerufen
8
statische Programmelemente
static int border;
angelegt am Programmanfang (wenn die Klasse geladen wird)
static {...}
aufgerufen am Programmanfang (wenn die Klasse geladen wird)
static void setBorder(int n) {...}
Beim Erzeugen eines Window-Objekts (new Window(...))
- Objektfelder werden angelegt (x, y, w, h)
- Objektkonstruktor wird aufgerufen
nichtstatische Programmelemente
int x, y, w, h;
angelegt, wenn das Window-Objekt erzeugt wird
Window() {...}
aufgerufen, wenn das Window-Objekt erzeugt wird
void redraw() {...}
void foo() {
x = 0; redraw();
border = 1; setBorder(1);
}
Zugriffe
Zugriff auf static-Elemente über den Klassennamen
- Window.border = ...; Window.setBorder(3);
- Methoden der Klasse Window können Klassennamen weglassen (border = ...; setBorder(3);)
Zugriff auf nonstatic-Elemente über einen Objektnamen
- Window win = new Window(100, 50);
win.x = ...; win.redraw();
- Methoden der Klasse Window können auf eigene Elemente direkt zugreifen (x = ...; redraw();)
9
Zugriff auf eigene Elemente ohne Qualifikation
}
...
Window w = new Window();
w.x = 0; w.redraw();
Window.border = 1; Window.setBorder(1);
...
Zugriff auf fremde Elemente mit Qualifikation
Statische Felder leben während der gesamten Programmausführung!
10
Beispiel: Stack und Queue
Klasse Stack
Stack (Stapel, Kellerspeicher)
push(x);
x = pop();
class Stack {
int[] data;
int top;
fügt x hinten an den Stack an
entfernt und liefert hinterstes Stackelement
push(3);
3
push(4);
3
x = pop();
3
// y == 3
Queue (Puffer, Schlange)
put(x);
x = get();
fügt x hinten an die Queue an
entfernt und liefert vorderstes Queueelement
put(3);
3
put(4);
3
x = get();
y = get();
4
int pop () {
if (top < 0) {
Out.println("-- underflow"); return 0;
} else
return data[top--];
}
FIFO-Datenstruktur
(first in first out)
4
top
void push (int x) {
if (top == data.length - 1)
Out.println("-- overflow");
else
data[++top] = x;
}
// x == 4
y = pop();
data
Stack (int size) {
data = new int[size]; top = -1;
}
LIFO-Datenstruktur
(last in first out)
4
0
// x == 3
}
// y == 4
Benutzung
Stack s = new Stack(10);
s.push(3);
s.push(6);
int x = s.pop() + s.pop(); // x == 9
11
Klasse Queue
Klassifikation
class Queue {
int[] data;
int head, tail, n;
Queue (int size) {
data = new int[size]; head = 0; tail = 0; n = 0;
}
void put (int x) {
if (n == data.length)
Out.println("-- overflow");
else {
data[tail] = x; n++;
tail = (tail+1) % data.length;
}
}
}
int get () {
if (n == 0) {
Out.println("-- underflow"); return 0;
} else
int x = data[head]; n--;
head = (head+1) % data.length;
return x;
}
12
Dinge der realen Welt lassen sich oft klassifizieren
z.B. Artikel eines Web-Shops
Artikel
data
head
tail
n == 3
Buch
Audio
Kamera
...
data
tail
head
n == 4
HardCover SoftCover eBook CD
Cassette
Digital
Analog
Man beachte
Benutzung
Vererbung
• Ein eBook hat alle Eigenschaften eines Buchs; zusätzlich hat es ...
Ein Buch hat alle Eigenschaften eines Artikels; zusätzlich hat es ...
Queue q = new Queue(10);
q.put(3);
q.put(6);
int x = q.get(); // x == 3
int y = q.get(); // y == 6
• CD und Cassette lassen sich gleichermaßen als Audio behandeln
Buch, Audio und Kamera lassen sich gleichermaßen als Artikel behandeln
13
14
Vererbung
Überschreiben von Methoden
class Article {
int code;
int price;
boolean available() {...}
void print() {...}
Article(int c, int p) {...}
}
Oberklasse
Basisklasse
class Book extends Article {
String author;
String title;
void print() {...}
Book(int c, int p,
String a, String t) {...}
}
Unterklasse
Article
code
price
available()
print()
Article(c, p)
class Article {
...
void print() {
Out.print(code + " " + price);
}
}
Book
author
title
erbt: code, price, available, print
ergänzt: author, title, Konstruktor
überschreibt: print
print()
Book(c, p, a, t)
}
Book book = new Book(code, price, author, title);
erzeugt Book-Objekt
Book-Konstruktor
Article-Konstruktor (code = c; price = p;)
author = a; title = t;
Article(int c, int p) {
code = c; price = p;
}
book
class Book extends Article {
...
void print() {
super.print();
Out.print(" " + author + ": " + title);
}
Wenn keine Oberklasse angegeben wird, ist sie Object
Benutzung
Book(int c, int p, String a, String t) {
super(c, p);
author = a; title = t;
}
code
price
author
title
book.print();
print aus Book
print aus Article
Out.print(...);
Ausgabe: code price author: title
15
Klassenhierarchien
16
Kompatibilität zwischen Klassen
Article
Unterklassen sind Spezialisierungen ihrer Oberklassen
code
price
Book-Objekte können Article-Variablen zugewiesen werden
available()
print()
Article a = new Book(code, price, author, title);
a
Book
Audio
Camera
author
title
songs
supplier
print()
print()
print()
...
CD
Cassette
tracks
lengh
print()
print()
Jedes Buch ist ein Artikel
Aber: nicht jeder Artikel ist ein Buch
code price
author: title
code
price
author
title
nur Article-Felder sind über a zugreifbar
a.code
a.price
if (a instanceof Book)
Book b = (Book) a;
...
b
17
code
price
author
title
// Laufzeittyptest
// Typumwandlung mit Laufzeittypprüfung
alle Book-Felder sind über b zugreifbar
b.code
b.price
b.author
b.title
18
Dynamische Bindung
Heterogene Datenstruktur
Article
Article[] a;
Grundlagen der Programmierung
available()
print()
Book CD
Book
Camera
CD
Alle Varianten können als Artikel behandelt werden
void printArticles() {
for (int i = 0; i < a.length; i++) {
if (a[i].available()) {
a[i].print();
}
}
}
Prof. H. Mössenböck
Book
Audio
Camera
print()
print()
print()
12. Dynamische Datenstrukturen
ruft available() aus Article auf
ruft je nach Artikelart das print() aus
Book, CD oder Camera auf
Dynamische Bindung
obj.print() ruft die print-Methode des Objekts auf, auf das obj gerade zeigt
19
Warum "dynamisch"
Verknüpfen von Knoten
• Elemente werden zur Laufzeit (dynamisch) mit new angelegt
• Datenstruktur kann dynamisch wachsen und schrumpfen
class Node {
int
val;
Node next;
Node(int v) {val = v;}
}
Wichtigste dynamische Datenstrukturen
Erzeugen
a
Node a = new Node(3);
Node b = new Node(5);
Verknüpfen
Liste
Baum
Graph
a.next = b;
b
3
a
5
b
3
5
Bestehen aus "Knoten", die über "Kanten" miteinander verbunden sind.
Knoten ... Objekte
Kanten ... Zeiger
2
3
Unsortierte Liste
Unsortierte Liste (Forts.)
Einfügen am Listenende
Einfacher ist es, am Listenanfang einzufügen
class List {
Node head = null, tail = null;
void append (int val) { // Einfügen am Listenende
Node p = new Node(val);
if (head == null) head = p; else tail.next = p;
tail = p;
}
...
}
head
tail
8
3
head
7
class List {
Node head = null;
void prepend (int val) { // Einfügen am Listenanfang
Node p = new Node(val);
p.next = head; head = p;
}
...
}
tail
val
head
p
p
8
3
7
head
tail
val
head
val
val
p
p
4
5
Unsortierte Liste (Forts.)
head
8
class List {
Node head = null;
...
p
Node search (int val) { // Suchen eines Werts
Node p = head;
while (p != null && p.val != val) p = p.next;
// p == null || p.val == val
return p;
}
void delete (int val) { // Löschen eines Werts
Node p = head, prev = null;
while (p != null && p.val != val) {
prev = p; p = p.next;
}
// p == null || p.val == val
if (p != null) // p.val == val
head
if (p == head) head = p.next
8
else prev.next = p.next;
}
}
prev prev
p
}
3
p
7
Suchen von 7
Grundlagen der Programmierung
p
Prof. H. Mössenböck
13. Rekursion
3
7
Löschen von 3
p
6
Was heißt "rekursiv"
Ablauf einer rekursiven Methode
n=4
24
long fact (long n) {
if (n == 1) return 1;
else return fact(n-1) * n;
}
Eine Methode m() heißt rekursiv, wenn sie sich selbst aufruft
m()
m()
m()
n()
m()
direkt rekursiv
indirekt rekursiv
n=3
6
long fact (long n) {
if (n == 1) return 1;
else return fact(n-1) * n;
}
Beispiel: Berechnung der Fakultät (n!)
rekursive Definition
n! = 1 * 2 * 3 * ... * (n-1) * n
n! = (n-1)! * n
1! = 1
(n-1)!
Rekursive Methode zur Berechnung der Fakultät
4! = 3! * 4
3! = 2! * 3
2! = 1! * 2
1! = 1
2
n=2
long fact (long n) {
if (n == 1) return 1;
else return fact(n-1) * n;
}
Allgemeines Muster
long fact (long n) {
if (n == 1)
return 1;
else
return fact(n-1) * n;
}
Jede Aktivierung von fact hat ihr eigenes n
und rettet es über den rekursiven Aufruf hinweg
1
n=1
long fact (long n) {
if (n == 1) return 1;
else return fact(n-1) * n;
}
if (Problem klein genug)
nichtrekursiver Zweig;
else
rekursiver Zweig mit kleinerem Problem
2
Beispiel: binäres Suchen rekursiv
Ablauf des rekursiven binären Suchens
z.B. Suche von 17 (Array muss sortiert sein)
a
0
2
1
3
2
5
low
0
a
2
3 4 5 6 7
7 11 13 17 19
m
1
3
2
5
• Index m des mittleren Element bestimmen
• 17 > a[m] in rechter Hälfte weitersuchen
high
4 5 6 7
7 11 13 17 19
rekursiver Zweig
4
low
3 4 5 6 7
7 11 13 17 19
m
high
6
low = 6, high = 7
nichtrekursiver Zweig
0 1 2
2 3 5
m=3
static int search (int elem, int[] a, int low, int high) {
if (low > high) return -1;
int m = (low + high) / 2;
if (elem == a[m]) return m;
if (elem < a[m]) return search(elem, a, low, m-1);
return search(elem, a, m+1, high);
}
high
static int search (int elem, int[] a, int low, int high) {
if (low > high) return -1; // empty
int m = (low + high) / 2;
if (elem == a[m]) return m;
if (elem < a[m]) return search(elem, a, low, m-1);
return search(elem, a, m+1, high);
}
6
elem = 17, low = 0, high = 7
static int search (int elem, int[] a, int low, int high) {
if (low > high) return -1;
int m = (low + high) / 2;
if (elem == a[m]) return m;
if (elem < a[m]) return search(elem, a, low, m-1);
return search(elem, a, m+1, high);
}
low = 4, high = 7
3
low m
3
0 1 2
2 3 5
m=5
3 4 5 6 7
7 11 13 17 19
low m
high
6
static int search (int elem, int[] a, int low, int high) {
if (low > high) return -1;
int m = (low + high) / 2;
if (elem == a[m]) return m;
if (elem < a[m]) return search(elem, a, low, m-1);
return search(elem, a, m+1, high);
}
m=6
0 1
2 3 4 5 6
2 3
5 7 11 13 17 19
7
low high
m
5
Beispiel: größter gemeinsamer Teiler
rekursiv
static int ggt (int x, int y) {
int rest = x % y;
if (rest == 0) return y;
else return ggt(y, rest);
}
iterativ
static int ggt (int x, int y) {
int rest = x % y;
while (rest != 0){
x = y; y = rest;
rest = x % y;
}
return y;
}
Grundlagen der Programmierung
Prof. H. Mössenböck
14. Schrittweise Verfeinerung
Jeder rekursive Algorithmus kann auch iterativ programmiert werden
• rekursiv: meist kürzerer Quellcode
• iterativ: meist kürzere Laufzeit
Rekursion v.a. bei rekursiven Datenstrukturen nützlich (Bäume, Graphen, ...)
6
Entwurfsmethode für Algorithmen
Vorgehensweise
Schrittweise Verfeinerung
Wie kommt man von der Aufgabenstellung zum Programm?
1. Zerlege Aufgabe in Teilaufgaben und spezifiziere deren Schnittstelle
Beispiel
2. Nimm an, dass die Teilaufgaben schon gelöst sind
3. Implementiere Gesamtaufgabe mit Hilfe der Teillösungen
4. Sind die Teilaufgaben einfach genug?
ja => implementiere sie direkt in einer Programmiersprache
nein => Zerlege sie weiter (Schritt 1)
geg.: Text aus Wörtern
ges.: Wie oft kommt jedes Wort im Text vor?
Welche Befehle würde man sich wünschen, um diese Aufgabe zu lösen?
•
•
•
•
lies Wort
speichere und suche Wort in einer Tabelle
erhöhe Wortzähler für gespeichertes Wort
drucke Zähler für alle gespeicherten Worte
Was bringt das?
Komplexität
tatsächlich
c
zu erwarten
Leider gibt es keine Sprache mit diesen Befehlen
Befehle als Methoden implementieren (d.h. gewünschte "Sprache" selbst bauen)
< c/2
2
Programmgröße
n/2
n
Komplexität steigt überproportional
mit der Programmgröße
Halbierung der Programmgröße
reduziert die Komplexität
um mehr als die Hälfte!
3
Beispiel
Beispiel (Forts.)
Aufgabe: "Zähle die Häufigkeit von Wörtern in einem Text"
3. Implementiere Gesamtaufgabe mit Hilfe der Teillösungen
1. Zerlege Aufgabe in Teilaufgaben und spezifiziere deren Schnittstelle
word = readWord();
class WordCount {
public static void main (String[] arg) {
WordTable tab = new WordTable();
In.open("input.txt");
String word = readWord();
while (word != null) {
tab.count(word);
word = readWord();
}
In.close();
tab.print();
}
liefert nächstes Wort oder null, wenn kein Wort mehr
von der aktuellen Eingabedatei gelesen werden kann.
Klasse WordTable mit folgenden Methoden:
tab.count(word);
trägt word in tab ein (falls noch nicht vorhanden)
und erhöht Worthäufigkeit um 1
tab.print();
gibt Wörter und ihre Häufigkeiten aus
2. Nimm an, dass die Teilaufgaben schon gelöst sind
}
Was haben wir bisher geleistet?
4
Sehr viel! Wir haben die komplexe Aufgabe WordCount auf relativ einfache Teilaufgaben
wie readWord() oder tab.count(word) reduziert.
5
Beispiel (Forts.)
Beispiel (Forts.)
4. Zerlege Teilaufgaben weiter (readWord)
Grobstruktur der Worttabelle
ch = In.read();
Character.isLetter(ch)
word.append(ch)
lies ein Zeichen; In.done == false, wenn Dateiende
prüfe, ob ch ein Buchstabe ist
füge ch an das Wort word an
Datenstruktur:
verkettete Liste von Wörtern
und Häufigkeiten
first
word
freq
next
Alle Teilaufgaben sind bereits in der Java-Bibliothek implementiert.
Implementiere readWord() mit ihnen
static String readWord () {
StringBuilder word = new StringBuilder();
char ch;
//----- skip nonletters
do ch = In.read(); while (In.done() && ! Character.isLetter(ch));
// !In.done() || ch is a letter
//----- build the word
while (In.done() && Character.isLetter(ch)) {
word.append(ch);
ch = In.read();
}
// ! In.done() || ch is not a letter
if (word.length > 0) return word.toString(); else return null;
}
class Element {
String word;
int freq;
Element next;
Element (String w) {
word = w; freq = 1;
}
class WordTable {
Element first = null;
void count (String word) {...}
void print () {...}
}
}
6
7
Beispiel (Forts.)
Beispiel (Forts.)
4. Zerlege Teilaufgaben weiter (tab.count(word))
4. Zerlege Teilaufgaben weiter (tab.print())
Ist so einfach, dass man es sofort implementieren kann
elem = tab.find(word);
tab.enter(word);
freq++;
suche word in tab und liefere entspr. Element oder null
trage word in tab ein (es kommt noch nicht vor)
erhöhe Worthäufigkeit
void print () {
for (Element e = first; e != null; e = e.next)
Out.println(e.word + ": " + e.freq);
}
void count (String word) {
Element e = tab.find(word);
if (e == null) tab.enter(word); else e.freq++;
}
find und enter sind so einfach, dass man sie sofort implementieren kann
Element find (String word) {
Element e = first;
while (e != null && !word.equals(e.word)) {
e = e.next;
}
// e == null || word.equals(e.word)
return e;
}
void enter (String word) {
Element e = new Element(word);
e.next = first;
first = e;
}
8
Zusammensetzen der einzelnen Teile
class Element {
String word;
int freq;
Element next;
}
9
Zusammensetzen der einzelnen Teile
class WordCount {
class WordTable {
private Element first = null;
public static void main (String[] arg) {
WordTable tab = new WordTable();
In.open("input.txt");
String w = readWord();
while (w != null) {tab.count(w); w = readWord();}
In.close();
tab.print();
}
private Element find (String word) {
Element e = first;
while (e != null && !word.equals(e.word)) e = e.next;
return e;
}
Element (String w) {
word = w; freq = 1;
}
private void enter (String word) {
Element e = new Element(word);
e.next = first; first = e;
}
static String readWord () {
StringBuilder word = new StringBuilder();
char ch;
do ch = In.read(); while (In.done() && ! Character.isLetter(ch));
while (In.done() && Character.isLetter(ch)) {
word.append(ch);
ch = In.read();
}
if (word.length > 0) return word.toString(); else return null;
}
void count (String word) {
Element e = tab.find(word);
if (e == null) tab.enter(word); else e.freq++;
}
}
void print () {
for (Element e = first; e != null; e = e.next)
Out.println(e.word + ": " + e.freq);
}
}
10
11
Aufrufhierarchie
Weiteres Beispiel: Index erzeugen
Eingabe Seitennummer
WordCount
WordTable
read
isLetter
count
find
Ende einer Seitenangabe
1 = if-Anweisung; Verzweigung; Abfrage; #
2 = while-Anweisung; Schleife;
Abfrage; #
3 = ...
main
readWord
Stichwort
print
Zwischendatei
if-Anweisung
Verzweigung
Abfrage
while-Anweisung
Schleife
Abfrage
...
enter
1
1
1
2
2
2
Sortierung
Abfrage
Abfrage
if-Anweisung
Schleife
Verzweigung
while-Anweisung
...
1
2
1
2
1
2
Ausgabe
12
Abfrage 1, 2
if-Anweisung 1
Schleife 2
...
13
Idee
Paket = Sammlung zusammengehöriger Klassen (Bibliothek)
Grundlagen der Programmierung
Prof. H. Mössenböck
15. Pakete
Zweck
• mehr Ordnung in Programme bringen
• bessere Kontrolle der Zugriffsrechte (wer darf auf was zugreifen)
• Vermeidung von Namenskonflikten
Beispiele für Pakete in der Java-Klassenbibliothek
Paket
enthaltene Klassen
java.lang
java.io
java.awt
java.util
...
System, String, Integer, Character, Object, Math, ...
File, InputStream, OutputStream, Reader, Writer, ...
Button, CheckBox, Frame, Color, Cursor, Event, ...
ArrayList, Hashtable, BitSet, Stack, Vector, Random, ...
...
siehe: http://java.sun.com/javase/6/docs/api/
2
Anlegen von Paketen
Datei Circle.java
Pakete als Sichtbarkeitsgrenzen
Datei Rectangle.java
package graphics;
package graphics;
class Circle {
...
}
class Rectangle {
...
}
Was in einem Paket deklariert ist, ist in anderen Paketen unsichtbar
1. Zeile der Datei
package one;
package two;
class C {...}
class D {
C obj;
}
class D {...}
gleicher Name stört nicht
Paket graphics enthält die Klassen Circle und Rectangle
Circle
Felder
Methoden
Rectangle
Felder
Methoden
Compiler meldet einen Fehler!
C ist hier unsichtbar
Paket graphics
Zweck
• In verschiedenen Paketen können gleiche Namen verwendet werden
• Programmierer müssen nicht Rücksicht nehmen, welche Namen
schon woanders verwendet wurden
Wenn package-Zeile fehlt, gehören die Klassen zu einem namenlosem Standardpaket
3
4
Export von Namen
Import von Klassennamen
Namen können mit dem Zusatz public exportiert werden
Exportierte Klassennamen können in anderen Paketen importiert werden
(sie sind dann in anderen Paketen sichtbar)
auch in anderen
Paketen sichtbar
Durch gezielten Import der Klasse
Durch Qualifikation mit dem Paketnamen
package one;
package myPack;
package myPack;
public class C {
int x;
public int y;
void p() {...}
public void q() {...}
C () {...}
public C (int x, int y) {...}
}
import graphics.Circle;
import one.C;
class MyClass {
graphics.Circle c1;
java.awt.Circle c2;
...
}
nur in diesem Paket sichtbar
class MyClass {
Circle c;
...
}
Durch Import aller public-Klassen eines Pakets
class D {...}
package myPack;
public-Felder und -Methoden werden nur dann exportiert, wenn die Klasse selbst public ist.
Lokale Variablen und Parameter können nicht exportiert werden.
5
import graphics.*;
class MyClass {
Circle c;
Rectangle r;
...
}
6
Pakete und Verzeichnisse
Geschachtelte Pakete
Pakete können zu größeren Paketen zusammengefasst werden
Pakete werden auf Verzeichnisse abgebildet, Klassen auf Dateien
Klasse C
Paket P
samples
Datei C.java
Verzeichnis P
Samples
package P;
class A {...}
package P;
class B {...}
package P;
class C {...}
P
package samples.io;
public class Output
beides möglich
7
import samples.graphics.Circle; importiert die Klasse Circle
import samples.graphics.*
importiert alle public-Klassen aus samples.graphics
import samples.*;
importiert alle public-Klassen aus samples
(nicht aus samples.graphics)
samples.io.Output out;
Qualifikation einer Klasse aus einem geschachtelten Paket
8
Prinzip
für Felder und Methoden
• Verstecke die Implementierung komplexer Datenstrukturen vor Benutzern
• Erlaube den Zugriff auf die Daten nur über Methoden
private
int a;
int b;
protected int c;
nur in der Klasse sichtbar, in der das Element deklariert wurde
nur im Paket sichtbar, in dem das Element deklariert wurde
sichtbar:
- in der deklarierenden Klasse
- in deren Unterklassen (selbst wenn sie in anderen Paketen liegen)
- im deklarierenden Paket
auch in anderen Paketen sichtbar, wenn importiert
Zugriffsmethoden (Klassenschnittstelle)
"Black Box"
public
Klasse Stack
Warum?
int d;
package one;
• Verringert die Komplexität (Arbeiten mit den Daten wird einfacher)
• Implementierung der Daten kann geändert werden, ohne dass Benutzer etwas merken
• Schutz vor mutwilliger oder unabsichtlicher Zerstörung
push
Input.java
Output.java
Sichtbarkeitsattribute
Daten
Rectangle.java
io
io
Information Hiding (Geheimnisprinzip)
pop
Circle.java
Benutzung
cd C:\Samples
javac P/A.java
push
graphics
samples
C.java
P/A
P.A
package samples.graphics;
public class Rectangle
graphics
Übersetzung und Ausführung mit dem JDK
java
java
package samples.io;
public class Input
A.java
B.java
Paket P
package samples.graphics;
public class Circle
pop
public class C {
private int a;
int b;
protected int c;
public int d;
}
public class D {
...
}
9
b c d
a
d
package two;
import one.C;
public class E extends C {
}
c
...
public class F {
...
}
10
Beispiel: Stack mit Information Hiding
Dokumentationskommentare
public class Stack {
private int[] data;
private int top;
Dokumentationskommentare
public Stack (int size) {
data = new int[size]; top = -1;
}
public void push (int x) {
if (top >= data.length) error("stack overflow");
else data[++top] = x;
}
Klassenschnittstelle
/** ... */
Stack
können vor die Deklarationen von Klassen, Methoden, Feldern gesetzt werden.
Werkzeug javadoc erzeugt daraus Dokumentation in HTML
Stack()
push(int x)
pop(): int
/** A stack of integers.
This is a FIFO data structure storing integers in a stack-like way. */
public class Stack {
/** The elements in the stack. */
private int[] data;
...
/** Push an integer on the stack.
If the stack is full an error is reported and the program stops. */
public void push (int x) {
...
}
...
}
public int pop () {
if (top < 0) error("stack underflow");
else return data[top--];
}
private void error (String msg) {
Out.println(msg);
System.exit(0);
}
1. Satz bis Punkt
wird in Kurzdoku
übernommen
Rest wird in
Langdoku
übernommen
}
11
Erzeugte HTML-Datei (Stack.html)
12
javadoc
Aufrufsyntax
javadoc [options] {filename | packagename}
Optionen
•
•
•
•
-public
-private
-d path
...
zeigt nur public-Deklarationen
zeigt public- und private-Deklarationen
gibt Zugriffspfad für mehrere Klassen und Pakete an
Beispiel
javadoc -public myDirectory/Stack.java
erzeugt:
myDirectory/Stack.html
diverse andere Übersichtsdateien
Vollständige Dokumentation von javadoc
http://java.sun.com/j2se/javadoc/
13
14
Motivation
Fehler können nicht immer dort behandelt werden, wo sie auftreten
Grundlagen der Programmierung
void p() {
...
q();
...
}
Prof. H. Mössenböck
16. Ausnahmen
void q() {
...
r();
...
}
void r() {
...
...
...
}
Lösung
r() muss den Fehler an q() melden,
q() muss ihn an p() melden,
bis ihn jemand behandelt
Hier tritt ein Fehler auf. Wie soll r() reagieren?
•
•
•
•
•
Fehlermeldung ausgeben?
Programm abbrechen?
einfach weiterlaufen?
Korrekturmaßnahmen?
...
Vielleicht möchte q() oder r() reagieren?
2
Fehlerbehandlung in früheren Zeiten
Idee der Ausnahmebehandlung in Java
Jede Methode lieferte einen Fehlercode
geschützter Block
int result;
result = p(...);
if (result == ok) {
result = q(...);
if (result == ok) {
result = r(...);
if (result == ok) {
...
} else error(...);
} else error(...);
} else error(...);
...
Statement;
Statement;
Statement;
...
...
Statement;
Statement;
Statement;
...
Ausnahmebehandler
(exception handler)
Wenn im geschützten Block ein Fehler (eine Ausnahme) auftritt:
• Ausführung des geschützten Blocks wird abgebrochen
• Fehlerbehandlungscode wird ausgeführt
• Programm setzt nach dem geschützten Block fort
Problem
•
•
•
•
Fehlerbehandlungscode
aufwändig: vor lauter Fehlerbehandlung sieht man eigentliches Programm nicht mehr
Abfragen des Fehlercodes kann vergessen werden
Abfragen des Fehlercodes wird oft aus Bequemlichkeit nicht durchgeführt
Funktionen können neben Fehlercode kein anderes Ergebnis mehr liefern
3
4
Try-Anweisung in Java
Arten von Ausnahmen
try {
p(...);
q(...);
r(...);
geschützter
Block
} catch (Exception1 e) {
error(...);
} catch (Exception2 e) {
error(...);
void q(...) {
...
throw new Exception2();
...
}
Laufzeitfehler (Runtime Exceptions)
Auslösen
einer
Ausnahme
werden von der Java-VM ausgelöst
• Division durch 0
• Zugriff über null-Zeiger
• Indexüberschreitung
ArithmeticException
NullPointerException
ArrayIndexOutOfBoundsException
Müssen nicht behandelt werden
Wenn sie nicht behandelt werden, stürzt das Programm mit einer Fehlermeldung ab
Ausnahmebehandler
} catch (Exception3 e) {
Geprüfte Ausnahmen (Checked Exceptions)
error(...);
werden vom Benutzercode ausgelöst (throw-Anweisung)
}
• vordefinierte Ausnahmen
• selbst definierte Ausnahmen
Vorteile
z.B. FileNotFoundException
z.B. MyException
Müssen behandelt werden.
Compiler prüft, ob sie abgefangen werden
• Fehlerfreier Fall und Fehlerfälle sind sauberer getrennt
• Man kann nicht vergessen, einen Fehler zu behandeln
(Compiler prüft, ob es zu jeder möglichen Ausnahme einen Behandler gibt)
5
Hierarchie der Ausnahmeklassen
Throwable
Error
LinkageError
VirtualMachineError
...
Exception
RuntimeException
NullPointerException
ArithmeticException
IndexOutOfBoundsException
...
IOException
FileNotFoundException
...
AWTException
...
MyException1
MyException2
...
6
Ausnahmen sind als Klassen codiert
Fehlerinformationen stehen in einem Ausnahme-Objekt
Fehler, die nicht am Programm liegen
(müssen nicht abgefangen werden)
von der VM ausgelöste Fehler
(müssen nicht abgefangen werden)
class Exception extends Throwable {
Exception(String msg) {...}
// erzeugt neues Ausnahmeobjekt mit Fehlermeldung
String getMessage() {...}
// liefert gespeicherte Fehlermeldung
String toString() {...}
// liefert Art der Ausnahme und gespeichert Fehlermeldung
void printStackTrace() {...} // gibt Methodenaufrufkette aus
...
}
Eigene Ausnameklasse (speichert Informationen über speziellen Fehler)
class MyException extends Exception {
private int errorCode;
MyException(String msg, int errorCode) {...}
int getErrorCode() {...}
// toString(), printStackTrace(), ... von Exception geerbt
}
vordefinierte Ausnahmen
(müssen abgefangen werden)
selbst definierte Ausnahmen
(müssen abgefangen werden)
7
8
Throw-Anweisung
catch-Blöcke und finally-Block
Löst eine Ausnahme aus
try {
...
} catch (MyException e) {
Out.println(e.getMessage() + ", error code = ", + e.getErrorCode());
} catch (NullPointerException e) {
...
} catch (Exception e) {
...
} finally {
...
}
throw new MyException("invalid operation", 17);
"Wirft" ein Ausnahmeobjekt mit entsprechenden Fehlerinformationen
•
•
•
•
bricht normale Programmausführung ab
sucht passenden Ausnahmebehandler (catch-Block)
führt Ausnahmebehandler aus und übergibt ihm Ausnahmeobjekt als Parameter
setzt nach try-Anweisung fort, zu der der catch-Block gehört
• Passender catch-Block wird an Hand des Ausnahme-Typs ausgewählt
• catch-Blöcke werden sequentiell abgesucht
Achtung: speziellere Ausnahme-Typen müssen vor allgemeineren stehen
• Am Ende wird (optionaler) finally-Block ausgeführt
egal, ob im geschützten Block ein Fehler auftrat oder nicht
9
Zweck des finally-Blocks
Ablauflogik bei Ausnahmen
main()
Zum Sicherstellen, dass Abschlussarbeiten auch im Fehlerfall ausgeführt werden
try {
FileStream s = new FileStream(...);
...
...
...
s.close();
} catch (...) {
...
}
falsch
Datei wird im Fehlerfall nicht geschlossen
10
try {
f();
...
} catch (E1 e) {
...
}
...
FileStream s;
try {
s = new FileStream(...);
...
...
...
} catch (...) {
...
} finally {
s.close();
}
f()
try {
g();
...
} catch (E2 e) {
...
} finally {
...
}
...
g()
if (...) {
throw new E1();
} else if (...) {
throw new E2();
} else {
throw new E3();
}
...
throw new E1();
•
•
•
•
richtig
Datei wird auf jeden Fall geschlossen
11
keine try-Anweisung in g() bricht g() ab
kein passender catch-Block in f() führt finally-Block in f() aus und bricht f() dann ab
führt catch-Block für E1 in main() aus
setzt nach try-Anweisung in main() fort
12
Ablauflogik bei Ausnahmen
main()
try {
f();
...
} catch (E1 e) {
...
}
...
f()
Ablauflogik bei Ausnahmen
g()
try {
g();
...
} catch (E2 e) {
...
} finally {
...
}
...
main()
if (...) {
throw new E1();
} else if (...) {
throw new E2();
} else {
throw new E3();
}
...
try {
f();
...
} catch (E1 e) {
...
}
...
f()
try {
g();
...
} catch (E2 e) {
...
} finally {
...
}
...
g()
if (...) {
throw new E1();
} else if (...) {
throw new E2();
} else if (...) {
throw new E3();
}
...
throw new E2();
throw new E3();
•
•
•
•
• Compiler meldet einen Fehler, weil E3 nirgendwo in der Ruferkette abgefangen wird
keine try-Anweisung in g() bricht g() ab
führt catch-Block für E2 in f() aus
führt finally-Block in f() aus
setzt nach try-Anweisung in f() fort
fehlerfreier Fall
13
•
•
•
•
führt g() zu Ende aus
führt try-Block in f() zu Ende aus
führt finally-Block in f() aus
setzt nach finally-Block in f() fort
14
Spezifikation von Ausnahmen im Methodenkopf
Eigentlich müsste es heißen
Wenn eine Methode eine Ausnahme an den Rufer weiterleitet, muss sie das
in ihrem Methodenkopf mit einer throws-Klausel spezifizieren
void main() throws E3
void f() throws E1, E3
void g() throws E1, E2, E3
try {
f();
...
} catch (E1 e) {
...
}
...
try {
g();
...
} catch (E2 e) {
...
} finally {
...
}
...
if (...) {
throw new E1();
} else if (...) {
throw new E2();
} else if (...) {
throw new E3();
}
...
void f() {
try {
...
g();
...
} catch (E2 e) {
...
}
}
void g() throws E2 {
try {
...
throw new E1();
...
throw new E2();
...
} catch (E1 e) {
...
}
}
Compiler weiß dadurch, dass g() eine E2-Ausnahme auslösen kann.
Wer g() aufruft, muss daher
• entweder E2 abfangen
• oder E2 im eigenen Methodenkopf mit einer throws-Klausel spezifizieren
Man kann nicht vergessen, eine Ausnahme zu behandeln!
15
16
Weitere Java-Themen
Klassen
Prof. H. Mössenböck
-
17. Ausblick
Threads
Grundlagen der Programmierung
innere Klassen
anonyme Klassen
abstrakte Klassen
Interfaces
generische Klassen und Interfaces
Bibliotheken
-
GUI (Swing, AWT)
Collections (Listen, Hashtabellen, ...)
Streams (Ein-/Ausgabe)
Net und Web
Reflection
Anwendungen
- Applets
- Servlets und Java Server Pages
- Java Beans
2
Herunterladen