Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort
1 Java ist auch eine Sprache
2 Imperative Sprachkonzepte
3 Klassen und Objekte
4 Der Umgang mit Zeichenketten
5 Eigene Klassen schreiben
6 Exceptions
7 Äußere.innere Klassen
8 Besondere Klassen der Java SE
9 Generics<T>
10 Architektur, Design und angewandte Objektorientierung
11 Die Klassenbibliothek
12 Einführung in die nebenläufige Programmierung
13 Einführung in Datenstrukturen und Algorithmen
14 Einführung in grafische Oberflächen
15 Einführung in Dateien und Datenströme
16 Einführung in die <XML>-Verarbeitung mit Java
17 Einführung ins Datenbankmanagement mit JDBC
18 Bits und Bytes und Mathematisches
19 Die Werkzeuge des JDK
A Die Klassenbibliothek
Stichwort

Download:
- Aufgaben, ca. 1,1 MB
- Programme, ca. 12,8 MB

Buch bestellen
Ihre Meinung?

Spacer
Java ist auch eine Insel von Christian Ullenboom
Das umfassende Handbuch
Buch: Java ist auch eine Insel

Java ist auch eine Insel
Galileo Computing
1308 S., 10., aktualisierte Auflage, geb., mit DVD
ca. 49,90 Euro, ISBN 978-3-8362-1802-3
Pfeil 5 Eigene Klassen schreiben
Pfeil 5.1 Eigene Klassen mit Eigenschaften deklarieren
Pfeil 5.1.1 Attribute deklarieren
Pfeil 5.1.2 Methoden deklarieren
Pfeil 5.1.3 Die this-Referenz
Pfeil 5.2 Privatsphäre und Sichtbarkeit
Pfeil 5.2.1 Für die Öffentlichkeit: public
Pfeil 5.2.2 Kein Public Viewing – Passwörter sind privat
Pfeil 5.2.3 Wieso nicht freie Methoden und Variablen für alle?
Pfeil 5.2.4 Privat ist nicht ganz privat: Es kommt darauf an, wer’s sieht *
Pfeil 5.2.5 Zugriffsmethoden für Attribute deklarieren
Pfeil 5.2.6 Setter und Getter nach der JavaBeans-Spezifikation
Pfeil 5.2.7 Paketsichtbar
Pfeil 5.2.8 Zusammenfassung zur Sichtbarkeit
Pfeil 5.3 Statische Methoden und statische Attribute
Pfeil 5.3.1 Warum statische Eigenschaften sinnvoll sind
Pfeil 5.3.2 Statische Eigenschaften mit static
Pfeil 5.3.3 Statische Eigenschaften über Referenzen nutzen? *
Pfeil 5.3.4 Warum die Groß- und Kleinschreibung wichtig ist *
Pfeil 5.3.5 Statische Variablen zum Datenaustausch *
Pfeil 5.3.6 Statische Eigenschaften und Objekteigenschaften *
Pfeil 5.4 Konstanten und Aufzählungen
Pfeil 5.4.1 Konstanten über öffentliche statische finale Variablen
Pfeil 5.4.2 Typ(un)sichere Aufzählungen *
Pfeil 5.4.3 Aufzählungen mit enum
Pfeil 5.5 Objekte anlegen und zerstören
Pfeil 5.5.1 Konstruktoren schreiben
Pfeil 5.5.2 Der vorgegebene Konstruktor (default constructor)
Pfeil 5.5.3 Parametrisierte und überladene Konstruktoren
Pfeil 5.5.4 Copy-Konstruktor
Pfeil 5.5.5 Einen anderen Konstruktor der gleichen Klasse mit this() aufrufen
Pfeil 5.5.6 Ihr fehlt uns nicht – der Garbage-Collector
Pfeil 5.5.7 Private Konstruktoren, Utility-Klassen, Singleton, Fabriken
Pfeil 5.6 Klassen- und Objektinitialisierung *
Pfeil 5.6.1 Initialisierung von Objektvariablen
Pfeil 5.6.2 Statische Blöcke als Klasseninitialisierer
Pfeil 5.6.3 Initialisierung von Klassenvariablen
Pfeil 5.6.4 Eincompilierte Belegungen der Klassenvariablen
Pfeil 5.6.5 Exemplarinitialisierer (Instanzinitialisierer)
Pfeil 5.6.6 Finale Werte im Konstruktor und in statischen Blöcken setzen
Pfeil 5.7 Assoziationen zwischen Objekten
Pfeil 5.7.1 Unidirektionale 1:1-Beziehung
Pfeil 5.7.2 Bidirektionale 1:1-Beziehungen
Pfeil 5.7.3 Unidirektionale 1:n-Beziehung
Pfeil 5.8 Vererbung
Pfeil 5.8.1 Vererbung in Java
Pfeil 5.8.2 Spielobjekte modellieren
Pfeil 5.8.3 Die implizite Basisklasse java.lang.Object
Pfeil 5.8.4 Einfach- und Mehrfachvererbung *
Pfeil 5.8.5 Die Sichtbarkeit protected
Pfeil 5.8.6 Konstruktoren in der Vererbung und super()
Pfeil 5.9 Typen in Hierarchien
Pfeil 5.9.1 Automatische und explizite Typanpassung
Pfeil 5.9.2 Das Substitutionsprinzip
Pfeil 5.9.3 Typen mit dem instanceof-Operator testen
Pfeil 5.10 Methoden überschreiben
Pfeil 5.10.1 Methoden in Unterklassen mit neuem Verhalten ausstatten
Pfeil 5.10.2 Mit super an die Eltern
Pfeil 5.10.3 Finale Klassen und finale Methoden
Pfeil 5.10.4 Kovariante Rückgabetypen
Pfeil 5.10.5 Array-Typen und Kovarianz *
Pfeil 5.11 Drum prüfe, wer sich ewig dynamisch bindet
Pfeil 5.11.1 Gebunden an toString()
Pfeil 5.11.2 Implementierung von System.out.println(Object)
Pfeil 5.11.3 Nicht dynamisch gebunden bei privaten, statischen und finalen Methoden
Pfeil 5.11.4 Dynamisch gebunden auch bei Konstruktoraufrufen *
Pfeil 5.11.5 Eine letzte Spielerei mit Javas dynamischer Bindung und überschatteten Attributen *
Pfeil 5.12 Abstrakte Klassen und abstrakte Methoden
Pfeil 5.12.1 Abstrakte Klassen
Pfeil 5.12.2 Abstrakte Methoden
Pfeil 5.13 Schnittstellen
Pfeil 5.13.1 Schnittstellen deklarieren
Pfeil 5.13.2 Implementieren von Schnittstellen
Pfeil 5.13.3 Markierungsschnittstellen *
Pfeil 5.13.4 Ein Polymorphie-Beispiel mit Schnittstellen
Pfeil 5.13.5 Die Mehrfachvererbung bei Schnittstellen *
Pfeil 5.13.6 Keine Kollisionsgefahr bei Mehrfachvererbung *
Pfeil 5.13.7 Erweitern von Interfaces – Subinterfaces
Pfeil 5.13.8 Konstantendeklarationen bei Schnittstellen
Pfeil 5.13.9 Initialisierung von Schnittstellenkonstanten *
Pfeil 5.13.10 Abstrakte Klassen und Schnittstellen im Vergleich
Pfeil 5.14 Zum Weiterlesen

5 Eigene Klassen schreibenZur nächsten Überschrift

»Das Gesetz ist der abstrakte Ausdruck des allgemeinen an und für sich seienden Willens.«
– Georg Wilhelm Friedrich Hegel (1770–1831)

In den letzten Kapiteln haben wir viel über Objektorientierung erfahren, aber eher von der Benutzerseite her. Wir haben gesehen, wie Objekte aufgebaut werden, und auch einfache Klassen mit statischen Methoden haben wir implementiert. Es wird Zeit, das Wissen um alle objektorientierten Konzepte zu vervollständigen.


Rheinwerk Computing - Zum Seitenanfang

5.1 Eigene Klassen mit Eigenschaften deklarierenZur nächsten ÜberschriftZur vorigen Überschrift

Die Deklaration einer Klasse leitet das Schlüsselwort class ein. Im Rumpf der Klasse lassen sich deklarieren:

  • Attribute (Variablen)
  • Methoden
  • Konstruktoren
  • Klassen- sowie Exemplarinitialisierer
  • innere Klassen, innere Schnittstellen und innere Aufzählungen

Eine ganz einfache Klassendeklaration

Wir wollen die Konzepte der Klassen und Schnittstellen an einem kleinen Spiel verdeutlichen. Beginnen wir mit dem Spieler, den die Klasse Player repräsentiert:

Listing 5.1: com/tutego/insel/game/v1/Player.java, Player

class Player
{
}

Die Klasse hat einen vom Compiler generierten Konstruktor, sodass sich ein Exemplar unserer Klasse mit new Player() erzeugen lässt.


Rheinwerk Computing - Zum Seitenanfang

5.1.1 Attribute deklarierenZur nächsten ÜberschriftZur vorigen Überschrift

Diese Player-Klasse hat bisher keine Attribute und kann bisher nichts. Geben wir dem Spieler zwei Attribute: eines für den Namen und ein zweites für einen Gegenstand, den er trägt. Die Datentypen sollen beide String sein:

Listing 5.2: com/tutego/insel/game/v2/Player.java, Player

class Player
{
String name;
String item;
}
Hinweis

Eine spezielle Namenskonvention für Objektvariablen gibt es nicht. So ist es zwar möglich, zur Unterscheidung von lokalen Variablen ein Präfix wie »f« oder »_« voranzustellen, doch sogar die Eclipse-Macher sind davon abgekommen. Objektvariablen können auch grundsätzlich wie Methoden heißen, doch ist das unüblich, da Variablennamen im Allgemeinen Substantive und Methoden Verben sind. Da Bezeichner nie so heißen können wie Schlüsselwörter, fallen Variablen wie enum schon raus (das führte in Java 5 zu einigen Quellcodeänderungen, da dort enum als neues Schlüsselwort eingeführt wurde).

Eine zweite Klasse Playground erzeugt in der statischen main()-Methode für den mutigen Spieler ein Player-Objekt, schreibt und liest die Attribute:

Listing 5.3: com/tutego/insel/game/v2/Playground.java, Playground

class Playground
{
public static void main( String[] args )
{
Player p = new Player();
p.name = "Mutiger Manfred";
p.item = "Schlips";

System.out.printf( "%s nimmt einen %s mit.", p.name, p.item );
}
}
Abbildung

Abbildung 5.1: Das UML-Diagramm zeigt Abhängigkeiten von Playground und Player

Initialisierung von Attributen

Anders als lokale Variablen initialisiert die Laufzeitumgebung alle Attribute mit einem Standardwert:

  • 0 bei numerischen Werten und char
  • false bei boolean
  • null bei Referenzvariablen

Gefällt uns das nicht, lassen sich die Variablen mit einem Wert belegen:

class Player
{
String name = "";
}

Gültigkeitsbereich, Sichtbarkeit und Lebensdauer

Lokale Variablen beginnen ihr Leben in dem Moment, in dem sie deklariert und initialisiert werden. Endet der Block, ist die lokale Variable nicht mehr gültig, und sie kann nicht mehr verwendet werden, da sie aus dem Sichtbarkeitsbereich verschwunden ist. Bei Objektvariablen ist das anders. Eine Objektvariable lebt ab dem Moment, zu dem das Objekt mit new aufgebaut wurde, und sie lebt so lange, bis der Garbage-Collector das Objekt wegräumt. Sichtbar und gültig ist die Variable aber immer im gesamten Objekt und in allen Blöcken.[129](Das gilt nicht für statische Methoden und statische Initialisierungsblöcke, aber diese werden erst später vorgestellt.)

Abbildung
Spätestens, wenn zwei Klassen im Editor offen sind, möchten Tastaturjunkies schnell zwischen den Editoren wechseln. Das geht in Eclipse mit Strg + F6. Allerdings ist dieses Tastaturkürzel in der Windows-Welt unüblich, sodass es umdefiniert werden kann, etwa zu Strg + ÿ_. Das geht so: Unter Windows • Preferences aktivieren wir unter General • Keys das Kommando Next Editor. Im Textfeld Binding lässt sich zunächst das alte Kürzel löschen und einfach Strg + ÿ_ drücken. Das Ganze lässt sich auch für Previous Editor und Strg + ª + ÿ_ wiederholen.

Abbildung

Abbildung 5.2: Tastaturkürzel in Eclipse einstellen


Rheinwerk Computing - Zum Seitenanfang

5.1.2 Methoden deklarierenZur nächsten ÜberschriftZur vorigen Überschrift

Zu Attributen gesellen sich Methoden, die üblicherweise auf den Objektvariablen arbeiten. Anders als unsere bisherigen statischen Methoden werden diese aber für Player keine Klassenmethoden sein. Geben wir dem Spieler zwei Methoden: clearName() soll den Namen auf den Leerstring "" zurücksetzen, und hasCompoundName() soll verraten, ob der Spielername aus einem Vor- und Nachnamen zusammengesetzt ist. Der Name »Parry Hotter« ist zum Beispiel zusammengesetzt, »Spuckiman« aber nicht.

Listing 5.4: com/tutego/insel/game/v3/Player.java, Player

class Player
{
String name = "";
String item = "";

void clearName()
{
name = "";
}

boolean hasCompoundName()

{
return (name == null) ? false : name.contains( " " );
}
}
Abbildung

Abbildung 5.3: Das Klassendiagramm von Player zeigt zwei Attribute und zwei Methoden.

Testen wir die Methode mit zwei Spielern:

Listing 5.5: com/tutego/insel/game/v3/Playground.java, main()

Player parry = new Player();
parry.name = "Parry Hotter";
System.out.printf( "'%s' hat zusammengesetzten Namen: %b%n",
parry.name, parry.hasCompoundName() );

Player spucki = new Player();
spucki.name = "Spuckiman";
System.out.printf( "'%s' hat zusammengesetzten Namen: %b%n",
spucki.name, spucki.hasCompoundName() );
spucki.clearName();
System.out.printf( "Spuckis Name ist leer? %b%n", spucki.name.isEmpty() );

Wie zu erwarten, ist die Ausgabe:

'Parry Hotter' hat zusammengesetzten Namen: true
'Spuckiman' hat zusammengesetzten Namen: false
Spuckis Name ist leer? true
Abbildung

Abbildung 5.4: Ein Sequenzdiagramm stellt nur die Objekterzeugung und Methodenaufrufe, jedoch keine Attributzugriffe dar.

Abbildung
Um schnell von einer Methode (oder Variablen) zur anderen zu navigieren, zeigt Strg + O ein Outline an (dieselbe Ansicht wie in der Ansicht Outline). Im Unterschied zur Ansicht Outline lässt sich in diesem kleinen gelben Fenster mit den Cursor-Tasten navigieren, und ein ¢ befördert uns zur angewählten Methode oder dem angewählten Attribut. Wird in der Ansicht erneut Strg + O gedrückt, befinden sich dort auch die in den Oberklassen deklarierten Eigenschaften. Sie sind grau, und zusätzlich befinden sich hinter den Eigenschaften die Klassennamen.

Methodenaufrufe und Nebeneffekte

Alle Variablen und Methoden einer Klasse sind in der Klasse selbst sichtbar. Das heißt, innerhalb einer Klasse werden die Objektvariablen und Methoden mit ihrem Namen verwendet. Somit greift die Methode hasCompoundName() direkt auf das nötige Attribut name zu, um die Programmlogik auszuführen. Dies wird oft für Nebeneffekte (Seiteneffekte) genutzt. Die Methode clearName() ändert ausdrücklich eine Objektvariable und verändert so den Zustand des Objekts. hasCompoundName() liest dagegen nur den Zustand, modifiziert ihn aber nicht. Methoden, die Zustände ändern, sollten das in der API-Beschreibung entsprechend dokumentieren.

Objektorientierte und prozedurale Programmierung im Vergleich

Entwickler aus der prozeduralen Welt haben ein anderes Denkmodell verinnerlicht, sodass wir an dieser Stelle die Besonderheit der Objektorientierung noch einmal verdeutlichen wollen. Während in der guten objektorientierten Modellierung die Objekte immer gleichzeitig Zustand und Verhalten besitzen, gibt es in der prozeduralen Welt nur Speicherbereiche, die referenziert werden; Daten und Verhalten liegen hier nicht zusammen. Problematisch wird es, wenn die prozedurale Denkweise in Java-Programme abgebildet wird. Dazu ein Beispiel: Die Klasse PlayerData ist ein reiner Datencontainer für den Zustand, aber Verhalten wird hier nicht deklariert:

Listing 5.6: PlayerData.java

class PlayerData
{
String name = "";
String item = "";
}

Anstatt nun die Methoden ordentlich, wie in unserem ersten Beispiel, mit an die Klasse zu hängen, würde in der prozeduralen Welt ein Unterprogramm genau ein Datenobjekt bekommen und von diesem Zustände erfragen oder ändern:

Listing 5.7: PlayerFunctions.java

class PlayerFunctions
{
static void clearName( PlayerData data )
{
data.name = "";
}

static boolean hasCompoundName( PlayerData data )
{
return (data.name == null) ? false : data.name.contains( " " );
}
}

Da die Unterprogramme nun nicht mehr an Objekte gebunden sind, können sie statisch sein. Genauso falsch wären aber auch Methoden (egal ob statisch oder nicht) in der Klasse PlayerData, wenn sie ein PlayerData-Objekt übergeben bekommen.

Beim Aufruf ist dieser nicht-objektorientierte Ansatz gut zu sehen. Setzen wir links den falschen und rechts den korrekt objektorientiert modellierten Weg ein:

Tabelle 5.1: Gegenüberstellung prozedurale und objektorientierte Programmierung

Prozedural Objektorientiert

PlayerData parry = new PlayerData();
parry.name = "Parry Hotter";
PlayerFunctions.hasCompoundName( parry );
PlayerFunctions.clearName( parry );

Player parry = new Player();
parry.name = "Parry Hotter";
parry.hasCompoundName();
parry.clearName();

Ein Indiz für problematische objektorientierte Modellierung ist also, wenn externen Methoden Objekte übergeben werden, anstatt die Methoden selbst an die Objekte zu setzen.


Rheinwerk Computing - Zum Seitenanfang

5.1.3 Die this-ReferenzZur nächsten ÜberschriftZur vorigen Überschrift

In jeder Objektmethode und jedem Konstruktor steht eine Referenz mit dem Namen this bereit, die auf das eigene Exemplar zeigt. Mit dieser this-Referenz lassen sich elegante Lösungen realisieren, wie die folgenden Beispiele zeigen:

  • Die this-Referenz löst das Problem, wenn Parameter beziehungsweise lokale Variablen Objektvariablen verdecken.
  • Liefert eine Methode als Rückgabe die this-Referenz auf das aktuelle Objekt, lassen sich Methoden der Klasse einfach hintereinandersetzen.
  • Mit der this-Referenz lässt sich einer anderen Methode eine Referenz auf uns selbst geben.

Überdeckte Objektvariablen nutzen

Trägt eine lokale Variable den gleichen Namen wie eine Objektvariable, so verdeckt sie diese. Das folgende Beispiel deklariert in der Player-Klasse eine Objektvariable name, und eine Methode quote() deklariert in der Parameterliste eine Variable, die ebenfalls name heißt. Somit bezieht sich in der Methode jeder Zugriff auf name auf die Parametervariable und nicht auf die Objektvariable:

class Player
{
String name; // Objektvariable name

void quote( String name ) // Lokale Parametervariable name
{
System.out.println( "'" + name + "'" ); // Bezieht sich auf Parametervariable
}
}

Das heißt aber nicht, dass auf die äußere Variable nicht mehr zugegriffen werden kann. Die this-Referenz zeigt auf das aktuelle Objekt, und damit ist auch ein Zugriff auf Objekteigenschaften jederzeit möglich:

class Player
{
String name;

void quote( String name )
{
System.out.println( "'" + this.name + "'" ); // Zugriff auf Objektvariable
System.out.println( "'" + name + "'" ); // Zugriff auf Parametervariable
}
}

Häufiger Einsatzort für das this in Methoden sind Methoden, die Zustände initialisieren. Gerne nennen Entwickler die Parametervariablen so wie die Exemplarvariablen, um damit eine starke Zugehörigkeit auszudrücken. Schreiben wir eine Methode setName():

class Player
{
String name;

void setName( String name )
{
this.name = name;
}
}

Der an setName() übergebene Wert soll die Objektvariable name initialisieren. So greift this.name auf die Objektvariable direkt zu, sodass die Zuweisung this.name = name; die Objektvariable mit dem Argument initialisiert.

Abbildung

Abbildung 5.5: Klassendiagramm für Player mit Setter

this für kaskadierte Methoden *

Die append()-Methoden bei StringBuilder liefern die this-Referenz, sodass sich Folgendes schreiben lässt:

StringBuilder sb = new StringBuilder();
sb.append( "Android oder iPhone" ).append( '?' );

Jedes append() liefert das StringBuilder-Objekt, auf dem es aufgerufen wird. Wir wollen diese Möglichkeit bei einem Spieler programmieren, sodass die Methoden name() und item() Spielername und Gegenstand zuweisen. Beide Methoden liefern ihr eigenes Player-Objekt über die this-Referenz zurück:

Listing 5.8: com/tutego/insel/game/v4/Player.java, Player

class Player
{
String name = "", item = "";

Player name( String name )
{
this.name = name;
return this;
}

String name()
{
return name;
}

Player item( String item )
{
this.item = item;
return this;
}

String item()
{
return item;
}

String id()
{
return name + " hat " + item;
}
}
Abbildung

Abbildung 5.6: Player-Klasse mit kaskadierbaren Methoden

Erzeugen wir einen Player, und kaskadieren wir einige Methoden:

Listing 5.9: com/tutego/insel/game/v4/Playground.java, main()

Player parry = new Player().name( "Parry" ).item( "Helm" );
System.out.println( parry.name() ); // Parry
System.out.println( parry.id() ); // Parry hat Helm

Der Ausdruck new Player() liefert eine Referenz, die wir sofort für den Methodenaufruf nutzen. Da name(String) wiederum eine Objektreferenz vom Typ Player liefert, ist dahinter direkt .item(String) möglich. Die Verschachtelung von name(String).item(String) bewirkt, dass Name und Gegenstand gesetzt werden und der jeweils nächste Methodenaufruf in der Kette über this eine Referenz auf dasselbe Objekt, aber mit verändertem internen Zustand bekommt.

Hinweis

Bei Anfragemethoden könnten wir versucht sein, diese praktische Eigenschaft überall zu verwenden. Üblicherweise sollten Objekte jedoch ihre Eigenschaften nach der JavaBeans-Konvention mit void setXXX() setzen, und dann liefern sie ausdrücklich keine Rückgabe. Eine mit dem Objekttyp deklarierte Rückgabe Player setName(String) verstößt also gegen diese Konvention, sodass die Methode in dem Beispiel einfach Player name(String) heißt. Beispiele dieser Bauart sind in der Java-Bibliothek an einigen Stellen zu finden. Sie werden auch Builder genannt.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.

>> Zum Feedback-Formular
<< zurück
  Zum Katalog
Zum Katalog: Java ist auch eine Insel





Java ist auch eine Insel
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Katalog: Java ist auch eine Insel






 Java ist auch
 eine Insel


Zum Katalog: Java SE Bibliotheken






 Java SE Bibliotheken


Zum Katalog: Professionell entwickeln mit Java EE 7






 Professionell
 entwickeln mit
 Java EE 7


Zum Katalog: Einstieg in Eclipse






 Einstieg in
 Eclipse


Zum Katalog: Einstieg in Java






 Einstieg in
 Java


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Rheinwerk Verlag GmbH 2011
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Rheinwerk Computing]

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de