2.2 Programme
 
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 definiert die Hauptklasse und eine Funktion.
Auch wenn die folgenden Programmcodezeilen am Anfang etwas befremdend wirken, wir werden sie zu einem späteren Zeitpunkt noch genauer erklären. Wir geben der folgenden Datei den Namen Main.java.
Listing 2.1
Main.java
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
}
}
Ein Java-Programm muss immer in einer Klasse definiert sein. Wir haben sie Main genannt, der Name ist jedoch beliebig. In geschweiften Klammern folgen benutzerdefinierte 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. Wir wissen zwar nicht, wie die Funktion das macht, aber sie kann es. Der Begriff »Funktion« und der objektorientierte Name »Methode« sind in diesem Tutorial synonym verwendet. Vor einer Methode stehen unterschiedliche Modifizierer. Der Modifizierer static sagt, dass die Methode auch ohne Objekt benutzt werden kann. Wir werden in den folgenden Kapiteln nur mit statischen Methoden arbeiten.
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.1
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 der Name args, mit dem die Parameter angesprochen werden. Der Name ist willkürlich, wir werden allerdings immer args verwenden.
Hinter den beiden Geteilt-Zeichen // befinden sich Kommentare. Sie gelten bis zum Ende der Zeile und dienen dazu, Erläuterungen zu den Quellcodezeilen hinzuzufügen, die ihn verständlicher machen.
Programme übersetzen und starten
Der Quellcode eines Java-Programms ist so allein nicht ausführbar. 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 andere Gattung für ein 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 kompilierte und interpretierte Sprache zugleich.
Zum Übersetzen der Programme bietet Sun im JDK den Compiler javac an. Der Interpreter heißt java. Wir haben im einführenden Kapitel über den Ablauf und die möglichen Fehler schon gesprochen.
Beispiel Das Programm mit dem Namen Main.java übersetzen und starten:
$ javac Main.java
$ java Main
|
Befindet sich im Quellcode nur die kleinste Ungenauigkeit, die der Compiler nicht toleriert, gibt er einen Fehler aus. Erst wenn Bytecode erzeugt wurde, kann der Interpreter diesen ausführen. Leider sehen wir noch nichts, da wir keine Anweisung gegeben haben. Dies soll sich nun ändern, denn wir wollen lernen, wie eine Bildschirmausgabe aussieht.
2.2.1 Kommentare
 
Programmieren heißt nicht nur, einen korrekten Algorithmus in einer Sprache auszudrücken, sondern auch, unsere Gedanken verständlich zu formulieren. Dies geschieht beispielsweise durch eine sinnvolle Namensgebung für Programmobjekte wie Klassen, Funktionen und Variablen. Ein selbsterklärender Klassenname hilft den Entwicklern erheblich. Doch die Lösungsidee und der Algorithmus wird auch durch die schönsten Variablennamen nicht zwingend klarer. Damit Außenstehende (und wir nach Monaten selbst) unsere Lösungsidee schnell nachvollziehen und später das Programm erweitern oder abändern können, werden Kommentare in den Quelltext eingeführt. Sie dienen nur den Lesern der Programme, haben aber auf die Abarbeitung keine Auswirkungen.
Kommentarblöcke können durch /* und */ abgetrennt werden. Zwischen diesen Zeichen kann nahezu jeder beliebige Text stehen. Da es keinen Präprozessor gibt, ist es Aufgabe des Compilers, die Bemerkungen aus dem Quelltext zu entfernen. Da im Einzelfall nur Zeilen auskommentiert werden sollen, ist in Java auch das in C++2
verwendete // erlaubt, welches wir schon kennen gelernt haben.
// Zeilenkommentar
/*
* Blockkommentar
*/
Dokumentationskommentare
Neben dem Blockkommentar bietet Java eine interessante Möglichkeit der Programmdokumentation. Die Zeichen /** und */ beschreiben einen so genannten Dokumentationskommentar (engl. Doc-Comment), welcher vor Methoden- oder Klassendefinitionen angewendet wird.
Beispiel Dokumentationskommentar:
/**
* Hier drinnen ist ein Doc-Kommentar
*/
|
Der Teil zwischen den Kommentaren wird mit einem externen Dienstprogramm in HTML- oder Framemaker-Dokumente umgewandelt.
Blockkommentare dürfen nicht geschachtelt sein. Ein Zeilenkommentar darf aber im Blockkommentar enthalten sein.
2.2.2 Funktionsaufrufe als Anweisungen
 
Bisher kennen wir keine konkreten Anweisungen. Daher möchte ich jetzt eine einfache Anweisung vorstellen, die wichtig für Programme ist: der Funktionsaufruf. Allgemein hat er folgendes Format:
funktionsname();
Innerhalb der Klammern dürfen wir Parameter angeben. Es lassen sich auch Funktionen in dieser Form anwenden, die ein Ausdruck sind und ein Ergebnis zurückgeben, beispielsweise eine Sinus-Funktion. Der Rückgabewert wird dann verworfen. Im Fall der Sinus-Funktion macht das wenig Sinn, denn sie macht außer der Berechnung nichts anderes.
Wir interessieren uns für eine Funktion, die eine Zeichenkette auf dem Bildschirm ausgibt. Sie heißt println(). 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()3
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 hervorruft.
Die printXXX()4
-Methoden können in Klammern unterschiedliche Parameter bekommen. Ein Parameter ist ein Wert, den wir der Funktion beim Aufruf mitgeben wollen. Wir wollen die Diskussion über Parameter aber noch etwas verschieben. Unser printXXX()-Aufruf soll lediglich Zeichenketten ausgeben. (Ein anderes Wort für Zeichenketten ist String.) Ein String ist eine Folge von Buchstaben, Ziffern und Sonderzeichen in doppelten Anführungszeichen:
"Ich bin ein String"
"Ich auch. Und ich koste 7.59 _"
Um die obere Ausgabe mit dem Funktionsaufruf und einem trennenden Zeilenvorschub auf den Bildschirm zu bekommen, schreiben wir:
System.out.println( "Ich bin ein String" );
System.out.println(); // Gibt eine Leerzeile aus
System.out.println( "Ich auch. Und ich koste 7.59 _" );
Auch wenn eine Funktion keine Parameter erwartet, muss beim Aufruf hinter dem Funktionsnamen ein Klammerpaar folgen. Dies ist konsequent, da wir so wissen, dass es ein Funktionsaufruf ist und nichts anderes. Andernfalls führt es zu Verwechslungen mit Variablen.
Eine weitere Eigenschaft von Java wird ebenfalls an dem Funktionsaufruf sichtbar. Es gibt Methoden, die unterschiedliche Parameter (eine andere Anzahl oder einen unterschiedlichen Typ) besitzen, aber gleichen Namen tragen. Diese Funktionen nennen wir überladen. Die printXXX()-Methoden sind sogar vielfach überladen und akzeptieren neben Strings auch weitere Werte als Parameter.
Programmieren wir eine ganze 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" );
// Hier enden unsere Programme
}
}
Hinweis Anweisungen wie ein Funktionsaufruf enden immer mit einem Semikolon.
|
Erste Idee der Objektorientierung
In einer objektorientierten Programmiersprache sind alle Methoden an bestimmte Objekte gebunden (daher der Begriff objektorientiert). Betrachten wir zum Beispiel das Objekt Radio. Ein Radio spielt Musik ab, wenn der Ein-Schalter betätigt wird und ein Sender und die Lautstärke eingestellt sind. Ein Radio bietet also bestimmte Dienste (Operationen) an, wie Musik an/aus, lauter/leiser. Zusätzlich hat ein Objekt auch noch einen Zustand. Es ist zum Beispiel die Farbe oder das Baujahr. Wichtig in objektorientierten Sprachen ist, dass die Operationen und Zustände immer (und da gibt es keine Ausnahmen) an Objekte beziehungsweise Klassen gebunden sind (mehr zu dieser Unterscheidung später). Der Aufruf einer Methode auf ein Objekt richtet die Anfrage genau an ein bestimmtes Objekt. Steht in einem Java-Programm einfach nur die Anweisung lauter, so weiß der Compiler nicht, wen er fragen soll. Was ist, wenn es auch noch einen Fernseher gibt? Aus diesem Grunde verbinden wir das Objekt, das etwas kann, mit der Operation. Ein Punkt trennt das Objekt von der Operation oder dem Zustand.
So gehört auch println() zu einem Objekt, welches die Bildschirmausgabe übernimmt. Dass eine Methode immer zu einem Objekt gehört, können wir auch an unserem eigenen Programm überprüfen. main() gehört zu der Klasse Main, die später ein Objekt bilden kann. Daher können wir in Java auch nicht einfach die Ausgabeanweisung schreiben. println() gehört zu einem Objekt mit dem Namen out. Dieses Objekt ist wiederum Teil der Klasse System. Wir können das vergleichen mit einem Aufruf zum Beispiel von
BRD.aktuellerBundeskanzler.fragen( "Wieso kassieren ARD" +
" und ZDF jährlich 11 Mrd. Mark Rundfunkgebühren"+
" und müssen davon nichts abführen?" );
Mehr zu diesen Aufrufen zu einem späterem Zeitpunkt. Das obige Beispiel macht aber jetzt schon deutlich, dass Strings mit dem Plus zusammengehängt werden können.
2.2.3 Die leere Anweisung
 
Die einfachste Anweisung besteht nur aus einem Semikolon und ist 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 eine Anwendung der leeren Anweisung sehen.
2.2.4 Der Block
 
Ein Block innerhalb von Methoden oder statistischen Blöcken fasst eine Gruppe von Anweisungen zusammen, die hintereinander ausgeführt werden. Dazu werden die Anweisungen zwischen geschweiften Klammern geschrieben:
{
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, denn er bildet einen lokalen Bereich für die darin befindlichen Anweisungen inklusive der Variablen. Blöcke können auch geschachtelt werden.
1
Na ja, so ganz präzise ist das auch nicht. In einem static-Block könnten wir auch einen Funktionsaufruf reinsetzen, 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.
2
In C++ haben die Entwickler übrigens das aus der Vor-Vorgängersprache BCPL wieder eingeführt, was in C entfernt wurde.
3
Bei uns am Niederrhein steht glubschi für etwas Glitschiges, Schleimiges.
4
Abkürzung für Methoden, die mit print- beginnen, also print() und println().
|