Kapitel 3 Imperative Programmierung in Java

Werbung
Kapitel 3
Imperative Programmierung in
Java
Im vorigen Kapitel haben wir generelle Eigenschaften der imperativen
wie der objektorientierten Programmierung kennengelernt. Auch Teile
der Syntax der objektorientierten Anteile von Java wurden vorgestellt.
Dieses Kapitel wird sich nun mit der Syntax und Semantik der imperativen Anteile von Java beschäftigen, d.h. im wesentlichen mit den
Rümpfen von Methoden. Weder Syntax noch Semantik werden allerdings formal eingeführt, wie wir es in Kapitel 1 kennengelernt haben,
sondern im wesentlichen anhand von Beispielen. Zunächst wird die
Semantik von Mini-Java erläutert, dessen Syntax wir bereits in Kapitel 1 definiert haben. Danach werden die wichtigsten der noch fehlenden Java-Konstrukte vorgestellt. Eine komplette Syntaxbeschreibung
von Java findet sich auf der folgenden SUN-Webseite:
http://java.sun.com/docs/books/jls/second edition/html/syntax.doc.html
3.1
Mini-Java
Ein Mini-Java Programm besteht aus genau einer Klasse. In dieser Klasse gibt
es genau eine main-Methode. Folgende Konstrukte sind Anweisungen (statements
gemäß Mini-Java-Syntax, vgl. Beispiel 1.2.2):
1. Die Deklaration einer Variablen vom Typ int mit sofortiger Initialisierung:
int ident = expression;
Jeder Bezeichner (ident) darf in höchstens einer Variablendeklaration vorkommen. Diese kontextsensitive Bedingung lässt sich nicht in der EBNFDefinition formulieren.
2. Die Zuweisung eines Wertes an eine Variable:
29
ident = expression;
Diese Variable muss vorher deklariert worden sein und den gleichen Typ
wie der Ausdruck haben. Diese Nebenbedingung ist ebenfalls nicht in der
EBNF-Definition ausgedrückt.
3. Eine bedingte Anweisung (if-then Anweisung):
if(condition) statement
Der Bool’sche Ausdruck (condition) wird ausgewertet; ist er true, so wird
die Anweisung (statement) ausgeführt. Ist er false, so wird die Anweisung
nicht ausgeführt und die Programmausführung mit der nächsten Anweisung
hinter der if-then Anweisung fortgesetzt.
4. Eine abweisende Schleife (while-Schleife):
while(condition) statement
Der Bool’sche Ausdruck wird ausgewertet; ist er true, so wird die Anweisung so lange ausgeführt, bis der Bool’sche Ausdruck false wird.
5. Ein Block.
{ statement1 ; statement2 ; . . . }
Die Statements in der geschweiften Klammer werden von links nach rechts
nacheinander abgearbeitet.
6. Eine Anweisung zum Schreiben auf der Standardausgabe:
System.out.println(...);
System ist eine Klasse, die klassenbezogene Methoden zur Darstellung des
Zustandes des Systems bereitstellt. out ist eine Klassenvariable der Klasse
System, ihr Inhalt ist der Standardausgabestrom. Die Methode println
wird also auf das klassenbezogene Datenfeld out angewendet – es wird ein
String mit abschließendem Zeilenvorschub auf dem Standardausgabestrom
ausgegeben.
7. Die leere Anweisung.
;
Es geschieht nichts.
8. Die Deklaration eines eindimensionalen Feldes (Arrays) mit sofortiger Initialisierung:
int[] array = new int[3];
deklariert ein Feld namens array, erzeugt ein Feld mit drei int-Komponenten
und weist dieses der Feldvariablen array zu. Beachte, dass die Dimension
30
(3) einer Feldvariablen nicht bei der Deklaration (int[] array) angegeben
wird, sondern nur bei der Erzeugung (new int[3]). Die erste Komponente
eines Feldes hat den Index 0. Die Länge des Feldes kann aus dessen Datenfeld length ausgelesen werden (array.length).
9. Die Zuweisung eines Wertes an die i-te Komponente eines Feldes, wobei
0 ≤ i ≤ array.length−1:
array[i ] = expression;
Alle weiteren Konstrukte haben eine offensichtliche Bedeutung, bis auf
Integer.parseInt(argsIdent[expression])
Betrachten wir hierfür noch einmal die main-Methode: Sie hat als Parameter ein
Feld von Zeichenketten. Diese Zeichenketten sind die Programmargumente und
werden normalerweise vom Anwender beim Programmaufruf eingegeben.
class Echo {
public static void main(String[] args) {
int i = 0;
while(i < args.length) {
System.out.println(args[i]);
i = i+1;
}
}
}
> java Echo 10 2
10
2
>
Um eine Zeichenkette in eine ganze Zahl zu konvertieren, wird die Klassenmethode parseInt der Klasse Integer mit dieser Zeichenkette als Argument aufgerufen. Sie liefert die entsprechende ganze Zahl als Ergebnis zurück bzw. meldet
einen Fehler, falls die Zeichenkette keine ganze Zahl dargestellt hat.
class BadAddOne {
public static void main(String[] args) {
int i = 0;
while(i < args.length) {
int wert = args[i];
wert = wert+1;
System.out.println(wert);
31
i = i+1;
}
}
}
> javac BadAddOne.java
BadAddOne.java:6: Incompatible type for declaration.
Can’t convert java.lang.String to int.
int wert = args[i];
>
Stattdessen muss eine explizite Typkonvertierung stattfinden:
class AddOne {
public static void main(String[] args) {
int i = 0;
while(i < args.length) {
int wert = Integer.parseInt(args[i]);
wert = wert+1;
System.out.println(wert);
i = i+1;
}
}
}
> java AddOne 6 3 20
7
4
21
>
3.2
Von Mini-Java zu Java
Jedes Mini-Java Programm ist ein Java Programm. In diesem Abschnitt werden
die Datentypen und imperativen Konstrukte von Java erläutert, die nicht bereits
in Mini-Java vorhanden sind.
3.2.1
Elementare Datentypen
Unicode: Java, als Sprache für das World Wide Web, benutzt einen 16-Bit Zeichensatz, genannt Unicode. Die ersten 256 Zeichen von Unicode sind identisch mit dem 8-Bit Zeichensatz Latin-1, wobei wiederum die ersten 128
Zeichen von Latin-1 mit dem 7-Bit ASCII Zeichensatz übereinstimmen.
32
Elementare Datentypen und deren Literale:
Typ boolean true und false
Typ int
29 (Dezimalzahl) oder
035 (Oktaldarstellung wegen führender 0) oder
0x1D (Hexadezimaldarstellung wegen führendem 0x) oder
0X1d (Hexadezimaldarstellung wegen führendem 0X)
Typ long
29L (wegen angehängtem l oder L)
Typ short
short i = 29; (Zuweisung, es gibt kein short-Literal)
Typ byte
byte i = 29; (Zuweisung, es gibt kein byte-Literal)
Typ double
18.0 oder 18. oder 1.8e1 oder .18E2
Typ float
18.0f (wegen angehängtem f oder F)
Typ char
’Q’, ’\u0022’, ’\u0b87’
Typ String
"Hallo" (String ist kein elementarer Datentyp; s. Kapitel 4)
Initialbelegungen: Während ihrer Deklaration kann eine Variable wie in MiniJava initialisiert werden.
final double PI = 3.141592654;
float radius = 1.0f;
Sind für Datenfelder einer Klasse keine Anfangswerte angegeben, so belegt
Java sie mit voreingestellten Anfangswerten. Der Anfangswert hängt vom
Typ des Datenfeldes ab:
Feld-Typ
boolean
char
Ganzzahl (byte, short, int, long)
Gleitkommazahl
andere Referenzen
Anfangswert
false
’\u0000’
0
+0.0f oder +0.0d
null
Lokale Variablen in einer Methode (oder einem Konstruktor oder einem
klassenbezogenen Initialisierungsblock) werden von Java nicht mit einem
Anfangswert initialisiert. Vor ihrer ersten Benutzung muss einer lokalen
Variablen ein Wert zugewiesen werden (ein fehlender Anfangswert ist ein
Fehler).
3.2.2
Kommentare
// Kommentar bis zum Ende der Zeile
/* Kommentar
zwischen */
33
Achtung: /* */ können nicht geschachtelt werden!
/* falsch
/* geschachtelter Kommentar */
*/
3.2.3
&&
||
!
Bool’sche Operatoren
logisches und
logisches oder
logisches nicht
Die Auswertung eines Bool’schen Ausdrucks erfolgt von links nach rechts, bis der
Wert eindeutig feststeht. Folgender Ausdruck ist deshalb robust:
if(index>=0 && index<array.length && array[index]!=0) ...
3.2.4
Bitoperatoren
Die Bitoperatoren & (und ) und | (oder ) sind definiert durch:
& 0 1
0 0 0
1 0 1
| 0 1
0 0 1
1 1 1
int-Zahlen werden durch diese Operatoren bitweise behandelt.
Beispiel 3.2.1 Es seien x und y folgendermaßen gewählt: x = 60 (in Binärdarstellung 00111100) und y = 15 (binär: 00001111). In diesem Fall ist x&y = 12
und x|y = 63:
x | y
00111100 (60)
| 00001111 (15)
00111111 (63)
x & y
00111100 (60)
& 00001111 (15)
00001100 (12)
Wenn man 0 als false und 1 als true interpretiert, so entspricht & dem logischen
und (&&) und | dem logischen oder (||).
3.2.5
Inkrement und Dekrement
Man kann den Wert einer Variablen x (nicht den eines Ausdrucks) durch den Operator ++ um 1 erhöhen bzw. durch - um 1 erniedrigen. Es gibt Präfix- und Postfixschreibweisen, die unterschiedliche Wirkungen haben: Bei der Präfixschreibweise
wird der Wert zuerst modifiziert und danach der veränderte Wert zurückgeliefert.
Bei der Postfixschreibweise wird zuerst der Wert der Variablen zurückgeliefert,
dann wird sie modifiziert.
34
int i = 10;
int j = i++;
System.out.println(j);
int i = 10;
int j = ++i;
System.out.println(j);
>
10
>
11
Der Ausdruck i++ ist gleichbedeutend mit i = i+1, jedoch wird i nur einmal
ausgewertet!
Beispiel 3.2.2
(A)
arr[where()]++;
Die Methode where() wird einmal aufgerufen.
(B)
arr[where()] = arr[where()]+1;
Hierbei wird die Methode where() jedoch zweimal aufgerufen.
Seiteneffekte können hier sogar das Ergebnis beeinflussen: In dem Kontext arr[0]
= 0; arr[1] = 1; arr[2] = 2; und
private static int zaehler = 0;
private static int where() {
zaehler = zaehler+1;
return zaehler;
}
liefert (A) arr[1] = 2 bzw. (B) arr[1] = 3.
3.2.6
Zuweisungsoperatoren
i += 2; ist gleichbedeutend mit i = i+2; außer, dass der Ausdruck auf der
linken Seite von i += 2; nur einmal ausgewertet wird (vgl. Inkrement und Dekrement).
Entsprechend sind -=, &= und |= definiert.
3.2.7
Die nichtabweisende Schleife
Zusätzlich zur abweisenden Schleife gibt es eine nichtabweisende Schleife in Java:
do
statement
while(condition);
Die condition wird erst nach der Ausführung von statement ausgewertet. Solange
sie true ist, wird statement wiederholt.
35
3.2.8
for-Schleife
for(init-statement; condition; increment-statement)
statement
ist gleichbedeutend mit (mit Ausnahme vom Verhalten bei continue):
{
init-statement
while(condition) {
statement
increment-statement
}
}
Übliche Verwendung der for-Schleife:
for(int i=0; i<=10; i++) {
System.out.println(i);
}
Der Gültigkeitsbereich der (Lauf-)Variablen i beschränkt sich auf die for-Schleife!
int i = 0;
for(int i=0; i<=10; i++) {
System.out.println(i);
}
ist jedoch nicht möglich, da die Variable i vorher schon deklariert wurde.
Die Initialisierungs- bzw. Inkrementanweisung einer for-Schleife kann eine durch
Kommata getrennte Liste von Ausdrücken sein. Diese werden von links nach
rechts ausgewertet.
Beispiel 3.2.3 (Arnold & Gosling [1], S. 144)
public static int zehnerPotenz(int wert) {
int exp, v;
for(exp=0,v=wert; v>0; exp++, v=v/10)
; // leere Anweisung
return exp;
}
Alle Ausdrücke dürfen auch leer sein; dies ergibt eine Endlosschleife:
for(;;) {
System.out.println("Hallo");
}
36
3.2.9
if-then-else Anweisung
if(condition)
statement1
else
statement2
Die condition wird ausgewertet; ist sie true, so wird statement1 ausgeführt. Ist
sie false, so wird statement2 ausgeführt. Der else-Zweig darf entfallen; dies
ergibt dann die if-then Anweisung aus Mini-Java. Ein else bezieht sich immer
auf das letzte if, das ohne zugehöriges else im Programm vorkam. Was passiert,
wenn mehr als ein if ohne ein else vorangeht? Das folgende Beispiel zeigt eine
falsche (d.h. nicht intendierte) und eine richtige Verwendung (Schachtelung) von
if-then-else Anweisungen.
Beispiel 3.2.4 (Arnold & Gosling [1], S. 139)
public double positiveSumme(double[] werte) {
double sum = 0.0;
if(werte.length > 1)
for(int i=0; i<werte.length; i++)
if(werte[i] > 0)
sum += werte[i];
else // hoppla!
sum = werte[0];
return sum;
}
Der else-Teil sieht so aus, als ob er an die Feldlängenprüfung gebunden wäre,
aber das ist eine durch die Einrückung erweckte Illusion, und Java ignoriert Einrückungen. Statt dessen ist ein else-Teil an das letzte if gebunden, das keinen
else-Teil hat. So ist der vorangehende Block äquivalent zu:
public double positiveSumme(double[] werte) {
double sum = 0.0;
if(werte.length > 1)
for(int i=0; i<werte.length; i++)
if(werte[i] > 0)
sum += werte[i];
else // hoppla!
sum = werte[0];
return sum;
}
37
Das war vielleicht nicht beabsichtigt. Um den else-Teil an das erste if zu binden,
müssen Klammern zur Erzeugung eines Blocks verwendet werden:
public double positiveSumme(double[] werte) {
double sum = 0.0;
if(werte.length > 1) {
for(int i=0; i<werte.length; i++)
if(werte[i] > 0)
sum += werte[i];
}
else {
sum = werte[0];
}
return sum;
}
3.2.10
Mehrdimensionale Felder
Mehrdimensionale Felder werden in Java durch Felder von Feldern realisiert.
Beispiel 3.2.5 (Jobst [4], S. 37)
public class Array2Dim {
public static void main(String[] args) {
int[][] feld = new int[3][3];
//Weise feld[i][j] den Wert (i+1)*10+j zu
for(int i=0; i<feld.length; i++) {
for(int j=0; j<feld[i].length; j++) {
feld[i][j] = (i+1)*10+j;
System.out.print(feld[i][j]+" ");
}
System.out.println();
}
}
}
> java Array2Dim
10 11 12
20 21 22
38
30 31 32
Da Felder in Java dynamisch sind, kann bei mehrdimensionalen Feldern jedes
verschachtelte Feld eine andere Größe aufweisen.
Beispiel 3.2.6 (Jobst [4], S. 38)
public class DemoArray {
public static void main(String[] args) {
int[][] feld = new int[3][];
for(int i=0; i<feld.length; i++) {
feld[i] = new int[i+1];
for(int j=0; j<feld[i].length; j++) {
feld[i][j] = (i+1)*10+j;
System.out.print(feld[i][j]+" ");
}
System.out.println();
}
}
}
> java DemoArray
10
20 21
30 31 32
Felder können bei ihrer Deklaration sofort initialisiert werden:
39
Beispiel 3.2.7 (Jobst [4] S. 39)
public class DemoFeldInitial {
public static void main(String[] args) {
int[][] feld = {{1,2,3},{4,5},{7,8,9,10}};
//Ausgabe des Feldes
for(int i=0; i<feld.length; i++) {
for(int j=0; j<feld[i].length; j++)
System.out.print(feld[i][j]+" ");
System.out.println();
}
}
}
>
1
4
7
java DemoFeldInitial
2 3
5
8 9 10
3.2.11
switch-Anweisung
switch(expression) {
case const1: statement1 break;
case const2: statement2 break;
...
default: statement
}
Der Ausdruck (expression) muss ganzzahlig sein. Nach case müssen Konstanten stehen, die bei der Übersetzung des Programms berechnet werden können.
Der Ausdruck wird berechnet. Danach wird das Programm an derjenigen caseAnweisung fortgesetzt, deren Konstante dem Wert des Ausdrucks entspricht. Mit
break kann man switch verlassen. Nach case darf nur jeweils eine Konstante
stehen. Wenn es keine passende Konstante gibt, so wird das Programm bei der
default Anweisung fortgesetzt (falls vorhanden).
Achtung: Das nächste case erzwingt nicht das Verlassen der switch-Anweisung.
Es impliziert auch nicht das Ende der Anweisungsausführung.
Beispiel 3.2.8 (Jobst [4], S. 15)
40
public class DemoFuerSwitch {
public static void main (String[] args) {
for(int i=0; i<=10; i++)
switch(i) {
case 1:
case 2:
System.out.println(i+" Fall 1,2");
case 3:
System.out.println(i+" Fall 3");
case 7:
System.out.println(i+" Fall 7");
break;
default:
System.out.println(i+" sonst");
}
}
}
// Weiter bei Fall 3
// Weiter bei Fall 7
> java DemoFuerSwitch
0 sonst
1 Fall 1,2
1 Fall 3
1 Fall 7
2 Fall 1,2
2 Fall 3
2 Fall 7
3 Fall 3
3 Fall 7
4 sonst
5 sonst
6 sonst
7 Fall 7
8 sonst
9 sonst
10 sonst
>
3.3
Aufgaben
Aufgabe 3.3.1 Schreiben Sie ein Programm Primes in Mini-Java, das eine
Zahl als Argument erhält und überprüft, ob diese Zahl eine Primzahl ist. Wenn
sie eine ist, dann soll die Zahl wieder ausgegeben werden; wenn nicht, dann sollen
41
die Zahlen, durch die sie teilbar ist, ausgegeben werden.
Aufgabe 3.3.2 Implementieren Sie eine Klasse Factorial, in der die Fakultät
einer Zahl durch die Methoden fwhile und ffor berechnet werden kann. Dabei
soll die Methode fwhile die Fakultät einer Zahl mit einer while-Schleife berechnen, während ffor dazu eine for-Schleife benutzt.
Aufgabe 3.3.3 Schreiben Sie ein Programm, das das Pascalsche Dreieck bis zu
einer Tiefe von zwölf berechnet, dabei jede Reihe des Dreiecks in einem Array
mit entsprechender Länge speichert und alle zwölf Arrays in einem Array von
int-Arrays hält. Entwerfen Sie Ihre Lösung so, dass die Ergebnisse durch eine
Methode ausgegeben werden, die die Länge der einzelnen Arrays des Hauptarrays
berücksichtigt und nicht von einer konstanten Länge 12 ausgeht. Anschließend
sollten Sie Ihren Code bezüglich der Konstante 12 modifizieren können, ohne
dabei die Ausgabemethode ändern zu müssen.
Aufgabe 3.3.4 Schreiben Sie unter Verwendung von if/else bzw. unter Verwendung von switch je eine Methode, die einen Stringparameter erhält und
einen String zurückliefert, in dem alle Sonderzeichen des Ursprungsstrings durch
ihre Java-Äquivalente ersetzt wurden. Ein String, der beispielsweise ein Anführungszeichen (") enthält, sollte einen String zurückliefern, in dem das " durch \"
ersetzt worden ist. Bitte ersetzen Sie alle Zeichen, die in der folgenden Tabelle
aufgeführt werden durch ihre Äquivalente:
Sonderzeichen
"
´
\
Java-Äquivalent
\"
\´
\\
42
Herunterladen