2.2 Anweisungen formen Programme 

2.2.1 Anweisungen 

Java zählt zu den imperativen Programmiersprachen, in denen der Programmierer die Abarbeitungsschritte seiner Algorithmen durch Anweisungen (engl. statements) vorgibt. Anweisungen können unter anderem sein:
- Ausdrucksanweisungen etwa für Zuweisungen oder Funktionsaufrufe
- Fallunterscheidungen (zum Beispiel mit if)
- Schleifen für Wiederholungen (etwa mit for oder do-while)
|
2.2.2 Eine Klasse bildet den Rahmen 

Programme setzen sich aus Anweisungen zusammen. In Java können jedoch nicht einfach Anweisungen in eine Datei geschrieben und dem Compiler übergeben werden. Sie müssen zunächst in einen Rahmen gepackt werden. Dieser Rahmen deklariert eine Klasse mit ihren Funktionen und Variablen.
Die nächsten Programmcodezeilen werden am Anfang etwas befremdlich wirken (wir erklären die Elemente später genauer). Die folgende Datei erhält den (frei wählbaren) Namen Main.java.
Listing 2.1 Main.java
public class Main { public static void main( String[] args ) { // Hier ist der Anfang unserer Programme // Jetzt ist hier Platz für unsere eigenen Anweisungen // Hier enden unsere Programme } }
Hinter den beiden Geteilt-Zeichen // befindet sich ein Kommentar. Er gilt bis zum Ende der Zeile und dient dazu, Erläuterungen zu den Quellcodezeilen hinzuzufügen, die den Code verständlicher machen.

Java ist eine objektorientierte Programmiersprache, die Programmlogik außerhalb von Klassen nicht erlaubt. Aus diesem Grund deklariert die Datei Main.java mit dem Schlüsselwort class eine Klasse Main, um später eine Funktion mit der Programmlogik anzugeben. Der Klassenname darf grundsätzlich beliebig sein, doch besteht die Einschränkung, dass in einer mit public deklarierten Klasse der Klassenname so lauten muss wie der Dateiname. Alle Schlüsselwörter in Java beginnen mit Kleinbuchstaben und Klassennamen üblicherweise mit Großbuchstaben.
In den geschweiften Klammern der Klasse folgen Deklarationen von Methoden, also Funktionen, die die Klasse anbietet. Eine Funktion ist eine Sammlung von Anweisungen unter einem Namen. Mathematische Funktionen sind mit Funktionen aus Programmiersprachen vergleichbar. Eine Sinus-Funktion schafft es beispielsweise, zu einem gegebenen Wert den Sinus zu berechnen. Der Begriff »Funktion« und der objektorientierte Name »Methode« werden in diesem Tutorial synonym verwendet.
2.2.3 Die Reise beginnt am main() 

Wir programmieren hier eine besondere Funktion, die sich main() nennt. Die Schlüsselwörter davor und die Angabe in dem Paar runder Klammern hinter dem Namen müssen wir einhalten. Die Funktion main() ist für die Laufzeitumgebung etwas ganz Besonderes, denn beim Aufruf des Java-Interpreters mit einem Klassennamen wird unsere Funktion als Erstes ausgeführt. [Na ja, so ganz präzise ist das auch nicht. In einem static-Block könnten wir auch einen Funktionsaufruf setzen, doch das wollen wir hier einmal nicht annehmen. static-Blöcke werden beim Laden der Klassen in die virtuelle Maschine ausgeführt. Andere Initialisierungen sind dann auch schon gemacht. ] Demnach werden genau die Anweisungen ausgeführt, die innerhalb der geschweiften Klammern stehen. Halten wir uns fälschlicherweise nicht an die Syntax für den Startpunkt, so kann der Interpreter die Ausführung nicht beginnen, und wir haben einen semantischen Fehler gemacht, obwohl die Funktion selbst korrekt gebildet ist. Innerhalb von main() befindet sich ein Parameter mit dem Namen args. Der Name ist willkürlich gewählt, wir werden allerdings immer args verwenden.

2.2.4 Programme übersetzen und starten 

Der Quellcode eines Java-Programms lässt sich so allein nicht ausführen. Ein spezielles Programm, der Compiler (auch Übersetzer genannt), transformiert das geschriebene Programm in eine andere Repräsentation. Ein Quellcode mit Anweisungen für Programme muss aber nicht zwingend übersetzt werden. Eine Ablaufmodell ist der Interpreter. Er liest die Datei Schritt für Schritt ein und führt dann die Anweisungen aus. Der Compiler liest die Datei in einem Rutsch und meldet Fehler. Häufig werden Skriptsprachen interpretiert, früher waren es oft BASIC-Programme. Compiler für geläufige Programmiersprachen wie C(++) oder Delphi übersetzen den Quellcode in Maschinencode. Das ist Binärcode, der vom Prozessor im Computer direkt ausführbar ist. Da unterschiedliche Rechner aber unterschiedliche Prozessoren besitzen, sind die Programme nicht direkt auf verschiedenen Rechnerplattformen ausführbar. Java ist jedoch als Programmiersprache entworfen worden, die plattformunabhängig ist, sich also nicht an einen physikalischen Prozessor klammert. Der Compiler übersetzt den Quellcode nicht in Binärcode für einen konkreten Prozessor, sondern in einen plattformunabhängigen Code, den Bytecode. Prozessoren wie Intel-, AMD- oder G5-CPU können aber mit diesem Bytecode nichts anfangen. Hier hilft ein Interpreter weiter. Dieser liest Anweisung für Anweisung aus dem Bytecode und führt entsprechende Befehle auf dem Mikroprozessor aus. Daher ist Java eine compilierte und interpretierte Sprache zugleich.

2.2.5 Funktionsaufrufe als Ausdrücke und Anweisungen 

In den vorangehenden kleinen Beispielen haben wir schon mit System.out.println() ein Beispiel für einen Funktionsaufruf gesehen. Allgemein hat ein Funktionsaufruf das Format:
funktionsname() // Syntax eines Funktionsaufrufes
Funktionsaufrufe können als Ausdrücke eingesetzt werden, wenn sie mit einem Semikolon abgeschlossen werden.
funktionsname(); // Funktionsaufruf als Anweisung
Innerhalb der Klammern dürfen wir Argumente angeben, wenn die Funktion dies gestattet. Die println()-Funktion erlaubt zum Beispiel Strings als Argumente, die dann auf der Konsole erscheinen.
2.2.6 print(), println() und printf() für Bildschirmausgaben 

Mit der Funktion println() lassen sich Meldungen auf dem Bildschirm ausgeben. Die meisten Methoden verraten durch ihren Namen, was sie leisten, und für eigene Programme ist es sinnvoll, aussagekräftige Namen zu verwenden. Wenn die Java-Entwickler die Methode glubschi() genannt hätten, würde uns der Sinn der Methode verborgen bleiben. println() zeigt jedoch durch den Wortstamm »print« an, dass etwas geschrieben wird. Die Endung ln (kurz für line) bedeutet, dass noch ein Zeilenvorschubzeichen ausgegeben wird. Umgangssprachlich heißt das: Eine neue Ausgabe beginnt in der nächsten Zeile. Neben println() existiert die Bibliotheksfunktion print(), die keinen Zeilenvorschub anhängt.
Implementieren [»Implementieren« stammt vom lateinischen Wort »implere« ab, was für »erfüllen« und »ergänzen« steht. ] wir eine vollständige Java-Klasse, die etwas auf dem Bildschirm ausgibt.
Listing 2.2 Main.java
class Main { public static void main( String[] args ) { // Hier ist der Anfang unserer Programme System.out.println( "Hallo Javanesen" ); System.out.println(); // Gibt nur eine Leerzeile aus // Hier enden unsere Programme } }
Die printXXX()-Methoden [Abkürzung für Methoden, die mit print beginnen, also print() und println(). ] können in Klammern unterschiedliche Argumente bekommen. Ein Argument ist ein Wert, den wir der Funktion beim Aufruf mitgeben. Auch wenn eine Funktion keine Argumente erwartet, muss beim Aufruf hinter dem Funktionsnamen ein Klammernpaar folgen. Dies ist konsequent, da wir so wissen, dass es ein Funktionsaufruf ist und nichts anderes. Andernfalls führt es zu Verwechslungen mit Variablen. Die printXXX()-Aufrufe in unserem Beispiel geben Zeichenketten aus – ein anderes Wort für Zeichenkette ist String. Ein String ist eine Folge von Buchstaben, Ziffern oder Sonderzeichen in doppelten Anführungszeichen.
Ausdrucksanweisung
Wird ein Ausdruck mit einem Semikolon zu einer Anweisung, so nennen wir das Ausdrucksanweisung (engl. expression statements). Neben Funktionsaufrufen gibt es andere Formen von Ausdrucksanweisungen, wie etwa Zuweisungen, doch allen ist gemeinsam, dass sie mit einem Semikolon abgeschlossen werden. [Das Semikolon dient auch nicht wie in Pascal zur Trennung von Anweisungen, sondern schließt sie immer ab. ]
|
Es lassen sich auch Funktionen in dieser Form anwenden, die selbst ein Ausdruck sind und ein Ergebnis zurückgeben, beispielsweise eine Sinus-Funktion. Wird mit dem Ergebnis nichts gemacht, wird der Rückgabewert verworfen. Im Fall der Sinus-Funktion ist das nicht sinnvoll, denn sie macht außer der Berechnung nichts anderes.
System.out.println( Math.sin(0.528740) ); // Funktionsaufruf sin() als Ausdruck
Etwas wackelig ist der Begriff Ausdrucksanweisung bei Funktionen, die keine Rückgabe liefern. println() ist so eine Funktion. Sie gibt nichts zurück (void) und ist daher auch kein Ausdruck. Daher führt Folgendes zu einem Compilerfehler:
System.out.println( System.out.println() );
Überladene Methoden und variable Argumentlisten
Eine weitere Eigenschaft von Java sind Methoden, die unterschiedliche Argumente (eine andere Anzahl oder einen unterschiedlichen Typ) besitzen, aber einen gleichen Namen tragen. Diese Funktionen nennen wir überladen. Die printXXX()-Methoden sind sogar vielfach überladen und akzeptieren neben dem Argumenttyp String auch Typen wie einzelne Zeichen, Wahrheitswerte oder Zahlen. Weiterhin unterstützt Java seit der Version 5 auch variable Argumentlisten, was bedeutet, dass es möglich ist, bestimmten ausgezeichneten Funktionen beliebig viele Argumente zu übergeben.
System.out.printf( "Hallo %s%n", "Welt" ); // Hallo Welt System.out.printf( "%s ist %d Jahre alt%n", "Isy", 26 ); // Isy ist 26 Jahre alt |
System.out und System.err
Das Laufzeitsystem bietet uns zwei Ausgabekanäle, in die wir normale Ausgaben und Fehler leiten können. Der Vorteil ist, dass über diese Unterteilung die Fehler von der herkömmlichen Ausgabe getrennt werden können. Standardausgaben wandern in System.out, und Fehlerausgaben werden in System.err weitergeleitet. out und err sind vom gleichen Typ, sodass die println()- oder print()-Varianten bei beiden gleich bleiben.
System.out.println( "Das ist eine normale Ausgabe" ); System.err.println( "Das ist eine Fehlerausgabe" );
Abbildung 2.2 Eclipse stellt normale Ausgaben schwarz und Fehlerausgaben rot dar. Damit ist leicht zu erkennen, welche Ausgabe in welchen Kanal geschickt wurde.
Erste Idee der Objektorientierung

USA.president.ask( "Wenn alles 'Intelligent Design' ist, " + "warum haben einige Menschen als Atavismus einen Stummelschwanz oder Halsfisteln?" );
vergleichen. Mehr zu diesen Aufrufen zu einem späteren Zeitpunkt. Das obige Beispiel macht aber jetzt schon deutlich, dass Strings mit dem Plus aneinander gehängt werden können.
2.2.7 Modifizierer 

Die Deklaration einer Klasse oder Methode kann einen oder mehrere Modifizierer (engl. modifier) enthalten, die zum Beispiel die Nutzung einschränken oder parallelen Zugriff synchronisieren.
public class Main { public static void main( String[] args ) { System.out.println( "Hallo Welt" ); } } |
Der Modifizierer public ist ein Sichtbarkeitsmodifizierer. Er bestimmt, ob die Klasse beziehungsweise die Funktion für andere sichtbar ist oder nicht. Der Modifizierer static zwingt den Programmierer nicht dazu, vor dem Methodenaufruf ein Objekt der Klasse zu bilden. Anders gesagt: Die Eigenschaft, ob sich eine Methode nur über ein konkretes Objekt aufrufen lässt oder eine Eigenschaft der Klasse ist, so dass für den Aufruf kein Objekt der Klasse nötig wird, bestimmt dieser Modifizierer. Wir werden in den ersten beiden Kapiteln nur mit statischen Funktionen arbeiten und ab Kapitel 3 nicht-statische Methoden einführen.
2.2.8 Anweisungen und Blöcke 

Atomare, also unteilbare Anweisungen, heißen auch elementare Anweisungen. Zu ihnen zählen zum Beispiel Funktionsaufrufe, Variablendeklarationen oder die leere Anweisung [Sie wird verwendet, wenn die Sprachgrammatik eine Anweisung vorschreibt, aber in dem Programmablauf keine Anweisung vorkommen muss. So muss etwa hinter dem Schleifenkopf eine Anweisung folgen. Wir werden bei den Schleifen ein Beispiel der leeren Anweisung sehen. ] , die nur aus einem Semikolon besteht.
Programme bestehen in der Regel aus mehreren Anweisungen, die eine Anweisungssequenz ergeben. Die Laufzeitumgebung von Java führt jede einzelne Anweisung der Sequenz in der angegebenen Reihenfolge hintereinander aus.
Ein Block fasst eine Gruppe von Anweisungen zusammen, die hintereinander ausgeführt werden. Anders gesagt: Ein Block ist eine Anweisung, die in den geschweiften Klammern { } eine Folge von Anweisungen zu einer neuen Anweisung zusammenfasst.
{ Anweisung1; Anweisung2; ... }
Ein Block kann überall dort verwendet werden, wo auch eine einzelne Anweisung stehen kann. Der neue Block hat jedoch eine Besonderheit für Variablen, da er einen lokalen Bereich für die darin befindlichen Anweisungen inklusive der Variablen bildet.
Leerer Block und geschachtelte Blöcke
Ein Block ohne Anweisung nennt sich leerer Block. Er verhält sich wie eine leere Anweisung, also wie ein Semikolon. Blöcke können auch geschachtelt werden, sodass Folgendes in der Funktion main() in Ordnung ist:
public static void main( String[] args ) { { System.out.println( "Hallo Computer" ); {{}}{{}{}}} }