Kapitel 1 Objektorientierte Programmierung Klassen Methoden Überladen von Methoden Der this-Zeiger Konstruktoren Vererbung Attribute von Klassen, Methoden und Variablen Abstrakte Klassen und Methoden Interfaces Programmierkurs Birgit Engels Anna Schulze Zentrum für Angewandte Informatik Köln WS 07/08 1 / 58 Eigenschaften Objektorientierter Programmierung 2 / 58 Ziele Objektorientierter Programmierung Datenkapselung ◮ ◮ Daten und die zu diesen Daten gehörende Funktionalität zu einer Einheit zusammenfassen Einheiten werden als Objekte bezeichnet Geringere Komplexität Übersichtlichkeit Verringerung des Arbeitsaufwands durch Vererbung ◮ Eine Klasse kann Eigenschaften einer anderen Klasse erben ◮ Polymorphie ◮ ◮ Wiederverwendung vorhandener Klassen einfache Erweiterung von Klassen Mehrere gleichnamige Methoden, die sich durch Argumentenliste unterscheiden 3 / 58 4 / 58 Objektorientierung Klassische Programmierung versucht stets, einen Programmablauf zu definieren und diesen in ein Programm umzusetzen. Das ist nicht immer die natürliche Betrachtungsweise eines Problems. Oft beschreiben Programme Objekte der realen Welt, wie z.B. einen Drucker: ◮ ◮ 1.1 Klassen Ein Drucker ist kein Ablauf, sondern hat bestimmte Daten, die z.B. seine Eigenschaften beschreiben (Papiergröße, Papiermenge). Außerdem gehören zu ihm bestimmte Verfahren, wie gedruckt wird und wie Einstellungen geändert werden können. Auch abstrakte Dinge kann man sich als Objekte vorstellen, z.B. einen Buchungsvorgang bei einer Bank. Daten sind dabei z.B. Kontonummern von Quelle und Ziel einer Buchung. 5 / 58 Definition einer Klasse 6 / 58 Objekte einer Klasse public class Auto { public String name; public int erstzulassung; public class Auto { public String name; public int erstzulassung; } public int alter() { return 2007 - erstzulassung; } } public int alter() { return 2007 - erstzulassung; } } meinKombi ist Variable vom Typ der Klasse Auto, ihr wird mit new ein neu erAuto meinKombi; zeugtes Objekt zugewiesen meinKombi = new Auto(); Eine Klassendefinition wird durch das Schlüsselwort class eingeleitet Dann folgen in geschweiften Klammern Variablendefinitionen und Methodendefinitionen oder 7 / 58 Auto meinKombi = new Auto(); 8 / 58 Objekte einer Klasse Objekte einer Klasse Der Zugriff auf Variablen und Methoden erfolgt mit Punktnotation ◮ Auto meinKombi; meinKombi = new Auto(); ◮ Variablen: Objekt.Variable Methoden: Objekt.Methode(Parameter) Zuweisung: Die erste Anweisung ist eine normale Variablendeklaration Anstelle eines primitiven Typs wird hier der Typname einer zuvor definierten Klasse verwendet Die zweite Anweisung gerneriert mit Hilfe des new-Operators eine neue Instanz der Klasse Auto und weist sie der Variablen meinKombi zu meinKombi.name = ‘‘Mercedes 600’’; meinKombi.erstzulassung = 1972; Zugriff: System.out.println(‘‘Name: ’’ + meinKombi.name); Aufruf von Methoden: System.out.println(meinKombi.alter()); 9 / 58 Objekte sind Referenzen 10 / 58 Objekte sind Referenzen public class Auto { { public String name; Im Unterschied zu primitiven Datentypen werden Objekte als Referenz gespeichert. Die Zuweisung einer Referenz kopiert den Verweis auf das betreffende Objekt. Wenn zwei Variablen dieselbe Referenz zugewiesen bekommen, zeigen diese auf dasselbe Objekt. public static void main(String[] args) { Auto meinAuto = new Auto(); Auto meinZweitWagen; meinZweitWagen = meinAuto; meinZweitWagen.name = ‘‘BMW’’; meinAuto.name = ‘‘Käfer’’; System.out.println(meinZweitWagen.name); } } Was gibt der Algorithmus aus? Die Ausgabe des Algorithmus ist: Käfer. 11 / 58 12 / 58 Variablen sind keine Referenzen public class Variablen { int a,b; 1.2 Methoden a = 5; b = a; a = 6; System.out.println(’’b = ’’ + b); } Was gibt der Algorithmus aus? Die Ausgabe des Algorithmus ist: b = 5. 13 / 58 Methoden 14 / 58 Rückgabewert von Methoden public int alter() { return 2007 - erstzulassung; } Eine Methode stellt eine Anweisungsliste dar Sie wird innerhalb einer Klassendefinition angelegt Sie hat Zugriff auf alle Variablen des Objekts Globale Funktionen, die vollkommen unabhängig von einem Objekt oder einer Klasse existieren, gibt es ebensowenig wie globale Variablen Später: Klassenvariablen und -methoden, die nicht an eine konkrete Instanz gebunden sind Jede Methode ist typisiert der Typ bestimmt den Typ des Rückgabewerts Typ kann primitiver Typ, Objekttyp oder vom Typ void sein Methoden vom Typ void haben keinen Rückgabewert Rückgabewert wird mit return-Anweisung zurück gegeben: return Ausdruck; return-Anweisung führt zum Beenden der Methode 15 / 58 16 / 58 Methoden mit Parametern Methoden mit Parametern Alle Parameter werden per call by value übergeben, d.h. Beim Aufruf der Methode wird aktuelle Wert der Variable in die Parametervariable kopiert und an die Methode übergeben Veränderungen der Parametervariablen innerhalb der Methode bleiben lokal Im Beispiel wird wieoft nur innerhalb der Methode verändert, der Aufrufer bemerkt es nicht public void printalter(String text, int wieoft) { while (wieoft-- > 0) { System.out.println(text + alter()); } } Wie oft wird das Alter von meinKombi in folgendem Beispiel ausgegeben? Parameter werden durch Kommata getrennt innerhalb der Klammern der Methode angegeben Jeder Parameter besteht aus einem Typnamen und dem Namen des Parameters ... int a = 3; meinKombi.printAlter(a); meinKombi.printAlter(a); meinKombi.printAlter(a); ... 17 / 58 18 / 58 Call by value bei Objektvariablen Objektvariablen sind Referenzen Bei der Übergabe an eine Methode werden sie per Wert übergeben, d.h. aber der Wert der Referenz wird übergeben Veränderung an dem Objekt sind somit auch für den Aufrufer der Methode sichtbar call by value für Objektvariablen entspricht call by reference 1.3 Überladen von Methoden WICHTIG: Die Übergabe von Objekten an Methoden hat damit zwei wichtige Konsequenzen: Die Methode erhält keine Kopie, sondern arbeitet mit dem Originalobjekt. Sollen Objekte kopiert werden, so muss dies explizit durch Aufruf der Methode clone der Klasse Object erfolgen 19 / 58 20 / 58 Überladen von Methoden Überladen von Methoden public class Auto { public String name; public int erstzulassung; public int alter() { return 2007 - erstzulassung; } public int alter(String titel) { int alter = alter(); System.out.println(titel+alter); return alter; } } Definiere innerhalb einer Klasse zwei unterschiedliche Methoden mit demselben Namen Compiler arbeitet mit Signatur einer Methode, die sich aus dem Namen und über die Reihenfolge und Typen der Parameter zusammensetzt. Die Methoden müssen sich in dieser Signatur unterscheiden, d.h. in der Reihenfolge und/oder Typisierung der Parameter Beachte: alter wird in drei verschiedenen Bedeutungen verwendet. 21 / 58 22 / 58 Der this-Zeiger public class ThisExample { public int x=5; public int y=10; public void xPlusy() { int y=1; System.out.println(x+this.y); System.out.println(x+y); } public static void main(String[] args) { ThisExample test = new ThisExample(); test.xPlusy(); } } 1.4 Der this-Zeiger 23 / 58 Methoden dürfen auf die Variablen ihrer Klasse ohne Punktnotation zugreifen. Compiler interpretiert alle nicht lokalen Variablen x ohne Punktnotation als this.x. 24 / 58 Konstruktoren Konstruktoren sind Methoden, die bei der Initialisierung eines Objekts aufgerufen werden Ein Konstruktor erhält den Namen der Klasse und hat keine Rückgabewert public class Auto { public String name; public int erstzulassung; 1.5 Konstruktoren public Auto(String name) { this.name = name; } } 25 / 58 Auto dasAuto = new Auto(‘‘Porsche 911’’); 26 / 58 Überladen von Konstruktoren public Auto(String name) { this.name = name; } 1.6 Vererbung public Auto(String name, int erstzulassung) { this(name); this.erstzulassung = erstzulassung; } Wird ein Konstruktor in einer Methode derselben Klasse aufgerufen, muss dies als erste Anweisung innerhalb der Methode geschehen Sobald ein eigener Konstruktor definiert ist, kann der default-Konstruktor nicht mehr aufgerufen werden 27 / 58 28 / 58 Vererbung Zugriff auf vererbte Eigenschaften Unter Vererbung versteht man die Möglichkeit Eigenschaften vorhandener Klassen auf neue Klassen zu übertragen Mit dem Schlüsselwort extends wird die Basisklasse angegeben Hierdurch erbt die abgeleitete Klasse alle Variablen und Methoden der Basisklasse Besitzt eine Klasse das Attribut final, ist es nicht erlaubt, eine neue Klasse aus ihr abzuleiten Cabrio kfz1 = new Cabrio(): kfz1.name = ‘‘MX5’’; kfz1.erzulassung = 1995; System.out.println(‘‘Alter = ’’+kfz1.alter()); class Cabrio extends Auto { int vdauer; } Es kann auf sämtliche Variablen und Methoden der Basisklasse zugegriffen werden Die Klasse Cabrio unterscheidet sich von der Klasse Auto nur dadurch, dass sie zusätzlich die Zeit speichert, die zum Öffnen der Verdecks benötigt wird. 29 / 58 Verschachtelte Vererbung 30 / 58 Methodenüberlagerung Die Vererbung kann beliebig tief geschachtelt werden Geerbte Methoden können neu definiert werden (überlagert werden) Es wird immer die überlagernde Version verwendet class ZweisitzerCabrio extends Cabrio { boolean notsitze; } class ZweisitzerCabrio extends Cabrio { boolean notsitze; ZweisitzerCabrio kfz1 = new ZweisitzerCabrio(); kfz1.name = ‘‘911-T’’; public int alter() { return 12 * (2007 - erstzulassung); } Es kann auf sämtliche Variablen und Methoden der Basisklasse und auch ihrer geerbten Variablen und Methoden zugegriffen werden } 31 / 58 32 / 58 Dynamische Methodensuche Verhinderung Dynamischer Methodensuche Nicht immer kann bereits der Compiler entscheiden, welche Variante einer überlagerten Methode er aufrufen soll Ein Cabrio-Objekt kann einer Variablen vom Typ Auto zugewiesen werden D.h. die Variable vom Typ Auto kann während ihrer Lebensdauer Objekte verschiedenen Typs enthalten (z.B. vom Typ Auto, Cabrio, ZweisitzerCabrio Compiler muss Code generieren, der zur Laufzeit entscheidet welche überlagerte Methode er benutzt Das wird als dynamisches Binden bezeichnet Dies verursacht erheblichen Aufwand an Laufzeit (im Unterschied zu C/C++) Um zu verhindern, dass eine Methode dynamisch interpretiert wird, können folgende Attribute benutzt werden: Methoden vom Typ private sind in abgeleiteten Klassen nicht sichtbar und können daher nicht überlagert werden. Bei Methoden vom Typ final deklariert der Anwender explizit, dass sie nicht überlagert werden sollen. Auch bei static-Methoden, die ja unabhängig von einer Instanz existieren, besteht das Problem nicht 33 / 58 34 / 58 Aufrufen einer verdeckten Methode Wird eine Methode x in einer abgeleiteten Klasse überlagert, wird die ursprüngliche Methode x verdeckt Mit super.x() kann auf die verdeckte Methode zugegriffen werden super.super.x() ist nicht erlaubt Innerhalb des Konstruktors wird mit dem Aufruf super() der Konstruktor der Basisklasse aufgerufen Hierbei wird super wie eine normale Methode verwendet und kann mit oder ohne Parameter aufgerufen werden 1.7 Attribute von Klassen, Methoden und Variablen 35 / 58 36 / 58 Attribute von Klassen, Methoden und Variablen Sichtbarkeit Es gibt drei Sichtbarkeitsebenen für den Zugriff auf Elemente einer Klasse: public: Elemente des Typs public sind in der Klasse selbst (also in ihren Methoden), in Methoden abgeleiteter Klassen und für den Aufrufer von Instanzen einer Klasse sichtbar protected: Elemente des Typs protected sind in der Klasse selbst und in den Methoden abgeleiteter Klassen sichtbar. Der Aufrufer einer Instanz der Klasse kann auf Elemente der Klasse protected dagegen nur noch dann zugreifen, wenn er in demselben Paket definiert wurde private: Elemente des Typs private sind lediglich in der Klasse selbst sichtbar. Für abgeleitete Klassen und für Aufrufer von Instanzen bleiben private-Variablen verdeckt Attribute legen Eigenschaften von Klassen, Methoden und Variablen fest Sie haben Einfluß auf die Lebensdauer, Sichtbarkeit und Veränderbarkeit dieser Programmelemente Werden Methoden oder Variablen ohne eines der drei Sichtbarkeitsattribute definiert, so entspricht ihr Verhalten im wesentlichen dem von Elementen des Typs protected. 37 / 58 Sichtbarkeit Beispiel zu static- und final-Variablen static: Variablen und Methoden mit dem Attribut static sind nicht an die Existenz eines konkreten Objekts gebunden. ◮ ◮ 38 / 58 public class TestAuto { private static int objcnt = 0; private static final double STEUERSATZ = 18.9; Eine static-Variable existiert für jede Klasse genau einmal. D.h. Veränderungen an dieser Variable von einem Objekt sind für alle anderen Objekte auch sichtbar. (static-Variablen sind vergleichbar mit globalen Variablen Das static-Attribut erlaubt bei Methoden den Aufruf, ohne dass der Aufrufer ein Objekt der Klasse besitzt public TestAuto() { ++objcnt; } final: Variablen mit dem Attribut final dürfen nicht verändert werden, sind also als Konstanten anzusehen. Methoden des Typs final dürfen nicht überlagert werden; ebensowenig dürfen Klassen des Typs final zur Ableitung verwendet werden. } objcnt zählt die Anzahl der angelegten Objekte STEUERSATZ ist eine unveränderliche Variable (Konstante), ihr kann kein neuer Wert zugewiesen werden Wichtig: Bei final-Methoden kann der Compiler auf dynamisches Binden verzichten. Deswegen können final-Methoden wesentlich schneller aufgerufen werden als normale Methoden˙ 39 / 58 40 / 58 Abstrakte Klassen und Methoden Abstrakte Methoden enthalten nur die Deklaration des Methodenkopfes, aber keine Implementierung des Methodenrumpfes Anstelle der geschweiften Klammern steht ein Semikolon Zusätzlich wird die Definition mit dem Attribut abstract versehen Eine Klasse, die eine abstrakte Methode enthält, wird selbst als abstrakt angesehen und muss mit dem Schlüsselwort abstract versehen werden 1.8 Abstrakte Klassen und Methoden abstract class Student { String name; String vorlesungen[]; public abstract void vorlesungen(); } 41 / 58 Abstrakte Klassen und Methoden 42 / 58 Abstrakte Klassen und Methoden Abstrakte Klassen werden abgeleitet und dort werden eine oder mehrere abstrakte Methoden implementiert Eine abstrakte Klasse wird konkret, wenn alle ihre Methoden implementiert sind Abstrakte Methoden können nicht aufgerufen werden Sie definieren nur eine Schnittstelle, die durch Überlagerung in einer abgeleiteten Klasse implementiert werden kann Es können keine Objekte von abstrakten Klassen erstellt werden public class WIStudent extends Student { boolean altstudienordnung; public void vorlesungen() { vorlesungen = new String[1]; vorlesungen[0] = ‘‘Programmierkurs’’; } } 43 / 58 44 / 58 Interfaces In Java nur einfache Vererbung: Eine Klasse kann von maximal einer anderen Klasse abgeleitet werde Nicht möglich Mehrfachvererbung: Eine Klasse kann von mehr als einer anderen Klasse abgeleitet werden In Java Interfacees 1.9 Interfaces 45 / 58 Interfaces 46 / 58 Verwendung von Interfaces Die Implementierung eines Interfaces wird durch das Schlüsselwort implements angezeigt Es müssen alle Methoden, die im Interface definiert sind, implementiert werden Ein Interface ist eine besondere Form einer Klasse Ein Interface enthält ausschließlich abstrakte Methoden und Konstanten Anstelle von class werden zur Definition einen Interfaces die Schlüsselwörter public interface verwendet Das Schlüsselwort public ist optional Alle Methoden sind standardmäßig abstrakt Ein Interface kann keine Konstruktoren enthalten public class Auto implements Fortbewegungsmittel { public String name; public double spritVerbrauch; public interface Fortbewegungsmittel { public static final double spritPreis = 1.10; public double kilometerPreis(); } public double kilometerPreis() { return spritVerbrauch * spritPreis/100; } } 47 / 58 48 / 58 Interfaces und Vererbung Implementieren mehrerer Interfaces Eine Klasse kann mehrere Interfaces implementieren Eine Klasse kann sowohl abgeleitet sein als auch ein Interface implementieren public interface Sommer { ... } class Cabrio extends Auto implements Fortbewegungsmittel { ... } class Cabrio extends Auto implements Fortbewegungsmittel, Sommer { ... } 49 / 58 Interfaces sind Klassen Interfaces besitzen zwei wichtige Eigenschaften, die auch Klassen haben: Interfaces lassen sich vererben Variablen können vom Typ eines Interfaces sein: ◮ ◮ Man kann ihnen Objekte zuweisen, die aus Klassen stammen, die dieses oder eines der daraus abgeleiteten Interfaces implementieren Sie können Variablen zugewiesen werden, die zu Klassen gehören, die dieses Interface implementieren Deswegen ist folgende Interpretation sinnvoll: Ein Interface ist eine Typvereinbarung Eine Klasse, die dieses Interface implementiert, ist dann vom Typ des Interfaces Wegen der Mehrfachvererbung von Interfaces kann eine Instanzvariable damit insbesondere mehrere Typen haben und zu mehr als einem Typen zuweisungskompatibel sein 51 / 58 50 / 58