Vorlesung Informatik 1

Werbung
Vorlesung Informatik 1
Fachhochschule für Technik Esslingen
Studiengang Wirtschaftsinformatik
Teil 3: Objektorientierung
Dr. rer. nat. Andreas Rau
http://www.hs-esslingen.de/~rau
[email protected]
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#1
Neues Kapitel, neue Arbeitstechnik
Im Rahmen der Erarbeitung der Grundlagen haben wir zwar auch Java Klassen
verwendet, jedoch nicht in "artgerechter" Haltung! Dies muss sich nun ändern.
BISER bestand jedes Programm nur aus einer unabhängigen Datei.
AB SOFORT werden Programme aus mehreren abhängigen Datei bestehen.
Es macht Sinn, die Dateien eines Programms in einem Verzeichnis zu bündeln.
Bisher gab es in jeder Datei eine main()-Methode.
AB SOFORT wird es viele Dateien ohne main()-Methode geben.
Eine main()-Methode ist nur in der Start-Klasse des Programms notwendig!
Bisher haben wir keine Objekte erzeugt und alle Eigenschaften waren static.
AB SOFORT wollen wir Objekte erzeugen und static Eigenschaften sind uncool*.
Fehlermeldungen suggerieren etwas anderes, aber static ist nicht immer richtig!
*die main()-Methode muss natürlich static bleiben
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#2
Grundlagen
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#3
Rückblende: Klassen und Objekte(1)
In objektorientierten Sprachen werden Variablen mit Funktionen zum Zugriff und
zur Manipulation kombiniert und in Objekten gekapselt. Dadurch wird verbunden,
was zusammengehört und verborgen, was nicht für jeden relevant ist. Dies dient
der Abstraktion sowie der Wartbarkeit und Wiederverwendbarkeit.
Ein Klasse beschreibt die Struktur gleichartiger Objekte (Bauplan, Gußform) und
existiert nur einmal. Es kann jedoch viele Objekte zu einer Klasse geben. Die
Eigenschaften einer Klasse sind ihre Identität (z.B. Klassenname), ihr Zustand,
ihr Verhalten und ggf. ihre Vaterklasse(n). Klassen sind statisch vordefiniert.
Ein Objekt hat eine eindeutige Identität (z.B. Referenz bzw. Adresse im
Hauptspeicher), einen Zustand (Werte der Variablen), ein Verhalten
(Funktionsumfang) und gehört zu einer bestimmten Klasse. Objekte können
zur Laufzeit (gemäß Bauplan) dynamisch erzeugt und gelöscht werden.
In manchen Programmiersprachen sind Klassen auch (einzigartige) Objekte...
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#4
Wiederholung: Klassen und Objekte(2)
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#5
Eigenschaften von Objekten und Klassen
Die Eigenschaften von Klassen und Objekten werden wie folgt bezeichnet
Instanzvariablen – Zustand eines konkreten Objekts (für jedes Objekt); N-mal
● Instanzmethoden – Verhalten eines konkreten Objekts
● Klassenvariablen – Zustand der Klasse (gemeinsam für alle Objekte); 1-mal
● Klassenmethoden – Verhalten der Klasse (kein Bezug zu konkretem Objekt
●
Die Methoden gehören zur jeweiligen Klasse bzw. zu einem bestimmten Objekt,
sind aber anders als die Variablen im Speicher nur einmal vorhanden. Die
individuelle Bindung an ein bestimmtes Objekt erfolgt während der Ausführung.
Klassen und Objekte werden Ihrerseits zu Modulen (in Java: Pakete)
zusammengefasst. Dadurch wird eine weitere Abstraktionsebene zur
Beherrschung der Komplexität und zur Arbeitsteilung geschaffen.
Zur Durchsetzung des Geheimnisprinzips ist es möglich, den Zugriff auf Klassen,
Objekte und deren Bestandteile individuell als öffentlich (public) oder privat
(private) zu kennzeichnen.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#6
Aufbau und Bestandteile einer Java Klasse
public class BeispielKlasse {
Legende
static int klassenVariable;
static void klassenMethode( int formalerParameter) {
// ...
}
int instanzVariable;
int instanzMethode( int formalerParameter) {
// ...
}
public static void main( String[] args) {
int lokaleVariable;
klassenMethode( lokaleVariable);
BeispielKlasse objektReferenz;
objektReferenz = new BeispielKlasse();
lokaleVariable = objektReferenz.instanzMethode( lokaleVariable);
}
reservierte Wörter:
Sind durch die jeweilige
Programmiersprache
vorgegeben (müssen
genau so lauten) und
dürfen nicht für eigene
Bezeichner verwendet
werden.
eigene Bezeichner:
Werden vom
Programmierer
vergeben (könnten
auch anders lauten).
}
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#7
UML(1): Klassendiagramme
Die Unified Modelling Language (UML) ist eine Notation zur grafischen
Beschreibung von Software. Zu diesem Zweck kennt UML 13 verschiedene
Arten von Diagrammen die man sogar mischen darf. Wir beschränken uns an
dieser Stelle auf das Klassendiagramm mit dem die Eigenschaften und
Zusammenhänge zwischen Klassen und Objekten darstellen kann.
Objekt:Klasse
- instanceVariable : int = defaultValue
- classVariable : int = defaultValue
+
~
#
publicMethode(int p1, int p2) : int
privateMethode(int p1, int p2) : int
packageMethode(int p1, int p2) : int
protectedMethode(int p1, int p2) : int
Darstellung einer Klasse in UML (der JavaEditor erzeugt das auf Knopfdruck)
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#8
Rückblende: Beziehungen zwischen Objekten
Objekte kommunizieren und
kooperieren durch Austausch
von Nachrichten.
Praktisch bedeutet dies
gegenseitigen Aufruf von
Methoden.
Voraussetzung dafür ist, dass
sich die Objekte gegenseitig
kennen, d.h. eine Beziehung
zueinander haben (über Arten
von Beziehungen später mehr).
Praktisch werden Beziehungen
durch Variablen mit Referenzen
auf Objekte implementiert.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#9
Arten von Beziehungen zwischen Objekten und Klassen(1)
Vererbung (is-a)
Semantik: Jede Klasse in Java hat genau eine Superklasse.
Implementierung: Deklaration bei der Klasse (extends + Superklasse)
Beispiel: ein Auto ist-ein Fahrzeug
Klassenzugehörigkeit (instance-of)
Semantik: Jedes Objekt ist Instanz genau einer bestimmten Klasse.
Implementierung: Festlegung gemäß Erzeugung (new Operator + Klasse)
Beispiel: Klaus ist eine Exemplar der Klasse Person
Allgemeine Beziehung (knows-of/uses)
Semantik: Einfache und ggf. auch flüchtige Beziehung zwischen zwei Objekten.
Implementierung: Instanzvariable (dauerhaft) oder Methodenparameter (flüchtig)
Beispiel: Kunde kauft Artikel
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#10
Arten von Beziehungen zwischen Objekten und Klassen(2)
Komposition (strong whole-part)
Semantik: Die Komposition repräsentiert eine starke Teil-Ganzes Beziehung im
Sinne von "besteht aus". Die Lebensdauer des Kompositums und seiner Teile
sind gekoppelt. Ein Teil kann nur zu einem Kompositum gehören.
Implementierung: Ganzes hat Instanzvariablen für Teile
Beispiel: Ein Auto besteht aus Motor, Reifen, ... Auto kaputt => Motor kaputt
Aggregation (weak whole-part)
Semantik: Die Aggregation repräsentiert eine schwache Teil-Ganzes Beziehung
im Sinne von "trägt bei zu". Die Lebensdauer des Aggregats und seiner Teile
sind nicht gekoppelt. Ein Teil kann in mehr als einem Aggregat enthalten sein.
Implementierung: Ganzes hat Instanzvariable für Teile
Beispiel: Ein Team besteht aus Mitgliedern. Team wird aufgelöst, Mitglieder nicht
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#11
UML(2): Beziehungen
Zur Darstellung von Beziehungen zwischen Klassen kennt UML verschiedene Symbole
:Fahrzeug
:Auto
:FH
:Student
0..*
fährt mit
0..*
:Auto
:Reifen
:Student
:Bus
Vererbung
Komposition
Aggregation
Assoziation
Bei mehreren Vererbungs-, Kompositions- oder Aggregationsbeziehungen kann man die
Linienenden bei der übergeordneten Klasse zusammenfassen; das Netz wird zum Baum.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#12
Definition einer Klasse und Erzeugung von Objekten
Eine Klasse kann im einfachsten Fall wie folgt definiert werden.
public class Person {
String firstName, lastName;
}
Dieser Bauplan schreibt vor, dass jedes Objekt einen eigenen Vornamen und
Nachnamen hat.
Die Erzeugung von Objekten erfolgt mit Hilfe des new-Operators. Anschließend
kann auf die Eigenschaften des Objekts zugegriffen werden:
Person p = new Person();
p.firstName = "Karl";
p.lastName = "Napf"
System.out.println( p.firstName + " " + p.lastName);
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#13
Rückblende: Punktoperator(1)
An dieser Stelle nochmal ein Blick zurück auf den Punktoperator. Anders als der
Array-Zugriffsoperator, der nur mit speziellen Objekten (Arrays) funktioniert,
kann der Punktoperator mit beliebigen Objekten oder einer Klasse arbeiten.
Das sieht dann verallgemeinert so aus
<Wo>.<Was>
Das <Wo> gibt an, auf wen zugegriffen wird (bzw. wer die Eigenschaft hat). Dies
muss ein Objekt(=Variable) oder eine Klasse(=Klassenname) sein. Mit
elementaren Datentypen geht nichts! Auch bei verketteten Ausdrücken gilt, dass
links vom Punkt stets ein Objekt oder eine Klasse stehen muss!
(System.out).println( "Beispiel"); // System.out = ein Objekt
Es gibt jedoch noch eine Sonderregel: Steht links vom Punkt nichts, ist dies
gleichbedeutend mit "Ich Objekt" und wenn das nicht funktioniert "Meine Klasse".
Das <Was> ist der Name einer Eigenschaft. Dies kann eine Variable oder eine
Methode sein. Im letzteren Fall kommen noch der Funktionsaufruf-Operator
sowie ggf. Parameter für die Methode ins Spiel.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#14
Rückblende: Punktoperator(2)
public class Punktoperator {
public static void main(String[] args) { // Hier geht's los
setupDemo(); // Aufruf Klassenmethode über "Meine Klasse"
}
public static void setupDemo() {
(new Punktoperator()).runDemo(); // Aufruf neues Objekt(!)
}
public void runDemo() {
Integer i1 = new Integer(7); // neues Objekt
int i2 = i1.intValue(); // Aufruf Instanzmethode
String s = Integer.toString( i2); // Aufruf Klassenmethode
print( s); // Aufruf Instanzmethode via "Ich Objekt"
}
public print( String text) {
System.out.println( text);
}
}
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#15
Rückblende: Punktoperator(3)
Erkenntnisse aus dem Beispiel
Die Welt der Klassen ist statisch (==static)
● Alle Klassen sind von Anfang an vorhanden und bleiben es auch
● Die Welt der Objekte ist dynamisch (!=static)
● Objekte werden dynamisch erzeugt und gelöscht
●
Die Ausführung eines Programms beginnt immer in der Klassenwelt (sonst ist ja
am Anfang nichts da). Dazu wurde vereinbart, dass die erste Methode main()
heißt (das ist so ähnlich wie die Nelke im Knopfloch bei einem Blind-Date). Um
in die Objektwelt zu wechseln, muss zunächst ein Objekt erzeugt werden.
Interessanterweise "sieht man aus der Objektwelt die Klassenwelt" umgekehrt
jedoch nicht. Das liegt daran, dass es zwar zu jedem Objekt eine eindeutige
Klasse gibt, aber u.U. zu jeder Klasse mehrere Objekte. Blind zugreifen und
richtig treffen kann man also nur in einer Richtung (von Objekt zu Klasse).
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#16
Kapselung
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#17
Zugriff auf Objekte und Klassen(1)
Der Zugriff auf die Eigenschaften von Klassen und Objekten erfolgt mit Hilfe des
Zugriffsoperators. Mit seiner Hilfe kann auf Zustandsvariablen zugegriffen
werden. Auch Methoden werden auf diese Art und Weise aufgerufen. Dabei ist
prinzipiell der Bezug zu einem Objekt bzw. einer Klasse nötig. Dieser wird
entweder über eine Objektreferenz oder den Klassennamen hergestellt.
Person p = new Person();
p.firstName = "Donald";
p.lastName = "Duck";
System.out.println( Person.RETIREMENT_AGE);
Innerhalb einer Klasse, d.h. in deren Klassenmethoden und Instanzmethoden,
kann auch ohne einen solchen Bezug auf Zustandsvariablen zugegriffen
werden. Der Zugriff gilt dann immer für die aktuelle Klasse bzw. das aktuelle
Objekt (wie das im Hintergrund genau funktioniert folgt in Kürze).
public String getFullName() {
return firstName + " " + lastName;
}
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#18
Zugriff auf Objekte und Klassen(2)
Vor dem Zugriffsoperator muss also stets eine Referenz auf ein Objekt oder ein
Klassenname stehen. Dies gilt auch für verkettete Zugriffe wie
System.out.println( "Hallo");
Dieser Aufruf kann auch folgendermaßen geschrieben werden
(System.out).println( "Hallo");
Das ist natürlich viel zu umständlich und aufgrund der links-Assoziativität des
Zugriffsoperators auch nicht notwendig. Es wird jedoch deutlich, daß der
Ausdruck aus zwei Zugriffen besteht. Damit der zweite Zugriff funktioniert, muss
der erste Zugriff ein Objekt (in diesem Fall einen PrintStream, der in der
Klassenvariable out der Klasse System gespeichert ist) zurückliefern. Dies wird
bei folgender Schreibweise nochmals verdeutlicht.
PrintStream ps = System.out;
ps.println( "Hallo");
Auch bei anderen verketteten Ausdrücken müssen alle Zugriffe bis auf den
letzten ein Objekt zurückliefern. Dabei kann ein einzelner Zugriff entweder ein
Variablenzugriff oder ein Methodenaufruf sein.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#19
Modifizierer(1)
Wir kennen bereits zwei Modifizierer für die Bedeutung von Klassenelementen:
static
Zur Kennzeichnung von Variablen und Methoden, die zur Klasse gehören.
final
Für konstante Variablen (später auch für "konstante" Klassen und Methoden)
Für Klassen und Objekte kommen nun weitere Modifizierer hinzu
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#20
Modifizierer(2)
Der Zugriff auf die Eigenschaften einer Klasse und ihrer Objekte kann über sog.
Zugriffsmodifizierer reglementiert werden. Deren extremsten Ausprägungen sind
public
Kein Schutz bzw. Kapselung. Jeder darf auf Eigenschaften zugreifen.
private
Totaler Schutz bzw. Kapselung. Nur die eigene Klasse darf zugreifen.
Später werden wir noch weitere Zugriffsmodifizierer kennenlernen, die eine
feinere Einstellung der Zugriffsrechte, v.a. bei der Vererbung, erlauben.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#21
Zugriffsmethoden (Getter und Setter)
Ein wichtiges Ziel der Objektorientierung ist, Daten und Funktionen nicht nur
zusammenzuführen sondern die Daten in dem Zusammenhang auch zu
schützen, d.h. den Zugriff nur noch über Methoden zu erlauben. Um dies zu
erreichen kann man die Instanzvariablen als privat kennzeichnen und
entsprechende Zugriffsmethoden definieren. Diese können von vielen
Entwicklungsumgebungen auch automatisch generiert werden:
public class Person {
private String firstName, lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName( String newFirstName) {
firstName = newFirstName;
}
}
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#22
Pakete
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#23
Pakete(1)
Klassen fassen Daten und die Methoden zu ihrer Bearbeitung zusammen.
Pakete fassen Klassen zusammen, die ähnliche oder gemeinsame Aufgaben
haben und eng zusammenarbeiten. Dazu genießen Sie untereinander
besondere Zugriffsrechte. Das Paket muss in der Klasse deklariert werden:
package mypackage;
public class MyClass {
// ...
}
Klassen ohne explizite Paketdeklaration werden dem Default Paket zugeordnet.
Dieses wird auch als main-package bezeichnet, hat aber nichts mit der
gleichnamigen Methode zu tun sondern entspricht dem aktuellen Verzeichnis.
Nachdem Klassen auf Dateien abgebildet werden liegt es nahe, die
Paketstruktur auf Verzeichnisse abzubilden. Der Compiler prüft (1) ob der
Klassenname zum Dateiname und (2) der Paketname zum Verzeichnis passt.
Alle Klassen im selben Verzeichnis sind also im selben Paket.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#24
Pakete(2)
Um die Inhalte eines Pakets in einer Klasse bequem nutzen zu können, wird die
import-Anweisung verwendet.
import java.io.*; // Importieren aller Klassen im Paket java.io
import java.util.ArrayList; // Importieren einer einzelnen Klasse
Zusätzlich ist es möglich (aber nicht empfehlenswert), direkt im Quellcode über
einen qualifizierten Namen auf Paketinhalte zuzugreifen:
// Objektdeklaration mit voll qualifiziertem Klassenname
java.util.HashMap myMap;
Dies ist quasi eine Erweiterung des Zugriffsoperators und immer dann
notwendig, wenn zwei oder mehr Pakete identische Bezeichner enthalten (dann
ist ein import nicht möglich). Wenn möglich empfielt sich jedoch die Verwendung
der import-Anweisung, da auf diese Art und Weise
(1) der Quellcode lesbar wird, und
(2) sofort klar ist, welche anderen Klassen und Pakete verwendet werden.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#25
Pakete(3)
Pakete können wie Verzeichnisse geschachtelt werden. Der vollständige
Paketname entspricht dabei einem relativen Pfad ausgehend vom CLASSPATH.
Damit kann die virtuelle Maschine Klassen anhand ihres Pakets auf der
Festplatte lokalisieren.
Da der CLASSPATH mehrere Wurzelverzeichnisse enthalten kann ist es
außerdem möglich, den logischen Inhalt eines Pakets auf mehrere physikalische
Verzeichnisse zu verteilen, z.B. um Produktklassen und Testklassen zu trennen.
Beispiel
<root>/
src/
people/
Person.java
test/
people/
PersonTest.java
© Andreas Rau, 24.03.11
CLASSPATH = "<root>\src;<root>\test"
nicht "<root>\src\people; <root>\test\people"
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#26
Pakete(3a)
Neben der Zusammenfassung von Klassen ist ein weiterer Zweck von Paketen
sicherstellen, dass Klassennamen nicht kollidieren. Um im Gegenzug
sicherzustellen, das Paketnamen ihrerseits nicht kollidieren, hat sich eine
Konvention entwickelt, die auf "umgedrehten" Domainnamen basiert.
Beispiel:
package de.fhte.winf1.informatik;
public class Person {
}
wird gespeichert in
<Wurzelverzeichnis>\de\fhte\winf1\informatik\Person.java
CLASSPATH = "<Wurzelverzeichnis>"
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#27
Ausführung eines Java Programms auf der virtuelle Maschine
Aufruf mit "java [-cp <CLASSPATH>] <Startklasse>"
Startklasse laden
Wenn die Startklasse eine main-Methode hat {
Aufruf der Main-Methode
Solange Programmende nicht erreicht {
Wenn Verwendung einer neuen Klasse {
Wenn Verzeichnis gemäß Paketname im CLASSPATH gefunden {
Wenn Datei gemäß Klassenname gefunden {
Klasse laden
} Sonst {
Fehlermeldung "Class not found"
}
} Sonst {
Fehlermeldung "Class not found"
}
}
}
} Sonst {
Fehlermeldung "no such method: main()"
}
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#28
Pakete(4)
Typische Fehler bzw. Fallstricke beim Umgang mit Paketen
Der CLASSPATH bzw. die entsprechende Editoreinstellung sind falsch
Der CLASSPATH zeigt auf das Verzeichnis, welches das Unterverzeichnis mit
der Paketstruktur enthält, nicht auf die Verzeichnisse mit den Paketen selbst.
Bei der Deklaration wird nur der eigentliche Paketname angegeben
Es muss der Name des Pakets inkl. aller Vaterpakete angegeben werden
Beim Import wird nicht der vollständig qualifizierte Paketname angegeben
Es muss der Name des Pakets inkl. aller Vaterpakete angegeben werden
Import Statements stehen vor der Paketdeklaration
Um in ein Paket zu importieren müssen die Imports hinter der Deklaration stehen
Beim Import mit * werden keine Unterpakete importiert
Das ist richtig so, Unterpakete müssen explizit einzeln importiert werden.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#29
Wiederholung: Modifizierer
Damit können wir nun die Liste der Zugriffsmodifizierer vervollständigen:
private
● (default)
● protected
● public
●
Zugriff nur innerhalb der eigenen Klasse
Zugriff für Klassen im gleichen Paket
Zugriff für Klassen im gleichen Paket und Subklassen
Zugriff für Jedermann
Wie man sieht, bauen diese Zugriffsarten aufeinander auf. Jede schließt die
Zugriffsmöglichkeiten ihrer Vorgänger ein und erweitert sie.
public
protected
(default)
private
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#30
Wiederholung: Gültigkeitsbereiche
Mit Paketen und Klassen kennen wir nun (fast) alle Gültigkeitsbereiche in Java:
Paket
Datei
Klasse
Objekt
Instanzmethode
Klassenmethode
Block/Kontrollstruktur
Block/Kontrollstruktur
Der Zugriff auf andere Bereiche ist nur von Innen nach Außen möglich!
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#31
Wiederholung: Lebensdauer
Programmende
Objektzerstörung (GC)
Blockvariablen
Parameter, Lokale Variablen
Objekte, Instanzvariablen
Klassen, Klassenvariablen
Rücksprung
Blockaustritt
Blockeintritt
Methodenaufruf
Objekterzeugung (new)
Programmstart
Die Lebensdauer von Variablen ist eng an die Gültigkeitsbereiche gekoppelt
t
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#32
Lifecycle
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#33
Initialisierung und Konstruktoren
Um neue Objekte nicht immer in mehreren Schritten initialisieren zu müssen,
können bei der Erzeugung Parameter angegeben werden. Diese werden an eine
spezielle Methode, den Konstruktor, übergeben.
Person p = new Person( "Karl", "Napf");
Der Konstruktor ist eine Methode mit dem selben Namen wie die Klasse und hat
keinen Rückgabewert.
public Person Person( String firstName, String lastName);
Wie jede andere Methode (z.B. println), kann auch der Konstruktor überladen
werden, d.h. man kann mehrere Konstruktoren mit verschiedenen
Parameterlisten schreiben.
Wird kein eigener Konstruktor definiert (und nur dann), stellt der Compiler einen
Default-Konstruktor ohne Parameter zur Verfügung. Man kann also durch die
Erstellung eines eigenen Konstruktors die Initialisierung erzwingen.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#34
Garbage Collection
In Java gibt es den Luxus einer automatischen Speicherverwaltung. Der
Programmierer muss den Speicher für dynamische Objekte nicht selbst
verwalten und diese explizit löschen. Stattdessen werden alle Objekte, die nicht
mehr gebraucht werden (die nirgends mehr referenziert werden) automatisch
aufgeräumt. Damit gehören Speicherlecks und ähnliche Probleme der
Vergangenheit an.
Um dennoch die Möglichkeit zu haben, eigene Aufräumarbeiten vorzunehmen
(z.B. temporäre Dateien zu löschen oder Betriebsmittel außerhalb der virtuellen
Maschine freizugeben) wird beim aufräumen von Objekten eine spezielle
Methode aufgerufen. Diese kann selbst definiert werden und muss folgende
Signatur besitzen.
public void finalize()
Dieser Aufruf erfolgt jedoch nur, wenn das betreffende Objekt während der
Laufzeit des Programms aufgeräumt wird. Wird das Programm vorher beendet,
werden der gesamte Speicher freigegeben ohne die Objekte einzeln
aufzuräumen.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#35
Vererbung
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#36
Rückblende: Vererbung
Eigenschaften lassen sich durch Vererbung abstrahieren und wiederverwenden:
:Objekt
:Fahrzeug
:Landfahrzeug
Ein Auto ist-ein
spezielles Landfahrzeug
:Wasserfahrzeug
:Auto
:Pkw
:Boot
:Lkw
:Segelboot
:Motorboot
:Smart
Statt Vererbung spricht man auch von Spezialisierung oder ist-ein Beziehung.
Die Kunst besteht darin, die richtige Schrittweite zu finden – zum besseren
Verständnis und für Erweiterungen, z.B. Fahrrad oder Surfbrett).
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#37
Vererbung(1)
Bei der Vererbung werden alle Eigenschaften der Vaterklasse (engl. superclass)
in die abgeleitete Klasse (engl. Subclass) übernommen. Die Objekte der
abgeleiteten Klassen besitzen die selben Datenfelder und Methoden wie die
Objekte der Vaterklasse und können diese durch neue Datenfelder und
Methoden ergänzen.
public class Subclass extends Superclass {
// ...
}
geerbte Eigenschaften
ergänzte Eigenschaften
Wird keine Superklasse angegeben, wird implizit die Klasse Object verwendet.
public class Person { // extends Object
// ...
}
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#38
Vererbung(2)
Aufgrund der ist-ein Semantik wird die Vererbungshierarchie auch
Spezialisierung (von oben nach unten) bzw. Generalisierung (von unten nach
oben) bezeichnet. Als Konsequenz dieser Semantik können Objekte von
abgeleiteten Klassen an Stelle von Objekten der Vaterklasse verwendet werden.
Es ist also möglich, ein Auto einfach als (spezielles) Fahrzeug zu betrachten.
Beim Entwurf einer Vererbungshierarchie werden daher alle allgemeinen
Eigenschaften möglichst weit oben implementiert, um so in möglichst vielen
abgeleiteten Klassen wiederverwendet werden zu können und die Wartbarkeit
zu verbessern.
Diese Verallgemeinerung kann auch zu einem späteren Zeitpunkt geschehen.
Dabei wird eine Eigenschaft, die zunächst in einer speziellen Klasse
implementiert war, in eine darüberliegende Klasse verschoben und ggf. durch
zusätzliche Parameter ergänzt oder in mehrere Teilschritte zerlegt wird. Grund
hierfür kann die Entdeckung von Gemeinsamkeiten während der
Programmierung ("sowas hab ich doch schonmal gemacht") oder das auftreten
von neuen Klassen sein. Diesen Vorgang nennt man Refactoring.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#39
Überschreiben von Methoden
Das Verhalten von Objekten einer abgeleiteten Klasse kann angepasst werden,
indem Methoden der Vaterklasse durch neue Methoden ersetzt bzw.
überschrieben (engl. overridden) werden. Dazu muss die neue Methode den
selben Namen, die selben Parameter, den selben Rückgabewert und den
selben Zugriffsschutz haben
Für überschriebene Instanzmethoden wird erst zur Laufzeit entschieden, welche
Variante aufgerufen wird. Der Compiler erzeugt also noch keinen konkreten
Aufruf. Dies nennt man späte bzw. dynamische Bindung (engl. late binding /
dynamic binding). Dies funktioniert für jeden Methodenaufruf – auch die in einer
geerbten Methode. Die Vaterklasse kann also überschriebene Methoden in
abgeleiteten Klassen aufrufen. Über solche Hook-Methoden, kann man sich in
einer abgeleiteten Klasse in komplexere Abläufe "einklinken".
Ausschlaggebend dabei ist nicht der statische Typ der in der entsprechenden
Variablen gespeicherten Objektreferenz, sondern der dynamische Typ des
referenzierten Objekts. Der Aufruf von Klassenmethoden wird dagegen über den
Klassennamen fest an eine konkrete Klasse gebunden.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#40
Überladen vs Überschreiben
Beim Überladen einer Methode soll diese unter dem selben Namen für
verschiedene Datentypen bzw. Parameterlisten einsetzbar sein. Daher ist auch
der Rückgabewert typischerweise identisch (dies ist jedoch nicht zwingend). Die
Methoden sind in derselben Klasse realisiert und werden anhand ihrer
Parameterliste unterschieden.
Beim Überschreiben einer Methode soll deren Funktion in einer abgeleiteten
Klasse angepasst werden. Damit die neue Methode die alte Methode ersetzen
kann, muss Sie den selben Namen, dieselbe Parameterliste und den selben
Rückgabewert haben.
Methodenzweck
Methodenname
Parameterliste
Rückgabewert
Heimatklasse
Zugriffsrechte
© Andreas Rau, 24.03.11
überladen
identisch
identisch
unterschiedlich
typischerweise identisch
identisch
typischerweise identisch
überschreiben
identisch
identisch
identisch
identisch
unterschiedlich
identisch
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#41
Ergänzung: Modifizierer
Den Modifizierer final kennen wir bereits in seiner Funktion zur Kennzeichnung
von Konstanten. Im Zusammenhang mit der Vererbung hat er noch zwei weitere
Bedeutungen:
finale Klassen
Von einer finalen Klasse können keine weitere Klassen abgeleitet werden.
finale Methoden
Finale Methoden können in einer Subklasse nicht überschrieben werden.
Da dies eine erhebliche Einschränkung der Vererbung und der damit
verbundenen Vorteile darstellt, sollte der Einsatz von final zu diesem Zweck
wohl durchdacht sein.
Hinweis: Das engl. Adjektiv "final" bedeutet "endgültig" (z.B. bedeutet "all sales
final" soviel wie "Umtausch ausgeschlossen"). In dieser Bedeutung macht das
Schlüsselwort sowohl für Variablen als auch für Klassen und Methoden Sinn.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#42
Einschub: Subtypen
Ein abstrakter Datentyp ist zwar eindeutig und gekennzeichnet durch seine Werte und die
darauf möglichen Operationen. Bei der Betrachtung von Datentypen fallen jedoch gewisse
Ähnlichkeiten auf, z.B. zwischen den numerischen Datentypen. Auf Basis dieser
Ähnlichkeiten lassen sich Typfamilien definieren und hierarchisch darstellen. In diesem
Zusammenhang spricht man auch von Subtypen. Ein Subtyp ist ein Typ, der überall
anstelle des eigentlichen Typs verwendet werden kann (vgl. Liskov Substitution Principle).
Formal gesprochen: (Quelle: www.foldoc.org)
If S is a subtype of T then an expression of type S may be used anywhere that one of type
T can and an implicit type conversion will be applied to convert it to type T
Überall dort, wo ein Wert vom Typ T erwartet wird, darf also auch ein Wert vom Typ S
stehen und mit den Werten vom Typ S muss man alles tun können, was man auch mit
Werten vom Typ T machen könnte.
Damit darf ein Subtyp weniger Werte und mehr Operationen enthalten als sein
übergeordneter Typ. Mit anderen Worten: Subtypen sind eine Spezialisierung.
Wir werden sehen, dass dies bei der Vererbung erfüllt ist: Subklassen sind also Subtypen.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#43
Liskov Substitution Principle & Programming by Contract
Polymorphie ist nur möglich, weil jedes Objekt einer speziellen abgeleiteten
Klasse die Eigenschaften aller allgemeineren Vaterklassen hat. In der Praxis
funktioniert dies aber nur, wenn die Vererbung als „ist-ein“ Beziehung korrekt
verwendet wurde. Barbara Liskov formulierte dies in der folgenden Forderung:
„Methoden, die Referenzen auf Basisklassen benutzen, müssen in der Lage
sein, Objekte von abgeleiteten Klassen zu benutzen, ohne es zu bemerken“
Ob diese Forderung erfüllt wird liegt aber nicht an den Methoden sondern an den
Objekten der abgeleiteten Klassen. Zur Verdeutlichung kann man die Summe
(der Schnittstellen) aller Methoden einer Klasse als einen Vertrag betrachten:
Jede Methode stellt gewisse Erwartungen (Vorbedingungen) an ihre Parameter
und garantiert dafür eine bestimmte Leistung (Nachbedingung). Dies nennt man
auch „Programming by Contract“.
Um den ursprünglichen Vertrag der Basisklasse nicht zu verletzen darf eine
überschriebene Methode „weniger verlangen“ und „mehr liefern“ aber nicht
umgekehrt! Technisch gesehen bedeutet dies: der Wertebereich für Parameter
darf aufgeweitet und die Menge der Rückgabewerte eingeschränkt werden.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#44
Polymorphie
Eine Referenzvariable kann auf Objekte ihres Typs sowie Objekte aller davon
abgeleiteten Klassen zeigen ("ein Auto ist-ein spezielles Fahrzeug"). Das Objekt
hinter dieser Variablen kann also in vielen (poly) Gestalten (morph) auftreten.
Wird über diese Variable eine überschriebene Methode aufgerufen, wird stets
die "richtige" Methode ausgeführt, also die in der tatsächlichen Klasse des
Objekts definierte. Damit entscheidet das Objekt quasi selbst, wie es auf einen
Methodenaufruf reagiert. Fallunterscheidungen nach Objekttyp und die damit
verbundenen Probleme, z.B. bei der Einführung neuer Klassen, sind nicht mehr
nötig. Der Code funktioniert mit allen derzeitigen und künftigen Subklassen!
Beispiel:
Fahrzeug f = new Auto();
/* nicht mehr nötig
if (f instanceof Auto) {
f.driveTo( "Hamburg");
} else (f instanceof Schiff) {
f.swimTo( "Hamburg");
}
*/
f.moveTo( "Hamburg"); // ruft die Methode der Klasse Auto auf
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#45
Frühe Bindung / Späte Bindung
In Java gibt es zwei verschiedene Arten, Methoden aufzurufen (ja, wirklich!)
Frühe/Statische Bindung (Vorteil: schnell, effizient, sicher; Nachteil: unflexibel)
Der Compiler entscheidet anhand des deklarierten Parametertyps, welche
überladene Methode aufgerufen wird.
Beispiel:
Fahrzeug f = new Auto();
Presse.verschrotten( f); // ruft verschrotten(Fahrzeug) auf
Späte/Dynamische Bindung (Vorteil: flexibel; Nachteil: fehleranfällig)
Das Laufzeitsystem entscheidet anhand des tatsächlichen Objekttyps welche
überschriebene Methode aufgerufen wird.
Beispiel:
Fahrzeug f = new Auto();
f.move(); // ruft Auto.move() auf
Achtung: Aufrufe von Klassenmethoden werden immer früh gebunden!
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#46
Double-Dispatching
Bei der Auswahl von überladenen Methoden findet die Bindung stets früh statt.
Will man hier eine späte Bindung erreichen, muss man mit einer
überschriebenen Methode "zurückrufen" (dies nennt man double-dispatching).
Beispiel:
public class Presse {
public void verschrotten( Fahrzeug f) {
f.verschrotten( this);
}
}
public class Fahrzeug {
public verschrotten( Presse p) {
}
}
public class Auto extends Fahrzeug {
public verschrotten( Presse p) {
}
}
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#47
Delegation
Eine Klasse zu beerben ist nicht die Einzige und auch nicht die Beste
Möglichkeit, sich Ihre Dienste nutzbar zu machen. Mann kann auch einfach ein
separates Objekt der Klasse mit der gewünschten Funktionalität erzeugen und
für seine Zwecke einspannen. In diesem Fall spricht man von Delegation.
Die Vererbung sollte nur eingesetzt werden, wenn dies der "ist-ein"-Beziehung
gerecht wird. Dies ist regelmäßig dann der Fall, wenn die geerbte Funktionalität
auch (mit derselben Schnittstelle) nach Außen angeboten werden soll. Wird sie
hingegen nur intern verwendet, ist ein Hilfsobjekt meist die bessere Wahl.
In der Umsetzung bedeutet die Delegation also, dass man nicht von der
Hilfsklasse erbt sondern in seiner eigenen Klasse eine zusätzliche
Instanzvariable einführt und in dieser ein Objekt der Hilfsklasse speichert, um es
später aufrufen zu können.
Hinweis: Ein besonderer Anwendungsfall für Delegation ist eine sog. Fassade
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#48
Analogien - Klassen und Objekte
Wir stellen uns ein Industriegebiet mit vielen Maschinenfabriken vor...
Jede Klasse ist eine Maschinenfabrik. Sie hat einen Bauplan für Maschinen. Die
Produktion kann Anzeigen (Klassenvariablen) und Knöpfe (Klassenmethoden)
haben. Allerdings können einige dieser Elemente verdeckt (private) sein.
Ein Objekt ist eine Maschine aus einer bestimmten Fabrik und kann ebenfalls
Anzeigen (Instanzvariablen) und Knöpfe (Instanzmethoden) haben. Auch hier
ist nicht unbedingt alles öffentlich (public).
Mit Hilfe der Vererbung kann man Maschinen weiterentwicklen. Dabei bleiben
alle Elemente der usprünglichen Maschine erhalten und es können weitere
Elemente hinzugefügt werden um eine speziellere Maschine zu konstruieren.
Eine Maschine kann aber auch mit Hilfe der Aggregation aus mehreren
kleineren Maschinen zusammengebaut werden.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#49
Pseudovariablen
this und super
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#50
Die Magie im Hintergrund(1)
Die Pseudovariable this
Beim Zugriff auf Objekteigenschaften muss bekanntlich stets eine
Objektreferenz angegeben werden. Damit ist es insbesondere Methoden
möglich, mit verschiedenen Objekten zu arbeiten.
Beispiel
// kann als methodName(obj, p1, p2, p3) interpretiert werden
obj.methodName(p1, p2, p3);
Die Pseudovariable this ist also quasi ein unsichtbarer Parameter. Er ist nur in
Objektmethoden verfügbar und zeigt stets auf das aktuelle Objekt. Ein nicht
qualifizierter Zugriff auf (ohne obj.) bezieht sich immer auf das aktuelle Objekt.
Beispiel
myMethod(); // bedeutet implizit this.myMethod();
Zwingend notwendig ist der Einsatz von this also nur dann, wenn Klassenoder Instanzvariablen durch lokale Variablen verdeckt sind, z.B. im Konstruktor.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#51
Die Magie im Hintergrund(2)
Die Pseudovariable super
Gestattet beim Überschreiben einer Methode den Aufruf er bisherigen Methode
in der direkten Vaterklasse. Dies ist sowohl in Instanzmethoden als auch in
Klassenmethoden möglich. Damit kann die bisherige Implementierung entweder
ersetzt oder am Anfang bzw. am Ende ergänzt werden.
Beispiele
public void someMethod() { // Variante 1: komplett ersetzen
// my Code
}
public void someMethod() { // Variante 2: ergänzen am Anfang
// my Code
super.someMethod();
}
public void someMethod() { // Variante 3: ergänzen am Ende
super.someMethod();
// myCode
}
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#52
Die Magie im Hintergrund(3)
Die Pseudovariable super (Fortsetzung)
Um bei komplexen Methoden möglichst viel wiederverwenden zu können, bietet
es sich an, diese in mehrere Schritte aufzuteilen und diese jeweils als eigene
Methoden zu realisieren (auch eine Form von Refactoring).
Beispiel für Refactoring
public void someComplexMethod() {
someComplexMethodStep1();
someComplexMethodStep2();
}
Damit kann man entweder wie bisher den Gesamtablauf ersetzen oder ergänzen
oder dasselbe für jeden der einzelnen Schritte tun.
Beispiel
public void someComplexMethod() {
super.someComplexMethodStep1();
// my Code for step 2
}
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#53
Die Magie im Hintergrund(4)
Verwendung von super und this bei Konstruktoren
Neben
der
Verwendung
als
Pseudovariable
beim
Zugriff
auf
Objekteigenschaften können super und this auch zum expliziten Aufruf von
Konstruktoren verwendet werden. Dies ist nur in einem Konstruktor möglich und
muß in der ersten Zeile erfolgen (da sonst eine Default-Initialisierung erfolgt).
public class Person {
public Person() {
this( “N.“, “N.“); // ruft Person(String,String) auf
}
public Person( String firstName, String lastName) {
this.firstName = firstName; this.lastName = lastName;
}
}
public class Student extends Person {
public Student( String firstName, String lastName) {
super( firstName, lastName); // ruft Person(String,String) auf
}
}
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#54
Grundlegende
Objekteingenschaften
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#55
Die Klasse Object und allgemeine Objekteigenschaften
Die Klasse Object ist "die Mutter aller Klassen": Jede andere Klasse erbt direkt
oder indirekt von dieser Klasse. Daher stehen die Methoden dieser Klasse für
alle Objekte in Java zur Verfügung. Die wichtigsten davon sind:
public String toString()
Liefert eine Beschreibung des Objekts (zur Anzeige in GUIs und bei Debugging).
public boolean equals(Object obj)
Vergleicht zwei Objekte auf Gleichheit bzw. Äquivalenz (nicht Identität).
public int hashCode()
Liefert eine "Kennziffer" des Objekts. Diese Kennziffer kann für verschiedene
Objekte gleich sein und muss für gleiche Objekte gleich sein.
public Class getClass()
Liefert eine Objekt zurück, das die Klasse des Objekts repräsentiert.
Weitere Methoden von Object werden später beschrieben.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#56
Vergleiche: Gleichheit und Identität(1)
Gleichheit und Identität sind bei Objekten zwei verschiedene Dinge:
●
●
Die Identität von Objekten wird mit dem Operator == überprüft. Dieser liefert
eine Antwort auf die Frage "Ist dies dasselbe Objekt?".
Die Gleichheit von Objekten wird mit der Methode equals() überprüft. Diese
liefert eine Antwort auf die Frage "Sind diese beiden Objekten gleich(wertig)?".
Der Operator == vergleicht also gewissermaßen den "Inhalt einer
Schublade", die Methode equals() vergleicht Objekte. Bei primitiven
Datentypen ist diese Unterscheidung nicht notwendig: Ihre Werte werden direkt
mit == verglichen (Methoden stehen ohnehin nicht zur Verfügung).
Damit die Objekte einer Klasse später auch in Collections (Datenstrukturen – wie
Arrays, nur komfortabler) verwendet werden können, muss daneben für die
Methoden equals() und hashCode() folgender Zusammenhang gelten(!):
obj1.equals(obj2) ⇒ obj1.hashCode() == obj2.hashCode()
Es muss also möglich sein, ein Objekt auch über seinen hashCode zu finden.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#57
Vergleiche: Gleichheit und Identität(2)
Beispiel
public class Person {
public boolean equals( Object obj) { // Vergleich von instVars
if ( this == obj) return true;
if ( null != obj && obj instanceof Person) {
Person other = (Person)obj;
return (this.name.equals( other.name)
&& this.surname.equals( other.surname));
} else {
return false;
}
}
}
public int hashCode() { // Verwendung von instVars (wie equals)
return name.hashCode() + surname.hashCode();
}
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#58
Vergleiche: Gleichheit und Identität(3a)
Erweitertes Beispiel
public class Person {
public boolean equals( Object obj) { // Vergleich von instVars
if ( this == obj) return true;
return equalsImpl( obj);
}
Attributvergleich
null
!null
null
true
(false)
public boolean equalsImpl( Object obj) {
!null
equals()
equals()
if ( null != obj && obj instanceof Person) {
Person other = (Person)obj;
return (null==this.name ? null==other.name : this.name.equals( other.name))
&& (null==this.surname ? null==other.surname : this.surname.equals( other.surname));
}
}
return false;
public int hashCode() { // Verwendung von instVars (wie equals)
return hashCodeImpl();
}
public int hashCodeImpl() {
}
return (null!=name ? name.hashCode() : 7) + (null!=surname ? surname.hashCode() : 11);
}
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#59
Vergleiche: Gleichheit und Identität(3b)
Erweitertes Beispiel (Fortsetzung)
public class Student extends Person {
public boolean equalsImpl( Object obj) {
if ( super.equalsImpl( obj) {
if ( null != obj && obj instanceof Student) {
Student other = (Person)obj;
return (null==this.matrikelnr ? null==other.matrikelnr :
}
}
}
return false;
this.matrikelnr.equals( other.matrikelnr));
public int hashCodeImpl() {
return super.hashCodeImpl() + null!=matrikelnr ? matrikelnr.hashCode() : 162;
}
}
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#60
Die Klasse Class und Reflection(1)
Zur Laufzeit des Programms können die Eigenschaften aller Klassen, z.B. der
Klassenname, über ein entsprechendes Objekt der Klasse Class abgefragt
werden. So kann man z.B. vorhandene Konstruktoren, Methoden und Felder
sowie deren Eigenschaften abfragen und ggf. sogar darauf zugreifen. Die
Konstruktoren, Methoden und Felder werden dabei ihrerseits durch
entsprechende Klassen repräsentiert.
Dies wird als Reflection bezeichnet und ist keineswegs selbstverständlich: nicht
alle Programmiersprachen unterstützen dies (.net nennt dies Introspection). Die
zugehörigen Klassen sind im Paket java.lang.reflect enthalten.
Mit Reflection sind geprüfte dynamische Aufrufe zur Laufzeit möglich*. Dies ist
Grundlage zahlreicher trickreicher Programmierkonstrukte. Damit können z.B.
Klassen geschrieben werden, die mit beliebigen anderen Klassen umgehen und
diese z.B. in eine Datenbank speichern können.
*In interpretierten Skriptsprachen erfolgt dies mehr oder weniger ungeprüft mit einer eval-Funktion zur Ausführung
beliebiger Zeichenketten
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#61
Die Klasse Class und Reflection(2)
Der Zugriff auf das Class-Objekt kann dabei auf verschiedene Arten erfolgen:
(1) Durch direktes Hinschreiben
<Klassenname>.class
(2) Durch Zugriff auf ein Objekt dieser Klasse
obj.getClass()
(3) Durch dynamisches Laden über dem Klassennamen
Class.forName( "<Klassenname>")
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#62
Die Klasse Class und Reflection(3)
Interessante Methoden in der Klasse Class sind u.a.
String getName()
liefert den voll qualifizierten Namen der Klasse (incl. Paketname)
Package getPackage()
liefert einen Repräsentanten des Pakets (oder null)
Constructor[] getConstructors()
liefert ein Array mit allen Konstruktoren
Method[] getMethods()
liefert ein Array mit allen Methoden
Field[] getFields()
liefert ein Array mit allen Datenfeldern
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#63
Exception Handling
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#64
Fehlerbehandlung(1) - Hintergrund
Nicht immer läuft alles nach Plan: Entwickler machen Leichtsinns- bzw.
Denkfehler und rufen z.B. Methoden mit ungültigen Parametern auf.
Speichermedien und Netzwerke versagen. Benutzer geben Blödsinn ein oder
drücken die falschen Knöpfe. Einige dieser Fehler können dauerhaft beseitigt
werden! Mit dem Rest muss ein Programm klarkommen, um einsetzbar zu sein.
Somit muss ein Programm neben der Implementierung des Normalfalls auch
Code zum Umgang mit Sonderfällen und Fehlern (auch wenn man i.d.R. mit
Ersterem anfängt) enthalten. Dieser kann oft einen erheblichen Umfang haben
und kann die Lesbarkeit und Wartbarkeit des Programms erheblich erschweren.
Das Ziel der Fehlersuche und Fehlerbehebung ist die Elimination von Fehlern
innerhalb des Programms (z.B. Syntaxfehler, Denkfehler). Ziel der
Fehlerbehandlung hingegen ist der Umgang mit Fehlern deren Ursache
außerhalb des Programs liegt (z.B. Eingabefehler oder Fehler in der Peripherie).
Fehlersuche und Fehlerbehebung während der Entwicklung einerseits und
Fehlerbehandlung im Betrieb anderseits sind also zwei komplementäre
Aktivitäten zur Implementierung von korrekten, zuverlässigen und robusten
Programmen und damit ein wesentlicher Beitrag zur Qualität eines Programms.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#65
Fehlerbehandlung(2) - Grundprinzipien
Die Behandlung von Fehlern setzt zunächst voraus, daß man(a) mit Ihnen
rechnet und (b) ein Konzept zu ihrer Behandlung hat: Fehler mit denen keiner
rechnet, werden meist auch nicht erkannt, und Fehler, die nicht unterschiedlich
behandelt werden muss man wahrscheinlich auch nicht unterscheiden.
Umgekehrt kann es Sinn machen, (a) "unerwartete Fehler" durch "überflüssige"
else-Zweige u.ä. abzufangen und (b) Fehler detaillierter zu beschreiben als
"Fehler"/"kein Fehler", um Sie leichter beseitigen oder behandeln zu können.
Der grundsätzliche Ablauf bei der Behandlung von Fehlern besteht aus zwei
Schritten. Diese finden i.d.R. an getrennten Stellen im Programm statt:
Erkennung von Fehlern (z.B. in einer Bibliothek)
● Behandlung von Fehlern (z.B. durch Anzeige einer Fehlermeldung im GUI)
●
Bei der Erkennung eines Fehlers ist also meist noch nicht klar, wie er behandelt
werden soll. Die Information über aufgetretene Fehler muss daher möglichst
elegant an die richtige Stelle weitertransportiert werden.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#66
Fehlerbehandlung(3) – Klassische Fehlerbehandlung
Für die klassische Fehlerbehandlung werden Fehlerzustände in Variablen
gespeichert und über besondere Rückgabewerte oder zusätzliche Parameter
zurückgemeldet.
Beispiel:
public static double sqrt(double x) {
if (x >= 0) { // Sicherheitsüberprüfung
// Wurzel berechnen und zurückliefern
} else {
return -1; // Fehlerwert, Wurzel ist immer positiv
}
}
public static void main(String[] args) {
// Wert x eingeben
double w = sqrt(x);
if (w != -1) {
// Weiter
} else {
// Fehler
}
}
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#67
Fehlerbehandlung(4) – Klassische Fehlerbehandlung
Die klassische Fehlerbehandlung hat einige gravierenden Nachteile
Nicht immer gibt es "freie" Rückgabewerte für Fehler
● Zusätzliche Parameter sind aufwendig und verwässern die Schnittstelle
● Die Prüfung auf Fehler nach jedem Aufruf ist aufwendig.
● Das "saubere" Verlassen von Funktionen nach Fehlern ist aufwendig
● Hoher Aufwand für Rückmeldung von Fehlern über mehrere Aufrufebenen
● Keine klare Trennung zwischen produktivem Code und Fehlerbehandlung
● Keine/aufwendige Übergabe einer detaillierten Fehlerbeschreibung
● Identische Sprachmittel zur Funktionsrealisierung und Fehlerbehandlung
● Fehlerbehandlung kann vergessen werden
●
Diese Nachteile lassen sich in Java durch Exception-Handling vermeiden.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#68
Fehlerbehandlung(5) – Exception Handling
Exception Handling in Java und anderen Sprachen basiert auf folgenden Ideen:
Verwendung von besonderen Objekten zur Repräsentation von Fehlern
●
Übergabe einer detaillierten Fehlerbeschreibung im Objekt möglich
●
Systematisierung von Fehlern mit Hilfe einer Klassenhierarchie
● Verwendung einer besonderen Schnittstelle zur Übergabe der Fehlerobjekte
● Kein verwässern von Schnittstellen
● Kein zusätzlicher Aufwand
● Geht immer
● Klare Trennung von produktivem Code, Fehlerbehandlung und "Aufräumcode"
● Direktes anspringen der Fehlerbehandlung durch besonderen Mechanismus
● Keine Überprüfung nach jedem Aufruf nötig
● Einfache Rückmeldung über mehrere Aufrufebenen
● Unterstützung/Erkennung besonderer Konstrukte durch den Compiler
● Pflicht zur Deklaration von Exceptions bei Methoden
● Pflicht zur Behandlung von Exceptions
●
Damit sind die Nachteile der klassischen Fehlerbehandlung hinfällig.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#69
Fehlerbehandlung(6) – Exception Handling
Die technische Umsetzung in Java basiert auf 6 Elementen
● Basisklassen für Exceptions
● Try-Block als "Sicherheitszone" für fehleranfälligen Code
● Throw-Anweisung zum "werfen" von Exceptions im Fehlerfall
● Catch-Blöcke zur Behandlung von Fehlern aus einem try-Block
● Finally-Block zum aufräumen nach dem Verlassen von Blöcken/Funktionen
● Angabe von Exceptions bei der Funktionsdeklaration ("Beipackzettel")
Beispiel
public static void unstable() throws Exception {
throw new Exception();
}
public static void main(String[] args) {
try {
unstable();
} catch (Exception ex) {
// Fehler behandeln – wird nur im Fehlerfall ausgeführt
} finally {
// Aufräumen – wird immer ausgeführt
}
}
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#70
Fehlerbehandlung(7) – Exception Handling
Die catch-Blöcke haben eine gewisse Ähnlichkeit zu (a) Funktionen und (b) den
cases einer switch-Anweisung. Tatsächlich können einem try-Block mehrere
catch-Blöcke folgen. Der richtige catch-Block (case) wird dann anhand der
Klasse der Exception (Funktionsparameter) ausgewählt.
Der catch-Block kann also gewissermaßen überladen werden. Dabei ist es auch
möglich, durch Verwendung einer abstrakten Exceptionklasse mehrere Arten
von Exceptions gleich zu behandeln: Bei Verwendung der Klasse Exception, der
"Mutter aller Exceptions", verhält sich der resultierende catch-Block wie default
bei der switch-Anweisung. Dabei ist allerdings zu beachten, daß die Suche nach
dem richtigen catch-Block von oben nach unten verläuft. Daher müssen die
allgemeineren catch-Blöcke unten stehen.
Der finally-Block wird immer ausgeführt ist aber nicht immer nötig. Manchmal
gibt es auch keine catch-Blöcke sondern nur einen finally-Block. Dann wird die
Exception nach dem Aufräumen weitergereicht. Ein try-Block muss jedoch
immer mindestens einen catch-Block oder einen finally-Block haben (sonst nützt
er ja auch nichts).
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#71
Fehlerbehandlung(7a) – Exception Handling
Fehler selbst behandeln und hinterher aufräumen
try {
// Unsichere Operation ausführen
} catch (Exception ex) {
// Fehler behandeln – wird nur im Fehlerfall ausgeführt
} finally {
// Aufräumen – wird immer ausgeführt
}
Fehler selbst behandeln und nix zum aufräumen
try {
// Unsichere Operation ausführen
} catch (Exception ex) {
// Fehler behandeln – wird nur im Fehlerfall ausgeführt
}
Fehler nicht selbst behandeln aber vor der Weiterleitung aufräumen
try {
// Unsichere Operation ausführen
} finally {
// Aufräumen – wird immer ausgeführt
}
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#72
Fehlerbehandlung(8) – Exception Handling
Unterschiedliche Fehler spezifisch behandeln
public static void main(String[] args) {
try {
// throw ThisException oder ThatException
} catch (ThisException ex) { // spezielle Exception 1
// ThisException behandeln
} catch (ThatException ex) { // spezielle Exception 2
// ThatException behandeln
} catch (Exception ex) { // allgemeiner Handler ("Besenwagen") am Ende
// Alle anderen Exceptions behandeln
}
}
Alle Fehler gleich behandeln
public static void main(String[] args) {
try {
// throw ThisException oder ThatException
} catch (Exception ex) { // allgemeiner Handler für alle Exceptions
// Alle anderen Exceptions behandeln
}
}
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#73
Fehlerbehandlung(9a) – Exception Handling
Exception Handling funktioniert auch wunderbar über mehrere Aufrufe hinweg
Beispiel
public static void dritteFunktion() throws Exception {
throw new Exception();
}
public static void zweiteFunktion() throws Exception {
dritteFunktion();
}
public static void ersteFunktion() throws Exception {
zweiteFunktion();
}
public static void main(String[] args) {
try {
ersteFunktion();
} catch (Exception ex) {
// Exception behandeln
}
}
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#74
Fehlerbehandlung(9b) – Exception Handling
main()
ersteFunktion()
zweiteFunktion()
Normallfall: Schritt für Schritt zurück
return
dritteFunktion()
return
throw
return
Fehlerfall: direkt zum catch-Block()
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#75
Fehlerbehandlung(10) – Exception Handling
Die Klasse Exception ist Teil einer vordefinierten Klassenhierarchie:
Throwable
Error
"alles, was man werfen kann"
interne
Fehler
Exception
besondere
Laufzeitfehler
RuntimeException
eigene
Fehler
allgemeine
Exceptions
eigene Exceptions
Diese kann um eigene Exceptionklassen erweitert werden. In den
Standardbibliotheken wird von dieser Möglichkeit ausgiebig Gebrauch gemacht.
Eigene Exceptions werden i.d.R. von Exception abgeleitet.
Für RuntimeExceptions erzwingt der Compiler die Deklaration und
Behandlung nicht. Aber: Alle zur Laufzeit auftretenden aber nicht
abgefangene Exceptions (Runtime oder nicht), beenden das Programm!
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#76
Fehlerbehandlung(11) - Anwendung
In der Praxis begegnen uns Exceptions meist bei der Verwendung der Java
Bibliothek. Die entsprechenden Exceptions sind in der API Dokumentation bei
der jeweiligen Methode angegeben. Eigene Exception-Klassen zu
implementieren ist für kleine Anwendungen i.d.R. nicht nötig.
Beispiele für RuntimeExceptions:
● ArrayIndexOutOfBoundsException - beim Zugriff auf fehlende Array-Elemente
● NullPointerException - beim Zugriff auf Null-Referenzen
● ClassCastException - bei "falschen" Casts
● ArithmeticException - bei Berechnungen, z.B. Division durch Null
● NumberFormatException - bei Integer.parseInt( String); vgl. CommandLine
Beispiele für Exceptions:
● InterruptedException - bei Thread.sleep( int); vgl. GridView
● IOException - beim Zugriff auf Dateien (später)
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#77
Fehlerbehandlung(12) - Anwendung
In der Literatur bzw. im Internet finden sich 3 Regeln zum Exception-Handling:
"Be specific" - Exceptions spezifisch behandeln
● "Throw early" – Exceptions werfen, sobald etwas schief geht
● "Catch late" – Exceptions nicht sofort wieder fangen sondern laufen lassen
●
siehe auch http://today.java.net/pub/a/today/2003/12/04/exceptions.html
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#78
Interfaces
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#79
Interfaces(1a) - Hintergrund
Methoden aus der fachlichen Modellierung werden mit Hilfe der Vererbung
innerhalb einer (Teil)Hierarchie von Klassen verfügbar gemacht. Daneben gibt
es jedoch auch Methoden, die unabhängig von der Vererbungshierarchie in
verschiedenen Klassen benötigt werden (diese dienen typischerweise einem
eher technischen Zweck). Dieser technische Blickwinkel ist von der fachlichen
Hierarchie zunächst völlig unabhängig. Insbesondere können derartige Gruppen
von Methoden an mehreren Stellen der Klassenhierarchie eingeführt werden
(pflanzen sich aber dannach ganz normal über die Vererbung fort).
Eine solche Gruppe von Methoden mit einem technischen Zweck fasst man in
Java in einem sog. Interface zusammen. Das Konzept von Interfaces
(Schnittstellen) in Java bezieht sich also nicht auf die Schnittstelle einer
einzelnen Methode (diese wird als Signatur bezeichnet), sondern auf die
Schnittstelle eines ganzen Objekts, d.h. die Signaturen aller sichtbaren
Methoden (genauer gesagt: einer bestimmten Teilmenge davon).
Was hat dies für einen Sinn? Unter der Vielzahl von Methoden eines Objekts
lassen sich Gruppen von Methoden identifizieren, die einem gemeinsamem
Zweck dienen. Sie stellen sozusagen einen (abstrakten) Dienst zur Verfügung.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#80
Interfaces(1b) - Hintergrund
Interfaces werden ähnlich zu Klassen definiert, enthalten aber weniger
Eigenschaften. Insbesondere bestehen ihre „Methoden“ nur aus einer Signatur
und haben keinen Rumpf (es wird nur beschrieben, wie der Dienst nutzbar sein
soll, nicht wie er erbracht wird). Das muss die implementierende Klasse durch
„überscheiben“ tun. Für statische Methoden geht das nicht. Und da man
Interfaces mangels Implementierung auch nicht direkt instanzieren kann machen
Instanzvariablen natürlich auch keinen Sinn. Damit bleiben nur noch
Klassenvariablen übrig – aber die müssen final sein!
Interfaces lassen sich in Java Klassen zuordnen – auch mehrere! Damit lassen
sich im Bedarfsfall unterschiedlichste Klassen abstrakt als Erbringer eines
bestimmten Dienstes betrachten und nutzen. Manchmal ist die Funktion sogar
bereits vorhanden und wird nur durch ein Interface "freigeschaltet" (MarkerInterface). Interfaces sind sehr ähnlich zu abstrakten Basisklassen, vermeiden
aber die Nachteile der Mehrfachvererbung.
Analogie:
Eine geerbte Basisklasse ist ein Haus, auf das man aufbaut.
Ein geerbtes Interface ist nur eine Fassade, die man nachbaut.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#81
Interfaces(2) - Beispiele
Kopieren von Objekten (Cloneable)
Bei einer Zuweisung zwischen Referenzvariablen werden nur die Referenzen
kopiert, nicht die dahinterliegenden Objekte. Manchmal reicht das nicht.
Vergleichen von Objekten (Comparable)
Mit == können Objekte auf Identität verglichen werden (genauer: Gleichheit der
Objektreferenzen), mit equals() kann man Objekte auf Gleichheit testen. Das
Konzept der Gleichheit ist auf beliebige Klassen anwendbar. Für bestimmte
Anwendungen, z.B. sortieren, braucht man aber mehr: eine Ordnungsrelation.
Serialisierung von Objekten (Serializable)
Will man Objekte speichern und wieder einladen oder über das Netz übertragen
müssen diese in eine Bytefolge umgewandelt werden. Aber muss man dazu das
Rad immer neu erfinden?
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#82
Interfaces(3) – Grundeigenschaften
Interfaces definieren nur eine Schnittstelle, kein Verhalten und keinen Zustand.
Daher werden in einem Interface nur die Signaturen der enthaltenen Methoden
definiert, nicht deren Rümpfe. Die Implementierung eines Interface durch eine
Klasse ist gleichbedeutend mit der Implementierung aller enthaltenen Methoden
(diese kann jedoch auch in einer Vaterklasse erfolgen). Da Interfaces in
verschiedensten Klassen verwendet werden, macht eine Weitergabe von Code
wie bei der Vererbung auch keinen Sinn. Das Interface definiert also nur das
"WAS" – das "WIE" ist Aufgabe der jeweiligen Klasse.
Insofern ist ein Interface quasi eine Verkleidung ohne Inhalt bzw. einer Rolle.
Aus diesem Grund sind auch keine Instanzvariablen erlaubt – es gibt schlicht
keine Instanz. Es ist jedoch möglich, in einem Interface Konstanten zu
definieren. Diese sind syntaktisch identisch zu konstanten Klassenvariablen.
Die in einem Interface definierten Methoden ohne Rumpf werden auch als
abstrakte Methoden bezeichnet. Solche Methoden können auch in einer Klasse
definiert werden, um Signaturen für bestimmte Methoden vorzuschreiben. Damit
wird die Klasse zu einer sogenannten abstrakten Klasse, von der keine
Instanzen erzeugt werden können.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#83
Interfaces(4) – Beispiel
Beispiel für ein Interface und dessen Implementierung
public interface Cryptography
public static final int RSA
public static final int DSS
public static final int DES
}
{
= 1;
= 2;
= 3;
public void encrypt( int algorithm, String passphrase);
public void decrypt( int algorithm, String passphrase);
public class Agent extends Person implements Cryptography {
public void encrypt( int algorithm, String passphrase) {
// Implementierung von encrypt
}
}
public void decrypt( int algorithm, String passphrase) {
// Implementierung von decrypt
}
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#84
Interfaces(5) – Vergleich mit Klassen
Ein Interface ist äquivalent zu einer abstrakten Vaterklasse. Während jedoch
eine Klasse nur eine Vaterklasse haben kann, ist die Implementierung mehrerer
Interfaces ohne weiteres möglich. Damit vermeiden Interfaces, die
Notwendigkeit zur Mehrfachvererbung und die damit verbundenen Probleme.
Ein Interface ist wie eine Klasse ein Datentyp, d.h. man kann Variablen mit
einem Interface-Typ definieren und damit auf Objekte verweisen, die dieses
Interface implementieren. Der Test ist wie bei Klassen mit instanceof möglich.
Interfaces können voneinander erben. Dabei wird die Schnittstelle durch
zusätzliche Signaturen und Konstanten erweitert. Auf diese Art und Weise
können Hierarchien von Interfaces gebildet werden.
Klassen können Klassenvariablen, Instanzvariablen, Klassenmethoden und
Instanzmethoden definieren, in Interfaces sind nur konstante Klassenvariablen
und Instanzmethoden möglich.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#85
Interfaces(5a) - Eselsbrücke
Ein Interface kann man sich wie eine standardisierte Frontblende vorstellen,
über die ein unbestimmter Anwenderkreis zu einem bestimmten Zweck auf eine
unbekannte Maschine(=ein Objekt) zugreifen möchte.
Die Maschine kann noch viele zusätzliche Knöpfe und Anzeigen(=Methoden)
haben, aber diese interessieren den Anwenderkreis nicht, da sie für den
vorgesehenen Zweck nicht notwendig sind.
Die Nutzung der nötigen Funktionen über das vorhandene Bedienfeld der
Maschine ist nicht möglich, wenn dieses von der standardisierten Frontblende
abweicht, da die Anwender nicht jedesmal umlernen können/wollen.
Die Angabe „implements <InterfaceName>„ bedeutet also das Versprechen, die
notwendigen Funktionen über die vorgeschriebene Standardfrontblende
bereitzustellen. Die Implementierung der geforderten Methoden enspricht der
Integration der Standardfrontblende in das Bedienfeld der jeweiligen Maschine.
Im Kern handelt es sich um eine Vereinbarung mit deren Hilfe man
Teilfunktionen von Objekte benutzen kann ohne Ihren genauen Typ zu kennen.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#86
Interfaces(6) – Vergleichen von Objekten (Vertiefung)
Das Interface Comparable definiert die Methode compareTo(). Mit Hilfe dieser
Methode lassen sich beliebige* Objekte auf größer/gleich/kleiner Testen.
Interface Comparable {
public int compareTo(Object obj);
}
public class Person implements Comparable {
String name;
// ...
}
public int compareTo(Object obj) {
return this.name.compareTo( ((Person)obj).name);
}
Hinweis: Vordefinierte Interfaces sind ebenfalls in der API-Dokumentation
beschrieben und werden. Ihre Namen werden dort kursiv dargestellt.
*nicht ganz: bei Unverträglichkeit wird eine ClassCastException geworfen.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#87
Interfaces (7) - Kopieren von Objekten(a)
Bei der Zuweisung von Objektvariablen werden nur die Referenzen kopiert, nicht
die Objekte. Um die Objekte selbst zu kopieren gibt es mehrere Möglichkeiten:
Handarbeit
● Kopierkonstruktor
● Spezielle Methode
● Implementierung von Clonable
●
Handarbeit
Darunter versteht man das explizite Anlegen eines neuen Objekts mit den
selben Attributwerten wie das Originalobjekt mit Hilfe des new-Operators. Das ist
natürlich sehr mühsam und im Einzelfall gar nicht möglich, da nicht alle
Attributwerte nach außen sichtbar sind.
Beispiel
Person p2 = new Person();
p2.setName( p1.getName());
//... weitere Attribute kopieren
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#88
Interfaces(8) - Kopieren von Objekten(d)
Kopierkonstruktor
Darunter versteht man den Aufruf eines Konstruktor, dem das Originalobjekt
übergeben wird. Da der Konstruktor Teil der Klasse ist hat er Zugriff auf alle
Attribute und kann diese kopieren. Dies ist bequemer als Handarbeit, läßt sich
aber schlecht verallgemeinern, da die Klasse explizit genannt werden muss.
Beispiel
public class Person {
//...
public Person( Person template) {
name = template.name;
// ...weitere Attribute kopieren
}
}
// im Hauptprogramm
Person p2 = new Person( p1);
Neben Kopierkonstruktoren kennt man in C++ auch Konvertierkonstruktoren. Bei
diesen ist die Kopiervorlage ein Objekt einer anderen Klasse.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#89
Interfaces(9) - Kopieren von Objekten(b)
Spezielle Methode
Bei diesem Lösungsansatz wird der manuelle Kopiervorgang in eine spezielle
Methode gepackt. Diese kann dann z.B. über ein Interface in verschiedene
Klassen verfügbar gemacht werden. Damit ist eine Verallgemeinerung des
Aufrufs möglich. Es bleibt jedoch das Problem, bei der Implementierung der
Methode die Klasse explizit nennen zu müssen.
Beispiel
public class Person {
//...
public Person kopieren() {
Person duplicate = new Person();
duplicate.name = this.name;
// ...weitere Attribute kopieren
}
}
// im Hauptprogramm
Person p2 = p1.kopieren();
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#90
Interfaces(10) - Kopieren von Objekten(c)
Implementierung von Clonable
Das Interface Clonable stellt mit clone() eine Kopiermethode wie beim
vorhergehenden Ansatz zur Verfügung. Der Clou an dieser Methode ist, dass sie
(1) bereits bei Objekt als protected Methode implementiert ist und (2)
automatisch ein Objekt der richtigen Klasse erzeugt. Sie kann also in allen
Klassen durch Angabe des Interfaces und eine triviale Definition "freigeschaltet"
werden.
Beispiel
public class Person implements Cloneable {
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
// im Hauptprogramm
Person p2 = (Person)p1.clone();
Der Nachteil ist jedoch, daß der Rückgabewert immer vom Typ Object ist. Aber
wenn man weiss, was man kopiert hat, ist der anschließende cast idiotensicher.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#91
Interfaces(11) - Kopieren von Objekten(d)
flache Kopie und tiefe Kopie
Die Attribute eines Objekts werden beim Kopieren durch einfache Zuweisung
mitkopiert. Da sie jedoch ihrerseits (Instanz)variablen sind bedeutet dies, das in
einem Objekt enthaltene Objekte nicht mitkopiert werden (Die Kopie eines Autos
hätte denselben Motor wie das Original). Eine solche Kopie nennt man flache
Kopie. Um eine tiefe Kopie zu erzeugen, d.h. (ausgewählte) enthaltene Objekte
ebenfalls zu kopieren, muss die Methode clone() überschrieben und der
Kopiervorgang nach der flachen Kopie des oberen Objekts mit den enthaltenen
Objekten fortgesetzt werden.
Beispiel:
public class Auto implements Cloneable {
public Object clone() throws CloneNotSupportedException {
Auto obj = (Auto)super.clone(); // Originalobjekt kopieren
obj.motor = (Motor)obj.motor.clone(); // Motor kopieren
return obj;
}
}
Es ist jedoch keinesfalls immer nötig, eine tiefe Kopie zu machen!
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#92
Interfaces(12) - Anwendungsbereiche
Die Anwendungsbereiche für Interfaces sind überraschend vielfältig:
●
Abstrakte Beschreibung von Funktionen für mehrere Klassen ohne Vererbung
➔ Querschnittsfunktionen (sog. cross-cutting concerns) wie Persistenz
➔ Funktionen mit gleichbleibendem WAS aber unterschiedlichem WIE
➔ Trennung von Konzept und Implementierung
Beispiel: werden wir später z.B. bei den Collection Klassen finden
●
Ergänzende Kennzeichnung von Klassen
➔ Verwendung von Marker-Interfaces und instanceof
Beispiel: Freischaltung von Funktionen mit Cloneable und Serializable
●
Verallgemeinerung von Algorithmen
➔ Beschreibung geforderter Eigenschaften über ein Interface, z.B. Comparable
Beispiel: Sortieralgorithmen und das Interface Comparable
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#93
Patterns
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#94
Singleton-Pattern
Manchmal möchte man nicht, dass von einer Klasse Instanzen angelegt werden.
Beispiel:
public class Singleton {
private static Singleton instance;
private Singleton() {
}
}
public staic Singleton getInstance() {
if ( null==instance) {
instance = new Singleton();
}
return instance;
}
Damit kann man auch Dienste, die nur einmal benötigt werden als Objekt
implementieren.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#95
Visitor-Pattern
Ein Ansatz um eine Objekthierarchie für "beliebige Zwecke" zu durchlaufen.
Beispiel:
public class Visitor {
public void visit( Fahrzeug f) {
}
}
public void visit( Auto f) {
}
public class Fahrzeug {
public accept( Visitor v) {
v.visit( this);
}
}
Mit Hilfe dieses Ansatzes kann man quasi beliebige Operationen implementieren
und auf einer Menge von Klassen durchführen.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#96
Abschluss
Herzlichen Glückwunsch! Sie haben nun alle wesentlichen objektorientierten
Konzepte und Sprachfeatures von Java kennengelernt(!) und verstanden(?).
Im weiteren Verlauf der Vorlesung werden wir uns mit den Inhalten der JavaKlassenblibliothek beschäftigen und eine Reihe elementarer und mächtiger
Klassen für die Ein-/Ausgabe den Umgang mit Datenstrukturen oder die
Programmierung grafischer Oberflächen, den Zugriff auf Datenbanken sowie die
Netzwerkprogrammierung kennenlernen. Dabei werden wir bei Bedarf spezielle
Sprachfeatures wie innere Klassen kennenlernen.
Mit anderen Worten: Die Zeit des übens mit den Grundwerkzeugen und der
akademischen Beispiele geht langsam vorüber. Die reale Welt und Ihr erstes
echtes Projekt (quasi das Gesellenstück) nahen.
© Andreas Rau, 24.03.11
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-objektorientierung.odp
#97
Herunterladen