Читать книгу Algorithmen und Datenstrukturen - Gunter Saake - Страница 20
Definition 1.1 Programm
ОглавлениеEin Programm ist die Formulierung eines Algorithmus und der zugehörigen Datenstrukturen in einer Programmiersprache.
Die Grundbausteine von Programmen in höheren Programmiersprachen werden wir erst in den nächsten Kapiteln kennen lernen. Intuitiv wissen wir aber, dass zumindest Anweisungen wie elementare Operationen, bedingte Anweisungen und Schleifen, Datentypen zur Festlegung der Struktur der Daten und darauf definierte Operationen sowie die Möglichkeit der Formulierung von Ausdrücken benötigt werden.
Compiler
Die syntaktisch korrekte Kombination dieser Bausteine allein führt jedoch noch nicht zu einem ausführbaren Programm. Da Programme meist in einer für Menschen schreib- und lesbaren Form erstellt werden, sind sie für Computer, die nur einen von der jeweiligen Prozessorarchitektur (etwa Intels x86-Familie, die SPARC-Prozessoren von Sun oder die PowerPC-Prozessorfamilie der Macs bzw. IBM-Rechner) abhängigen Maschinencode »verstehen«, nicht direkt ausführbar. Es ist daher eine Übersetzung oder auch Kompilierung (engl. compile) des Programms aus der Programmiersprache in den Maschinencode notwendig. Die Übersetzung erfolgt durch ein spezielles Programm, das als Compiler bezeichnet wird. Neben der Übersetzung in Maschinencode führt ein solcher Compiler auch noch verschiedene Überprüfungen des Programms durch, z.B. ob das Programm hinsichtlich der Syntax korrekt ist, ob benutzte Variablen und Prozeduren deklariert sind u.Ä. Programmiersprachen wie C, C++ oder (Turbo-)Pascal basieren auf diesem Prinzip.
Interpreter Skriptsprachen
Da jede Änderung am Programmtext eine Neukompilierung erfordert, verzichtet man bei einigen Sprachen auf die Übersetzung, indem die Programme interpretiert werden. Das bedeutet, dass ein spezielles Programm – der sogenannte Interpreter – den Programmtext schrittweise analysiert und ausführt. Der Interpreter muss also Syntax und Semantik der Sprache kennen! Der Vorteil dieses Ansatzes ist, dass die Kompilierung entfällt und dadurch Änderungen vereinfacht werden. Auch ist das Programm (man spricht häufig auch von Skripten) unabhängig von einer konkreten Prozessorarchitektur. Diese Vorteile werden durch die Notwendigkeit eines Interpreters und eine durch die interpretierte Ausführung verschlechterte Performance erkauft. Beispiele für Sprachen, die mit diesem Prinzip realisiert werden, sind insbesondere die im Webumfeld beliebten Skriptsprachen wie JavaScript.
Bytecode
Java basiert auf einem kombinierten Ansatz (Abbildung 1–1). Programme werden zwar kompiliert, jedoch nicht in einen prozessorspezifischen Maschinencode, sondern in einen sogenannten Bytecode. Hierbei handelt sich um Code für eine abstrakte Stack-Maschine. Dieser Bytecode wird schließlich von einem Java-Interpreter, der virtuellen Java-Maschine (Java VM – engl. Virtual Machine), ausgeführt. Da der Bytecode relativ maschinennah ist, bleibt die Interpretation und Ausführung einfach, wodurch der Performance-Verlust gering gehalten werden kann. Andererseits lassen sich auf diese Weise auch übersetzte Java-Programme auf unterschiedlichen Prozessorarchitekturen ausführen, sofern dort eine Java-VM verfügbar ist.
Aufbau von Java-Programmen Klasse Methoden
Wie ist nun ein Java-Programm aufgebaut? Ein solches Programm besteht aus einer oder mehreren Klassen, die damit das Hauptstrukturierungsmittel in Java darstellen. Wir werden auf den Begriff der Klasse später im Zusammenhang mit Datenstrukturen noch genauer eingehen. Zunächst wollen wir darunter nur ein Modul verstehen, das eine Menge von Variablen und Prozeduren bzw. Funktionen kapselt. Im Sprachgebrauch von Java werden Prozeduren und Funktionen, d.h. die Zusammenfassung von Anweisungen zu einer logischen Einheit, auch als Methoden bezeichnet.
Abb. 1–1 Compiler und Interpreter bei Java
Abb. 1–2 Struktur eines Java-Programms
main-Methode
Abbildung 1–2 verdeutlicht den Aufbau eines Java-Programms aus mehreren Klassen. Jede Klasse ist durch einen Namen gekennzeichnet und kann Attribute und Methoden definieren. Mindestens eine Klasse muss eine spezielle Methode main besitzen, die den Einstiegspunkt für die Ausführung bildet. Wenn das Programm später ausgeführt werden soll, ist der Name der Klasse mit der main-Methode (engl. für »Haupt«-Methode) anzugeben. Beim Start wird dann zuerst diese Methode aufgerufen, die damit das »Hauptprogramm« bildet.
Kompilierung
Dateien mit Klassendefinitionen werden in Java mit der Endung .java gespeichert, wobei für jede öffentliche, d.h. von »außen« nutzbare Klasse eine eigene Datei verwendet wird, die den Namen der Klasse trägt. Die Klassen müssen zur Erstellung eines ausführbaren Programms zunächst mit dem Java-Compiler in Bytecode übersetzt (kompiliert) werden. Der Java-Compiler ist entweder in eine Entwicklungsumgebung wie Eclipse oder NetBeans integriert oder als ein Programm mit dem Namen javac verfügbar. Im ersten Fall erfolgt die Kompilierung einfach über die grafische Benutzerschnittstelle der Entwicklungsumgebung – meist sogar im Hintergrund, ohne dass explizit die Kompilierung angestoßen werden muss. Im zweiten Fall wird dies durch ein Kommando der folgenden Form vorgenommen:
> javac Klasse.java
Ausführung CLASSPATH
Als Ergebnis der Übersetzung entsteht für jede Klasse eine Datei mit der Endung .class, die den Bytecode enthält und durch die Java-Maschine ausgeführt werden kann. Diese Java-Maschine kann ein Interpreter für den Bytecode sein oder auch den Bytecode intern in direkt ausführbaren Maschinencode übersetzen. Letzteres wird als Just-in-Time-Kompilierung (JIT-Kompilierung) bezeichnet. Ein explizites Binden der übersetzten Klassen, wie es etwa bei C-Programmen durchgeführt werden muss, ist dagegen nicht notwendig. Zur Ausführung muss die Java-Maschine – ein Programm mit dem Namen java – gestartet werden, wobei der Name der Klasse als Parameter übergeben wird. Sind in der auszuführenden Klasse weitere Klassen referenziert, so wird der benötigte Bytecode vom Interpreter bei Bedarf nachgeladen. Dazu muss bekannt sein, wo dieser Code zu finden ist. Sowohl Compiler als auch Interpreter werten daher eine Umgebungsvariable CLASSPATH aus, die eine Liste von Verzeichnissen bzw. von Archiven mit .class-Dateien beinhaltet. Im einfachsten Fall ist dies das aktuelle Verzeichnis, das durch ».« bezeichnet wird. Sollen mehrere Verzeichnisse angegeben werden, so werden diese unter UNIX durch »:« bzw. unter Windows durch »;« getrennt.
Package
Ein weiteres wichtiges Strukturierungsmittel in Java sind Packages oder Pakete. Hierbei handelt es sich um einen Mechanismus zur Organisation von Java-Klassen. Durch Pakete werden Namensräume gebildet, die Konflikte durch gleiche Bezeichnungen verhindern. So kann eine Klasse Object definiert werden, ohne dass es dadurch zu Konflikten mit der Standardklasse java.lang.Object kommt. Ein Paket demo wird beispielsweise durch die Anweisung
package demo;
Importieren
vereinbart, wobei dies am Anfang der Quelldatei anzugeben ist. Jede Klasse, in deren Quelldatei diese Anweisung steht, wird dem Paket demo zugeordnet. Über die Notation demo.MeineKlasse kann danach eindeutig die im Paket demo definierte Klasse MeineKlasse identifiziert werden. Diese Schreibweise lässt sich jedoch abkürzen, indem zu Beginn der Quelldatei eine import-Anweisung eingefügt wird:
import demo.MeineKlasse;
Danach kann die Klasse MeineKlasse ohne vorangestellten Paketnamen verwendet werden. Alle Klassen eines Paketes lassen sich durch Angabe eines * importieren:
import demo.*;
Paketnamen Standard-API
Paketnamen können hierarchisch aufgebaut werden, wobei die einzelnen Namensbestandteile durch ».« getrennt werden. Allerdings wird durch Angabe des »*« beim Import kein rekursives Importieren durchgeführt, sondern es sind nur die Klassen des bezeichneten Paketes betroffen. Zu beachten ist weiterhin, dass die Pakete, die mit java. beginnen, zum Standard-API gehören und nicht verändert werden dürfen, d.h., eigene Paketnamen dürfen nicht mit java. beginnen.
Pakete liefern dem Java-Compiler auch Hinweise, wo die kompilierten Klassendateien (Dateien mit der Endung .class) abgelegt werden sollen. Danach werden Punkte im Paketnamen durch den Separator für Verzeichnisnamen ersetzt. Eine Klassendatei der Klasse algoj.sort.QuickSort wird demzufolge im Verzeichnis algoj/sort unter dem Namen QuickSort.class abgelegt.
Betrachten wir nun am Beispiel einer einfachen Klasse die Schritte der Erstellung eines Java-Programms. Die Klasse Hello aus Programm 1.1 soll die typische Aufgabe eines jeden ersten Programms aus Einführungsbüchern zu Programmiersprachen erfüllen: die Ausgabe der Meldung »Hello World!«.