© Holger Röder Winter 2008/2009 Programmentwicklung se Java: Kapitel 3 Objektorientierung in Java Programmentwicklung WS 2008/2009 Holger Röder [email protected] © Holger Röder Klassen, Objekte, Attribute, Methoden Vererbung, Klasse Object Pakete Abstrakte Attribute und Methoden Schnittstellen Lokale und anonyme Klassen Typüberprüfung und Typumwandlung Programmentwicklung Winter 2008/2009 Überblick über Kapitel 3 se 2 © Holger Röder Winter 2008/2009 Programmentwicklung se Klassen und Objekte Klassendefinitionen werden in Java mit dem Schlüsselwort class eingeleitet: public class Person { public String name; private double gehalt; public double getGehalt() { ... } } Innerhalb der geschweiften Klammern können beliebig viele Attribute und Methoden definiert werden. Der Name der öffentlichen (public) Java-Klasse in einer QuellcodeDatei muss dem Dateinamen entsprechen (hier: Person.java). Eine .java-Datei kann mehrere Klassendefinitionen enthalten, jedoch lediglich eine öffentliche Klasse! 3 © Holger Röder Objekte (Instanzen) einer Klasse müssen deklariert und mit dem newOperator erzeugt (instanziiert) werden: Person p; p = new Person(); Programmentwicklung Winter 2008/2009 Objekte se 4 © Holger Röder Winter 2008/2009 Programmentwicklung se Attribute („Zustand“) Definition eines Attributs (vgl. Variablendeklaration): {Modifier} Typ Name Beispiel: private double gehalt; Alle Attribute eines neu erzeugten Objekts haben zunächst Standardwerte, insbesondere sind alle Referenzen null. Der Zugriff auf Attribute eines Objekts erfolgt über die Punktnotation: p.name = "Max"; // direkter schreibender Attributzugriff System.out.println(p.name); // lesender Zugriff 5 © Holger Röder Winter 2008/2009 Programmentwicklung se Methoden („Verhalten“) Definition einer Methode: {Modifier} Typ Name ({Parameter}) { ... // Anweisungen Keine Parameter } Beispiel: public double getGehalt() { ... } Methoden sind typisiert: der Typ der Methode bestimmt den Typ des Rückgabewerts. Er kann ein beliebiger primitiver Datentyp, ein Referenzdatentyp oder void sein. Methoden vom Typ void liefern keinen Rückgabewert, andernfalls wird der Rückgabewert mit einer return-Anweisung zurückgegeben: public double getGehalt() { return gehalt; } 6 © Holger Röder Winter 2008/2009 Programmentwicklung se Methoden – Parameter, Aufruf Methoden können Parameter haben: public void setGehalt(double g) { gehalt = g; } Mehrere Parameter werden durch Kommas getrennt. Parameter werden ausschließlich by value übergeben. Der Aufruf einer Methode erfolgt über Punktnotation. Parameter werden in Klammern angegeben. Zur Unterscheidung von einem Attributzugriff müssen die Klammern auch angegeben werden, wenn keine Parameter übergeben werden. p.setGehalt(1000.0); double x = p.getGehalt(); 7 © Holger Röder Winter 2008/2009 Programmentwicklung se Variable Parameterlisten Seit Java 5 sind auch variable Parameterlisten möglich. Dazu wird der Typbezeichner des letzten Parameters mit ... (3 Punkten) versehen. Beim Aufruf können an dieser Stelle beliebig viele Argumente des passenden Typs übergeben werden. Der Zugriff darauf innerhalb der Methode erfolgt wie bei einem Array. public int addiere(int... summanden) { int summe = 0; for (int i = 0; i < summanden.length; i++) { summe += summanden[i]; } return summe; } ... addiere(1, 2, 4); // liefert 7 addiere(1, 1, 1, 1, 1, 5); // liefert 10 8 © Holger Röder Winter 2008/2009 Programmentwicklung se Überladene Methoden Java erlaubt es, mehrere Methoden mit gleichem Namen zu definieren (sogenannte überladene Methoden). Die Unterscheidung erfolgt anhand der Anzahl und Typisierung der Parameter (nicht anhand des Rückgabetyps!). void gibAus(int n) { System.out.println("Integer: " + n); } void gibAus(double d) { System.out.println("Double: " + d); } ... gibAus(12); // liefert "Integer: 12" gibAus(12.0); // liefert "Double: 12.0" 9 © Holger Röder Winter 2008/2009 Programmentwicklung se Konstruktoren Ein Konstruktur einer Klasse ist eine spezielle Methode, die bei der Initialisierung eines Objekts aufgerufen wird. Konstruktoren tragen den gleichen Namen wie die Klasse, haben keinen Rückgabewert (auch nicht void!), können beliebig viele Parameter besitzen und überladen werden. public class Person { Person() { ... } } Soll bei der Initialisierung eines Objekts ein parametrisierter Konstruktor ausgeführt werden, müssen die Parameter wie beim Methodenaufruf in Klammern übergeben werden: Person p = new Person(12); // Konstruktor Person(int) 10 © Holger Röder Winter 2008/2009 Programmentwicklung se Konstruktoren – Beispiel public class A { int zahl; A() { zahl = 1; } Parameterloser Konstruktor A(int n) { zahl = n; Parametrisierter Konstruktor } public static void main(String[] args) { A eins = new A(); A zwei = new A(42); } } 11 © Holger Röder Winter 2008/2009 Programmentwicklung se Konstruktoren – Details Wird überhaupt kein expliziter Konstruktor angegeben, wird vom Compiler implizit ein parameterloser Standard-Konstruktor erzeugt, der den parameterlosen Konstruktor der Oberklasse aufruft. Werden ausschließlich parametrisierte Konstruktoren angegeben, wird kein Standard-Konstruktor erzeugt. Die Klasse besitzt dann keinen parameterlosen Konstruktor. Konstruktoren einer Klasse können verkettet werden. Der Aufruf erfolgt wie ein Methodenaufruf über das Schlüsselwort this: A() { this(1); // ruft den Konstruktor A(int n) auf } Wichtig: Der this-Aufruf muss die erste Anweisung in der Konstruktor-Methode sein! 12 © Holger Röder Winter 2008/2009 Programmentwicklung se Destruktoren Der Destruktor einer Klasse wird unmittelbar vor dem Zerstören eines Objekts aufgerufen. Destruktoren werden in Java als geschützte (protected) Methoden mit Namen finalize ohne Rückgabewert und Parameter definiert: protected void finalize() { ... // Aufräumarbeiten } Da die Speicherverwaltung (und damit das Zerstören nicht mehr benötigter Objekte) in Java automatisch geschieht, ist die Ausführung eines Destruktors nicht garantiert! 13 © Holger Röder Winter 2008/2009 Programmentwicklung se Referenz auf das Objekt selbst – this In jeder Objektmethode kann über die Referenz this auf das jeweilige Objekt zugegriffen werden. this erlaubt den Zugriff auf Objektattribute, die durch lokale Variablen verdeckt werden: int alter; void setAlter(int alter) { this.alter = alter; } Methoden, die this als Rückgabewert liefern, können den internen Zustand des Objekts verändern und einfach verkettet werden: Person erhoeheAlter() { alter++; return this; } ... p.erhoeheAlter().erhoeheAlter().erhoeheAlter(); // 3 Jahre älter 14 © Holger Röder Winter 2008/2009 Programmentwicklung se Vererbung Das Schlüsselwort extends dient zur Ableitung von einer Oberklasse: public class Angestellter extends Person { ... } Die abgeleitete Klasse erbt alle Attribute und Methoden der Oberklasse (sofern dies nicht durch Modifier verhindert wird). In der abgeleiteten Klasse können neue Attribute und Methoden definiert werden und geerbte Attribute und Methoden der Oberklasse überlagert werden. Java erlaubt nur Einfachvererbung: eine Klasse kann nur eine Oberklasse erweitern. 15 © Holger Röder Winter 2008/2009 Programmentwicklung se Polymorphie Objekte einer abgeleiteten Klasse sind zuweisungskompatibel zu Variablen einer übergeordneten Klasse (Polymorphie). Person p = new Angestellter(); Die Methodensuche geschieht dynamisch (dynamisches Binden). p.methode() // ruft methode() in Angestellter auf, // falls definiert; andernfalls Suche in // Klassenhierarchie 16 © Holger Röder Winter 2008/2009 Programmentwicklung se Polymorphie – Beispiel Person.java public class Person { int alter = 35; void zeigeAlter() { System.out.println( "Alter der Person:" + alter); } } Angestellter.java public class Angestellter extends Person { void zeigeAlter() { System.out.println( "Alter des Angestellten:" + alter); } } ... Person p1 = new Person(); Person p2 = new Angestellter(); p1.zeigeAlter(); p2.zeigeAlter(); Dynamische Methodensuche: zeigeAlter() in der Unterklasse Angestellter überlagert die entsprechende Methode in der Oberklasse Person 17 © Holger Röder Winter 2008/2009 Programmentwicklung se Zugriff auf Oberklasse Der Zugriff auf die durch Überlagerung verdeckten Methoden und Attribute der Oberklasse kann mit dem Schlüsselwort super erfolgen: public class Angestellter { ... super.zeigeAlter(); // ruft Methode zeigeAlter() in Oberklasse Person auf } Innerhalb einer Konstruktor-Methode ist auch der Zugriff auf den Konstruktor der Oberklasse mit super möglich: super(); // ruft Konstruktor der Oberklasse auf Wie beim Zugriff mit this auf Konstruktoren derselben Klasse gilt: Der super-Aufruf muss die erste Anweisung im Konstruktor sein! 18 © Holger Röder Winter 2008/2009 Programmentwicklung se Die Klasse Object Wird eine Klasse nicht mit Hilfe von extends von einer Oberklasse abgeleitet, so besitzt sie implizit die Oberklasse Object. Alle Klassen in Java werden somit direkt oder indirekt (über ihre Oberklassenhierarchie) von Object abgeleitet. Elementare Methoden der Klasse Object, die häufig überlagert werden, sind: boolean equals(Object obj) testet, ob zwei Objekte den gleichen Inhalt haben String toString() liefert eine String-Repräsentation des Objekts protected Object clone() kopiert ein Objekt int hashCode() berechnet einen numerischen Hashwert, der als Schlüssel für die Speicherung des Objekts in einer Hashtabelle verwendet werden kann. 19 © Holger Röder Winter 2008/2009 Programmentwicklung se Pakete – Zuordnung Jede Java-Klasse gehört zu einem Paket. Entsprechend hat der vollständige Name einer Java-Klasse die Form Paket.Klasse Die Zugehörigkeit zu einem bestimmten Paket wird durch das Schlüsselwort package festgelegt: package Paketname; Die Paketangabe muss als erste Anweisung in der Quelldatei stehen. Der Paketname besteht üblicherweise aus mehreren Teilnamen, die durch Punkte getrennt sind: package de.firma.personenverwaltung; Der Paketnamen beschreibt auch den Pfad zur Klassendatei: Die Klasse Person im Package de.firma.personenverwaltung muss als Datei de/firma/personenverwaltung/Person.java gespeichert werden. Fehlt die explizite Paketangabe, gehört die Klasse automatisch zum Standardpaket (default package). 20 © Holger Röder Winter 2008/2009 Programmentwicklung se Pakete – Import Um eine Klasse zu verwenden, kann sie entweder über ihren vollen (qualifizierten) Namen angesprochen werden: java.util.Date d = new java.util.Date(); oder importiert werden: import java.util.Date; ... Date d = new Date(); Es existieren zwei Varianten der import-Anweisung: import Paket.Klasse; importiert die angegebene Klasse import Paket.*; importiert alle Klassen des Pakets Die import-Anweisung muss in der Quelldatei vor der Klassendefinition, aber nach einer möglichen Paketzuordnung stehen. Das Paket java.lang wird automatisch importiert. Die Klasse Object gehört zum Paket java.lang. 21 © Holger Röder Winter 2008/2009 Programmentwicklung se Modifier – Sichtbarkeit Modifier verändern die Eigenschaften von Klassen, Attributen und Methoden. Sichtbarkeit von Attributen und Methoden: Attribut/Methode sichtbar für ... public protected Standard private (package) Klasse selbst ja ja ja ja Beliebige Klasse im gleichen Paket ja ja ja nein Abgeleitete Klasse in einem anderen Paket ja ja nein nein Beliebige Klasse in einem anderen Paket ja nein nein nein Klassen sind standardmäßig nur innerhalb des eigenen Pakets sichtbar. Der Modifier public bewirkt, das eine Klasse auch in anderen Paketen sichtbar ist. 22 © Holger Röder Winter 2008/2009 Programmentwicklung se Modifier final – Veränderbarkeit Der Modifier final beschränkt die Veränderbarkeit der betroffenen Programmelemente: Von final-Klassen dürfen keine Unterklassen abgeleitet werden. final-Methoden dürfen nicht überlagert werden. final-Attribute dürfen nicht verändert werden, sind also Konstanten. Als final deklarierte Parameter von Methoden und lokale Variablen dürfen nach der Initialisierung nicht mehr verändert werden. void erhoeheGehalt(final double betrag) { betrag = 2 * betrag; // nicht erlaubt! ... } 23 © Holger Röder Der Modifier static beeinflusst die Lebensdauer und dient zur Definition statischer Attribute und Methoden. Statische Attribute und Methoden sind im Unterschied zu „normalen“ Attributen und Methoden nicht an konkrete Objekte gebunden. Programmentwicklung Winter 2008/2009 Modifier static – Lebensdauer se 24 © Holger Röder Winter 2008/2009 Statische Attribute Statische Attribute (Klassenattribute) werden je Klasse nur einmal angelegt: alle Objekte der Klasse „teilen“ sich das Attribut. public class Person { public static int personZaehler = 0; Person() { personZaehler++; // zählt Instanzen } ... } Programmentwicklung Zugriff von außen auf statische Attribute: Klassenname.Attribut int anzahlPersonObjekte = Person.personZaehler; se Konstanten werden in Java als static final-Attribute deklariert: public static final int RENTENALTER = 67; Zugriff auf Konstante: int r = Person.RENTENALTER 25 © Holger Röder Winter 2008/2009 Programmentwicklung se Initialisierung statischer Attribute Statische Attribute können bei der Deklaration initialisiert werden. static int x = 6; // Deklaration und Initialisierung Falls komplexere Initialisierungen notwendig sind, kann ein statischer Block verwendet werden: static int summe; static { // Statischer Block zur Initialisierung von summe for (int i = 1; i <= x; i++) { summe += i; } } Statische Blöcke ähneln normalen Konstruktoren. Sie werden jedoch nur einmal beim Laden der Klasse ausgeführt, nicht bei jeder Objektinstanziierung. 26 © Holger Röder Winter 2008/2009 Programmentwicklung se Statische Methoden Statische Methoden (Klassenmethoden) sind ebenfalls unabhängig von konkreten Objekten einer Klasse. Der Aufruf erfolgt in der Form Klassenname.Methodenname({Parameter}) Innerhalb von statischen Methoden kann nicht auf Instanzattribute oder –methoden zugegriffen werden! public class Chef extends Angestellter { double gehalt; static double getMaxGehalt() { return gehalt; // nicht erlaubt! } } 27 © Holger Röder Winter 2008/2009 Verwendung statischer Methoden und Attribute Typische Beispiele für statische Methoden und Attribute enthält die Klasse java.lang.Math: Math.sin(3.14); // statische Methode: berechnet Sinus Math.max(23, 42); // statische Methode: liefert Maximum Math.PI // statisches Attribut: Wert von Pi Weiteres Beispiel: Programmentwicklung Integer.parseInt("17"); // konvertiert String nach int se 28 © Holger Röder Mit Hilfe der import static-Anweisung können statische Attribute und Methoden einer Klasse importiert werden. Der Aufruf ist dann ohne qualifizierenden Bezeichner möglich: import static java.lang.Math.*; ... System.out.println(sin(PI)); // an Stelle von Math.sin(Math.PI); Programmentwicklung Winter 2008/2009 Statischer Import von Paketen se 29 © Holger Röder Winter 2008/2009 Programmentwicklung se Konstruktoren mit eingeschränkter Sichtbarkeit Um zu verhindern, dass Objekte einer Klasse „beliebig“ erzeugt werden können, kann ein privater Konstruktor verwendet werden. Die Objekterzeugung kann dann über statische Methoden erfolgen. public class { private A() { ... } // Privater Konstruktor public static A erzeuge() { return new A(); // Konstruktor sichtbar! } } A neuesObjekt1 = new A(); A neuesObjekt2 = A.erzeuge(); // Nicht möglich! // Möglich 30 © Holger Röder Winter 2008/2009 Programmentwicklung se Abstrakte Methoden und Klassen Abstrakte Methoden werden nur deklariert, verfügen jedoch über keine Implementierung. Sie werden mit dem Schlüsselwort abstract gekennzeichnet: public abstract double berechneGehalt(); Klassen, die mindestens eine abstrakte Methode enthalten, heißen abstrakte Klassen. Sie müssen ebenfalls gekennzeichnet werden: public abstract class Person { public abstract double berechneGehalt(); } Abstrakte Klassen können nicht instanziiert werden. Person p = new Person(); // nicht erlaubt! Abstrakte Klassen können neben abstrakten Methoden auch konkrete Methoden (mit Implementierung enthalten). 31 © Holger Röder Winter 2008/2009 Programmentwicklung se Konkretisierung abstrakter Klassen Von einer abstrakten Oberklasse abgeleitete Klassen können geerbte abstrakte Methoden implementieren. Erst wenn eine abgeleitete Klasse alle abstrakten Methoden ihrer Oberklassen implementiert, wird sie konkret und kann instanziiert werden: public class Angestellter extends Person { public double berechneGehalt() { return 1790.0; // implementiert abstrakte Methode } } ... Person p = new Angestellter(); // jetzt möglich! Die Konkretisierung kann auch schrittweise über mehrere Klassen in der Oberklassenhierarchie hinweg erfolgen. 32 © Holger Röder Winter 2008/2009 Programmentwicklung se Schnittstellen Eine Schnittstelle (Interface) ist eine Klasse, die ausschließlich abstrakte Methoden und Konstanten enthält. Sie wird mit dem Schlüsselwort interface deklariert. interface Persistent { void speichere(); } Alle Methoden sind implizit öffentlich und abstrakt. Schnittstellen können selbst von anderen Schnittstellen abgeleitet werden: interface EchtPersistent extends Persistent { ... } 33 © Holger Röder Winter 2008/2009 Programmentwicklung se Implementierung von Schnittstellen Klassen können Schnittstellen implementieren. Dabei muss die Klasse alle deklarierten Schnittstellen-Methoden implementieren: class Person implements Persistent { public void speichere() { // Implementierung der Speicherung von // Person-Objekten ... } } Objekte der Klasse, die die Schnittstelle implementiert, können dann überall dort verwendet werden, wo die Schnittstelle erwartet wird: Persistent p = new Person(); p.speichere(); 34 © Holger Röder Winter 2008/2009 Programmentwicklung se Implementierung mehrerer Schnittstellen Eine Klasse kann auch mehrere Schnittstellen implementieren. In diesem Fall muss die Klasse die deklarierten SchnittstellenMethoden aller Schnittstellen implementieren. interface Persistent { void speichere(); } ... interface Ausdruckbar { void druckeAus(); } ... class Datensatz implements Persistent, Ausdruckbar { public void speichere() { ... } public void druckeAus() { ... } } 35 © Holger Röder Winter 2008/2009 Programmentwicklung se Einsatz von Schnittstellen Eine Schnittstelle beschreibt eine bestimmte Sicht auf ein Objekt, also eine Rolle, die das Objekt einnehmen kann. Implementiert eine Klasse mehrere Schnittstellen, können Objekte dieser Klasse in verschiedenen Rollen auftreten. Mit Hilfe von Schnittstellen können gleiche Eigenschaften von Klassen definiert werden, die nicht (sinnvoll) in einer „normalen“ Vererbungshierarchie definiert werden können. interface Vergleichbar { boolean istKleiner(Vergleichbar v); } Die Probleme der klassischen Mehrfachvererbung werden bei der Verwendung von Schnittstellen weitgehend verhindert. 36 © Holger Röder Winter 2008/2009 Programmentwicklung se Konstanten in Schnittstellen Alle Attribute von Schnittstellen sind implizit statisch und final und somit Konstanten. Erbt eine abgeleitete Schnittstelle Konstanten gleichen Namens von verschiedenen Oberschnittstellen, so muss beim Zugriff die gewünschte Konstante eindeutig identifiziert werden: GrundFarben.java interface GrundFarben { int BLAU = 1; } AlleFarben.java SchoeneFarben.java interface SchoeneFarben { int BLAU = 20; } interface AlleFarben extends GrundFarben, SchoeneFarben{ } ... // int blau = AlleFarben.BLAU; // nicht zulässig! int blau = SchoeneFarben.BLAU; 37 © Holger Röder Winter 2008/2009 Programmentwicklung se Lokale Klassen Java bietet verschiedene Möglichkeiten, Klassen innerhalb anderer Klassen (sogenannte innere Klassen – inner classes) zu definieren. Lokale Klassen werden innerhalb eines Blocks deklariert und sind nur dort sichtbar. Der Zugriff auf Attribute und Methoden der äußeren Klasse ist erlaubt, ebenso der Zugriff auf finale Attribute und Parameter des umgebenden Blocks: public class Person { double gehalt = 3000.0; double berechneGehalt() { class Gehaltsrechner { ... // lokale Klasse zur Gehaltsberechnung } Gehaltsrechner g1 = new Gehaltsrechner(); ... } } 38 © Holger Röder Winter 2008/2009 Programmentwicklung se Anonyme Klassen Anonyme Klassen haben keinen Namen und erzeugen automatisch ein Objekt. Die Definition erfolgt in der Form new Oberklasse() { ... /* Eigenschaften */ }; Ist Oberklasse der Name einer Klasse, so ist die anonyme Klasse eine davon abgeleitete Unterklasse. Ist Oberklasse eine Schnittstelle, so erbt die anonyme Klasse von Object und implementiert diese Schnittstelle ... Person chef = new Person() { // anonyme Klasse mit überlagerter Methode double berechneGehalt() { return gehalt + 2000.0; // Bonus } }; 39 © Holger Röder Winter 2008/2009 Programmentwicklung se Anonyme Klassen – Exemplar-Initialisierung Anonyme Klassen haben keine Konstruktoren. Notwendige Initialisierungen können in einem ExemplarInitialisierungsblock durchgeführt werden. Person chef = new Person() { { // Exemplar-Initialisierungsblock gehalt = gehalt + 2000; } ... // Weitere Methoden der anonymen Klasse }; Auch normale Klassen können Exemplar-Initialisierungsblöcke enthalten. Diese werden bei der Objektinstanziierung vor den Konstruktoren ausgeführt. 40 © Holger Röder Winter 2008/2009 Programmentwicklung se Typüberprüfung mit instanceof Mit dem Operator instanceof kann überprüft werden, ob ein Objekt von einem bestimmten Typ ist. Der Ausdruck objekt instanceof klasse liefert true, wenn objekt eine Instanz der Klasse klasse oder einer ihrer Unterklassen ist, ansonsten false. public class A { ... } public class B extends A { ... Object o1 = new A(); Object o2 = new B(); boolean b1 = o1 instanceof boolean b2 = o1 instanceof boolean b3 = o2 instanceof boolean b4 = o2 instanceof ... } A; B; A; B; // // // // liefert liefert liefert liefert true false true true 41 © Holger Röder Winter 2008/2009 Programmentwicklung se Typumwandlung Typecasts der Form (Zieltyp) objekt sind zulässig, wenn objekt eine Instanz von Zieltyp oder einer Unterklasse ist. Andernfalls kommt es zu einem Laufzeitfehler. A.java public class A { void m() { System.out.println( "m() wurde aufgerufen!"); } } ... void aufruf(Object obj) { if (obj instanceof A) { A a = (A) obj; // "gefahrloser" Typecast a.m(); } } 42