1.5 Programmstruktur Strukturierung von Programmen durch Unterprogramme Module Klassen ... u.a. 1.5 1 Eine Klasse „mit Gedächtnis“: class A { static int state = 0; static void set(int x) { state = x; } static int get() { return state; } } 1.5 2 Modul (das Modúl, die Module; module) Klasse (class) Bezeichnungen für Sprachkonstrukte, mit denen verschiedenartige Elemente (members) mit gemeinsamem Gültigkeitsbereich zusammengefasst werden können. Im einfachsten Fall besteht ein Programm aus 1 Modul/Klasse, im allgemeinen Fall aus beliebig vielen. 1.5 3 1.5.1 Klassen in Java Syntax (vereinfacht): ClassDeclaration: [ public ] class Identifier ClassBody ClassBody: { { { Modifier } Declaration } } Declaration: FieldDeclaration MethodDeclaration ClassDeclaration ..... Initializer FieldDeclaration: VariableDeclaration Initializer: Block Modifier: public | static | final | . . . . . „member“ class Math { // math stuff static final double PI = 3.1415926535898; static double sin(double x) { ..... } static double sqrt(double x) {..... } static long seed; static int count; // for random // number of calls to random static { count = 0; } static { seed = 71; } // not required here // initialization of random seed static long random() { count++; long result; ................. return result; } } 1.5 5 Terminologie, Semantik, Konventionen: long seed vereinbart eine Klassenvariable (= nichtlokale Variable), die die Voreinstellung (default, 1.2.1 ) 0 hat; die Konstante PI wird explizit initialisiert. { seed = 71; } sorgt für eine weitere explizite Initialisierung. Klassennamen sollen mit einem Großbuchstaben beginnen. Gültigkeitsbereich aller vereinbarten Namen ist die gesamte Klasse. Variablennamen können allerdings in einer Methode durch andere Namen verdeckt werden (eingeschränkte Sichtbarkeit, 1.3.2) 1.5 6 Beachte: Durch Zuweisung an eine nichtlokale Klassenvariable kann ein Methodenaufruf ein Effekt bewirken, der die Ausführung der Methode überdauert und der sonst nur durch Variablenparameter (oder Parameter mit anderen Typen, 1.6) erzielbar wäre. Ein solcher Effekt wird auch Nebenwirkung (side effect) der Methode genannt. Funktionsprozeduren, die tatsächlich eine Funktion realisieren, sollten i.d.R. keine Nebenwirkungen haben! 1.5 7 1.5.2 Überladen von Namen Erweiterung des Signaturbegriffs (1.4.5): Signatur einer Klassenelements - Name bzw. - Name und Anzahl und Typen der Argumente (bei Methode) Elemente einer Klasse müssen verschiedene Signaturen haben. ! Diese Regel schwächt das Kollisionsverbot von Namen ab. Überladen (overloading) eines Namens liegt vor, wenn es mehrere gleichnamige Elemente mit verschiedenen Signaturen gibt. 1.5 8 Beispiel - die folgende Klasse ist fast korrekt: class static static static static static } neg { int neg = -1; void neg(){neg = neg(neg);} int neg(int x){return -x;} int neg(float x){return -(int)x;} float neg(float x){return -x;}// collides ? Welches Element ist gemeint, wenn der Name neg benutzt wird ? Z.B. ... neg = neg(neg); ... (s.o.) Wird durch den jeweiligen Kontext aufgelöst: 1.5 9 ... neg ... ? 1. Überladen wird statisch (!) aufgelöst: Falls keine öffnende Klammer folgt: Variable. [Fehler, falls keine solche Variable sichtbar.] 2. Sonst bei n Argumenten: Methode mit n Argumenten und passenden Argumenttypen, sofern es genau eine solche gibt. [Fehler, falls keine solche Methode sichtbar.] 3. Falls mehrere solche: die „spezifischste Methode“ (d.h. verwirf alle Methoden, zu denen es eine Methode gibt, deren Argumenttypen verträglich mit den Argumenttypen jener Methode sind). [Fehler, falls diese Methode nicht eindeutig bestimmbar.] 1.5 10 Beispiel 1: ... static void neg(){neg = neg(neg);} ... int neg (1.) neg(int x) Beispiel 2: void m(int i, char c) void m(short s, int i) m(in,ch) m(by,in) (3.) int short char byte m(ch,by) falsch, weil weder noch passt (2.) m(by,ch) falsch, weil sowohl als auch passt (3.) 1.5 11 1.5.3 Bezugnahmen zwischen Klassen class Math { ..... } class MoreMath { static double cos(double x) { return Math.PI/2 - Math.sin(x); } static double tg(double x) { return Math.sin(x)/cos(x); } ... } „Punktnotation“ 1.5 12 Zusammengesetzter Name (qualified identifier) (Punktnotation) QualifiedIdentifier: Identifier { . Identifier } ist überall dort erlaubt, wo bisher Identifier angegeben war. Klassendiagramm (class diagram) ist graphische Darstellung der Klassen eines Programms (als Rechtecke) und der Bezüge zwischen ihnen (durch Pfeile). Standardisierte Darstellungsform: UML (Unified Modeling Language) 1.5 13 1.5.4 Pakete Wenn ein Programm hunderte oder tausende von Klassen umfasst, sorgt eine weitere Strukturierung des Namensraums für bessere Übersicht: Paket (package) ist eine benannte Menge von Klassen. (Konvention: Paketname beginnt mit einem Kleinbuchstaben.) Programm umfasst ein oder mehrere Pakete. In einem Paket kann man auf die Klassen des gleichen Pakets und - mit Einschränkungen - auf die Klassen anderer Pakete Bezug nehmen. 1.5 14 package math; // falls nicht vorhanden, „anonymes Paket“ public class Math { ..... } public class MoreMath { public static double cos(double x) { return Math.PI/2 - Math.sin(x); } public static double tg(double x) { return Math.sin(x)/cos(x); } ... } public ? 1.5 15 public sorgt dafür, dass die entsprechenden Elemente auch außerhalb des Pakets sichtbar sind: Vollständiger Name (fully qualified name) z.B. von tg ist math.MoreMath.tg Importklausel import math.MoreMath; erlaubt Verzicht auf die Angabe des Paketnamens: 1.5 16 package myExtendedMath; import math.MoreMath; public class Ctg { public static double ctg(double x) { return 1/MoreMath.tg(x); } } import math.*; sorgt dafür, dass auf alle öffentlichen Klassen aus math ohne vorangestellten Paketnamen math Bezug genommen werden kann. 1.5 17 Syntax (vereinfacht): CompilationUnit: [ PackageHeader ] { Import } PackageBody PackageHeader: package Identifier ; Import: import ClassIdentifier ; import PackageIdentifier . * ; PackageBody: { ClassDeclaration } ClassIdentifier ist ein zusammengesetzter Name, der mit einem PackageIdentifier beginnt. 1.5 18 Ein Paket kann nicht nur Klassen, sondern auch Unterpakete (subpackages) enthalten. Beispiel: Anstelle des obigen math hätten wir die folgenden drei Pakete einführen können: package math; ..... // z.B. nur mit einigen Konstanten package math.trigonometry; ..... package math.random; ..... Achtung: 1.5 Unterpakete werden nicht durch textuelles Schachteln von Paketen eingeführt, sondern schlicht durch entsprechende Namensgebung mit Punktnotation. 19 1.5.5 Bibliotheksklassen Jedes Programmiersprachen-System umfasst von vornherein eine große Anzahl von Klassen/Modulen, die häufig benötigte Standard-Funktionalitäten bereitstellen - in sogenannten Klassen-Bibliotheken (class libraries) (bzw. Modul-Bibliotheken). Die Gesamtheit der sichtbaren Elemente einer Bibliothek (Klassen und ihre Methoden, Variablen, ...) wird auch Bibliotheksschnittstelle (application programming interface, API) genannt. 1.5 20 Einige Pakete der Java-Bibliothek: java.lang enthält die am häufigsten benutzten Klassen, z.B. Math mit Methoden sqrt, ... und System mit Methoden exit, ... - muss nicht explizit importiert werden! java.io enthält Klassen für die Ein/Ausgabe java.util enthält diverse nützliche Hilfsklassen java.util.zip enthält Klassen für die Kompression und Dekompression von Daten 1.5 21 1.5.6 Übersetzung und Ausführung Übersetzer (compiler für Hochsprache, assembler für Maschinensprache) macht aus dem vom Programmierer entwickelten Quellcode (source code) Objektcode (object code, binary code), bestehend aus Maschinenbefehlen des Prozessors, oder Zwischencode (intermediate code, byte code), der maschinenunabhängig ist und von einem Interpretierer (interpreter) ausgeführt wird. 1.5 22 Attraktiv ist getrennte Übersetzung (separate compilation) von Programmteilen: Korrektur erfordert nur Neuübersetzung eines Teils übersetzte Teile können in Bibliotheken vorgehalten werden Binden (linking, linkage) fügt getrennt übersetzte Teile zu einem Programm zusammen. Laden (loading) kopiert Programm in den Arbeitsspeicher, wo es gestartet wird. 1.5 23 Statisches Binden (static linking): Nachdem alle benötigten Programmteile übersetzt sind, werden sie durch den Binder (linker, linkage editor) zu einem Programm zusammengefügt. Übersetzer Binder 1.5 Bibliothek Lader 24 Aufgaben des Binders: Physisches Zusammenfügen der übersetzten Teile entsprechendes Verschieben der relativen Adressen Auflösen von externen Bezugnahmen Bindender Lader (linking loader) erlaubt dynamisches Binden - entweder erst beim Laden - oder sogar erst während der Ausführung (nach Bedarf!) 1.5 25 Java: Übersetzung nach Zwischencode (byte code) Java Virtual Machine (JVM) praktiziert dynamisches Laden und interpretiert Zwischencode Befehle: javac MyProg.java übersetzt Quelldatei und generiert für deren Klassen sowie für jede von diesen direkt oder indirekt benutzte Klasse jeweils eine Datei mit Zwischencode (.class). java MyProg 1.5 lädt die Klasse MyProg.class und startet deren Methode public static void main (gegebenenfalls Nachladen weiterer Klassen). 26