• Keine Ergebnisse gefunden

3 Entwurf von Algorithmen

N/A
N/A
Protected

Academic year: 2022

Aktie "3 Entwurf von Algorithmen"

Copied!
157
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

3 Entwurf von Algorithmen

3.1 Algorithmen, Programmiersprachen und Pro- gramme

3.2 Systematischer Entwurf von Algorithmen 3.3 Schrittweise Verfeinerung

3.4 Ablaufsteuerung (Kontrollstrukturen) 3.5 Modularität

3.6 Rekursion

3.7 Daten und Datenstrukturen

(2)

3.1 Algorithmen, Programmierspra- chen und Programme

Algorithmus: Verfahren zur systematischen, schrittwei- sen Lösung eines Problems

Beschrieben in:

- Umgangssprache - Programmiersprache

- mathematischem Formalismus (z. B. Tu- ring-Maschine)

Church-Turing-These

"All reasonable definitions of 'algorithm' are equivalent."

(3)

Algorithmus vs. Programm

Algorithmus

Programmierung

Programm in höherer

Programmiersprache

Übersetzung

Programm in Maschinensprache

Interpretation durch CPU

(Gewünschter Ablauf wird ausgeführt)

(4)

Programmentwicklung

Durchführbarkeitsstudie: Kann das Problem gelöst werden? (Analyse)

Spezifikation (specification document)

Design/Software Architektur/Design Pat- terns/Frameworks

Definition von Algorithmen

Implementation von Algorithmen

Code-Test

Wartung

(5)

Beispiele für Algorithmen

Prozess Algorithmus Typische Schritte im Algorithmus

Pullover stricken Strickmuster Linke Masche, rechte Masche stricken

Modellflugzeug bauen Montageanleitung Leime Rippe A an den Holm B

Kleider nähen Schnittmuster Nähe Saum an

Spiele Musik Notenblatt

Kochen Rezept Zutaten (4 Personen)

400 g Tomatensoße 1/2 l Wasser

3 Teelöffel Chilipulver 1 1/2 l Minutenreis

1/2 kg zerstoßene Tortilla-Chips

1/2 kg kleingeschnittenen Cheddarkä- se

Anleitung:

Vermenge Tomatensoße, Wasser, Chili- pulver in einem mittelgroßen Topf und bringe es zum Kochen. Rühre den Reis ein, decke den Topf ab, nehme ihn vom Feuer und lasse ihn 10 Minuten lang ste- hen. Streue Tortilla-Chips und Käse dar- über. Serviere das Essen, wenn ge- wünscht, mit Salatblättern und saurer Sahne.

(6)

Algorithmen als fundamentales Konzept

Unabhängig von der Programmiersprache

Unabhängig von der Rechner-Hardware

Der Entwurf von Algorithmen ist ein kreativer Pro- zeß, der nicht automatisiert werden kann.

Beispiel

Summiere alle Zahlen zwischen 1 und 100:

Lösung 1

1+2+3+4…+99+100 = 5050 O(n)

Lösung 2

(1+100)+(2+99)+…(50+51) = 50*101 = 5050 O(1)

Wir sehen: Die Effizienz eines Algorithmus ist ein wich- tiges Entwurfskriterium.

(7)

Richtlinien

Ist das Problem lösbar?

Welche Schritte müssen ausgeführt werden, um einen korrekten Algorithmus zu erhalten?

Korrektheit heißt:

- der Algorithmus erzeugt ein Resultat (ist endlich) - das Resultat ist korrekt

Ist der Algorithmus effizient?

(8)

Beispiele

Unlösbares Problem Halteproblem

Finde einen Algorithmus, der feststellt, ob ein beliebi- ger Algorithmus in endlicher Zeit irgendein Ergebnis liefert (=anhält).

Sehr komplexes Problem

Problem des Handlungsreisenden (traveling sales- man)

Für eine beliebige Topologie: Finde den kürzesten Weg, der alle Städte verbindet, die der Handlungsrei- sende aufsuchen soll.

(9)

Schwierigkeiten beim Algorithmenentwurf

Es existiert keine präzise Definition des Problems (Problem der genauen Spezifikation).

Spezialfälle kommen vor, die nicht betrachtet wurden.

Es ist schwer zu zeigen, dass der Algorithmus für alle möglichen Eingabedaten korrekt abläuft.

(10)

Beispiele für Schwierigkeiten (1)

Beispiel 1: Wegbeschreibung 1. Am Laden rechts abbiegen

2. Geradeaus bis zur nächsten Kreuzung 3. rechts abbiegen

4. dritte Querstraße links

An welchem Laden? (UNPRÄZISE)

Beispiel 2: Finde Süden mit der Uhr 1. Richte kleinen Zeiger auf die Sonne

2. Berechne die Winkelhalbierende zwischen dem klei- nen Zeiger und 12.00 h

3. Winkelhalbierende zeigt nach Süden

Ausrichten des kleinen Zeigers durch Drehen am Stell- knopf oder Drehen der gesamten Uhr (UNPRÄZISE) Funktioniert nicht auf der Südhalbkugel! (Sonderfall ver- gessen)

(11)

Beispiele für Schwierigkeiten (2)

Beispiel 3: Repariere Wasserhahn 1. Wasserhahn aufdrehen

2. Warten, bis kein Wasser mehr fließt 3. Hahn abschrauben

4. Dichtung gegen neue Dichtung auswechseln 5. Hahn anschrauben

6. Hahn zudrehen 7. Haupthahn öffnen

Endet nicht; Schritt 2 unendlich; es wurde vergessen, zu Anfang den Haupthahn zuzudrehen.

(12)

Folgerung

Der Entwurf von Algorithmen muss auf systematische Weise diszipliniert durchgeführt werden. Methoden zum systematischen Entwurf und zur Analyse von Algo- rithmen (Komplexität und Korrektheit) sind hilfreich.

In einem zweiten Schritt wird der Algorithmus als Com- puterprogramm formuliert. Dieser Schritt wird als Pro- grammierung bezeichnet. Durch die Verwendung einer in Syntax und Semantik wohldefinierten Sprache wird sichergestellt, dass die einzelnen Anweisungen

unmißverständlich sind

auf dem Zielrechner ausführbar sind (Mächtigkeit, Detaillierungsgrad).

(13)

Formulierungsfehler

Annahme

Es existiert ein Algorithmus, der nicht zu komplex ist.

Welche Fehler können in der Formulierung auftauchen?

3 Klassen von Fehlern:

- Syntaktische Fehler - Semantische Fehler - Logische Fehler

(14)

Klassifikation der Programmierfehler

a) Syntaktische Fehler

Werden durch den Compiler oder Interpreter gefun- den und als solche gekennzeichnet. Instruktion wird nicht ausgeführt.

Beispiel:

a = b * + c;

b) Semantische Fehler

Werden entweder während der Ausführung gefunden (Java Exception, segmentation fault im Betriebssy- stem) oder schlimmstenfalls nie gefunden.

Beispiel: Feldgrenzenverletzung

der Vektor a hat 100 Elemente i = 101;

a[i] = 17;

Beispiel: Teilen durch 0 r = 0.0;

(15)

Klassifikation der Programmierfehler

c) Logische Fehler

Das Programm tut nicht, was der Programmierer eigentlich wollte.

Beispiel:

// berechne Kreisumfang

circumference = PI * radius;

// anstatt 2*PI*radius

Viele semantischen und alle logischen Fehler können während des Testens und durch formale Verifikation gefunden werden.

Wichtig

Tests sind im Gegensatz zur formalen Verifikation keine Korrektheitsbeweise für ein Programm!

(16)

3.2 Systematischer Entwurf von Algo- rithmen

a) Problemstellung

(a) Ist die Problemstellung verstanden bzw. wie lautet das eigentliche Problem?

Wenn die Problemstellung nicht verstanden ist, dann weitere Informationen einholen. Wenn der Kern des Problems klar hervorgehoben ist, dann das eigentliche Problem formulieren.

(b) Ist die Problemstellung klar und exakt?

Wenn nein, dann weitere Informationen zur Pro- blemstellung beschaffen oder Problemstellung selbst präzisieren

(c) Hat das Problem bereits einen Namen?

Wenn nein, dann dem Problem einen kurzen und treffenden Namen geben.

(d) Was ist bekannt und was ist gegeben?

Zusammenstellen aller bekannten Eingabegrößen bzw. –objekte. Jedem Eingabeobjekt einen Namen geben und seinen Typ bzw. seine Art beschreiben.

(17)

Problemstellung (2)

(e) Was ist unbekannt bzw. was ist gesucht?

Zusammenstellen aller bekannten Ausgabegrößen bzw. Ausgabeobjekte. Jedem Ausgabeobjekt einen Namen geben und seinen Typ festlegen.

(f) Welche Bedingungen für Ein- und Ausgabeobjekte müssen erfüllt sein bzw. werden gefordert?

Zusammenstellen dieser Bedingungen (z.B. for- melmäßige Zusammenhänge).

(18)

Anmerkung zur Problemstellung

In der Praxis ist nur in den seltensten Fällen das zu lö- sende Problem von vorne herein klar beschrieben (spe- zifiziert).

Gründe

Derjenige, der das Problem hat, und derjenige, der den Algorithmus entwerfen soll, sind oft nicht iden- tisch.

Sie haben meist unterschiedliches Vorwissen über die Anwendungsumgebung.

Sie machen meist unterschiedliche „selbstverständli- che“ Annahmen.

Deshalb:

Spezifikationen sollten gemeinsam erarbeitet werden.

(19)

Beispiel für eine unklare Aufgabenstellung (1)

Problemstellung

Berechne die Funktion f(x) = log(x)

Fragen

Was ist die Basis des Logarithmus?

Wie ist x gegeben? In welchem Datentyp?

Wie soll f(x) ausgegeben werden?

Mit welcher Genauigkeit soll gerechnet werden?

Welchen Wertebereich hat x?

Was soll passieren, wenn ein unzulässiges x eingege- ben wird?

(20)

Beispiel für eine unklare Aufgabenstellung (2)

Spezifikation f(x) = log10(x)

x, f(x) als IEEE double precision floating point-Werte x: Eingabe

f(x): Ausgabe

Wertebereich: 0.3: 0.3; in allen anderen Fällen Fehler- meldung ausgeben.

(21)

Systematischer Entwurf von Algorithmen (1)

b) Lösungsplan

(a) Ist dasselbe Problem oder ein ähnliches bzw. ver- gleichbares Problem bekannt?

Wenn ja, dann versuche man, Kenntnisse über die Lösung zu erhalten.

(b) Ist ein allgemeineres Problem bekannt?

Wenn ja, dann versuche man, Kenntnisse über die Lösung dieses Problems zu erhalten. Man prüfe, ob das gegebene Problem als Sonderfall des all- gemeinen Problems behandelt werden kann.

Wenn ja, dann wende man die allgemeine Pro- blemlösung an. Wenn sich das Problem verallge- meinern lässt, ohne dass die Lösung erheblich schwerer wird, dann löse man das allgemeinere Problem.

(c) Lässt sich das Problem in ein einfacheres Teilpro- blem oder in mehrere einfachere, in sich geschlos- sene Teilprobleme aufteilen?

Wenn ja, dann teile man das Problem auf und löse die Teilprobleme.

(d) Man stelle einen in Schritte gegliederten Lösungs- plan auf.

(22)

Kommentare zum Lösungsplan

a.) Problem: Zeichne ein Dreieck auf dem Bildschirm.

b.) Allgemeineres Problem: Zeichnen eines Polygons mit n Ecken auf dem Bildschirm. (Dreieck: n = 3, Rechteck: n = 4)

Lösung des allgemeineren Problems erheblich schwe- rer?

Dreieck: Linie von Ecke 1 nach Ecke 2 Linie von Ecke 2 nach Ecke 3 Linie von Ecke 3 nach Ecke 1 Polygon: Linie von Ecke 1 zu Ecke 2

Linie von Ecke 2 nach Ecke 3 ...

Linie von Ecke n nach Ecke 1

c.) Teilproblem: Zeichnen einer Linie zwischen zwei Punkten auf dem Bildschirm.

(23)

Was ist ein guter Algorithmus?

Kriterien

Ausführungszeit (Komplexität)

Speicherbedarf für Programm und Daten

Entwicklungszeit. Wird zunehmend wichtiger!

Gut strukturiertes, wartbares Programm: sehr wichtig!

(24)

3.3 Schrittweise Verfeinerung

Zerlegung eines Algorithmus in Teilalgorithmen, von de- nen jeder einzelne einfacher und überschaubarer ist als der ursprüngliche Algorithmus. Dadurch verbessern sich die Chancen, einen insgesamt korrekten Algorithmus zu entwerfen.

Die Verfeinerung wird so lange fortgesetzt, bis die ein- zelnen Schritte unmittelbar auf dem Prozessor ausge- führt werden können: vom Menschen bis hin zur Pro- grammiersprache, vom Compiler bis hin zur Maschinen- sprache.

(25)

Beispiel für schrittweise Verfeinerung: (1)

Algorithmus "Koche Kaffee"

1. Koche Wasser

2. Gib Kaffeepulver in die Tasse 3. Fülle Wasser in die Tasse

(26)

Beispiel für schrittweise Verfeinerung: (2)

Erste Verfeinerung

(1.1) Fülle Wasserkessel (1.2) Schalte Herdplatte an

(1.3) Warte, bis das Wasser kocht (1.4) Schalte Herdplatte aus

(2.1) Öffne Kaffeeglas

(2.2) Entnehme einen Löffel Kaffee (2.3) Kippe Löffel in die Tasse

(2.4) Schließe Kaffeeglas

(3.1) Gieße Wasser aus dem Kessel in die Tasse, bis die Tasse voll ist

(27)

Beispiel für schrittweise Verfeinerung: (3)

Zweite Verfeinerung

{Algorithmus zur Zubereitung einer Tasse Kaffee}

{Zuerst Wasser kochen}

(1.1.1.) Stelle Kessel unter Wasserhahn (1.1.2.) Drehe Wasserhahn auf

(1.1.3.) Warte, bis Kessel voll ist (1.1.4.) Drehe Wasserhahn zu (1.2.) Schalte Herdplatte an (1.3.1.) Warte, bis Kessel pfeift (1.4.) Schalte Herdplatte aus {Gib Kaffeepulver in die Tasse}

(2.1.1.) Nehme Kaffeeglas aus dem Fach (2.1.2.) Entferne Deckel vom Kaffeeglas (2.2) Entnehme einen Löffel Kaffee (2.3.) Kippe Löffel in die Tasse

(2.4.1.) Schraube Deckel auf das Kaffeeglas (2.4.2.) Stelle Kaffeeglas in das Fach zurück {Fülle Wasser in die Tasse}

(3.1.) Gieße Wasser aus dem Kessel in die Tasse, bis die Tasse voll ist

(28)

3.4 Ablaufsteuerung (Kontrollstruktu- ren)

3.4.1 Sequenz (Folge von Anweisungen)

Der Kaffee-Algorithmus beinhaltet einfache Schritte, die einer nach dem anderen auszuführen sind. Wir sagen, ein solcher Algorithmus ist eine Folge (Sequenz) von Schritten, was bedeutet:

1) Zu einem Zeitpunkt wird nur ein Schritt ausgeführt.

Schritte werden nie parallel ausgeführt.

2) Jeder Schritt wird genau einmal ausgeführt:

keiner wird wiederholt, keiner wird ausgelassen.

3) Die Reihenfolge, in der die Schritte ausgeführt wer- den, ist die gleiche, in der sie niedergeschrieben sind.

4) Mit der Beendigung des letzten Schrittes endet der gesamte Algorithmus.

(29)

Sequenz (Folge von Anweisungen)

Die Ausführung eines solchen Algorithmus ist sehr starr.

Im Beispiel des Algorithmus "Koche Kaffee":

Was passiert, wenn manche Kaffeetrinker Milch und/oder Zucker mögen?

Lassen sich Teile das Algorithmus auch zum Teeko- chen verwenden?

Kann das Kaffeepulver in die Tasse gegeben werden, während das Wasser zum Kochen gebracht wird (Par- allelisierung)?

(30)

Flussdiagramm der Sequenz

Start

Statement 1

Statement 2

Statement 3

End

(31)

Struktogramm

Statement 1

Statement 2

Statement 3

(32)

3.4.2 Selektion

Bedingte Ausführung von Anweisungen

a) Einfache Form

Falls Bedingung

dann Anweisung Englisch

if condition

then statement(s)

b) Bedingte Anweisung mit Alternative (allgemeine Form):

Falls Bedingung

dann Anweisung 1 sonst Anweisung 2 Englisch

if condition

then statement 1 else statement 2 Anmerkung:

Die einfache Form ist ein Spezialfall der allgemeinen

(33)

Mehrfachauswahl

Falls

Bedingung 1 dann Anweisung 1 Bedingung 2 dann Anweisung 2 .

. .

andernfalls

Anweisung n + 1

Die Bedingungen 1 bis n müssen sich gegenseitig aus- schließen; d.h. es dürfen nicht zwei Bedingungen

gleichzeitig erfüllt sein.

(34)

Beispiele für bedingte Anweisungen

Beispiel 1: Einfache bedingte Anweisung

(2.1.1.) Nehme Kaffeeglas aus dem Fach (2.1.2.) Falls Kaffeeglas leer ist

dann nehme neues Kaffeeglas aus dem Schrank

(2.1.3.) Entferne Deckel vom Kaffeeglas

Beispiel 2: Bedingte Anweisung mit Alternative

max (x,y)

Falls x > y

dann gib x aus sonst gib y aus

(35)

Flussdiagramm der bedingten Anweisung

Bedingung

Anweisung 1

Anweisung 2

nächste Anweisung

wahr falsch

(36)

Flussdiagramm zur Berechnung des Maximums

x > y

gebe x aus gebe y aus

nächste Anweisung

wahr falsch

(37)

Struktogramm der bedingten Anweisung

nächste Anweisung Anweisung

1

Anweisung 2

Bedingung

erfüllt nicht

erfüllt

(38)

Struktogramm zur Berechnung des Maximums

nächste Anweisung

gebe x aus gebe y aus x > y

erfüllt nicht

erfüllt

(39)

Beispiel

Pseudocode

falls die Note des Studenten größer oder gleich 60 ist dann gebe „passed“ aus.

Java

if ( grade >= 60 ) {

System.out.println( “ passed “ );

}

Pseudocode

falls die Note des Studenten ist größer oder gleich 60 dann

gebe „passed“ aus.

sonst

gebe „failed“ aus.

Java

if ( grade >= 60 ) {

System.out.println( “ passed “ );

}

else {

System.out.println( “ failed “ );

(40)

Alternative Formulierung (1)

Java: erste Formulierung if ( grade >= 60 ) {

System.out.println( “ passed “ );

}

else {

System.out.println( “ failed “ );

}

Java: zweite Formulierung

System.out.println( grade >= 60

? “ passed “ : “ failed “ );

Hinweis

Man benutze die zweite Formulierung nur, wenn unbe- dingt notwendig, da sie nicht einfach zu lesen ist. Ihr Gebrauch ist oft gerechtfertigt, wenn sie als Argument einer Methode (Funktion) verwendet wird.

(41)

Alternative Formulierung (2)

Beispiel 3: geschachtelte bedingte Anweisung

max (x, y, z) Falls x > y

dann falls x > z

dann wähle x sonst wähle z sonst falls y > z

dann wähle y sonst wähle z

Die Einrückung im Text macht im Pseudocode deutlich, zu welcher Bedingung ein sonst gehört. Ohne Einrük- kung wäre die Zuordnung nicht immer eindeutig!

Beispiel:

falls cond1 dann falls cond1 dann

falls cond2 dann falls cond2 dann

instr1 instr1

sonst sonst

instr2 instr2

(42)

Geschachtelte falls/dann-Strukturen

Pseudocode

falls die Note des Studenten ist größer oder gleich 90 ist

dann

drucke “A”

sonst

falls die Note des Studenten ist größer oder gleich 80 ist

dann

drucke “ B “ sonst

falls die Note des Studenten ist größer oder gleich 70 ist

dann

drucke “ C “ sonst

falls die Note des Studenten ist größer oder gleich 90 ist

dann

drucke “ D ” sonst

drucke “ F “

(43)

Geschachteltes if/else in Java

if ( grade >= 90 ) {

System.out.println( “ A “ );

}

else {

if ( grade >= 80 ) {

System.out.println( “ B “ );

}

else {

if ( grade >= 70 ) {

System.out.println( “ C “ );

}

else {

if ( grade >= 60 ) {

System.out.println( “ D “ );

}

else {

System.out.println( “ F “ );

} } } }

(44)

Probleme mit geschachtelten Strukturen

if ( x > 5 ) if ( y > 5 )

System.out.println( “x and y are > 5” );

else

System.out.println( “ x is <= 5” );

FEHLER!!

Java bringt ein else stets mit dem vorherigen if in Ver- bindung. Daher heißt es richtig:

if ( x > 5 ) if ( y > 5 )

System.out.println( “x and y are > 5” );

else

System.out.println( “ y is <= 5” );

(45)

Andere häufige Probleme

if ( grade >= 60 ) {

System.out.println( “passed” );

}

else {

System.out.println( “failed” );

System.out.println( “You must take this course again” );

}

druckt für Note < 60:

failed

You must take this course again ohne Klammern:

if ( grade >= 60 )

System.out.println( “passeed” );

else

System.out.println( “failed” );

System.out.println( “You must take this couse again” );

druckt für Note >= 60:

passed

You must take this course again

(46)

Bemerkungen

Geschachtelte bedingte Ausdrücke sind schwierig zu testen: Tiefe n bedeutet, dass 2n verschiedene Kom- binationen zu testen sind!

Solche Strukturen sind auch schwer zu verstehen!

(47)

Mehrfachauswahl (1)

Allgemeine Form

falls

Variable = Bedingung 1: Anweisung 1 Variable = Bedingung 2: Anweisung 2

andernfalls Anweisung

oder

falls Variable gleich

Bedingung 1: Anweisung 1 Bedingung 2: Anweisung 2

andernfalls Anweisung

(48)

Mehrfachauswahl (2)

falls

Münze = Fünfer: addiere 5 auf Summe Münze = Zehner: addiere 10 auf Summe Münze = Fünfziger: addiere 50 auf Summe andernfalls:

gebe Münze zurück

Oder in anderer Schreibweise:

falls Münze gleich

Fünfer addiere 5 auf Summe Zehner addiere 10 auf Summe Fünfziger addiere 50 auf Summe andernfalls:

gebe Münze zurück

(49)

Flussdiagramm für die Mehrfachauswahl

Bedingung =

Wert 1 Wert 2 Wert 3

Anweisung 1 Anweisung 2 Anweisung 3

Anweisung 4

(50)

Mehrfachauswahl für das Münzbeispiel

Münze =

Fünfer Zehner Fünfziger

addiere 5 auf Summe

addiere 10 auf Summe

addiere 50 auf Summe

Anweisung 4

(51)

Struktogramm für die Mehrfachauswahl

Variable = 1

2

3

sonst Anweisung 2

Anweisung 1

Anweisung 3

Anweisung 4

(52)

Struktogramm für das Münzbeispiel

Münze = Fünfer

Zehner

Fünfziger

sonst addiere 10

auf Summe addiere 5 auf

Summe

addiere 50 auf

Summe

gib Münze zurück

(53)

Mehrfachauswahl in Java

switch (Ausdruck) {

case Wert1: Anweisung 1 break;

case Wert2: Anweisung 2 break;

...

default: Anweisung n+1;

break;

}

(54)

Java-Code für das Münzbeispiel

switch (coin) {

case 5: sum += 5; break;

case 10: sum += 10; break;

case 25: sum += 25; break;

default: giveBack(coin);

}

...

(55)

Beispiel: Java-Code für Notenzuweisung

switch ( grade ) {

case ‘A’: case ‘a’:

++aCount;

break;

case ‘B’: case ‘b’:

++bCount;

break;

case ‘C’: case ‘c’:

++cCount;

break;

case ‘D’: case ‘d’:

++dCount;

break;

case ‘F’: case ‘f’:

++fCount;

break;

default:

showStatus( “Incorrrect grade.

Try again. “ );

break;

}

(56)

3.4.3 Wiederholungsanweisung (Iteration, Schleife)

Wiederholte Ausführung einer Anweisung (oder einer Folge von Anweisungen), bis eine Endbedingung erfüllt ist.

wiederhole

Anweisung(en) bis Bedingung

(57)

Beispiele für Schleifen

Beispiel 1

Lies den gesuchten Namen ein.

Hole den ersten Namen aus der Liste.

wiederhole

falls Name der Gesuchte ist dann gib die Adresse aus

sonst

hole den nächsten Namen aus der Liste

bis der gesuchte Name gefunden oder die Namensliste erschöpft ist

Beispiel 2

Summe = 0 wiederhole

addiere 1 auf Summe

bis Summe*Summe < sqrNumber

(58)

Flussdiagramm

Anweisung

Abbruchbe - dingung

erfüllt

nicht erfüllt

(59)

Flussdiagramm für die Summation

sum = sum + 1

sum * sum

>=

sqrNumber sum = 0

erfüllt

nicht erfüllt

(60)

Struktogramm für die Schleife

Abbruchbedingung

(61)

Struktogramm für die Summation

sum * sum >= sqrNumber

sum = 0

sum = sum + 1

(62)

Pseudocode für Quadratzahlen

{Berechne die Quadratzahlen von 1-10}

setze Wert auf 0 wiederhole

addiere 1 auf Wert

drucke Wert und Wert*Wert bis Wert = 10

Bemerkung:

Die Abbruchbedingung wird nach der Anweisung geprüft.

(63)

Spezialfall: Endlosschleife (1)

Wiederhole

Anweisung(en) immer

Beispiel:

Wiederhole

Falls Reaktortemperatur zu hoch dann fahre Bremsstäbe ein

immer

Wiederhole

Schalte Ampeln fort immer

(64)

Spezialfall: Endlosschleife (2)

Anmerkung 1

Endlosschleifen sind häufig unbeabsichtigt, weil die Ab- bruchbedingung nicht korrekt formuliert wurde.

Anmerkung 2

Endlose Algorithmen können korrekt sein, wenn sie ein über die Zeit stets wiederkehrendes Problem stets auf's Neue lösen. Sie sind nicht korrekt, wenn sie unendlich lange brauchen, um ein Problem zu lösen.

Beispiel: Newton-Iteration zur Integralberechnung ohne Abbruchschranke

(65)

Spezialfall: Endlosschleife (3)

y

= f(x)

x x x x x x x x

1 3 2

4 5 6 7

Approximation eines Integrals Abbruchbedingung: | - | <Σ Σ ε

neu alt

(66)

Zweite Form der Iteration

Solange Bedingung führe aus

Anweisung(en) // Rumpf der Schleife

Anweisung n + 1 // nicht mehr in der Schleife

Die Anweisungen in der Schleife werden ausgeführt, solange die Bedingung erfüllt ist.

Unterschied zur ersten Form: Die Bedingung wird vor der Ausführung des Schleifenrumpfes geprüft. Deshalb ist diese Form vorzuziehen, wenn damit gerechnet wer- den muss, dass in manchen Fällen bereits beim erstma- ligen Eintritt in die Schleife die Abbruchbedingung erfüllt ist, der Schleifenrumpf also nicht ausgeführt werden soll.

Die beiden Formen der Iteration sind äquivalent, sie las- sen sich (unter Verwendung der bedingten Anweisung) ineinander überführen (⇒ Übungsaufgabe).

(67)

Erste Form vs. zweite Form der Iteration

Beispiel: Suche größte Zahl aus einer Liste Algorithmus 1

Setze die erste Zahl der Liste als bislang größte Zahl

Wiederhole

Lese die nächste Zahl der Liste

Falls diese Zahl > bislang größte Zahl dann setze diese Zahl als bislang größte Zahl

bis Liste erschöpft ist

Schreibe die bislang größte Zahl nieder

Dieser Algorithmus ist inkorrekt, falls eine gegebene Li- ste nur eine Zahl enthält! Daher:

Algorithmus 2

Setze die erste Zahl der Liste als bislang größte Zahl

Solange die Liste nicht erschöpft ist führe aus

Lese die nächste Zahl der Liste

Falls diese Zahl > bislang größte Zahl dann setze diese Zahl als bislang

(68)

Java-Code für die Summation

sum = 0;

do {

sum++;

System.out.println(sum);

} while (sum * sum < 10);

sum = 0;

while (sum * sum < 10) { sum++;

System.out.println(sum);

}

(69)

Schleifenbeispiele (1)

yPos = 25;

for (int counter = 1; counter <= 10;

counter++) {

g.drawString(Integer.toString(counter), 25 yPos);

yPos += 15;

}

ist äquivalent zu

for (int counter = 1; counter <= 10;

counter = counter + 1) {

}

und zu

for (int counter = 1; counter <= 10;

counter += 1) {

}

und zu

for ( int counter = 1; counter <= 10;

++counter) {

(70)

Schleifenbeispiele (2)

x = 2;

y = 10;

for (int j = x; j <= 4 * x * y; j += y/x) ist äquivalent zu

for (int j = 2; j <= 80; j += 5)

(71)

Schleifenbeispiele (3)

for ( int i = 1; i <= 100; i++ ) for ( int i = 100; i >= 1; i-- ) for ( int i = 7; i <= 77; i += 7 ) for ( int i = 20; i >= 2; i -= 2 ) for ( int j = 2; j <= 20; j += 3 ) for ( int j = 99; j >= 0; j -= 11 ) for ( int number = 2; number <= 100;

sum += number, number += 2 ) for ( int sum = 0, number = 2;

number <= 100;

sum += number, number += 2 )

(72)

Äquivalenz von

for-

und

while-

Schleifen

for ( Ausdruck1; Ausdruck2; Ausdruck3 )

Anweisung ist äquivalent zu

=> Übungsaufgabe!

(73)

Java-Code für Grafikbeispiel

import java.awt.Graphics;

import java.applet.Applet;

public class ContinueTest extends Applet { public void paint(Graphics g)

{

int xPos = 25;

for ( int count = 1; count <= 10;

count++ ) {

if ( count == 5 ) { continue;

}

g.drawString(Integer.toString(count), xPos, 25);

xPos += 10;

} } }

Resultat

1 2 3 4 6 7 8 9 10

Die Anweisung continue dient dazu, das Ausdrucken der 5 zu unterbinden.

(74)

Zusammenfassung der Ablaufsteuerung (Kontrollstrukturen)

a) Sequenz (Folge) b) Selektion

c) Iteration

Diese drei Konstrukte genügen, um jeden erdenklichen Algorithmus auszudrücken!

(Beweis: Theoretische Informatik)

(75)

3.5 Modularität

3.5.1 Das Konzept der Modularität

Frage

Ist es möglich, Teile aus einem Algorithmus an anderer Stelle, in demselben oder in einem anderen Algorith- mus, wieder zu verwenden?

Ist es möglich, Teile aus dem Algorithmus so voneinan- der abzugrenzen, dass sie unabhängig voneinander entwickelt (verfeinert) werden können?

⇒ Modularität

Ein Modul ist ein Teilalgorithmus, der ein Teilproblem selbständig löst, ohne auf außerhalb liegende Schritte des Hauptalgorithmus Bezug zu nehmen. Module sind die Bausteine von Algorithmen.

Die Wirkung eines Moduls wird über Parameter gesteu- ert. Beispiele für Module sind Unterprogramme (Proze- duren, Funktionen) in traditionellen Programmierspra- chen und Klassen in Java.

(76)

Beispiele für Module (1)

Beispiel

Algorithmus "koche Kaffee" und Algorithmus "koche

Tee" benutzen beide das Modul "koche Wasser" mit den Schritten

{ koche_Wasser }

(1.1.1.) Stelle Kessel unter Wasserhahn (1.1.2.) Drehe Wasserhahn auf

(1.1.3.) Warte, bis Kessel voll ist (1.1.4.) Drehe Wasserhahn zu (1.2.) Schalte Kessel an

(1.3.) Warte, bis Kessel pfeift (1.4.) Schalte Kessel aus

(77)

Beispiele für Module (2)

{Algorithmus zur Zubereitung einer Tasse Kaffee}

(1.) rufe Modul koche_Wasser auf {Gib Kaffeepulver in die Tasse}

(2.1.1.) Nehme Kaffeeglas aus dem Fach (2.1.2.) Entferne Deckel vom Kaffeeglas (2.2.) Entnehme einen Löffel Kaffee (2.3.) Kippe Löffel in die Tasse

(2.4.1.) Setze Deckel auf das Kaffeeglas (2.4.2.) Stelle Kaffeeglas in das Fach zurück {Fülle Wasser in die Tasse}

(3.1. Gieße Wasser aus dem Kessel in die Tasse, bis die Tasse voll ist

{Algorithmus zur Zubereitung einer Tasse Tee}

(1.) rufe Modul koche_Wasser auf {Gib den Teebeutel in die Tasse}

(2.1) Öffne die Packung mit den Teebeuteln (2.2) Entnimm einen Teebeutel

(2.3) Lege den Teebeutel in die Tasse {Fülle Wasser in die Tasse}

(3.1.) Gieße Wasser aus dem Kessel in die Tasse, bis die Tasse voll ist

(78)

Beispiele für Module (3)

Algorithmus "Zeichne zwei Quadrate"

Positionieren auf Punkt A

Quadrat der Seitenlänge 10 cm zeichnen Positionieren auf Punkt B

Quadrat der Seitenlänge 20 cm zeichnen

B

A

20 cm 10 cm x

(79)

Definition des Moduls

Definiere das Zeichnen eines Quadrates als Modul mit dem Parameter "Seitenlänge".

Annahme: Der Plotter kann die folgenden Befehle un- mittelbar ausführen:

bewege (x) bewege Stift x cm vorwärts

links (x) drehe Stift um x Grad nach links rechts (x) drehe Stift um x Grad nach rechts Stift heben hebe den Stift vom Papier

Stift senken senke den Stift auf das Papier.

Dann kann das Modul "Quadratzeichnen" wie folgt defi- niert werden:

Modul Quadratzeichnen (Größe)

{Zeichnet ein Quadrat mit der Seitenlänge Größe cm.

Das Quadrat wird gegen den Uhrzeigersinn gezeichnet, beginnend mit der aktuellen Position des Zeichenstiftes.

Die erste Kante wird entsprechend der aktuellen Aus- richtung des Zeichenstiftes gezeichnet. Der Zeichenstift wird auf den Ausgangspunkt zurückgesetzt und abge- hoben.}

Stift senken

Wiederhole 4-mal Bewege (Größe) Links (90)

Stift heben

(80)

Parameter

Bei der Definition eines Moduls: formale Parameter Beim Aufruf eines Moduls: aktuelle Parameter

Anzahl, Reihenfolge und Datentyp müssen überein- stimmen.

Damit kann der Algorithmus "Zeichne zwei Quadrate"

geschrieben werden als:

(1) Positioniere Stift auf Punkt A (2) Quadratzeichnen (10)

(3) Positioniere Stift auf Punkt B (4) Quadratzeichnen (20)

(81)

Modulares Zeichenprogramm

Programmablauf

B

A A A

nach (1)

nach (2)

nach (3)

nach (4)

A B

(82)

Dokumentieren von Modulen

Neben den explizit angegebenen Parametern sollten die impliziten Annahmen, die ein Modul macht, sorgfältig dokumentiert sein! Beispiel: Zeichenrichtung der ersten Kante. Am besten als Kommentar im Kopf (Header) des Moduls. Ebenso müssen Nebeneffekte nach Möglichkeit vermieden oder sorgfältig dokumentiert sein.

Die Schritte (1) und (3) können im Zuge der schrittwei- sen Verfeinerung ebenfalls als Module beschrieben oder direkt in Befehle an den Stift des Plotters aufgelöst wer- den.

(83)

3.5.2 Modularität in Java: Klassen und Objekte

Mit einer Klassendeklaration definiert man neue Refe- renztypen und legt gleichzeitig deren Implementation fest. Jede Klasse (außer Object) ist implizit Subklasse der Java-Klasse Object; es gibt also eine gemeinsame Wurzel der Java-Vererbungshierachie. Im Rumpf einer Klasse kann man folgendes deklarieren:

Variablen eines beliebigen Typs, in denen man Ob- jektzustände speichert,

Methoden, das sind die Operationen, die auf Objekte der Klasse angewendet werden können, die also das Objektverhalten implementieren,

Konstruktoren, das sind spezielle Methoden, mit de- nen man die Variablen initialisiert,

Static-Initialisierer, das sind spezielle Anwei-

sungsfolgen, die nach dem Laden der Klasse (eben- falls zum Zweck der Initialisierung) einmal ausgeführt werden, und

eingebettete Klassen, das sind Klassen, die wieder einen neuen Referenztyp deklarieren; meistens han- delt es sich dabei um Hilfsklassen, die man nur lokal benötigt.

(84)

Klassenvariable vs. Instanzvariable

Variablen können als Klassenvariablen deklariert wer- den, die Java einmal pro Klasse anlegt, oder als In- stanzvariablen, die für jedes Objekt neu angelegt wer- den.

Ebenso ist es möglich, eine Methode als Klassenme- thode zu deklarieren, die ohne Objekt aufgerufen wird, oder als Instanzmethode, die immer für ein bestimmtes Objekt aufgerufen wird. Der Unterschied wird durch die Verwendung des Modifizierers static ausgedrückt.

Konstruktoren sind Methoden sehr ähnlich, werden aber nicht mit der für Methoden üblichen Aufrufsyntax aufgerufen.

(85)

Klassendeklaration

Eine Klassendeklaration spezifiziert einen neuen Refe- renztypen. Die Deklaration kann aus bis zu sechs Teilen bestehen:

optionalen Modifizierern, wie public, abstract, final, usw., die spezielle Attribute der Klasse (z.B.

Zugriffsrechte) festlegen,

dem Schlüsselwort class,

einem Bezeichner, mit dem die Klasse benannt wird,

einem optionalen extends mit nachfolgender Angabe einer Superklasse,

einem optionalen implements mit nachfolgender Li- ste von Interfaces, und

dem Klassenrumpf, der – in { ... } eingeschlossen – die Deklaration einer beliebigen Anzahl von Variablen, Methoden, Konstruktoren, Initialisierern und einge- betteten Klassen enthält.

(86)

Beispiel für eine Klassendeklaration

Beispielsweise wird mit der folgenden Klassendeklarati- on eine Klasse Timer deklariert, die drei Variablen min, sek und tsdSek enthält. Weiterhin sind drei Methoden stelle, tick und zeigeAn deklariert. Sämtliche oben als optional genannten Bestandteile fehlen.

class Timer {

int min, sek, tsdSek;

void stelle(int i, int j, int k) { min = i;

sek = j;

tsdSek = k;

}

void tick() {

if (++tsdSek == 1000) { tsdSek = 0;

if (++sek == 60) { sek = 0;

min++;

} } }

(87)

Geltungsbereich von Klassenelementen

Der Geltungsbereich des Namens eines Klassenele- ments – der Code, in dem man das Element einfach durch seinen Bezeichner ansprechen kann – ist der gesamte Rumpf der Klassendeklaration. Man kann die einzelnen Deklarationen der Klassenelemente daher im Rumpf beliebig anordnen.

Nach der Deklaration der Timer-Klasse ist es möglich, ihren Namen zur Deklaration von Variablen wie jeden anderen Typnamen (zum Beispiel int, double) zu be- nutzen. Es muß aber beachtet werden, dass es sich um einen Referenztyp handelt, dass also mittels

Timer t;

kein Timer-Objekt erzeugt wird, sondern, daß t lediglich eine Variable ist, die eine Referenz auf ein solches Ob- jekt enthalten kann. Ein Objekt wird erst durch explizites new in einem „Instanzerzeugungs-Ausdruck“, z.B.

t = new Timer();

erzeugt. Nach der Objekterzeugung sind die Instanz- und Klassenvariablen mit Standardwerten initialisiert. Im Beispiel haben alle drei Variablen min, sek und

tsdSek den Wert 0.

(88)

Zugriff auf Klassenelemente

Innerhalb der Klassendeklaration kann man auf die Klassenelemente mit ihrem Bezeichner zugreifen. Im Timer-Beispiel wird in allen drei Methoden auf alle In- stanzvariablen zugegriffen.

Auch das Aufrufen einer Methode innerhalb einer ande- ren Methode ist möglich:

class X { void g() { ...

f();

}

void f() { ... } }

(89)

Beispiel für den Zugriff auf Klassenelemente

Code in anderen Klassen greift auf Instanzvariablen und Instanzmethoden mit einer Objektreferenz unter Ver- wendung von "." zu (als Interpunktionszeichen, das in diesem Kontext auch als Zugriffs-„Operator“ bezeichnet wird). Zum Beispiel:

Timer t1, t2

t1 = new Timer();

t2 = new Timer();

t1.min =10;

t2.sek = 23;

t1.tsdSek = 5;

t2.stelle(10,23,5);

for (int i = 0; i < 1500; i++) t2.tick();

t1 10 23 5

min sek tsdSek

t2 10 24 505

min sek tsdSek

(90)

Zugriffsrechte auf Klassenelemente von außer- halb

Ob man, wie im letzten Beispiel, auf die Klassenele-

mente von außerhalb ihrer Klasse zugreifen kann, hängt davon ab, welche Zugriffsrechte vergeben wurden und von wo aus der Zugriff erfolgt. Zur expliziten Festlegung von Zugriffsrechten verwendet man die Modifizierer pu- blic, protected oder private. Üblicherweise spezi- fiziert man Instanzvariablen als private, um die in ih- nen gespeicherten Werte oder die von ihren referen- zierten Objekte zu „kapseln“, also:

Class Timer {

private int min, sek, tsdSek;

...

}

Auf die Variablen kann dann nur noch von Code inner- halb der Timer-Deklaration zugegriffen werden, und im Beispiel müssen die drei Zuweisungen durch einen Auf- ruf t1.stelle(10, 23, 5); ersetzt werden.

(91)

Verdeckung von Namen

Der Name einer Instanzvariablen kann durch eine lokale Variable oder einen Methoden- oder Konstruktorpara- meter desselben Namens verdeckt werden. Unter Ver- wendung des Schlüsselworts this kann man dann dennoch auf die verdeckte Variable zugreifen. Dies dient nicht der Verständlichkeit des Codes, ist aber bei Konstruktoren gängige Java-Praxis.

Zum Beispiel wird hier der Wert des Parameters i bzw.

der lokalen Variable i an die Instanzvariable i zugewie- sen:

class X {

private int i;

X(int i) { this.i = i;

}

void f() { int i = 10;

this.i += i;

} }

(92)

Instanz- und Klassenvariable

Wie bei lokalen Variablen besteht die Deklaration einer Variablen, die Klassenelement ist, aus optionalen Modi- fizierern, einer Typangabe und einem Bezeichner mit optionalem Initialisierer. Im Unterschied zu lokalen Va- riablen, die mittels Modifizierer final als symbolische Konstante spezifiziert werden können, sind hier auch noch die Modifizierer static und transient sowie einer der Zugriffsrechtspezifizierer public, protected oder private zulässig.

Wenn man eine Variable als static deklariert, wird sie zur Klassenvariablen. Dies bedeutet, daß es genau ei- ne derartige Variable gibt, gleichgültig, wieviele Objekte der Klasse existieren. Klassenvariablen werden beim Laden der Klasse erzeugt.

Instanzvariablen sind Variablen, die nicht als static deklariert wurden. Sie werden für jedes Objekt bei sei- ner Erzeugung angelegt.

(93)

Klassenvariable im Timer-Beispiel

Wir erweitern die Klasse Timer um eine Klassenvaria- ble anzTicks, die immer die Anzahl der tick-Aufrufe enthalten soll:

class StatTimer {

static long anzTicks = 0;

private int min, sek, tsdSek;

void stelle(int i, int j, int k,) { .... unverändert }

void tick(){

anzTicks++;

... Rest unverändert }

void zeigeAn() { ... unverändert } }

Wir sehen, dass innerhalb der Klassendeklaration auf eine Klassenvariable wie auf eine Instanzvariable ein- fach mit ihrem Bezeichner zugegriffen werden kann.

(94)

Zugriff auf Klassenvariable

Am folgenden Beispiel erkennt man weiterhin, dass von außerhalb der Klasse mittels einer Objektreferenz oder sinnvoller, weil eine Klassenvariable unabhängig von Objekten existiert, auch mit dem Klassennamen an- stelle einer Objektreferenz zugegriffen werden kann:

// Zugriff ohne Objektreferenz

out.println("Anzahl ticks: " + StatTimer.anzTicks);

StatTimer s1 = new StatTimer(), s2 = new StatTimer();

for (int i = 0; i < 21500; i++) s1.tick();

for (int i = 0; i < 821500; i++) s2.tick();

// Zugriff mit Objektreferenz

out.println("Anzahl ticks: " + StatTimer.anzTicks + "\t" + s1.anzTicks + "\t" + s2.anzTicks);

(95)

Zugriff auf Klassenvariable per Objektreferenz

Programmierhinweis

Der Zugriff auf eine Klassenvariable über eine Objekt- referent ist zwar erlaubt, aber nicht zu empfehlen, da ir- reführend!

Nach Bearbeitung des Beispiels von der vorigen Seite finden wir im Speicher:

s1 0 21 500

min sek tsdSek

s2 13 41 500

min sek tsdSek

843000

StatTimer.anzTicks

(96)

Die Initialisierung von Variablen

Eine Variablendeklaration kann einen Initialisierer ent- halten. Ein Initialisierer ist ein Ausdruck oder – bei

Feldtypen – eine Initialisiererliste. Er folgt nach dem Va- riablenbezeichner und dem Zuweisungsoperator =.

Semantisch wird die Initialisierung wie die Zuweisung des Werts des Ausdrucks an die deklarierte Variable bzw. – bei Feldtypen – der Werte der Elemente der In- itialisiererliste an die Feldkomponenten behandelt. In- itialisiererwerte müssen zuweisbar an die deklarierten Variablen sein. Die Initialisierung von Klassenvariablen (also von als static deklarierten Variablen) wird genau einmal, beim Initialisieren der Klasse nach dem Laden, vorgenommen. Instanzvariablen (also Variablen, die nicht als static deklariert sind) werden jedesmal,

wenn ein Objekt der Klasse dynamisch erzeugt wird, in- itialisiert.

Sind keine Initialisierer angegeben, werden die Stan- dardwerte für den jeweioigen Typ benutzt. Im StatTimer- Beispiel hätten wir also einfach static long anzTicks;

schreiben können, ohne daß sich dadurch etwas ändern würde.

(97)

Konstante Klassenelemente

Instanz- und Klassenvariablen können mit dem final- Modifizierer als symbolische Konstanten deklariert werden. In diesem Fall sollte die Deklaration einen In- itialisierer enthalten. Alternativ kann für als final de- klarierte Instanzvariablen in jedem Konstruktor bzw. für als final deklarierte Klassenvariablen in einem sta- tic-Initialisierer genau eine Zuweisung vorgenommen werden. Im letzteren Fall nennt man die Variable wieder

„unspezifiziert final“. Der Wert der final-

“Variablen“ kann nach ihrer Initialisierung bzw. der er- sten Zuweisung nicht mehr verändert werden.

Beispiel

class Rechnung {

static long nr = 0;

final long nummer = ++nr;

final Date datum = new Date();

...

}

Hier sind die Nummer der Rechnung und das Rech- nungsdatum als konstante Instanzvariablen deklariert, die bei der Erzeugung eines Rechnung-Objekts mit der nächsten Nummer bzw. dem aktuellen Datum initialisiert werden.

(98)

final

-Variable vom Referenztyp

Es ist zu beachten, dass eine final-Variable eines Referenztyps – wie oben die Instanzvariable datum – immer dasselbe Objekt referenziert, dass es aber mög- lich ist, das Objekt durch Methodenaufrufe zu verän- dern.

Da in Java Felder (arrays) Objekte sind, trifft dies auch auf Felder zu. Wenn eine final-Variable ein Feld refe- renziert, können die Feldkomponenten durch Operatio- nen auf dem Feld modifiziert werden; die Variable ver- weist aber immer auf dasselbe Feld.

(99)

Klassenspezifische Konstanten

In vielen Fällen wird man keine objektspezifischen, son- dern klassenspezifische Konstanten benötigen. Dann verwendet man die Modifizierer static und final gemeinsam. Die übliche Reihenfolge bei der Benutzung mehrerer Modifizierer ist public static final bzw.

private static final.

(100)

Methoden

Java-Methoden können nur innerhalb von Klassen- rümpfen deklariert werden. Sie spezifizieren ausführba- ren Code, der durch einen Methodenaufruf ausgeführt wird, bei dem eine feste Anzahl von Werten als Argu- mente übergeben werden. Eine Methodendeklaration besteht aus bis zu sechs Teilen:

optionalen Modifizierern, wie public, abstract, final, usw., die wie gewohnt spezielle Attribute der Methode festlegen,

dem Typ des Resultats, das bei einem Methodenauf- ruf berechnet wird,

der Liste der Methodenparameter, mit denen die Werte, die bei einem Aufruf zu übergeben sind, spezi- fiziert werden,

einer optionalen throws-Klausel, die anzeigt, wel- che Ausnahmen durch einen Aufruf der Methode aus- geworfen werden können, und

den Methodenrumpf, der – in { ... } eingeschlossen – die Folge der Anweisungen enthält.

(101)

Rückgabe von Resultaten

Als Typ des Resultats ist jeder Java-Typ zulässig. Oder es kann mittels void angezeigt werden, dass die Me- thode keinen Wert liefert.

Bei Resultaten eines Feldtyps sind auch Schreibweisen wie int f()[] { ...} als Äquivalent für int [] f() { ... } zuläs- sig.

Die formalen Parameter einer Methode werden (falls vorhanden) durch eine Liste von Typen und Bezeich- nern spezifiziert, die durch Kommas getrennt sind. Die Parameter können als final deklariert werden; dann ist jede Zuweisung an sie im Methodenrumpf ein Fehler.

(102)

Methodenaufruf

Ein Methodenaufruf ist, syntaktisch gesehen, ein ele- mentarer Ausdruck. Durch Abschluss mit einem ";"

wird der Aufruf zu einer Anweisung (einer Ausdruck- sanweisung).

Bei jedem Aufruf einer Methode werden die Parameter neu erzeugt und mit den aktuellen Werten initialisiert, bevor mit der Ausführung des Methodenrumpfes be- gonnen wird. Die Parameter haben als Geltungsbereich den gesamten Methodenrumpf und werden ansonsten wie lokale Variable behandelt; sie dürfe nicht durch Re- Deklarationen verdeckt werden.

Die Argumente werden als Werte übergeben und können daher durch den Aufruf nicht modifiziert werden; Java kennt nur einen „call by value“.

(103)

Auswirkungen außerhalb der Methode selbst

Ob die Veränderung eines Parameters innerhalb des Methodenrumpfes sich auf die aufrufende Methode

auswirkt, hängt davon ab, ob es sich bei seinem Typ um einen elementaren Typ oder einen Referenztyp han- delt.

(104)

Beispiel: Parameter vom Referenztyp

// Parameter.java import java.io.*;

class Parameter {

static void m(int i, Zaehler z) { i++;

z.inkrementiere();

}

public static void main(String[] args) { PrintWriter out = new

PrintWriter(System.out,true);

int a = 2;

Zaehler b = new Zaehler();

b.wert(2);

out.println(a + " " + b.wert());

m(a,b);

out.println(a + " " + b.wert());

}

}

Beim Aufruf von m werde die Parameter i und z er-

zeugt, in i wird der Wert von a, also 2, in z wird die Re- ferenz auf das Zaehler-Objekt b (die Adresse dieses Objekts) kopiert. Das Inkrementieren von i ändert ledig- lich den Parameterwert. Dagegen verweisen der Para-

(105)

Speicherinhalt zum Referenztypbeispiel

b a

i z

2

2 Wert

2

(106)

Die

return

-Anweisung

Eine return-Anweisung beendet die Ausführung einer Methode bzw. eines Konstruktors und springt zu der auf- rufenden Methode bzw. zum aufrufenden Konstruktor zurück. Sie hat die Form

return Ausdruckopt ;

In der Form return; (also ohne Asudruck) darf sie nur in Methoden vorkommen, die mit void als Ergebnistyp deklariert wurden, oder in Konstruktoren. Der Metho- denaufruf liefert dann keinen Wert zurück. Falls eine Methode mit Ergebnistyp void keine return-

Anweisung enthält, fügt der Java-Compiler implizit als letzte Anweisung ein return; ein.

Eine return-Anweisung mit Ausdruck darf nur in einer Methode stehen, die nicht mit void als Typ des Ergeb- nisses deklariert ist. Eine Methode mit einem Ergeb- nistyp ungleich void muss mit einem expliziten return und der Rückgabe eines Werts beendet werden. Bei ei- nem return mit Ausdruck muss der Typ des Ausdrucks zuweisungskompatibel zum Ergebnistyp der Methode

(107)

Klassenmethoden

So wie es instanz- und klassenspezifische Variable gibt, gibt es auch instanz- und klassenspezifische Methoden.

Klassenspezifische Methoden werden mit static de- klariert. Die bedeutet, dass die Methode nicht für ein be- stimmtes Objekt, sondern unabhängig von Objektin-

stanzen für die Klasse aufgerufen wird. Wie bei Klas- senvariablen benutzt man dann den Klassennamen an- stelle eines Objektnamens (Instanznamens).

(108)

Restriktionen für Klassenmethoden (1)

Für Klassenmethoden gibt es eine Reihen von Restrik- tionen. So können sie nur auf Klassenvariablen ihrer Klasse zugreifen und dürfen das Schlüsselwort this nicht verwenden.

Beispiel

class X{

int x;

static int y;

static void f() {x++}; // Fehler: x

// ist nicht static static void g( {y++ } // korrekt: y

// ist static ....

}

Der Grund für diese Einschränkung ist offensichtlich:

Wenn f nicht für ein bestimmtes Objekt aufgerufen wird, ist this undefiniert. In unserem Beispiel wäre bei der Existenz von mehreren Instanzen nicht klar, in welcher Instanz die Instanzvariable x inkrementiert werden soll.

(109)

Restriktionen für Klassenmethoden (2)

Klassenmethoden können nur andere Klassenmethoden ihrer Klasse aufrufen.

Beispiel

class X{

int x;

static void f1() {g()} // korrekt:

// g ist static static void f2() {h()} // Fehler:

// h ist nicht static static void g(...}

....

}

Hier setzt Java den Aufruf von g wie X.g(); um. Der Aufruf von h scheitert hier, weil er eine Objektreferenz benötigt, über die eine static-Methode aber nicht verfügt.

Klassenmethoden können darüber hinaus nicht als ab- stract deklariert werden.

(110)

Beispiele für vordefinierte Klassenmethoden

Ein Beispiel für eine Klasse, die nur static-Variable und Methoden deklariert, ist Math. Wir haben u.a. die Konstante PI und die Methoden random und sqrt die- ser Klasse benutzt. Aus der Klasse Integer haben wir die Klassenmethoden parseInt und toHexString aufgerufen.

in und out sind Klassenvariablen der Klasse System, die wir wiederholt bei der Deklaration von Ein- bzw.

Ausgabeobjekten verwendet haben. Beide haben einen Referenztyp: in hat den Typ InputStream, out den Typ PrintStream.

Schließlich ist main die Klassenmethode, die von der Java-VM für die initiale Klasse aufgerufen wird, mit der also stets die Programmausführung beginnt.

(111)

Erzeugen und Vernichten von Objekten

Objekte werden in Java immer dynamisch, also zur

Laufzeit im Hauptspeicher erzeugt. Um die Freigabe von Speicher müssen wir uns dennoch nicht kümmern. Dies ist die Aufgabe des Garbage-Collectors, Er sorgt dafür, dass nicht mehr referenzierte Objekte automatisch im Speicher gelöscht werden.

Objekte werden typischerweise mit dem Schlüsselwort new in einem elementaren Ausdruck erzeugt. Diesen Weg haben wir in allen bisher betrachteten Beispielen gewählt und z.B. z = new Z(); geschrieben.

Es ist auch möglich, die völlig analog arbeitende Metho- de newInstance zusammen mit dem entsprechenden Klassenliteral einzusetzen, und statt dessen die Form

z = (Z)Z.class.newInstance();

zu benutzen.

Diese beiden Vorgehensweisen werden als explizite Objekterzeugung bezeichnet.

(112)

Implizite Erzeugung von Objekten

Darüber hinaus können Objekte implizit erzeugt wer- den, beispielsweise String-Objekte zur Aufnahme der Werte von Zeichenketten oder im Zusammenhang mit der Auswertung des Operators +, Felder, wenn eine In- itialisiererliste angegeben ist, beliebige Objekt, wenn wir clone aufrufen, usw.

In allen Fällen wird nach der Reservierung des benötig- ten Speicherplatzes und dem Eintragen der Standard- werte in die Instanzvariablen ein Konstruktor der ent- sprechenden Klasse aufgerufen. Dies ist eine spezielle Methode, die den Namen der Klasse trägt und keinen Ergebnistyp deklariert.

(113)

Beispiel für einen Konstruktor

Wir versehen die Klasse Timer mit zwei Konstruktoren:

class Timer {

private int min, sek, tsdSek;

Timer() {

min = sek = tsdSek = 0;

}

Timer(int min, int sek, int tsdSek) { this.min = min;

this.sek = sek;

this.tsdSek = tsdSek;

}

... sonst alles unverändert }

Nun wird bei einer Anwendung der Art Timer t1 = new Timer(),

t2 = new Timer (10, 23, 5);

zur Erzeugung des ersten, von t1 referenzierten Ob- jekts der erste Konsruktor Timer() aufgerufen, das zweite Objekt wird mittels Timer(int, int, int) konstruiert.

(114)

Der Standardkonstruktor

Wenn wir eine Klasse X ohne Konstruktor deklarieren, erzeugt Java einen Standardkonstruktor, der die Form X() { super(), }

hat. Sofern wir mindestens einen Konstruktor imple- mentieren, entfällt diese implizite Deklaration. Auch ein von uns deklarierter Konstruktor ohne Parameterliste, wie oben der erste Timer-Konstruktor, wird oft als Standardkonstruktor bezeichnet.

(115)

Initialisierungsblöcke

Es ist möglich, innerhalb einer Klasse Initialisierungs- blöcke zu deklarieren. Das sind Codeblöcke, die auf das Schlüsselwort static folgen:

static Block;

Initialisierungsblöcke werden genau einmal ausgeführt, wenn die Klasse geladen wird.

Die Initialisierung von Klassenvariablen und das Aus- werten von static-Initialisierungsblöcken wird in der Reihenfolge vorgenommen, in der sie in der Klassende- klaration aufeinander folgen. Man kann static-

Initialisierungsblöcke beispielsweise dazu verwenden, Klassenvariable, die Felder, Mengen oder Listen sind, mit Anfangswerten zu versehen. Auch als unspezifiziert final deklarierte Klassenvariablen müssen auf diese Weise initialisiert werden.

Referenzen

ÄHNLICHE DOKUMENTE

gesehen. Es hat, wie das Mobilfunkgerät und wie vielleicht die Uhr zuvor, unser Leben ganz entscheidend verändert. Die beiden Gedichte von M1a können als Einstieg in die

er selbst, mit Erfahrungen aus der Praxis, vom Lande und vertraut er sich diesen Monographien iiber Hacke, Haken, Pflug an, so wird er wahrscheinlich bald be merken, dafi

◮ Branch-and-Bound: Wenn man weiß, dass eine Lösung mit Kosten k existiert (zum Beispiel durch obigen Algorithmus), dann kann ein Backtrack-Algorithmus alle Teillösungen.

In jedem Fall war es nicht allein Josephus’ Idee, zwischen Worten und Taten als Modi der Bildungsvermittlung zu unterscheiden: Fast zeitgleich findet sich bei dem T heoretiker

Die Privatversicherten und alle Gruppen der Ärzteschaft in Praxis und Klinik sollten ganz klar erkennen, welche Auswirkungen der von der Bundesregierung geplante Erlaß einer

Im Zuge der kontinuierlichen Intema- tionalisierung unserer Gesellschaft, wie Diskussionen um EG-Beitritt, intema- tionale Forschungsprogramme, Kabel- fernsehen, etc.. zeigen,

Einstieg Durch eine Textarbeit und Internetrecherche zur Hyperinl ation im Jahr 1923 wird das Vorwissen der Schüler mittels eines konkreten Beispiels aktiviert. Die Schüler

Unter einer Funktio- nallösung wäre zu verstehen, dass für einen definierten Betreu- ungsbereich sowohl die Notarz- teinsätze, wie auch die Dringlich- keitseinsätze von einem Notarzt