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 Objektorientierte Beziehungsfragen
7 Ausnahmen müssen sein
8 Äußere.innere Klassen
9 Besondere Typen der Java SE
10 Generics<T>
11 Lambda-Ausdrücke und funktionale Programmierung
12 Architektur, Design und angewandte Objektorientierung
13 Komponenten, JavaBeans und Module
14 Die Klassenbibliothek
15 Einführung in die nebenläufige Programmierung
16 Einführung in Datenstrukturen und Algorithmen
17 Einführung in grafische Oberflächen
18 Einführung in Dateien und Datenströme
19 Einführung ins Datenbankmanagement mit JDBC
20 Einführung in <XML>
21 Testen mit JUnit
22 Bits und Bytes und Mathematisches
23 Die Werkzeuge des JDK
A Java SE-Paketübersicht
Stichwortverzeichnis


Download:

- Beispielprogramme, ca. 35,4 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 Objektorientierte Beziehungsfragen
Pfeil 6.1 Assoziationen zwischen Objekten
Pfeil 6.1.1 Unidirektionale 1:1-Beziehung
Pfeil 6.1.2 Zwei Freunde müsst ihr werden – bidirektionale 1:1-Beziehungen
Pfeil 6.1.3 Unidirektionale 1:n-Beziehung
Pfeil 6.2 Vererbung
Pfeil 6.2.1 Vererbung in Java
Pfeil 6.2.2 Spielobjekte modellieren
Pfeil 6.2.3 Die implizite Basisklasse java.lang.Object
Pfeil 6.2.4 Einfach- und Mehrfachvererbung *
Pfeil 6.2.5 Die Sichtbarkeit protected
Pfeil 6.2.6 Konstruktoren in der Vererbung und super(…)
Pfeil 6.3 Typen in Hierarchien
Pfeil 6.3.1 Automatische und explizite Typumwandlung
Pfeil 6.3.2 Das Substitutionsprinzip
Pfeil 6.3.3 Typen mit dem instanceof-Operator testen
Pfeil 6.4 Methoden überschreiben
Pfeil 6.4.1 Methoden in Unterklassen mit neuem Verhalten ausstatten
Pfeil 6.4.2 Mit super an die Eltern
Pfeil 6.4.3 Finale Klassen und finale Methoden
Pfeil 6.4.4 Kovariante Rückgabetypen
Pfeil 6.4.5 Array-Typen und Kovarianz *
Pfeil 6.5 Drum prüfe, wer sich ewig dynamisch bindet
Pfeil 6.5.1 Gebunden an toString()
Pfeil 6.5.2 Implementierung von System.out.println(Object)
Pfeil 6.5.3 Nicht dynamisch gebunden bei privaten, statischen und finalen Methoden
Pfeil 6.5.4 Dynamisch gebunden auch bei Konstruktoraufrufen *
Pfeil 6.5.5 Eine letzte Spielerei mit Javas dynamischer Bindung und überdeckten Attributen *
Pfeil 6.6 Abstrakte Klassen und abstrakte Methoden
Pfeil 6.6.1 Abstrakte Klassen
Pfeil 6.6.2 Abstrakte Methoden
Pfeil 6.7 Schnittstellen
Pfeil 6.7.1 Schnittstellen deklarieren
Pfeil 6.7.2 Implementieren von Schnittstellen
Pfeil 6.7.3 Ein Polymorphie-Beispiel mit Schnittstellen
Pfeil 6.7.4 Die Mehrfachvererbung bei Schnittstellen
Pfeil 6.7.5 Keine Kollisionsgefahr bei Mehrfachvererbung *
Pfeil 6.7.6 Erweitern von Interfaces – Subinterfaces
Pfeil 6.7.7 Konstantendeklarationen bei Schnittstellen
Pfeil 6.7.8 Statische ausprogrammierte Methoden in Schnittstellen
Pfeil 6.7.9 Erweitern von Schnittstellen
Pfeil 6.7.10 Default-Methoden
Pfeil 6.7.11 Erweiterte Schnittstellen deklarieren und nutzen
Pfeil 6.7.12 Erweiterte Schnittstellen, Mehrfachvererbung und Mehrdeutigkeiten *
Pfeil 6.7.13 Bausteine bilden mit Default-Methoden *
Pfeil 6.7.14 Initialisierung von Schnittstellenkonstanten *
Pfeil 6.7.15 Markierungsschnittstellen *
Pfeil 6.7.16 (Abstrakte) Klassen und Schnittstellen im Vergleich
Pfeil 6.8 Zum Weiterlesen
 

Zum Seitenanfang

6.4Methoden überschreiben Zur vorigen ÜberschriftZur nächsten Überschrift

Wir haben gesehen, dass eine Unterklasse durch Vererbung die sichtbaren Eigenschaften ihrer Oberklasse erbt. Die Unterklasse kann nun wiederum Methoden hinzufügen. Dabei zählen überladene Methoden – also Methoden, die den gleichen Namen wie eine andere Methode aus einer Oberklasse tragen, aber eine andere Parameteranzahl oder andere Parametertypen haben – zu ganz normalen, hinzugefügten Methoden.

 

Zum Seitenanfang

6.4.1Methoden in Unterklassen mit neuem Verhalten ausstatten Zur vorigen ÜberschriftZur nächsten Überschrift

Besitzt eine Unterklasse eine Methode mit dem gleichen Methodennamen und der exakt gleichen Parameterliste (also der gleichen Signatur) wie schon die Oberklasse, so überschreibt die Unterklasse die Methode der Oberklasse. Ist der Rückgabetyp void oder ein primitiver Typ, so muss er in der überschreibenden Methode der gleiche sein. Bei Referenztypen kann der Rückgabetyp etwas variieren, doch das werden wir in Abschnitt 6.4.4 genauer sehen.

Implementiert die Unterklasse die Methode neu, so sagt sie auf diese Weise: »Ich kann’s besser.« Die überschreibende Methode der Unterklasse kann demnach den Programmcode spezialisieren und Eigenschaften nutzen, die in der Oberklasse nicht bekannt sind. Die überschriebene Methode der Oberklasse ist dann erst einmal aus dem Rennen, und ein Methodenaufruf auf einem Objekt der Unterklasse würde sich in der überschriebenen Methode verfangen.

[»]Hinweis

Wir sprechen nur von überschriebenen Methoden und nicht von überschriebenen Attributen, da Attribute nicht überschrieben, sondern nur überdeckt[ 159 ](Die JLS unterscheidet genau genommen »shadowing« und »hiding«. Interessierte Leser mögen das unter https://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.4.1 nachlesen. ) werden. Attribute werden auch nicht dynamisch gebunden – eine Eigenschaft, die später in Abschnitt 6.5.5, »Eine letzte Spielerei mit Javas dynamischer Bindung und überdeckten Attributen *«, genauer erklärt wird.

Überschreiben von toString()

Aus der absoluten Basisklasse java.lang.Object bekommen alle Unterklassen eine Methode toString() vererbt, die meist zu Debug-Zwecken eine Objektkennung ausgibt:

Listing 6.23java/lang/Object.java, toString()

public String toString() {

return getClass().getName() + "@" + Integer.toHexString(hashCode());

}

Die Methode liefert den Namen der Klasse, gefolgt von einem "@" und einer hexadezimalen Kennung. Die Klasse GameObject ohne eigenes toString() soll die Wirkung testen:

Listing 6.24com/tutego/insel/game/ve/GameObject.java, GameObject

public class GameObject {

public String name;

}

Auf einem GameObject-Objekt liefert toString() eine etwas kryptische Kennung:

GameObject go = new GameObject();

System.out.println( go.toString() ); // com.tutego.insel.game.ve.GameObject@e48e1b

Es ist also eine gute Idee, toString() in den Unterklassen zu überschreiben. Eine String-Kennung sollte den Namen der Klasse und die Zustände eines Objekts beinhalten. Für einen Raum, der einen (geerbten) Namen und eine eigene Größe hat, kann dies wie folgt aussehen:

Listing 6.25com/tutego/insel/game/ve/Room.java, Room

public class Room extends GameObject {



public int size;



@Override public String toString() {

return String.format( "%s[name=%s, size=%d]",

getClass().getSimpleName(), name, size ); }

}
Room ist eine Unterklasse von GameObject und hat ein eigenes toString().

Abbildung 6.6Room ist eine Unterklasse von GameObject und hat ein eigenes toString().

Und der Test sieht so aus:

Listing 6.26com/tutego/insel/game/ve/Playground.java, main()

Room winterfield = new Room();

winterfield.name = "Winterfield";

winterfield.size = 2040000;

System.out.println( winterfield ); // Room[name=Winterfield, size=2040000]

Zur Erinnerung: Ein println(Object) auf einem beliebigen Objekt ruft die toString()-Methode von diesem Objekt auf.

Die Annotation @Override

Unsere Beispielklasse Room nutzt die Annotation @Override an der Methode toString() und macht auf diese Weise deutlich, dass die Klasse eine Methode des Obertyps überschreibt. Die Annotation @Override bedeutet nicht, dass diese Methode in Unterklassen überschrieben werden muss, sondern nur, dass sie selbst eine Methode überschreibt. Annotationen sind zusätzliche Modifizierer, die entweder vom Compiler überprüft werden oder von uns nachträglich abgefragt werden können. Obwohl wir die Annotation @Override nicht nutzen müssen, hat diese den Vorteil, dass der Compiler überprüft, ob wir tatsächlich eine Methode aus der Oberklasse überschreiben. Haben wir uns zum Beispiel im Methodennamen verschrieben und somit der Unterklasse unbeabsichtigt eine neue Methode hinzufügt, so würde das der Compiler aufgrund seiner Kenntnis von @Override als Fehler melden. Einfache Schreibfehler wie tostring() fallen schnell auf. Überladene Methoden und überschriebene Methoden sind etwas anderes, da eine überladene Methode mit der Ursprungsmethode nur »zufällig« den Namen teilt, aber sonst keinen Bezug zur Logik hat. Und so hilft @Override, dass Entwickler wirklich Methoden überschreiben und nicht aus Versehen Methoden mit falschen Parametern überladen.

Garantiert überschrieben? *

Überschrieben werden nur Methoden, die exakt mit der Signatur einer Methode aus der Oberklasse übereinstimmen. Sind Parametertypen gleich, so müssen sie auch aus dem gleichen Paket stammen. So kann es passieren, dass eine Unterklasse Sub doch nicht die Methode printDate(Date) aus Super überschreibt, obwohl es auf den ersten Blick so aussieht:

Deklaration der Basisklasse

Überladene, keine überschriebene Methode

import java.util.Date;



public class Super {

void printDate( Date date ) {}

}
import java.sql.Date;



public class Sub extends Super {

void printDate( Date date ) {}

}

Zwar sehen die Signaturen optisch gleich aus, da aber Date aus verschiedenen Paketen stammt, ist die Signatur nicht wirklich gleich. Die Methode aus printDate(Date) aus Sub überlädt printDate(Date) aus Super, aber überschreibt sie nicht. Letztendlich bietet Sub zwei Methoden:

  • void printDate( java.util.Date date ) {}

  • void printDate( java.sql.Date date ) {}

Es ist gut, wenn eine überschreibende Methode explizit kenntlich gemacht wird. Dazu gibt es die Annotation @Override, die an die Methode der Unterklasse gesetzt werden sollte. Denn verspricht eine Methode das Überschreiben, doch macht sie das, wie in unserem Beispiel, nicht, ergibt das einen Compilerfehler, und dem Entwickler wird der Fehler vor Augen geführt. Mit @Override wäre dieser Fehler aufgefallen.

Finale Parameter in der Vererbung *

In der Vererbung von Methoden spielen finale Parametervariablen keine Rolle. Wir können es als zusätzliche Information für die jeweilige Methode betrachten. Eine Unterklasse kann demnach beliebig das final hinzufügen oder auch wegnehmen. Alte Bibliotheken lassen sich so leicht weiterverwenden.

 

Zum Seitenanfang

6.4.2Mit super an die Eltern Zur vorigen ÜberschriftZur nächsten Überschrift

Wenn wir eine Methode überschreiben, dann entscheiden wir uns für eine gänzlich neue Implementierung. Was ist aber, wenn die Funktionalität im Großen und Ganzen gut war und nur eine Kleinigkeit fehlte? Im Fall der überschriebenen toString()-Methode realisiert die Unterklasse eine völlig neue Implementierung und bezieht sich dabei nicht auf die Logik der Oberklasse.

Möchte eine Unterklasse sagen: »Was meine Eltern können, ist doch gar nicht so schlecht«, kann mit der speziellen Referenz super auf die Eigenschaften im Namensraum der Oberklasse zugegriffen werden (natürlich ist das Objekt hinter super und this das gleiche, nur der Namensraum ist ein anderer). Auf diese Weise können Unterklassen immer noch etwas Eigenes machen, aber die Realisierung aus der Elternklasse ist weiterhin verfügbar.

In unserem Spiel hatte GameObject kein toString(), ändern wir dies:

Listing 6.27com/tutego/insel/game/vf/GameObject.java, GameObject

public class GameObject {



public String name;



@Override public String toString() {

return String.format( "%s[name=%s]", getClass().getSimpleName(), name );

}

}

Die Unterklasse Room erweitert GameObject und sollte toString() neu realisieren, da ein Raum ein zusätzliches Attribut hat, nämlich die Größe. Wird toString() allerdings in Room überschrieben, muss sich toString() auch um die geerbten Eigenschaften kümmern, sprich: den Namen. Das ist ungünstig, denn kommt zum Beispiel in der Oberklasse GameObject ein Attribut hinzu, müssen alle toString()-Methoden von allen Unterklassen geändert werden, wenn sie alle Attributbelegungen mit in die String-Kennung einbinden möchte.

Eine Lösung für das Problem ist, in toString() einer Unterklasse wie Room einfach auf die toString()-Methode der Oberklasse GameObject zuzugreifen, und dann das zusätzliche Attribut mit aufzunehmen.

Listing 6.28com/tutego/insel/game/vf/Room.java, Room

public class Room extends GameObject {



public int size;



@Override public String toString() {

return super.toString() + "[size=" + size+ "]";

}

}

Stünde statt super.toString() nur toString() im Rumpf, würde der Methodenaufruf in die Endlosrekursion führen, daher funktioniert es ohne super-Referenz nicht.

Ein Test zeigt das Resultat:

Listing 6.29com/tutego/insel/game/vf/RoomToString.java, main()

Room enterprise = new Room();

enterprise.name = "Enterprise";

enterprise.size = 725;

System.out.println( enterprise ); // Room[name=Enterprise][size=725]

Eigenschaften der super-Referenz *

Nicht nur in überschriebenen Methoden kann die super-Referenz sinnvoll eingesetzt werden: Sie ist auch interessant, wenn Methoden der Oberklasse aufgerufen werden sollen und nicht eigene überschriebene. So macht das folgende Beispiel klar, dass auf jeden Fall toString() der Oberklasse Object aufgerufen werden soll und nicht die eigene überschriebene Variante:

Listing 6.30ToStringFromSuper.java

public class ToStringFromSuper {



public ToStringFromSuper() {

System.out.println( super.toString() ); // Aufruf von Object toString()

}



@Override

public String toString() {

return "Nein";

}



public static void main( String[] args ) {

new ToStringFromSuper(); // ToStringFromSuper@3e25a5

}

}

Natürlich kann super nur dann eingesetzt werden, wenn in der Oberklasse die Methode eine gültige Sichtbarkeit hat. Es ist also nicht möglich, mit diesem Konstrukt das Geheimnisprinzip zu durchbrechen.

Eine Aneinanderreihung von super-Schlüsselwörtern bei einer tieferen Vererbungshierarchie ist nicht möglich. Hinter einem super muss eine Objekteigenschaft stehen; sie gilt also für eine überschriebene Methode oder ein überdecktes Attribut. Anweisungen wie super.super.lol() sind somit immer ungültig. Eine Unterklasse empfängt alle Eigenschaften ihrer Oberklassen als Einheit und unterscheidet nicht, aus welcher Hierarchie etwas kommt.

 

Zum Seitenanfang

6.4.3Finale Klassen und finale Methoden Zur vorigen ÜberschriftZur nächsten Überschrift

Soll eine Klasse keine Unterklassen bilden, werden Klassen mit dem Modifizierer final versehen. Dadurch lässt sich vermeiden, dass Unterklassen Eigenschaften nachträglich verändern können. Ein Versuch, von einer finalen Klasse zu erben, führt zu einem Compilerfehler. Dies schränkt zwar die objektorientierte Wiederverwendung ein, wird aber aufgrund von Sicherheitsaspekten in Kauf genommen. Eine Passwortüberprüfung soll zum Beispiel nicht einfach überschrieben werden können.

In der Java-Bibliothek gibt es eine Reihe finaler Klassen, von denen wir einige bereits kennen:

  • String, StringBuilder

  • Integer, Double … (Wrapper-Klassen)

  • Math

  • System, Locale

  • Color

[+]Tipp

Eine protected-Eigenschaft in einer als final deklarierten Klasse ergibt wenig Sinn, da ja keine Unterklasse möglich ist, die diese Methode oder Variable nutzen kann. Daher sollte die Eigenschaft dann paketsichtbar sein (protected enthält ja paketsichtbar) oder gleich private oder public.

Nicht überschreibbare (finale) Methoden

In der Vererbungshierarchie möchte ein Designer in manchen Fällen verhindern, dass Unterklassen eine Methode überschreiben und mit neuer Logik implementieren. Das verhindert der zusätzliche Modifizierer final an der Methodendeklaration. Da Methodenaufrufe immer dynamisch gebunden werden, könnte ein Aufrufer unbeabsichtigt in der Unterklasse landen, was finale Methoden vermeiden.

Dazu ein Beispiel: Das GameObject speichert einen Namen intern im protected-Attribut name und erlaubt Zugriff nur über einen Setter/Getter. Die Methode setName(String) testet, ob der Name ungleich null ist und mindestens ein Zeichen enthält. Diese Methode soll final sein, denn eine Unterklasse könnte diese Zugriffsbeschränkungen leicht aushebeln und selbst mit einer überschriebenen setName(String)-Methode die protected-Variable name beschreiben, auf die jede Unterklasse Zugriff hat:

Listing 6.31com/tutego/insel/game/vg/GameObject.java, GameObject

public class GameObject {



protected String name;



public String getName() {

return name;

}



public final void setName( String name ) {

if ( name != null && ! name.isEmpty() )

this.name = name;

}

}

Bei dem Versuch, in einer Unterklasse die Methode zu überschreiben, meldet der Compiler einen Fehler:

Listing 6.32com/tutego/insel/game/vg/Player.java, Player

public class Player extends GameObject {



@Override

public void setName( String name ) // inline

^ Cannot override the final method from GameObject

{

this.name = name;

}

}

Wir belassen es bei dem Beispiel bei einer protected-Variablen, denn Unterklassen möchten vielleicht die Variable verändern, aber eben nicht über setName(String). Die Unterklassen können zum Beispiel die Variable name auf den Leer-String "" zurücksetzen, was ein Aufruf über setName(String) nicht vermag.

[»]Hinweis

Auch private Methoden können final sein, aber private Methoden lassen sich ohnehin nicht überschreiben (sie werden überdeckt), sodass final überflüssig ist.

 

Zum Seitenanfang

6.4.4Kovariante Rückgabetypen Zur vorigen ÜberschriftZur nächsten Überschrift

Überschreibt eine Methode mit einem Referenztyp als Rückgabe eine andere, so kann die überschreibende Methode einen Untertyp des Rückgabetyps der überschriebenen Methode als Rückgabetyp besitzen. Das nennt sich kovarianter Rückgabetyp und ist sehr praktisch, da sich auf diese Weise Entwickler oft explizite Typumwandlung sparen können.

Ein Beispiel soll dies verdeutlichen: Die Klasse Loudspeaker deklariert eine Methode getThis(), die lediglich die this-Referenz zurückgibt. Eine Unterklasse überschreibt die Methode und liefert den spezielleren Untertyp:

Listing 6.33BigBassLoudspeaker.java

class Loudspeaker {

Loudspeaker getThis() {

return this;

}

}



class BigBassLoudspeaker extends Loudspeaker {

@Override BigBassLoudspeaker getThis() { // statt »Loudspeaker getThis()«

return this;

}

}

Die Unterklasse BigBassLoudspeaker überschreibt die Methode getThis(), auch wenn der Rückgabetyp nicht Loudspeaker, sondern BigBassLoudspeaker heißt.

Der Rückgabetyp muss auch nicht zwingend der Typ der eigenen Klasse sein. Gäbe es zum Beispiel mit Plasmatweeter eine zweite Unterklasse von Loudspeaker, so könnte getThis() von BigBassLoudspeaker auch den Rückgabetyp Plasmatweeter deklarieren. Hauptsache, der Rückgabetyp der überschreibenden Methode ist eine Unterklasse des Rückgabetyps der überschriebenen Methode der Basisklasse.

BigBassLoudspeaker ist ein spezieller LoudSpeaker.

Abbildung 6.7BigBassLoudspeaker ist ein spezieller LoudSpeaker.

[»]Hinweis

Merkwürdig ist in diesem Zusammenhang, dass es in Java schon immer veränderte Zugriffsrechte gegeben hat. Eine Unterklasse kann die Sichtbarkeit erweitern. Auch bei Ausnahmen kann eine Unterklasse speziellere Ausnahmen bzw. ganz andere Ausnahmen als die Methode der Oberklasse erzeugen.

 

Zum Seitenanfang

6.4.5Array-Typen und Kovarianz * Zur vorigen ÜberschriftZur nächsten Überschrift

Die Aussage »Wer wenig will, kann viel bekommen« gilt auch für Arrays, denn wenn eine Klasse U eine Unterklasse einer Klasse O ist, ist auch U[] ein Untertyp von O[]. Diese Eigenschaft nennt sich Kovarianz. Da Object die Basisklasse aller Objekte ist, kann ein Object-Array auch alle anderen Objekte aufnehmen.

Bauen wir uns eine statische Methode set(…), die einfach ein Element an die erste Stelle ins Array setzt:

Listing 6.34ArrayCovariance.java, set()

public static void set( Object[] array, Object element ) {

array[ 0 ] = element;

}

Die Kovarianz ist beim Lesen von Eigenschaften nicht problematisch, beim Schreiben jedoch potenziell gefährlich. Schauen wir, was mit unterschiedlichen Array- und Elementtypen passiert:

Listing 6.35ArrayCovariance.java, main()

Object[] objectArray = new Object[ 1 ];

String[] stringArray = new String[ 1 ];

System.out.println( "It's time for change" instanceof Object ); // true

set( stringArray, "It's time for change" );

set( objectArray, "It's time for change" );

set( stringArray, new StringBuilder("It's time for change") ); // inline

Der String lässt sich in einem String-Array abspeichern. Der zweite Aufruf funktioniert ebenfalls, denn ein String lässt sich auch in einem Object-Array speichern, da ein Object ja ein Basistyp ist. Vor einem Dilemma stehen wir dann, wenn das Array eine Referenz speichern soll, die nicht typkompatibel ist. Das zeigt der dritte set(…)-Aufruf: Zur Compilezeit ist alles noch in Ordnung, aber zur Laufzeit kommt es zu einer ArrayStoreException:

Exception in thread "main" java.lang.ArrayStoreException: java.lang.StringBuilder

at ArrayCovariance.set(ArrayCovariance.java:5)

at ArrayCovariance.main(ArrayCovariance.java:19)

Das haben wir aber auch verdient, denn ein StringBuilder-Objekt lässt sich nicht in einem String-Array speichern. Selbst ein new Object() hätte zu einem Problem geführt.

Das Typsystem von Java kann diese Spitzfindigkeit nicht zur Übersetzungszeit prüfen. Erst zur Laufzeit ist ein Test mit dem bitteren Ergebnis einer ArrayStoreException möglich. Bei Generics ist dies etwas anders, denn hier sind vergleichbare Konstrukte bei Vererbungsbeziehungen verboten.

 


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 9-Standard-Bibliothek

Java SE 9-Standard-Bibliothek




Zum Katalog: Professionell entwickeln mit Java EE 8

Professionell entwickeln mit Java EE 8




Zum Katalog: Entwurfsmuster

Entwurfsmuster




Zum Katalog: IT-Projektmanagement

IT-Projektmanagement




 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich

InfoInfo



 

 


Copyright © Rheinwerk Verlag GmbH 2017

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