Rheinwerk Computing < openbook >


 
Inhaltsverzeichnis
Materialien
Vorwort
1 Java ist auch eine Sprache
2 Imperative Sprachkonzepte
3 Klassen und Objekte
4 Arrays und ihre Anwendungen
5 Der Umgang mit Zeichenketten
6 Eigene Klassen schreiben
7 Objektorientierte Beziehungsfragen
8 Ausnahmen müssen sein
9 Geschachtelte Typen
10 Besondere Typen der Java SE
11 Generics<T>
12 Lambda-Ausdrücke und funktionale Programmierung
13 Architektur, Design und angewandte Objektorientierung
14 Java Platform Module System
15 Die Klassenbibliothek
16 Einführung in die nebenläufige Programmierung
17 Einführung in Datenstrukturen und Algorithmen
18 Einführung in grafische Oberflächen
19 Einführung in Dateien und Datenströme
20 Einführung ins Datenbankmanagement mit JDBC
21 Bits und Bytes, Mathematisches und Geld
22 Testen mit JUnit
23 Die Werkzeuge des JDK
A Java SE-Module und Paketübersicht
Stichwortverzeichnis


Download:

- Listings, ca. 2,7 MB


Buch bestellen
Ihre Meinung?



Spacer
<< zurück
Java ist auch eine Insel von Christian Ullenboom

Einführung, Ausbildung, Praxis
Buch: Java ist auch eine Insel


Java ist auch eine Insel

Pfeil 6 Eigene Klassen schreiben
Pfeil 6.1 Eigene Klassen mit Eigenschaften deklarieren
Pfeil 6.1.1 Attribute deklarieren
Pfeil 6.1.2 Methoden deklarieren
Pfeil 6.1.3 Verdeckte (shadowed) Variablen
Pfeil 6.1.4 Die this-Referenz
Pfeil 6.2 Privatsphäre und Sichtbarkeit
Pfeil 6.2.1 Für die Öffentlichkeit: public
Pfeil 6.2.2 Kein Public Viewing – Passwörter sind privat
Pfeil 6.2.3 Wieso nicht freie Methoden und Variablen für alle?
Pfeil 6.2.4 Privat ist nicht ganz privat: Es kommt darauf an, wer’s sieht *
Pfeil 6.2.5 Zugriffsmethoden für Attribute deklarieren
Pfeil 6.2.6 Setter und Getter nach der JavaBeans-Spezifikation
Pfeil 6.2.7 Paketsichtbar
Pfeil 6.2.8 Zusammenfassung zur Sichtbarkeit
Pfeil 6.3 Eine für alle – statische Methoden und statische Attribute
Pfeil 6.3.1 Warum statische Eigenschaften sinnvoll sind
Pfeil 6.3.2 Statische Eigenschaften mit static
Pfeil 6.3.3 Statische Eigenschaften über Referenzen nutzen? *
Pfeil 6.3.4 Warum die Groß- und Kleinschreibung wichtig ist *
Pfeil 6.3.5 Statische Variablen zum Datenaustausch *
Pfeil 6.3.6 Statische Eigenschaften und Objekteigenschaften *
Pfeil 6.4 Konstanten und Aufzählungen
Pfeil 6.4.1 Konstanten über statische finale Variablen
Pfeil 6.4.2 Typunsichere Aufzählungen
Pfeil 6.4.3 Aufzählungstypen: typsichere Aufzählungen mit enum
Pfeil 6.5 Objekte anlegen und zerstören
Pfeil 6.5.1 Konstruktoren schreiben
Pfeil 6.5.2 Verwandtschaft von Methode und Konstruktor
Pfeil 6.5.3 Der Standard-Konstruktor (default constructor)
Pfeil 6.5.4 Parametrisierte und überladene Konstruktoren
Pfeil 6.5.5 Copy-Konstruktor
Pfeil 6.5.6 Einen anderen Konstruktor der gleichen Klasse mit this(…) aufrufen
Pfeil 6.5.7 Immutable-Objekte und Wither-Methoden
Pfeil 6.5.8 Ihr fehlt uns nicht – der Garbage-Collector
Pfeil 6.6 Klassen- und Objektinitialisierung *
Pfeil 6.6.1 Initialisierung von Objektvariablen
Pfeil 6.6.2 Statische Blöcke als Klasseninitialisierer
Pfeil 6.6.3 Initialisierung von Klassenvariablen
Pfeil 6.6.4 Eincompilierte Belegungen der Klassenvariablen
Pfeil 6.6.5 Exemplarinitialisierer (Instanzinitialisierer)
Pfeil 6.6.6 Finale Werte im Konstruktor und in statischen Blöcken setzen
Pfeil 6.7 Zum Weiterlesen
 

Zum Seitenanfang

6.6    Klassen- und Objektinitialisierung * Zur vorigen ÜberschriftZur nächsten Überschrift

Eine wichtige Eigenschaft guter Programmiersprachen ist ihre Fähigkeit, keine uninitialisierten Zustände zu erzeugen. Bei lokalen Variablen achtet der Compiler auf die Belegung, also darauf, ob vor dem ersten Lesezugriff schon ein Wert zugewiesen ist. Bei Objektvariablen und Klassenvariablen haben wir bisher festgestellt, dass die Variablen automatisch mit 0, null oder false oder mit einem eigenen Wert belegt werden. Wir wollen jetzt sehen, wie dies genau funktioniert.

 

Zum Seitenanfang

6.6.1    Initialisierung von Objektvariablen Zur vorigen ÜberschriftZur nächsten Überschrift

Wenn der Compiler eine Klasse mit Objekt- oder Klassenvariablen sieht, dann müssen diese Variablen an irgendeiner Stelle initialisiert werden. Werden sie einfach deklariert und nicht mit einem Wert initialisiert, so regelt die virtuelle Maschine die Vorbelegung. Spannender ist der Fall, wenn den Variablen explizit ein Wert zugewiesen wird (der auch 0 sein kann). Dann erzeugt der Compiler automatisch einige zusätzliche Zeilen, da – vereinfacht gesagt – außerhalb von Konstruktoren und Methoden kein Code stehen darf.

Betrachten wir dies zuerst für eine Objektvariable:

Listing 6.39    src/main/java/com/tutego/insel/oop/Joystick.java

class Joystick {



int numberOfButtons = 6;



Joystick() { }



Joystick( int numberOfButtons ) {

this.numberOfButtons = numberOfButtons;

}



Joystick( String producer ) { }

}

Die Variable numberOfButtons wird mit 6 belegt. Allerdings baut der Compiler daraus Code, der die Initialisierung in jeden Konstruktor setzt:

class Joystick {



int numberOfButtons;



Joystick() {

numberOfButtons = 6;

}



Joystick( int numberOfButtons ) {

this.numberOfButtons = 6;

this.numberOfButtons = numberOfButtons;

}



Joystick( String producer ) {

numberOfButtons = 6;

}

}

Wir erkennen, dass die Variable wirklich nur beim Aufruf des Konstruktors initialisiert wird. Die Zuweisung steht dabei in der ersten Zeile. Dies kann sich als Falle erweisen, denn problematisch ist etwa die Reihenfolge der Belegung.

Manuelle Nullung

Genau genommen initialisiert die Laufzeitumgebung jede Objekt- und Klassenvariable zunächst mit 0, null oder false und später mit einem Wert. Daher ist die Nullung von Hand nicht nötig:

class NeedlessInitNull {

int i = 0; // unnötig

String s = null; // unnötig

}

Der Compiler würde nur zusätzlich in jeden Konstruktor die Initialisierung i = 0, s = null einsetzen.[ 153 ](Wir wollen hier den Fall, dass der Konstruktor der Oberklasse i einen Wert ungleich 0 setzt, nicht betrachten. ) Aus diesem Grund ist auch Folgendes nicht meisterhaft:

class NeedlessInitNull {

int i = 0;

NeedlessInitNull( int i ) { this.i = i; }

}

Die Belegung für i wird sowieso überschrieben.

 

Zum Seitenanfang

6.6.2    Statische Blöcke als Klasseninitialisierer Zur vorigen ÜberschriftZur nächsten Überschrift

Eine Art Konstruktor für das Klassenobjekt selbst (und nicht für das Exemplar der Klasse) ist ein static-Block, der einmal oder mehrmals in eine Klasse gesetzt werden kann. Jeder Block wird genau dann ausgeführt, wenn die Klasse vom Klassenlader in die virtuelle Maschine geladen wird.[ 154 ](In der Regel geschieht dies nur einmal während eines Programmlaufs. Unter gewissen Umständen – es gibt einen eigenen Klassenlader für die Klasse – kann jedoch eine Klasse auch aus dem Speicher entfernt und dann mit einem anderen Klassenlader wieder neu geladen werden. Dann werden die static-Blöcke neu ausgeführt. ) Der Block heißt Klasseninitialisierer oder statischer Initialisierungsblock:

Listing 6.40    src/main/java/com/tutego/insel/oop/StaticBlock.java, Ausschnitt

class StaticBlock {



static {

System.out.print( "Gut, dass " );

}



public static void main( String[] args ) {

System.out.println( "zum Nachbarn hat." );

}

static {

System.out.print( "Neuer Boateng " );

}

}

Lädt der Klassenlader die Klasse StaticBlock, so führt er zuerst den ersten static-Block aus und dann den zweiten static-Block. Da die Klasse StaticBlock auch das main(…) besitzt, führt die virtuelle Maschine anschließend die Startmethode aus, sodass auf dem Bildschirm kommt: »Gut, dass Neuer Boateng zum Nachbarn hat.«

Java-Programme ohne main(…) *

Lädt der Klassenlader eine Klasse, so führt er als Allererstes die statischen Blöcke aus. Mit dieser Eigenschaft lassen sich Programme ohne statische main(…)-Methode schreiben. In den statischen Block wird einfach das Hauptprogramm geschrieben. Da die virtuelle Maschine aber immer noch nach dem main(…) sucht, müssen wir die Laufzeitumgebung schon vorher beenden. Dies geschieht dadurch, dass mit System.exit(int) die Bearbeitung abgebrochen wird:

Listing 6.41    src/main/java/com/tutego/insel/oop/StaticNowMain.java, Ausschnitt

class StaticNowMain {

static {

System.out.println( "Jetzt bin ich das Hauptprogramm" );

System.exit( 0 );

}

}

Nicht jede Laufzeitumgebung nimmt das jedoch ohne Murren hin. Mit diesem Vorgehen ist der Nachteil verbunden, dass bei Ausnahmen im versteckten Hauptprogramm manche virtuellen Maschinen unsinnige Fehler melden – etwa den, dass die Klasse StaticNowMain nicht gefunden wurde, oder auch einen ExceptionInInitializerError, der anstelle einer vernünftigen Exception kommt.

 

Zum Seitenanfang

6.6.3    Initialisierung von Klassenvariablen Zur vorigen ÜberschriftZur nächsten Überschrift

Abschließend bleibt die Frage, wo Klassenvariablen initialisiert werden. Im Konstruktor ergibt dies keinen Sinn, da für Klassenvariablen keine Objekte angelegt werden müssen. Dafür gibt es den static{}-Block. Dieser wird immer dann ausgeführt, wenn der Klassenlader eine Klasse in die Laufzeitumgebung geladen hat. Für eine statische Initialisierung wird also wieder der Compiler etwas einfügen:

Was wir schreiben

Was der Compiler generiert

class Beer {

static String isFreeFor = "Homer";

}
class Beer {

static String isFreeFor;

static {

isFreeFor = "Homer";

}

}

Tabelle 6.8    Wie der Compiler initialisierte statische Variablen realisiert

Klasseninitialisierer sind nicht ganz ungefährlich, denn wenn der Code eine Ausnahme auslöst, dann gibt es einen harten java.lang.ExceptionInInitializerError. Leser können das testen, indem sie den oben gezeigten Code so ändern:

static String isFreeFor = "Homer".substring( -1 );

und dann aus dem Hauptprogramm aufrufen:

System.out.println( Beer.isFreeFor );
 

Zum Seitenanfang

6.6.4    Eincompilierte Belegungen der Klassenvariablen Zur vorigen ÜberschriftZur nächsten Überschrift

Sind Variablen final, heißt das lediglich, dass es eine einmalige Zuweisung geben darf. Ob die Werte zur Laufzeit berechnet werden oder nicht, hat zunächst nichts mit final zu tun. Im folgenden Beispiel ist die Variable eine zur Compilezeit bekannte Konstante:

public class Finance {

public static final int TAX = 19;

}

Greift eine andere Klasse auf die Variable TAX zu, ist das im Quellcode nicht als direkter Variablenzugriff Finance.TAX kodiert, sondern der Compiler hat das Literal 19 direkt an jeder Aufrufstelle eingesetzt. Dies ist eine Optimierung des Compilers, die er laut Java-Spezifikation vornehmen kann.

Wir sprechen in diesem Zusammenhang von einer compile-time constant expression, wenn gilt:

  • ein Attribut ist final,

  • der Datentyp ist ein primitiver oder String,

  • das Attribut wird mit einer vom Compiler berechneten Konstanten initialisiert.

Das Einsetzen der konstanten Werte ist praktisch, bringt aber Probleme mit sich, wenn das finale Attribut sich ändert. Dann muss nämlich auch jede Klasse übersetzt werden, die Bezug auf die Konstante hatte. Werden die abhängigen Klassen nicht neu übersetzt, ist in ihnen immer noch der alte Wert eincompiliert.

Die Lösung ist, die bezugnehmenden Klassen neu zu übersetzen und sich am besten anzugewöhnen, bei einer Änderung einer Konstanten gleich alles neu zu compilieren. Ein anderer Weg transformiert die finale Variable in eine später initialisierte Form:

public class Finance {

public static final int TAX = Integer.valueOf( 19 );

}

Die Initialisierung findet im statischen Initialisierer statt, und die Konstante mit dem Literal 19 ist zunächst einmal verschwunden. Der Compiler wird also beim Zugriff auf Finance.TAX keine Konstante 19 vorfinden und daher das Literal an den Aufrufstellen nicht einbauen können. In der Klassendatei wird der Bezug Finance.TAX vorhanden sein, und eine Änderung der Konstanten erzwingt keine neue Übersetzung der Klassen.

 

Zum Seitenanfang

6.6.5    Exemplarinitialisierer (InstanzinitialisiererZur vorigen ÜberschriftZur nächsten Überschrift

Neben den Konstruktoren haben die Sprachschöpfer eine weitere Möglichkeit vorgesehen, Objekte zu initialisieren. Diese Möglichkeit wird insbesondere bei anonymen inneren Klassen wichtig, also bei Klassen, die sich in einer anderen Klasse befinden.

Ein Exemplarinitialisierer ist ein Konstruktor ohne Namen. Er besteht in einer Klassendeklaration nur aus einem Paar geschweifter Klammern und gleicht einem statischen Initialisierungsblock ohne das Schlüsselwort static:

Listing 6.42    src/main/java/com/tutego/insel/oop/JavaInitializers.java, Ausschnitt

public class JavaInitializers {



static {

System.out.println( "Statischer Initialisierer");

}



{

System.out.println( "Exemplarinitialisierer" );

}



JavaInitializers() {

System.out.println( "Konstruktor" );

}



public static void main( String[] args ) {

new JavaInitializers();

new JavaInitializers();

}

}

Die Ausgabe ist:

Statischer Initialisierer

Exemplarinitialisierer

Konstruktor

Exemplarinitialisierer

Konstruktor

Der statische Initialisierer wird nur einmal abgearbeitet: genau dann, wenn die Klasse geladen wird. Konstruktor und Exemplarinitialisierer werden pro Aufbau eines Exemplars abgearbeitet. Der Programmcode vom Exemplarinitialisierer wird dabei vor dem eigentlichen Programmcode im Konstruktor abgearbeitet.

Mit Exemplarinitialisierern Konstruktoren vereinfachen

Die Exemplarinitialisierer können gut dazu verwendet werden, Initialisierungsarbeit bei der Objekterzeugung auszuführen. In den Blöcken lässt sich Programmcode setzen, der sonst in jeden Konstruktor kopiert oder andernfalls in einer gesonderten Methode zentralisiert werden müsste. Mit dem Exemplarinitialisierer lässt sich der Programmcode vereinfachen, denn der gemeinsame Teil kann in diesen Block gelegt werden, und wir haben eine Codeduplizierung im Quellcode vermieden. Allerdings hat die Technik gegenüber einer langweiligen Initialisierungsmethode auch Nachteile:

  • Zwar ist im Quellcode die Duplizierung nicht mehr vorhanden, aber in der Klassendatei steht sie wieder. Das liegt daran, dass der Compiler alle Anweisungen des Exemplarinitialisierers in jeden Konstruktor kopiert.

  • Exemplarinitialisierer werden schnell übersehen. Ein Blick auf den Konstruktor verrät uns dann nicht mehr, was er alles macht, da verstreute Exemplarinitialisierer Initialisierungen ändern oder hinzufügen können. Die Initialisierung trägt damit nicht zur Übersichtlichkeit bei.

  • Ein weiteres Manko ist, dass die Initialisierung nur bei neuen Objekten, also mit new, durchgeführt wird. Wenn Objekte wiederverwendet werden sollen, ist eine private Methode wie initialize(…), die das Objekt wie frisch erzeugt initialisiert, gar nicht so schlecht. Eine Methode lässt sich immer aufrufen, und damit sind die Objektzustände wie neu.

  • Die API-Dokumentation führt Exemplarinitialisierer nicht auf; die Konstruktoren müssen also die Aufgabe erklären.

Mehrere Exemplarinitialisierer

In einer Klasse können mehrere Exemplarinitialisierer auftauchen. Sie werden der Reihe nach durchlaufen, und zwar vor dem eigentlichen Konstruktor. Der Grund liegt in der Realisierung der Umsetzung: Der Programmcode der Exemplarinitialisierer wird an den Anfang aller Konstruktoren gesetzt. Objektvariablen wurden schon initialisiert. Ein Programmcode wie …

Listing 6.43    src/main/java/com/tutego/insel/oop/WhoIsAustin.java, Ausschnitt

class WhoIsAustin {



String austinPowers = "Mike Myers";



{

System.out.println( "1 " + austinPowers );

}



WhoIsAustin() {

System.out.println( "2 " + austinPowers );

}

}

… wird vom Compiler also umgebaut zu:

class WhoIsAustin {



String austinPowers;



WhoIsAustin() {

austinPowers = "Mike Myers";

System.out.println( "1 " + austinPowers );

System.out.println( "2 " + austinPowers );

}

}

Wichtig ist, abschließend zu sagen, dass vor dem Zugriff auf eine Objektvariable im Exemplarinitialisierer diese Variable im Programm deklariert sein muss und somit dem Compiler bekannt sein muss. Korrekt ist:

class WhoIsDrEvil {



String drEvil = "Mike Myers";



{

System.out.println( drEvil );

}

}

Während Folgendes zu einem Fehler führt:

class WhoIsDrEvil {



{

System.out.println( drEvil ); // inline image Compilerfehler

}



String drEvil = "Mike Myers";

}

Das ist eher ungewöhnlich, denn würden wir die print-Anweisung in einen Konstruktor setzen, wäre das erlaubt.

[»]  Hinweis

Exemplarinitialisierer ersetzen keine Konstruktoren! Sie sind selten im Einsatz und eher für innere anonyme Klassen gedacht, ein Konzept, das später in Kapitel 9, »Geschachtelte Typen«, vorgestellt wird.

 

Zum Seitenanfang

6.6.6    Finale Werte im Konstruktor und in statischen Blöcken setzen Zur vorigen ÜberschriftZur nächsten Überschrift

Wie die Beispiele im vorangegangenen Abschnitt zeigen, werden Objektvariablen erst im Konstruktor gesetzt und statische Variablen in einem static-Block. Diese Tatsache müssen wir jetzt mit finalen Variablen zusammenbringen, was uns dahin führt, dass auch sie in Konstruktoren bzw. in Initialisierungsblöcken zugewiesen werden. Im Unterschied zu nichtfinalen Variablen müssen finale Variablen auf jeden Fall gesetzt werden, und nur genau ein Schreibzugriff ist möglich.

Finale Werte aus dem Konstruktor belegen

Eine finale Variable darf nur einmal belegt werden. Das bedeutet nicht zwingend, dass sie am Deklarationsort mit einem Wert belegt werden muss – das kann auch später passieren. Der Konstruktor darf zum Beispiel finale Objektvariablen beschreiben. Das Paar aus finaler Variable und initialisierendem Konstruktor ist ein häufig genutztes Idiom, wenn Variablenwerte später nicht mehr geändert werden sollen. So ist im Folgenden die Variable pattern final, da sie nur einmalig über den Konstruktor gesetzt und dann nur noch gelesen wird:

Listing 6.44    src/main/java/com/tutego/insel/oop/Pattern.java, Ausschnitt

public class Pattern {



private final String pattern;



public Pattern( String pattern ) {

this.pattern = pattern;

}



public String getPattern() {

return pattern;

}

}
[»]  Java-Stil

Immer dann, wenn sich bis auf die direkte Initialisierung vor Ort oder im Konstruktor die Belegung nicht mehr ändert, sollten Entwickler finale Variablen verwenden.

Konstante mit Dateiinhalt initialisieren

Mit diesem Vorgehen lassen sich auch »variable« Konstanten angeben, deren Belegung sich erst zur Laufzeit ergibt. Wir können auch Werte in eine Datei legen und damit die finale statische Konstantenvariable belegen. Eine Änderung der Konstanten erzwingt also keine Neuübersetzung des Java-Programms.

Im nächsten Beispiel soll eine Datei im Klassenpfad eine Konstante enthalten, und zwar die Hubble-Konstante[ 155 ](Die Hubble-Konstante bestimmt die Expansionsgeschwindigkeit des Universums und ist eine zentrale Größe in der Kosmologie. Dummerweise ist die genaue Bestimmung schwer und der Name Konstante eigentlich unpassend, weshalb heute der Begriff Hubble-Parameter vorgezogen wird. Weitere Details unter https://de.wikipedia.org/wiki/Hubble-Konstante. ):

Listing 6.45    src/main/resources/hubble-constant.txt

72

Die Klasse liest in einem static-Block den Wert aus der Datei und belegt die finale statische Konstante:

Listing 6.46    src/main/java/com/tutego/insel/oop/LateConstant.java, Ausschnitt

public class LateConstant {



public final static int HUBBLE;

public final String ISBN;



static {

try ( java.util.Scanner scanner = new java.util.Scanner(

LateConstant.class.getResourceAsStream( "/hubble-constant.txt") ) ) {

HUBBLE = scanner.nextInt();

}

}



public LateConstant() {

ISBN = "3572100100";

}



public static void main( String[] args ) {

System.out.println( HUBBLE ); // 77



System.out.println( new LateConstant().ISBN ); // 3572100100

}

}

Im Beispiel arbeiten mehrere Klassen zusammen, um eine Zahl einzulesen. Am Anfang steht das Class-Objekt, das Zugriff auf den Klassenlader liefert, der einen Zugang zur Datei im Modulpfad ermöglicht. LateConstant.class ist die Schreibweise, um das Class-Objekt unserer eigenen Klasse zu beziehen. Die Methode getResourceAsStream(…) ist eine Objektmethode des Class-Objekts und gibt einen Datenstrom zum Dateiinhalt, den die Klasse Scanner als Eingabequelle zum Lesen nutzt. Die Objektmethode nextInt() liest anschließend eine Ganzzahl aus der Datei aus.

 


Ihre Meinung?

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de

<< zurück
 Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: Java ist auch eine Insel Java ist auch eine Insel

Jetzt Buch bestellen


 Buchempfehlungen
Zum Rheinwerk-Shop: Captain CiaoCiao erobert Java

Captain CiaoCiao erobert Java




Zum Rheinwerk-Shop: Java SE 9 Standard-Bibliothek

Java SE 9 Standard-Bibliothek




Zum Rheinwerk-Shop: Algorithmen in Java

Algorithmen in Java




Zum Rheinwerk-Shop: Objektorientierte Programmierung

Objektorientierte Programmierung




 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und in die Schweiz

InfoInfo



 

 


Copyright © Rheinwerk Verlag GmbH 2021

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



Cookie-Einstellungen ändern