7. Klassen, Objekte, Typen 7.1 7.2 7.3 7.4 7.5 Klassen zur Programmstrukturierung Verweise (Zeiger) Elementare Speicherverwaltung Verbundtypen Klassen und Objekte 7.6 Konstruktoren Klassen (class) werden verwendet 1. zur Strukturierung von Programmen analog zum Modul in anderen Programmiersprachen - besonders Modula 2 2. als Typen, die als Schablone für die Erzeugung von Objekten dienen → objektorientierte Programmierung(*) (*) nicht das einzige Merkmale für OOP, siehe unten hs / fub - alp2-07 2 Beispiel: Class Math { static static static ... static static static static } ... } final double PI = 3.1415926535898 double abs(double a){...} double cos(double x){...} double log(double x){...} int count; // counts number of calls of random() {count = 0;} // explizite Initialisierug double random(){ count++; ... return ...; Terminologie: (Statische) Methoden und Variablen (Felder, fields), Sammelbegriff für beide Arten: Komponenten, Mitglieder (member) hs / fub - alp2-07 3 Klassenvariable z static int count ist wegen static eine Klassenvariable z Explizite Initialisierung durch anonymen Block möglich. z Klassenvariablen sind nichtlokal. z Es gibt in einem Programm nur ein Exemplar der Variable. → deshalb zählen mit count im Bsp. möglich. Verwunderlich? Bisher eher nicht… Gültigkeitsbereich alle vereinbarten Bezeichner sind in der gesamten Klasse gültig, können aber verdeckt werden… hs / fub - alp2-07 4 public class NumberCuriosity { static int oNumber = 077; static int dNumber = 77; public static void testeSichtbarkeit(){ int oNumber = 999; NumberCuriosity.oNumber++; System.out.println(oNumber+", "+ NumberCuriosity.oNumber); } public static void main (String [] args) { System.out.println(oNumber); testeSichtbarkeit(); } } Achtung: nichtlokale Effekte (Nebeneffekte) durch Methode hs / fub - alp2-07 5 Programm .. besteht aus einer (selten!) oder mehreren (vielen!) Klassen. … Module: analog Punktnotation zur Adressierung von Komponenten anderer Klassen Identifier {.Identifier}0n class MyClass{ static double myFunction(double x) { return Math.sin(x) * Math.exp(-x); } … } hs / fub - alp2-07 6 Importe und Benennung Vollständiger Name (fully qualified name): Paket-, Klassen- und Komponentenname in Punktnotation (*) math.myMathPackage.Math.PI Komponente, hier KONSTANTE Klassenname (Konvention:groß) Paketname (Konvention: klein) mit Unterpaket (*) Pakete können ferner hierarchisch in Unterpaketen organisiert sein hs / fub - alp2-07 7 … Benennung von Komponenten import <Klassenbezeichner>; - single type import - Komponenten der Klasse können ohne Qualifizierung durch Paketbezeichner verwendet werden. import math.myMathPackage.Math; ...{ double res = Math.sin(x); …} Jetzt kann auf alle öffentlichen Klassen von math ohne Paketnamen zugegriffen werden: import math.*; ...{ double res = Math.sin(x); …} hs / fub - alp2-07 8 7. Klassen, Objekte, Typen 7.1 7.2 7.3 7.4 7.5 Klassen zur Programmstrukturierung Verweise (Zeiger) Elementare Speicherverwaltung Verbundtypen Klassen und Objekte 7.6 Konstruktoren Klassendiagramme Grafische Darstellung von Klassen und deren Beziehungen mit Hilfe von UML Klassendiagrammen. "Vererbungsbeziehung" (später) aus Wikipedia hs / fub - alp2-07 10 Bibliotheksklassen z Jedes Programmiersprachsystem besitzt Programmbibiliotheken (Modul- / Klassenbibliotheken) für häufig benutzte Standardfunktionen. z Java: besonders vielfältige Sammlung von Bibliotheksklassen. Wegen Plattformunabhängigkeit und guter Dokumentation? z Bibliotheksschnittstelle definiert die Gesamtheit aller in der Bibliothek sichtbaren Elemente (Anwendungsprogrammschnittstelle, application programming interface, API) hs / fub - alp2-07 11 siehe http://java.sun.com/j2se/1.5.0/docs/api/ hs / fub - alp2-07 12 Getrenntes Übersetzen und Binden z Getrenntes Übersetzen von Programmbestandteilen Stand der Technik. Vorteil: nur geänderte Teile neu zu übersetzen z Erfordert Binden (linkage, linking) der Programmteile zu einem Ganzen z Geschieht vor Ausführung durch statischen Binder (linkage editor) oder dynamisch zur Laufzeit hs / fub - alp2-07 13 Aufgaben eines Binders z Adressumwandlung (address relocation): relative in absolute Adressen Programmteile beginnen mit Adresse 0! Beachte: Adressen sind Adressen im Virtuellen Adressraum, Umwandlung in absolute Speicheradressen per Hardware bei jedem Speicherzugriff (!) z Auflösen externer Bezüge, besonders von Unterprogrammaufrufen hs / fub - alp2-07 14 Übersetzte Klassendatei class VMcode {static int prog() { int i; i = 3; int j = 5; return (i+j)*(4+6); } } push(3) push(5) plus push(4) push(6) Konstanten schon ausgewertet: gut! plus mul Method VMcode() 0 aload_0 1 invokespecial #3 <Method ....(> 4 return Method int prog() 0 iconst_3 Lokale Variablen i und j 1 istore_0 2 iconst_5 3 istore_1 4 iload_0 5 iload_1 6 iadd 7 bipush 10 9 imul 10 ireturn erhält man mit javap -c <datei> hs / fub - alp2-07 15 Die Class-Datei „offset“ hexadezimal iconst_3 0x6 erster Befehl der Methode magic number (version d. Compilers usw diverse Längen Ziffern: 0...9,A,B,C,D,E,F iload_1 0x1b iadd 0x60 bipush 10 hs / fub - alp2-07 16 Dynamisches Binden z Binden während des Ladens des Programms (Linkage loader) z Während der Ausführung: der Maschinencode eines aufgerufenes Programm wird bei Aufruf von P geladen, nachdem die Adressanpassung (relocation) stattgefunden hat. zur Laufzeit javac Sourcecode VMcode javac VMcode java Binden/ Laden Ausführen = interpretieren mit VM Bib hs / fub - alp2-07 17 7.2 Verweis und Verweistypen Verweis, Zeiger (reference, pointer) ist ein Wert, der den Bezug auf eine anonyme oder benannte Variable erlaubt. Der Wert ist eine (Speicher-) Adresse. Verweise sind (meist) getypt: Verweistyp* (reference type) Z.B. Modula: TYPE intPtr = POINTER TO INTEGER z.B. C: int *intPtr Alle bisherigen Typen waren Werttypen (value types) hs / fub - alp2-07 18 Wert- und Verweistypen Werttyp T: der Name einer Variablen des Typs ist ein Alias für die Adresse der Variablen int i=1; j = i int j=2; Zuweisung: Kopiersemantik hs / fub - alp2-07 19 Verweistyp T: Name einer Variablen mit Verweistyp T: Alias für die Adresse der Variablen, die die Adresse eines Werts (!) des Typs enthält TYPE iPtr = POINTER TO INTEGER TYPE jPtr = POINTER TO INTEGER NEW(iPtr); NEW(jPtr); jPtr = iPtr; INTEGER-Werte noch undefiniert (!) Zuweisung: Referenzsemantik hs / fub - alp2-07 20 Referenzierung und Dereferenzierung Modula2: Zeiger ermöglichen Erzeugung und Nutzung von anonymen Variablen. VAR i,j: INTEGER; VAR p1,p2: POINTER TO INTEGER; NEW(p1); erzeugt anonyme Variable π und speichert NEW(p2); deren Adresse in p1. i := 4; p2^ := i; ^ ist Dereferenzierungsoperator, p1 := p2; j := p1^; bezeichnet Wert an der entsprechenden Adresse hs / fub - alp2-07 21 Nach Ausführung der Anweisungen: p1 p1=p2 p2 4 NEW(p1) NEW(p1) π 4 4 i j Hier muss dem Programm dynamisch Speicher zugewiesen werden (später) hs / fub - alp2-07 22 Verweise und Adressen in C Ein Unterschied: benannten Variablen können mit Zeigern adressiert werden. Adressoperator & liefert Adresse: int i; int *ip; ip = &i; Dereferenzierungsoperator * Adressoperator & In der Vereinbarung spielt * die Rolle von POINTER TO in Modula2. hs / fub - alp2-07 23 Analoges Beispiel in C: int x = 1, y=2; int *ip1,*ip2; ip1 = &x; ip1 enthält Adresse von x x = 4; ip2 = ip1; ip2 enthält Wert von ip1 = Adresse von x *ip2 = 3; Variable, dessen Adresse ip2 enthält, erhält Wert 3 y = *ip1; Variable j erhält Wert der Adresse, auf die ip1 zeigt. Damit: ip1 == &x, ip2==&x, x==3, y==3 ⇒ *ip1 ==3, *ip2==3 hs / fub - alp2-07 24 (Typ-)Sicherheit Adressierung von benannten Objekten gefährlich. int x = 1, y=2; int *ip1,*ip2; ip1 = &x; y = ip1; Jetzt enthält y eine Adresse! Nicht erlaubt: Zuweisung ohne Initialisierung int *ip; *ip1 = 100; Ausschließlich Adressierung anonymer Objekte ist sicherer. hs / fub - alp2-07 25 Verweise Java (Details später) Zur Erinnerung: boolean[] isPrime; isPrime = new boolean[max + 1]; erzeugt ein Feld mit dem Namen isPrime. Tatsächlich handelt es sich bei isPrime um einen Verweistyp! Es gibt keinen Dereferenzierungs- oder Adressoperator in Java! isPrime[n] dereferenziert implizit ! hs / fub - alp2-07 26 Parameterübergabe Wenn eine Sprache (wie C oder Java) nur Wertparameter kennt, kann man mit Verweisen ähnliche Effekte erzielen wie mit VAR Parametern: Achtung:C-Code, swap (x,y) ohne Wirkung int x,y; { int tmp; tmp=x; x=y; y=tmp; } … swap(a,b); Besser, wenn auch nicht schön. swap (px,py) int *px,*py; { int tmp; tmp=*px; *px=*py; *py=tmp; } … swap(&a,&b); hs / fub - alp2-07 27 7.3 Elementare Speicherverwaltung z Vereinbarung einer static Variable veranlasst den Übersetzer Speicher in der durch den Typ festgelegten Größe zu reservieren. class A{ static int i=1; … i++; ..} Übersetzer .. add 0xf124, 1 … 0xf124 0xf128 Programm Daten Die Variable i ist während der Lebensdauer dieser Klasse gültig. hs / fub - alp2-07 28 Automatische dynamische Speicherzuweisung if (…) {…} else { int i =1 while (i>0) { int j = i+1; … } i++; } j i Stapel (stack) Speicherplatz für i und j wird dynamisch, d.h. zur Laufzeit (!)), und automatisch, auf dem Stapel, einem speziellen Speicherbereich im Hauptspeicher, angelegt – und wieder frei gegeben. (C Terminologie: automatic storage) hs / fub - alp2-07 29 Dynamische (programmierte) Speicherzuteilung VAR p1 : POINTER TO INTEGER; ... NEW(p1); Was passiert hier? VAR p1…: Übersetzer legt Speicherplatz für einen Zeiger (Verweis) an (!), ggf. auf Stapel. Die Ausführung des Befehls NEW(p1) legt dynamisch Speicher für ein Objekt des Typs (hier INTEGER) an, auf das p1 zeigt und p1 hat diese Adresse als Wert. ? NEW (p1) Speicherhalde (heap) p1 hs / fub - alp2-07 30 Freigeben von Speicher Dynamisch zugewiesener ("allozierter")(*) Speicher sollte / muss freigegeben werden, wenn er nicht mehr gebraucht wird. Freigabeoperation: DISPOSE (p) Gibt den Speicher frei, der mit NEW(p) zugewiesen wurde. Die Größe des freizugebenden Speichers ergibt sich aus dem Verweistyp. NULL p1 DISPOSE(p1) "hängender Zeiger" (dangling pointer) (*) storage allocation hs / fub - alp2-07 31 Programmierte Speicherzuweisung Operationen zur dynamischen Speicherzuweisung / -freigabe nicht notwendig als Befehle der Sprache. Wegen enger Verbindung zur Betriebssystem: (unverzichtbare) Bibliotheksfunktionen. C, C++: malloc(),free() Programmierte Speicherzuweisung gefährlich! Ohne korrekte Rückgabe Gefahr des Speicherlecks – immer weniger verfügbarer Speicher. hs / fub - alp2-07 32 Automatische Freigabe des Speichers (garbage collection) • Bei modernen Sprachen wie Java, C# üblich. • Reduziert Fehleranfälligkeit signifikant. • Algorithmen nichttrivial und… • … zeitintensiv • Inakzeptabel: Unterbrechung der Programmausführung und Durchkämmen des Speichers auf freizugebende Speicherbereiche. • Deshalb: Aufsammeln der freien Bereiche als Hintergrundaktivität. (Algorithmen in Alp3) hs / fub - alp2-07 33 7.4 Verbundtypen Bisher: Variable eines Typs kann einen Wert enthalten. Ausnahme: Feld (array) als Folge fester Länge von Elementen gleichen Typs. boolean [] prime = new int[MAX]; Verbundtyp (Produkttyp,Tupeltyp) (structure, record type,…) T: Wert wT von T setzt sich aus Werten von n Basistypen T1, …, Tn zusammen. d.h.: wT ∈ T1 ¥ T2 ¥ ... ¥ Tn (Deshalb auch Tupeltyp) hs / fub - alp2-07 34 Werte von Verbundtypen: Datensatz, Verbund, Tupel (record*, tuple**, structure) Verbundtypen (fast) immer mit benannten Komponenten Modula2: TYPE MesswertT RECORD Versuchsnr: INTEGER, Wert: REAL; VAR Messwert: MesswertT; C: typedef struct{int versuchsnr; double wert;}vTyp vTyp d1,d2; (*) NICHT: Rekord (!), (**) besonders in Relationalen Datenbanken ; Typname hs / fub - alp2-07 35 Adressierung von Verbundkomponenten Variablen.Komponentenname Selektor IF (Messwert.Versuchsnr > 10) THEN… if (d1.versuchsnr == 100) {…} hs / fub - alp2-07 36 Verbunde: dynamisch oder automatisch? Beides! VAR x : RECORDTYP; VAR recPtr: POINTER TO RECORDTYP; NEW(recPtr); Speicher auf dem Stapel. Speicher auf der Halde. Analog in C Dagegen C# (!): Verbunde werden aus Verbundtypen mit dem Operator new auf dem Stapel erzeugt ⇒ Gültigkeit auf Block begrenzt, ⇒ automatische Speicherbereinigung. hs / fub - alp2-07 37 7.5 Klassen und Objekte Klassen (class type) sind Verbundtypen! aber; Class MyComplex { static double re,im; } ?? Dagegen i Class Complex { double re,im; } Klassenvariable unzureichend: nur eine komplexe Zahl modelliert! Klassentyp: Verbundtyp, aus dem sich beliebig viele Objekte erzeugen lassen. hs / fub - alp2-07 38 Objekte sind Verweistypen! Java Complex c1,c2; z erzeut einen getypten Verweis, der Objekte des Typs Complex referenzieren kann. z erzeugt kein Objekt. Anfangswert: null , d.h. undefinierter Verweis z Erzeugung von Objekten c1 = new Complex(); Erzeugt wird ein Objekt (Exemplar, instance *) der Klasse instance [engl.] != Instanz !! Vorbelegung: c1.re und c1.im haben Standardwert (int: 0) hs / fub - alp2-07 39 Zugriff auf Komponenten Variablen, die einen Klassentyp besitzen, sind in Java (und C#) immer Verweistypen! Unterschied zu Modula2, C u.a. ! Es gibt keinen Dereferenzierungsoperator. Deshalb: c1.re = 2.0; Implizite Dereferenzierung bei jedem Zugriff auf ein Objekt mit Hilfe einer Variablen vom (Klassen-)typ des Objekts. Java kennt nur diese Verweise. hs / fub - alp2-07 40 Klassen und Objekten: ein Beispiel class Point { Verbundtyp mit Methoden ! int x,y; void translate(int dx, int dy) {x+=dx; y+=dy;} double distance(Point q) {…} } Point p1,p2; Programm//p1.x = 20; // null point exception abbruch! Point q1 = new Point(); q1.x=100; q1.y=100; p1 = new Point(); p2 = new Point(); p1 = q1; p1.x==100 & p1.y==100 & p1==q1 hs / fub - alp2-07 41 Objekte und Methoden Methodenaufruf: p1.translate (10,-5); wendet die Methode auf das Objekt p1 an und bewirkt für dieses Objekt einen Effekt: // p1.x ==110 // p1.y == 95 mehr zur Programmierung mit Objekten in Abschnitt 8 Objekte als Argumente: double d = p1.distance (q1); soll euklidischen Abstand bestimmen. Angenommen: {…; q1.x=20; q1=null; return abstand;} ändert q1 nicht (Wertparameter), aber das Attribut x von q1 ! hs / fub - alp2-07 42 Dualität Klassentyp – Modul z z z Java: Klasse ist Typschablone zur Erzeugung von Objekten und Modul, in dem Variablen, Konstanten, Funktionen zusammengefasst sind. Statische Komponenten sind unabhängig vom Zustand (eines Objekts) Initialisierung beim Laden der Klasse (class loader) Math: Class Math { nur statische Elemente static final double PI = 3.1415926535898 static double abs(double a){...} ..} hs / fub - alp2-07 43 Objekte besitzen Zustand z Objektzustand gegeben durch Werte der nichtstatischen Attribute class Point { int x,y; void translate(int dx, int dy) {x+=dx; y+=dy;} double distance(Point q) {…} } z Methoden m() können Zustand lesen und / oder verändern. Falls kein Bezug auf Objektzustand, m() statisch vereinbaren. z Beispiel: Anzahl erzeugter Punktobjekte! Offensichtlich unabhängig vom Zustand eines Objekts. hs / fub - alp2-07 44 Dualität einer Klasse (Java) class MyPoint { static private int count=0; static public int getCount(){return count;} private int x,y; void translate(int dx, int dy) {x+=dx; y+=dy;} double distance(MyPoint q) {…} } Zugriff auf dynamische Komponenten durch Objektvariablen: MyPoint p = new MyPoint(); if(p.x > 0) {...} Zugriff auf statische Komponenten: Klassenname if(MyPoint.count > 1000) {...} // wg. private nur eingeschränkt Am Rande: Bei Einhalten Groß-/Kleinkonvention static/dynamic erkennbar hs / fub - alp2-07 45 Objekt und Klasse Zugriff auf Klassenattribute durch Methode erlaubt. p1 = new MyPoint(100,100); p1.translate(10,-10); p1.count++; // Manchmal sinnvoll // meist gefährlich, hier fehlerhaft MyPoint.count++; MyPoint.translate(5,15); // Fehler Keine Ausführung von nicht-statischen Methoden unabhängig von einem Objekt. Auch: Definition statischer Methoden, die dynamische Attribute verwenden, verboten. class MyPoint {… static void nullify(){ if(count>MAX){x=0; y=0;} }} //Fehler. hs / fub - alp2-07 46 Gleichheit und Ungleichheit MyPoint p = new MyPoint(1,1); MyPoint q = new MyPoint(p) aber: p != q Gleichheit von Objekten a) Identität (der Verweise) q = p; b) Gleichheit der Komponentenwerte boolean equal(Point p){ return(x==p.x && y==p.y && count==p.count); } if (p1.equal(p2){...}; hs / fub - alp2-07 47 Zusammenfassung: Verweistypen und Verbunde in Programmiersprachen Java C (*) Modula2 C# Verbundtyp Klasse class Struktur struct Satztyp RECORD class und struct Verweistyp Objektvariable mit Klassentyp Alle Typen Alle Typen <var> *<type> POINTER TO.. Objektvar., struct-Expl., nicht zwingend Dereferenzierung implizit *<var>=.. ..=*<var> <var>^=.. ..=<var>^ Speicherallokation Halde (heap) Halde Kontextabhg., malloc(): heap ALLOCATE Speicherverwaltung Automatisch manuell (Garbage Coll.) free() (*) gilt auch für C++, dort zusätzlich Klassen, Objekte manuell DISPOSE implizit class: Halde, struct: Stapel (stack) Automatisch (Garbage Coll., stack) hs / fub - alp2-07 48 7.5 Konstruktoren Point q1 = new Point(); • erzeugt (*) ein Objekt vom Klassentyp Point mit vordefinierten Werten (Java: int mit 0). • Point() ist der Standardkonstruktor. • Jede Klasse besitzt Standardkonstruktor, der nicht vereinbart wird. • Andere Konstruktoren mit eigenen Initialisierungen und überladenem Namen können definiert werden. Point (int i, int j){x=i; y=j;} Point p = new Point(70,100); (*) nicht: 'instantiiert' oder 'instanziiert'!! hs / fub - alp2-07 49 Technischer Ablauf der Objektkonstruktion MyName myVar = new MyName(…) (1) Objekt auf der Halde erzeugen. (2) Angegebenen Konstruktor ausführen. (3) Adresse des Objekts zurückgeben und den Wert der Variable des Typs der Klasse zuweisen. Beachte: wenn es keine Variable (mehr) gibt, die auf ein Objekt verweist, ist es für immer verloren! ⇒ Garbage Collection Point p = new Point(10,15); p = null; Dieses Objekt verschwindet auf Nimmerwiedersehen. hs / fub - alp2-07 50 Initialisierung von Objekten Vier Möglichkeiten: class Foo{ int x,y;} 1. Implizit mit Voreinstellung int x,y; //x==0; y==0 2. Explizite direkte Initialisierung int x=100,y=100; 3. Explizite Initialisierung mit anonymem Block int x,y; {x=50; y=70;} 4. Initialisierung durch Konstruktor (am häufigsten) Foo p = new Foo(10,20) hs / fub - alp2-07 51 Regeln im Umgang mit Konstruktoren (1) <Klassenname>() ist der Standardkonstruktor einer Klasse. Falls kein anderer Konstruktor <Klassenname>(<parameterliste>) definiert wird, wird er automatisch generiert. (Sonst nicht !) (2) Kein return in Konstruktor, kein Ergebnistyp, auch nicht void. (3) Methode darf keinen Konstruktor aufrufen – aber umgekehrt. hs / fub - alp2-07 52 Regeln (2) (4) Konstruktor darf anderen Konstruktor aufrufen, aber nur als erste Anweisung in der Form this(…) this: generische Bezeichnung für das aktuelle Objekt class MyPoint { static int count=0; int x,y; MyPoint(int i, int j){ this.x=i; this.y=j; MyPoint.count++; } MyPoint(MyPoint p){ this(p.x, p.y); } .. } Dieser Konstruktor wird aufgerufen! hs / fub - alp2-07 53 Beachte die Feinheiten! class MyPoint { static int count=0; int x,y; MyPoint(int i, int j){ this.x=i; this.y=j; MyPoint.count++; } MyPoint(MyPoint p){ count++; Doppelt this(p.x, p.y); }..} gezählt Was liefert..? Point p1=new Point(10,15); p2=new MyPoint(p1); boolean b = p1.equal(p2); Kopie anlegen hs / fub - alp2-07 54