IBIT - Angewandte Statistik Manuskript zu der Veranstaltung PROGRAMMIEREN MIT JAVA Es handelt sich bei dem Manuskript um internes Arbeitsmaterial, das nicht dazu geeignet ist, veröffentlicht zu werden. Es darf daher ohne Genehmigung des Verfassers in keiner Form (auch auszugsweise) reproduziert, vervielfältigt bzw. verbreitet oder in eine andere Sprache übersetzt werden. 2005 Dr. Hans-Peter Bäumer, Carl von Ossietzky Universität Oldenburg, IBIT – Angewandte Statistik, Postfach 2503, D-26111 Oldenburg Email: [email protected] Alle Rechte bleiben vorbehalten. Dieses Manuskript ist urheberrechtlich geschützt. Programmieren mit Java Einführung 1. Einführung 1.1 Grundfunktionen und typische Arbeitsschritte eines Compilers, wesentliche Unterschiede zu einem Interpreter 1.2 Grundlegende Merkmale von Java 1.3 Verwandtschaftsbeziehungen von einigen (imperativen) Programmiersprachen im Überblick 1.4 Phasen in der Entwicklung objektorientierter Programme 1.5 Typographie 1 Programmieren mit Java 1.1 Grundfunktionen und typische Arbeitsschritte eines Compilers 1.1 Grundfunktionen und typische Arbeitsschritte eines Compilers Wie zu jeder imperativen Programmiersprache, so gehört auch zu der rein objektorientierten Programmiersprache Java ein Compiler. Die Kenntnis typischer Arbeitsschritte eines Compilers erleichtert auch im Fall von Java erheblich die Entwicklung von Programmen, die weniger fehleranfällig sind. zwei Grundfunktionen eines Compilers 1. Grundfunktion Ein Compiler ist ein Programm, das ein in der Quellsprache formuliertes Programm, das Quellprogramm, liest und in ein äquivalentes Programm einer anderen Sprache, das Zielprogramm, übersetzt. 2. Grundfunktion Der Compiler hat als eine weitere wichtige Aufgabe, Warnungen sowie Fehlermeldungen auszugeben, um den Programmierer bei der Entwicklung eines fehlerfreien Quellprogramms zu unterstützen. 2 Programmieren mit Java 1.1 Grundfunktionen und typische Arbeitsschritte eines Compilers Ein Compiler bewältigt einen wesentlichen Teil seiner Aufgaben in 3 Analyseschritten: ➘ der lexikalischen Analyse ➘ der syntaktischen oder hierarchischen Analyse ➘ der semantischen Analyse. lexikalische Analyse Zerlegung des zu verarbeitenden Zeichenstroms in Symbole ➘ Ein Symbol bezeichnet eine Zeichenfolge, der eine spezifische Bedeutung zukommt. Beispiel: Zeichenfolge quaderOf ist ein Name Der Begriff Name wird noch festgelegt werden. Vorläufig wird ein Name, wie quaderOf oder a, als ein Symbol aufgefasst werden, das dazu dient, einen Speicherbereich zu identifizieren. Von einem Compiler werden Namen üblicherweise erkannt, indem die sequentielle Bearbeitung der Zeichen so lange fortgesetzt wird bis ein Zeichen auftritt, dass in einem Namen unzulässig ist. Diese Zeichenfolge wird aus dem Zeichenstrom eliminiert, zu einem Symbol zusammengefasst und ein Eintrag in der Symboltabelle vorgenommen. 3 Programmieren mit Java 1.1 Grundfunktionen und typische Arbeitsschritte eines Compilers Diese zeichenorientierte Analyse ist im Allgemeinen nicht ausreichend, um in dem Zeichenstrom Ausdrücke oder Anweisungen korrekt zu analysieren: Beispiel: Korrespondenz von Klammern in einem Ausdruck muss korrekt erkannt werden Formel zur Berechnung der Oberfläche eines Quaders Vorläufig wird jede endliche Sequenz von Ziffern des Dezimalsystems mit oder ohne Dezimalpunkt, wie 1.975302468 oder 12515, als Zahl aufgefasst. Die Begriffe Ausdruck und Zahl werden noch präzisiert werden. Daher ist es erforderlich, dem Zeichenstrom eine Struktur, beispielsweise eine hierarchische Struktur, zu unterstellen und eine syntaktische oder auch hierarchische Analyse anzuschließen. syntaktische Analyse Zusammenfassen der Symbole, die in der lexikalischen Analyse ermittelt worden sind, zu grammatikalisch korrekten Sätzen 4 Programmieren mit Java 1.1 Grundfunktionen und typische Arbeitsschritte eines Compilers In diesem Ausschnitt von Quellcode in Java sind quaderOf, a, b und c Namen und 2.0 ist eine Zahl. Für sich genommen bildet quaderOf=2.0*( keinen grammatikalisch korrekten Satz, denn die bei der lexikalischen Analyse ermittelte Folge von Symbolen gehört nicht zu der Menge der grammatikalisch korrekten Sätze. semantische Analyse Überprüfung von (grammatikalisch korrekten) Sätzen auf semantische Fehler: ➘ Überprüfung, ob die Datentypen von Operanden in einem Ausdruck verträglich sind ➘ Überprüfung, ob die Gültigkeitsbereiche von Namen eingehalten werden Anschaulich formuliert ist der folgende Satz zwar syntaktisch korrekt, nicht jedoch semantisch: Der Computer denkt. 5 Programmieren mit Java 1.1 Grundfunktionen und typische Arbeitsschritte eines Compilers statische semantische Analyse Die semantische Analyse zur Compilationszeit wird als statisch bezeichnet. Nicht alle semantischen Regeln einer Programmiersprache lassen sich zur Compilationszeit überprüfen. Abbildung aus Aho, A.V., Sethi, R. & Ullmann, J.D. (1988): Compilerbau. Teil 1. Bonn etc.: Addison-Wesley, S. 12 6 Programmieren mit Java 1.1 Grundfunktionen und typische Arbeitsschritte eines Compilers, wesentliche Unterschiede zu einem Interpreter Interpreter Anstatt ein Quellprogramm in Zwischencode zu übersetzen und daraus ein Objektprogramm zu generieren, das auf dem jeweiligen Zielrechner ausführbar ist, übersetzt ein Interpreter jede Anweisung des Quellcodes und veranlasst, dass die damit verbundenen Operationen direkt ausgeführt werden. Ein Interpreter erzeugt demnach im Gegensatz zu einem Compiler keinen speicherbaren Maschinencode, also kein Objektprogramm. Die Ausführung eines Programms, das in einer Interpretersprache kodiert worden ist, nimmt in der Regel mehr CPU-Zeit in Anspruch als die Ausführung eines funktional gleichen Programms in einer Compilersprache. Interpreter werden häufiger dann eingesetzt, wenn in einer Kommandosprache kodierte Anweisungen zu übersetzen und auszuführen sind. Typisch ist dann, dass jede Operation damit verknüpft ist, ein komplexes Programm zu aktivieren, wie beispielsweise einen Editor. Java nimmt eine Zwischenstellung ein. Java-Compiler Von einem Java-Compiler (in der engeren Bedeutung des Begriffs) wird ein portierbarer Zwischencode erzeugt. Eigenschaften des Zielbetriebssystems werden nicht berücksichtigt. Dieser Zwischencode wird als Bytecode bezeichnet. 7 Programmieren mit Java 1.1 Grundfunktionen und typische Arbeitsschritte eines Compilers, wesentliche Unterschiede zu einem Interpreter Optimierung des Zwischencodes In der Regel dient ein Schritt in der Erzeugung von Zwischencode zur Codeoptimierung. Damit wird vorbereitet, schließlich einen effizienteren Maschinencode zu generieren. Der Aufwand, der zur Optimierung des Zwischencodes betrieben wird, kann zwischen verschiedenen Compilern sehr unterschiedlich ausfallen. Da Java Bytecode ein portierbarer Zwischencode ist, entfällt eine maschinenspezifische Optimierung. Java Virtual Machine (JVM) Java Bytecode lässt sich auch auffassen als binärer Code in einer portierbaren Maschinensprache für eine CPU Architektur, die als Java Virtual Machine (JVM) bezeichnet wird. In der Regel wird die JVM als Software implementiert, die Bytecode interpretiert. Viele Implementationen der JVM verfügen über just-in-time (JIT) Interpreter, die Java Bytecode direkt in Maschinenbefehle des jeweiligen Zielbetriebssystems übersetzen. Die Ausführungszeit des von Byte- in den Maschinencode des jeweiligen Zielrechners überführten Programms soll sich (nach Herstellerangaben) nur geringfügig von der Ausführungszeit eines Objektprogramms unterscheiden, dessen Quellcode in C++ kodiert worden ist. 8 Programmieren mit Java 1.2 Grundlegende Merkmale von Java Vom Quellcode zur Klasse, die unter der JVM startbar ist nach Heinisch, C., Müller, F. & Goll, J. (2005): Java als erste Programmiersprache. Vom Einsteiger zum Profi. Stuttgart etc.: Teubner, S. 63 9 Programmieren mit Java 1.2 Grundlegende Merkmale von Java 10 Neben der Programmiersprache Java und der JVM ist ein weiteres grundlegendes Merkmal von Java die Java Plattform. Java Plattform Eine Plattform wird definiert durch das API (application programming interface), auf das vom Softwareentwickler zurückgegriffen werden kann, um ein lauffähiges Programm zu codieren. Das jeweilige API wird in der Regel durch das Betriebssystem des Zielrechners festgelegt. Um in einer imperativen Programmiersprache das gleiche Programm unter MS Windows XP, Solaris Version 10 oder einem Unixbasierten Betriebssystem zu entwickeln, verwendet der Softwareentwickler 3 unterschiedliche APIs. In diesem Sinne sind MS Windows, Solaris und Linux als 3 verschiedene Plattformen zu verstehen. Eine Anwendung, die auf einer Java Plattform entwickelt worden ist, läuft unter jedem Betriebssystem, von dem diese Java Plattform unterstützt wird. Dabei sollte eine Java Plattform jedoch nicht als Betriebssystem missverstanden werden. Eine Java Plattform ist eine vordefinierte Menge von Klassen, die mit Java zur Verfügung gestellt und die auch als Java Klassenbibliothek bezeichnet wird. Eine Klasse ist eine Einheit von Java Quellcode, mit der eine Datenstruktur definiert und dazu eine Menge von Methoden bereitgestellt wird, die auf dieser Datenstruktur operieren. Der Begriff Klasse wird im Folgenden noch präzisiert und Klassen werden ausführlich behandelt werden. Eng verwandte Klassen werden unter Java zu Paketen zusammengefasst. Auch der Begriff Paket wird im Folgenden noch präzisiert und Pakete werden ausführlich behandelt werden. Programmieren mit Java 1.2 Grundlegende Merkmale von Java 11 Die Java 1.2 Plattform wird wegen ihrer bedeutenden Erweiterungen auch als Java 2 Plattform oder genauer als Java 2 Software Development Kit, Standard Edition (J2SE), Version 1.5, bezeichnet und umfasst eine Vielzahl von Klassen, die in 166 Paketen zusammengefasst sind. Das folgende Diagramm veranschaulicht den konzeptionellen Aufbau einer J2SE Plattform. Die Java Klassenbibliothek setzt sich zusammen aus der Java Base API Java Standard Extension API. Die 13 mit java eingeleiteten (Haupt-)Pakete der Java Base API werden im Folgenden kurz vorgestellt. Programmieren mit Java 1.2 Grundlegende Merkmale von Java 12 java.applet Dieses Paket stellt Klassen bereit, um den Softwareentwickler bei der Codierung von Applets zu unterstützen. Ein Applet ist ein Java Programm, das in eine Webseite integriert ist, heruntergeladen und auf dem Client-Rechner ausgeführt wird. java.awt Dieses Paket stellt Klassen bereit, um den Softwareentwickler bei der Codierung plattformunabhängiger Anwenderoberflächen und von grafischen Darstellungen zu unterstützen. Dabei steht awt als Abkürzung für abstract window toolkit. java.beans Dieses Paket stellt Klassen bereit, um den Programmierer bei der Entwicklung von Komponenten unter Java zu unterstützen. Dabei steht beans als Bezeichnung für Komponenten des Komponentenmodells unter Java. java.io Dieses Paket stellt Klassen bereit, um den Softwareentwickler bei der Codierung von Ein-Ausgabe Operationen zu unterstützen. Dabei steht io als Abkürzung für input output. java.lang Dieses Paket stellt Klassen bereit, um den Programmierer bei der Entwicklung von Java Quellcode zu unterstützen. Dabei steht lang als Abkürzung für language. java.math Dieses Paket stellt Klassen bereit, um den Softwareentwickler bei der Codierung von numerisch hochgenauen Algorithmen zu unterstützen. Dabei steht math als Abkürzung für mathematics. Programmieren mit Java 1.2 Grundlegende Merkmale von Java 13 java.net Dieses Paket stellt Klassen bereit, um den Softwareentwickler bei der netzwerkzentrierten Programmierung zu unterstützen. Dabei steht net als Abkürzung für networking. java.nio Dieses Paket stellt Klassen bereit, die dazu dienen, die Ein- und Ausgabe großer Datenmengen besser zu unterstützen. So wird zu jedem einfachen Datentyp eine Klasse zum gepufferten Lesen und Schreiben großer Datenmengen bereitgestelt. Dabei steht nio als Abkürzung für new input output. java.rmi Dieses Paket stellt Klassen bereit, die dazu dienen, Schnittstellen zu identifizieren, um deren Methoden von einer nichtlokalen JVM aufrufen zu können. Dabei steht rmi als Abkürzung für remote method invocation. java.security Dieses Paket stellt Klassen bereit, um die Softwareentwicklung nach dem Java Sicherheitskonzept zu unterstützen. Dabei steht security als Bezeichnung für dieses Sicherheitskonzept. java.sql Dieses Paket stellt Klassen bereit, um den Softwareentwickler bei der Programmierung des Zugriffs auf Datenbanken zu unterstützen. Dabei steht sql als Abkürzung für structured query language. java.text Dieses Paket stellt Klassen bereit, um den Softwareentwickler bei der (sprachunabhängigen) Formatierung von Datumsangaben, Uhrzeit und weiteren Textbestandteilen zu unterstützen. Programmieren mit Java 1.2 Grundlegende Merkmale von Java 14 java.util Dieses Paket stellt eine Kollektion von Klassen bereit, die in unspezifischer Bedeutung Hilfsmittel für die Programmierung sind. Dabei steht util als Abkürzung für utilities. Über die Pakete der Java Base API hinaus wird die Java Plattform durch die Pakete der Java Standard Extension API erweitert. Diese Pakete sind als echte Erweiterung in dem Sinne zu verstehen, dass sie in der Regel nicht mit der SDK ausgeliefert werden. Als Beispiel sei die Java Telephony API (JTAPI) erwähnt. Hauptanwendungsgebiete Hauptanwendungsgebiete von Java sind die Entwicklung von Applets, von plattformunabhängigen Anwendungen, von Serveranwendungen (Servlets), von Anwendungen auf Handys oder ähnlichen mobilen Geräten (MIDlets) und von ’Server-Seiten’ (Java Server Pages), um dynamisch Internetseiten generieren zu lassen. Integrierte Entwicklungsumgebung Integrierte Entwicklungsumgebungen dienen dazu, den Programmierer bei der Softwareentwicklung in einer Programmiersprache zu unterstützen. Sie umfassen unter Java neben Compiler und Java Plattform weitere Softwarekomponenten, wie einen Debugger und einen spezifischen Editor, um Quellcode zu erfassen und komfortabel korrigieren zu können, und bieten ferner Werkzeuge zur Verwaltung von Softwareprojekten an. Borland JBuilder 2005 Developer versteht sich als eine integrierte Entwicklungsumgebung für Software, die in Java codiert wird. 15 Programmieren mit Java 1.3 Verwandtschaftsbeziehungen einiger (imperativer) Programmiersprachen im Überblick 1.3 Verwandtschaftsbeziehungen einiger (imperativer) Programmiersprachen im Überblick In den Anfängen der EDV war das Erstellen eines Programms äußerst mühsam und sehr zeitaufwendig: ➘ Programme wurden in Maschinensprache, später auch in Assembler kodiert. ➘ Die Umwandlung in Maschinensprache erfolgte durch Programme. Eine wesentliche Voraussetzung dafür, ein lauffähiges Programm zu erstellen, waren weitreichende Kenntnisse der Eigenschaften der Hardware. Der überwiegende Anteil an den Kosten des Betriebs einer Rechenanlage entfiel auf den Bereich Programmierung und Korrektur von Programmierfehlern. erstes Abstraktionsniveau Formulierung von Ausdrücken als Quellcode ohne direkt auf Maschinenregister zugreifen zu müssen Eine Arbeitsgruppe der IBM entwickelte Ende 1953 die erste höhere Programmiersprache: ➘ FORTRAN (FORmula TRANslation) 16 Programmieren mit Java 1.3 Verwandtschaftsbeziehungen einiger (imperativer) Programmiersprachen im Überblick Die Verfügbarkeit einer höheren Programmiersprache bedeutete eine weitreichende Innovation: ➘ Nicht nur Computerexperten, sondern auch andere Wissenschaftler und Ingenieure erhielten Zugang zu den Leistungen von Rechenanlagen. ➘ Ein Programmierer musste nicht mehr in erster Linie Eigenschaften der Hardware kennen und berücksichtigen, sondern konnte sich auf die softwareseitigen Aspekte einer Problemlösung konzentrieren. Seither haben sich weitere imperative Programmiersprachen etabliert. zweites Abstraktionsniveau Kontrollstrukturen und Paradigma des strukturierten Programmierens Algorithmus Algorithmus im engeren Sinn bezeichnet eine Rechenvorschrift. Allgemeiner schreibt ein Algorithmus vor, welche Handlungen in welcher Reihenfolge auszuführen sind, um ein angestrebtes Resultat zu erreichen. Prozessor Um einen Algorithmus auszuführen, um das angestrebte Ziel zu erreichen, bedarf es eines Hilfsmittels, eines ausführenden ‘Organs‘, im engeren Sinne eines Prozessors. 17 Programmieren mit Java 1.3 Verwandtschaftsbeziehungen einiger (imperativer) Programmiersprachen im Überblick Beispiel aus dem Alltag für einen Algorithmus: Kochrezept Beispiel aus dem Alltag für einen Prozessor: Koch Merkmale, die einen Algorithmus charakterisieren ➘ eine Menge von Objekten, die nach einer Vorschrift zu bearbeiten sind ➘ eine Menge von Operationen, die auf den Objekten auszuführen sind ➘ einen Anfangszustand, in dem sich die Objekte befinden, bevor sie bearbeitet werden ➘ einen Endzustand, in dem sich die Objekte befinden, nachdem sie entsprechend der Vorschrift bearbeitet worden sind In einem Programm, das in einer Programmiersprache formuliert ist, muss im Unterschied zu einem Kochrezept jede Anweisung an das ausführende Organ, den Prozessor, explizit und eindeutig angegeben sein. Sequentielle und parallele Ausführung von Anweisungen Im einfachsten Fall werden die Anweisungen, die ein Programm an den bestimmten Prozessor enthält, sequentiell abgearbeitet, also in der Reihenfolge, die im Programm vorgegeben ist. 18 Programmieren mit Java 1.3 Verwandtschaftsbeziehungen einiger (imperativer) Programmiersprachen im Überblick Werden Anweisungen, die ein Programm enthält, gleichzeitig von mehr als einem Prozessor arbeitsteilig ausgeführt, liegen parallele Betriebssystem-Prozesse vor. Quasi parallele Prozesse Prozesse können innerhalb eines Betriebssystem-Prozesses auch quasi parallel ablaufen. Dabei bezeichnet quasi parallel den Fall, in dem mehrere Prozesse in einer spezifischen Reihenfolge sequentiell von einem Prozessor ausgeführt werden. Beispielsweise werden innerhalb eines Server-Betriebssystem-Prozesses verschiedene Anwenderanfragen quasi parallel abgearbeitet. Solche Prozesse werden im Angelsächsischen als ’Threads’ bezeichnet und im Folgenden noch ausführlich behandelt werden. Kontrollfluss Die Reihenfolge der Ausführung von Anweisungen wird als Kontrollfluss bezeichnet. Kontrollstruktur Eine Anweisung, mit der die Reihenfolge der Ausführung von Anweisungen beeinflusst wird, heißt Kontrollstruktur. Wird von einem ausschließlich sequentiellen Programmablauf ausgegangen, dann umfasst eine imperative Programmiersprache Kontrollstrukturen für die ➘ Selektion ➘ Iteration ➘ Sequenz. 19 Programmieren mit Java 1.3 Verwandtschaftsbeziehungen einiger (imperativer) Programmiersprachen im Überblick Sequenz und Block Sequenz beschreibt vereinfachend, dass Anweisungen in der Reihenfolge bearbeitet werden, in der sie im Programm angegeben sind. Jede Anweisung einer Sequenz wird auch als sequentielle Anweisung bezeichnet. Die Kontrollstruktur für die Sequenz ist der Block. Sequentielle Anweisung Eine sequentielle Anweisung ist dadurch charakterisiert, dass sie nur über die unmittelbar vorangehende Anweisung zugänglich ist und ihr einziger Ausgang in die unmittelbar folgende Anweisung führt. Kontrollstrukturen werden im Folgenden noch ausführlich behandelt werden. Beispiel für Iteration und Selektion: Euklidscher Algorithmus zur Bestimmung des ggT von 2 natürlichen Zahlen Bezeichne x eine natürliche Zahl wie auch y . Solange x ≠ y gilt, wiederhole die folgenden Operationen (Iteration): Falls x < y , berechne y − x und weise das Ergebnis y zu. Andernfalls berechne x − y und weise das Ergebnis x zu (Selektion). Ist der Wert von x identisch mit dem Wert von y , dann geben x wie y den Wert des ggT an. 20 Programmieren mit Java 1.3 Verwandtschaftsbeziehungen einiger (imperativer) Programmiersprachen im Überblick Der Euklidsche Algorithmus zur Berechnung des ggT von 2 natürlichen Zahlen wird demnach charakterisiert durch: ➘ die zweielementige Menge von Objekten {x, y} , wobei o.B.d.A. x ≠ y vorausgesetzt wird ➘ die Menge von Operationen { =, ! =, <, −} . Dabei bezeichnet = den einfachen Zuweisungsoperator, ! = den Ungleichheitsoperator, < den Kleiner-als-Operator und − den arithmetischen Operator für die Subtraktion. Damit ist der Algorithmus jedoch noch nicht vollständig charakterisiert. Anfangs- und Endzustand sind noch zu beschreiben. Anfangszustand: Variablen und ihre Initialisierung Um die Ausführung des Algorithmus für 2 beliebige natürliche Zahlen realisieren zu können, werden 2 Variablen, x und y , eingeführt. Die Ausführung des Algorithmus beginnt damit, dass diesen Variablen Anfangswerte zugewiesen werden. Unter imperativen Programmiersprachen werden Speicherbereiche, auf die Schreibzugriff zulässig ist, auch als Variablen bezeichnet. Die Zuweisung eines Anfangswerts an eine solche Variable heißt auch Initialisierung. Auf die Begriffe Variable und Initialisierung wird im Folgenden noch ausführlich eingegangen werden. Endzustand 21 Der gewünschte Endzustand der Variablen x und y wird so festgelegt, dass x = = y gilt, wobei = = den Gleichheitsoperator bezeichnet. 22 Programmieren mit Java 1.3 Verwandtschaftsbeziehungen einiger (imperativer) Programmiersprachen im Überblick Strukturierte Programmierung Ein Programm, in dem ausschließlich Kontrollstrukturen mit der Eigenschaft einer sequentiellen Anweisung eingesetzt werden, unterliegt den Regeln der strukturierten Programmierung. drittes Abstraktionsniveau Klassen (abstrakte Datentypen) und Paradigma des objektorientierten Programmierens Zentrale Begriffe auf der Ebene der Sprachstrukturen sind ➘ Klasse oder abstrakter Datentyp ➘ abgeleitete Klasse und Klassenhierarchie ➘ polymorphe Operation. Klasse Klasse bezeichnet einen abgeschlossenen Baustein, der sowohl die zugehörigen Datenkomponenten, die Verfahren zum Zugriff auf diese Datenkomponenten als auch die zugehörigen Algorithmen umfasst. Die Trennung von Daten und Algorithmen wird aufgehoben. Unter Java bildet eine Klasse eine Einheit aus Datenkomponenten und der Definition von Funktionen, die auch als Methoden bezeichnet werden. Datenkomponenten und Methoden einer Klasse werden auch als ihre Elemente bezeichnet. Dieses Merkmal wird auf konzeptioneller Ebene als Datenkapselung bezeichnet. 23 Programmieren mit Java 1.3 Verwandtschaftsbeziehungen einiger (imperativer) Programmiersprachen im Überblick Datenkomponenten, die zu einer Klasse gehören, dienen dazu, die Informationen zu beschreiben, die zu dieser Klasse gehören. Eine Datenkomponente ist im Wesentlichen die Deklaration einer Variablen. Methoden beschreiben die Fähigkeiten, die zu einer Klasse gehören. Eine Methode ist im Wesentlichen die Definition einer Funktion. abgeleitete Klasse, Klassenhierarchie Von einer Klasse lassen sich weitere Klassen ableiten. Eine abgeleitete Klasse übernimmt (erbt) die Datenkomponenten und Methoden ihrer Elternklasse. Ausgehend von einer Basisklasse kann eine Hierarchie aus Eltern- und Kindklassen aufgebaut werden. Dieses Merkmal wird auf konzeptioneller Ebene als Vererbung bezeichnet. polymorphe Operation Wenn zu einer Operation mehrere Methoden in unterschiedlichen Klassen mit gleichem Namen und identischer Schnittstelle existieren, wird eine solche Operation als polymorph bezeichnet. Demnach stimmt die Deklaration einer Methode in einer Klassenhierarchie überein, wobei jede Klasse in der Hierarchie diese Methode in einer für sich geeigneten Art und Weise implementiert. Beispiel für eine polymorphe Operation ist das Verschieben einer ebenen geometrischen Figur auf dem Bildschirm. Im Kontext von Sprachkonzepten heißt dieses Merkmal Polymorphismus. 24 Programmieren mit Java 1.3 Verwandtschaftsbeziehungen einiger (imperativer) Programmiersprachen im Überblick Beispiel für eine Klasse Ggt : Euklidscher Algorithmus zur Bestimmung des ggT von 2 natürlichen Zahlen Die Klasse Ggt verfügt über zwei Datenkomponenten, x und y , von geeignetem Datentyp. Ferner hat die Klasse einen Konstruktor, um x und y zu initialisieren. Die Klasse wird über eine Methode verfügen, um den Wert von x zu setzen, über eine weitere Methode, um den Wert von y zu setzen, über eine Methode, um den Wert von x zu lesen sowie über eine Methode, um den Wert von y zu lesen. Schließlich wird zu der Klasse eine Methode gehören, die dazu dient, den ggT von x und y nach dem Euklidschen Algorithmus zu bestimmen. Wird eine Klasse von der Klasse Ggt abgeleitet, dann verfügt die abgeleitete Klasse über die Datenkomponenten und Methoden der Elternklasse, also beispielsweise über eine Methode, um den ggT von x und y nach dem Euklidschen Algorithmus zu bestimmen. Klassen, abgeleitete Klassen, der Aufbau von Klassenhierarchien und polymorphe Operationen werden im Folgenden noch ausführlich behandelt werden. Programmieren mit Java 1.3 Verwandtschaftsbeziehungen einiger (imperativer) Programmiersprachen im Überblick 24 25 Programmieren mit Java 1.3 Verwandtschaftsbeziehungen einiger (imperativer) Programmiersprachen im Überblick (1) 1954 - 57 FORTRAN (FORmula TRANslation) Programmiersprache für technisch-naturwissenschaftliche Anwendungen (2) 1959 - 60 COBOL (COmmon Business Oriented Language) Programmiersprache für kaufmännische Anwendungen zur Verarbeitung umfangreicher Datenmengen (3) 1958 - 60 ALGOL 60 (ALGOrithmic Language) Programmiersprache für mathematisch-naturwissenschaftliche Anwendungen (4) 1963 - 64 PL/1 (Programming Language 1) Programmiersprache, die aus ALGOL-, COBOL- und FORTRAN-Elementen entwickelt wurde mit dem Ziel, eine einheitliche Programmiersprache sowohl für den kommerziellen und als auch den technisch-wissenschaftlichen Bereich zu schaffen (5) 1962 - 67 SIMULA Programmiersprache, die ursprünglich entworfen worden ist, um Software zur Simulation diskreter Ereignisse zu entwickeln und die einen ausgeprägten Einfluss auf die Weiterentwicklung von Programmiermethoden genommen hat (6) 1963 - 68 ALGOL 68 Weiterentwicklung von ALGOL 60 (7) 1968 - 71 Pascal Programmiersprache, die strukturiertes Programmieren konzeptionell unterstützt und in der Ausbildung favorisiert wurde (8) 1969 BCPL (Basic Combined Programming Language) Programmiersprache, mit der die Struktur von ALGOL und die Leistungsfähigkeit von Assembler kombiniert werden (9) 1970 - 72 Smalltalk rein objektorientierte Programmiersprache, die u.a. auf Simula und LISP basiert und sich durch ihre Einfachheit auszeichnet (10) 1973 - 78 C Programmiersprache, die blockstrukturiertes Programmieren unterstützt und hardwarenahes Programmieren erlaubt 26 Programmieren mit Java 1.3 Verwandtschaftsbeziehungen einiger (imperativer) Programmiersprachen im Überblick (11) 1977 - 80 Modula Programmiersprache, die auf Pascal basiert, weitgehend maschinenunabhängig ist und Konzepte der Softwaretechnik unterstützt, wie Modularisierung (12) 1977 - 80 Ada Programmiersprache, die für eingebettete Systeme entwickelt worden ist, und vor allem in Anwendungsbereichen mit hohen Sicherheitsanforderungen (u.a. kommerzielle Luftfahrt, US Verteidigungsministerium) eingesetzt wird (13) 1980 - 1983 C++ Weiterentwicklung der Programmiersprache C zu einer hybriden Programmiersprache, mit der objektorientiertes Programmieren unterstützt wird (14) 1995 Java rein objektorientierte Programmiersprache mit einer C++-ähnlichen Syntax, aus der portierbarer Bytecode generiert wird und von der die Entwicklung von Applets, Servlets, MIDlets und ’Server-Seiten’ unterstützt wird Einführende Informationen zu einer Auswahl höherer Programmiersprachen wird unter der folgenden URL angeboten: http://www.engin.umd.umich.edu/CIS/course.des/cis400 Programmieren mit Java 1.4 Phasen in der Entwicklung objektorientierter Programme 27 1.4 Phasen in der Entwicklung objektorientierter Programme Phase 0: Kernaufgabe der zu entwickelnden Software grundlegende Leistung Phase 1: Anforderungsanalyse und Zusammenstellung der Anwendungsfälle (Gesamtmenge an Szenarien) Ein Anwendungsfall ist eine Aktion zwischen einem System und einem Akteur (einer Person, einem anderen Rechnersystem) Bestimmung der Akteure Bestimmung der möglichen (zulässigen) Aktionen Wie übt ein Akteur eine Aktion aus? Varianten des Agierens Probleme, Ausnahmen Programmieren mit Java 1.4 Phasen in der Entwicklung objektorientierter Programme 28 Leffingwell, D. & Widrig, D. (2003): Managing Software Requirements: A Use Case Approach. The Addison-Wesley Object Technology Series. Reading etc.: Addison-Wesley Professional, 2nd ed. Schneider, G. & Winters, J.P. (2001): Applying Use Cases: A Practical Guide. Amsterdam etc.: Addison-Wesley Longman, 2nd ed. Seemann, J. & Gudenberg, J. W. v. (2004): Software-Entwurf mit UML. Objektorientierte Modellierung mit Beispielen in Java, Berlin etc.: Springer, 2., überarb. Aufl. Phase 2: Grobdesign Beschreibung der Klassen und insbesondere der Schnittstellen der Methoden der jeweiligen Klasse Eine Klasse ist, wie bereits angedeutet, eine vom Programmierer definierte Datenstruktur, die, um eine spezifische Aufgabe zu behandeln, gemeinsame Datenkomponenten, Methoden zum Zugriff auf diese Datenkomponenten und gemeinsame Funktionalitäten zusammenfasst. Zaehler Klassenname zaehlerstand Datenkomponente setze_zaehlerstand Methoden ermittle_zaehlerstand bereinige_zaehlerstand erhoehe_zaehlerstand reduziere_zaehlerstand Programmieren mit Java 1.4 Phasen in der Entwicklung objektorientierter Programme 29 Die zu jeder spezifischen Aufgabe gehörige Datenstruktur wird auf eine Klasse abgebildet. Jede Klasse sollte möglichst einfach aufgebaut sein. Jede dieser Klassen wird sich im Verlauf der Arbeit am Grobdesign weiterentwickeln. Der Anspruch, bereits zu Beginn der Phase 2 jede Klasse vollständig zu spezifizieren, erweist sich in der Regel als zu restriktiv. Erste kompilierbare Quellcodeanteile werden formuliert, um die Leistungsfähigkeit und den Leistungsumfang einzelner Klassen zu überprüfen. Phase 3: Entwicklung des Kerns des Zielprogramms Übertragung des Grobdesigns in kompilier- und ausführbaren Quellcode, um das Grobdesign im Abgleich zu dem Resultaten aus der Phase 1 auf seine Tragfähigkeit zu überprüfen Es wird eine erste, in der Regel unvollständige Fassung des Zielprogramms entwickelt, so dass sich ausführbarer Code generieren lässt. Kritische Leistungsmerkmale werden herausgearbeitet und im Quellcode lokalisiert. Aus den Erfahrungen, die mit dieser ersten Fassung des Zielprogramms gesammelt werden, resultiert in der Regel eine Weiterentwicklung des Grobdesigns. Insgesamt handelt es sich bei der Phase 3 um einen ersten Schritt der Programmentwicklung in einer Sequenz von Schritten, also um die Entwicklung eines Rahmens, der in der Phase 4 ausgefüllt wird. Programmieren mit Java 1.4 Phasen in der Entwicklung objektorientierter Programme 30 Phase 4: Iteration Einfügen weiterer begrenzter Teilprojekte in den bereits entwickelten Rahmen, um die Funktionalität des bestehenden Programms in Richtung auf das Zielprogramm zu erweitern Die Grundlage für jeden Iterationsschritt bildet ein weiterer Anwendungsfall, der in das bestehende Programm integriert wird. Dabei ist jeder Anwendungsfall als ein Bündel untereinander verknüpfter Funktionalitäten zu verstehen. Ein besonderer Vorteil dieser Vorgehensweise besteht darin, dass sich nach jedem Iterationsschritt der Status des bestehenden Programms direkt bestimmen lässt. Die Phase 4 wird beendet, wenn das entwickelte Programm die postulierten Funktionalitäten (möglichst) fehlerfrei erbringt. Phase 5: Evolution Übergang in eine Phase fortwährender Modifikation des Programms nach Abschluss des primären Entwicklungszyklus Aus den Tests des bestehenden Programms und aus seinen praktischen Anwendungen erwächst ein tieferes Verständnis für die zu bewältigenden Aufgaben und für den internen Aufbau des Quellcodes. Daraus resultieren Modifikationen des bestehenden Programms, die sukzessive zu leicht verständlichem und eleganterem Quellcode wie auch zur verbesserten Funktionalität führen sollten. Einer der Hauptvorteile objektorientierten Programmierens besteht darin, dass Modifikationen des jeweiligen Programms für den (die) Programmierer eine vergleichsweise leicht zu bewältigende Aufgabe ist. Programmieren mit Java 1.5 Typographie 31 1.5 Typographie Schriftarten, Schreibweisen und Metasymbole Eine vollständige und formal exakte Beschreibung der Syntax der Programmiersprache Java in einer Metasprache ist im Kontext der Veranstaltung nicht vorgesehen. Um die Lesbarkeit des Texts zu verbessern und um inhaltliche Bezüge zu verdeutlichen, werden unterschiedliche Schriftarten und Darstellungsformen verwendet. Beispiele, auch (auszugsweise) Programmbeispiele, und die Syntax der Programmiersprache Java werden in der Schriftart Courier dargestellt und stets in einen Rahmen gesetzt. Auszüge aus Beispielen und Bezeichnungen der Syntax im Text werden ebenfalls in Courier dargestellt. Kommentare im Quellcode und literale Zeichenkonstanten werden in der üblichen Groß-Kleinschreibweise formuliert. Auch einige syntaktische Metasymbole werden im Folgenden eingeführt werden. Eine optionale Angabe in der Syntax wird kursiv gesetzt werden. Eine optional zu wiederholende Angabe angabe wird durch das Metasymbol angabe... beschrieben werden. Dabei schließt angabe... den Fall mit ein, dass angabe nicht wiederholt wird. Wird ein Begriff festgelegt, so wird er in der charakterisierenden Beschreibung fett und kursiv gesetzt.