Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort
1 Java ist auch eine Sprache
2 Sprachbeschreibung
3 Klassen und Objekte
4 Der Umgang mit Zeichenketten
5 Mathematisches
6 Eigene Klassen schreiben
7 Angewandte Objektorientierung
8 Exceptions
9 Generics, innere Klassen
10 Die Klassenbibliothek
11 Threads und nebenläufige Programmierung
12 Datenstrukturen und Algorithmen
13 Raum und Zeit
14 Dateien und Datenströme
15 Die eXtensible Markup Language (XML)
16 Grafische Oberflächen mit Swing
17 Grafikprogrammierung
18 Netzwerkprogrammierung
19 Verteilte Programmierung mit RMI und Web–Services
20 JavaServer Pages und Servlets
21 Applets
22 Midlets und die Java ME
23 Datenbankmanagement mit JDBC
24 Reflection und Annotationen
25 Logging und Monitoring
26 Sicherheitskonzepte
27 Java Native Interface (JNI)
28 Dienstprogramme für die Java-Umgebung
Stichwort
Download: - ZIP, ca. 14,1 MB
Buch bestellen
Ihre Meinung?
<< zurück | Galileo Computing / <openbook> / Java-Insel | vor >>
Java ist auch eine Insel (8. Auflage) von Christian Ullenboom Programmieren mit der Java Standard Edition Version 6
Java ist auch eine Insel (8. Aufl.) 8., aktual. Auflage, geb., mit DVD 1.475 S., 49,90 Euro Galileo Computing ISBN 978-3-8362-1371-4 |
| 6 Eigene Klassen schreiben
|  | 6.1 Eigene Klassen mit Eigenschaften deklarieren
|  |  | 6.1.1 Attribute deklarieren
|  |  | 6.1.2 Methoden deklarieren
|  |  | 6.1.3 Die this-Referenz
|  | 6.2 Privatsphäre und Sichtbarkeit
|  |  | 6.2.1 Für die Öffentlichkeit: public
|  |  | 6.2.2 Kein Public Viewing – Passwörter sind privat
|  |  | 6.2.3 Wieso nicht freie Methoden und Variablen für alle?
|  |  | 6.2.4 Privat ist nicht ganz privat: Es kommt darauf an, wer’s sieht
|  |  | 6.2.5 Zugriffsmethoden für Attribute deklarieren
|  |  | 6.2.6 Setter und Getter nach der JavaBeans-Spezifikation
|  |  | 6.2.7 Paketsichtbar
|  |  | 6.2.8 Zusammenfassung zur Sichtbarkeit
|  | 6.3 Statische Methoden und statische Attribute
|  |  | 6.3.1 Warum statische Eigenschaften sinnvoll sind
|  |  | 6.3.2 Statische Eigenschaften mit static
|  |  | 6.3.3 Statische Eigenschaften über Referenzen nutzen?
|  |  | 6.3.4 Warum die Groß- und Kleinschreibung wichtig ist
|  |  | 6.3.5 Statische Variablen zum Datenaustausch
|  |  | 6.3.6 Statische Eigenschaften und Objekteigenschaften
|  | 6.4 Konstanten und Aufzählungen
|  |  | 6.4.1 Konstanten über öffentliche statische finale Variablen
|  |  | 6.4.2 Eincompilierte Belegungen der Klassenvariablen
|  |  | 6.4.3 Typ(un)sichere Aufzählungen
|  |  | 6.4.4 Aufzählungen mit enum
|  | 6.5 Objekte anlegen und zerstören
|  |  | 6.5.1 Konstruktoren schreiben
|  |  | 6.5.2 Der Default-Konstruktor
|  |  | 6.5.3 Parametrisierte und überladene Konstruktoren
|  |  | 6.5.4 Copy-Konstruktor
|  |  | 6.5.5 Einen anderen Konstruktor der gleichen Klasse aufrufen
|  |  | 6.5.6 Ihr fehlt uns nicht – der Garbage-Collector
|  |  | 6.5.7 Private Konstruktoren, Utility-Klassen, Singleton, Fabriken
|  | 6.6 Klassen- und Objektinitialisierung
|  |  | 6.6.1 Initialisierung von Objektvariablen
|  |  | 6.6.2 Statische Blöcke als Klasseninitialisierer
|  |  | 6.6.3 Initialisierung von Klassenvariablen
|  |  | 6.6.4 Exemplarinitialisierer (Instanzinitialisierer)
|  |  | 6.6.5 Finale Werte im Konstruktor und in statischen Blöcken setzen
|  | 6.7 Assoziationen zwischen Objekten
|  |  | 6.7.1 Unidirektionale 1:1-Beziehung
|  |  | 6.7.2 Bidirektionale 1:1-Beziehungen
|  |  | 6.7.3 Unidirektionale 1:n-Beziehung
|  | 6.8 Vererbung
|  |  | 6.8.1 Vererbung in Java
|  |  | 6.8.2 Spielobjekte modelliert
|  |  | 6.8.3 Implizite Basisklasse java.lang.Object
|  |  | 6.8.4 Einfach- und Mehrfachvererbung
|  |  | 6.8.5 Sichtbarkeit protected
|  |  | 6.8.6 Konstruktoren in der Vererbung und super
|  | 6.9 Typen in Hierarchien
|  |  | 6.9.1 Automatische und explizite Typanpassung
|  |  | 6.9.2 Das Substitutionsprinzip
|  |  | 6.9.3 Typen mit dem binären Operator instanceof testen
|  | 6.10 Methoden überschreiben
|  |  | 6.10.1 Methoden in Unterklassen mit neuem Verhalten ausstatten
|  |  | 6.10.2 Mit super an die Eltern
|  |  | 6.10.3 Finale Klassen und finale Methoden
|  |  | 6.10.4 Kovariante Rückgabetypen
|  |  | 6.10.5 Array-Typen und Kovarianz
|  | 6.11 Dynamisches Binden/Polymorphie
|  |  | 6.11.1 Unpolymorph bei privaten, statischen und finalen Methoden
|  |  | 6.11.2 Polymorphie bei Konstruktoraufrufen
|  | 6.12 Abstrakte Klassen und abstrakte Methoden
|  |  | 6.12.1 Abstrakte Klassen
|  |  | 6.12.2 Abstrakte Methoden
|  | 6.13 Schnittstellen
|  |  | 6.13.1 Deklarieren von Schnittstellen
|  |  | 6.13.2 Implementieren von Schnittstellen
|  |  | 6.13.3 Markierungsschnittstellen
|  |  | 6.13.4 Ein Polymorphie-Beispiel mit Schnittstellen
|  |  | 6.13.5 Die Mehrfachvererbung bei Schnittstellen
|  |  | 6.13.6 Keine Kollisionsgefahr bei Mehrfachvererbung
|  |  | 6.13.7 Erweitern von Interfaces – Subinterfaces
|  |  | 6.13.8 Vererbte Konstanten bei Schnittstellen
|  |  | 6.13.9 Abstrakte Klassen und Schnittstellen im Vergleich
|  | 6.14 Dokumentationskommentare mit JavaDoc
|  |  | 6.14.1 Einen Dokumentationskommentar setzen
|  |  | 6.14.2 Mit javadoc eine Dokumentation erstellen
|  |  | 6.14.3 HTML-Tags in Dokumentationskommentaren
|  |  | 6.14.4 Generierte Dateien
|  |  | 6.14.5 Dokumentationskommentare im Überblick
|  |  | 6.14.6 JavaDoc und Doclets
|  |  | 6.14.7 Veraltete (deprecated) Typen und Eigenschaften
## 6.9 Typen in Hierarchien Die Vererbung bringt einiges Neues in Bezug auf Kompatibilität von Typen mit. Dieser Abschnitt beschäftigt sich mit den Fragen, welche Typen kompatibel sind, wie sich ein Typ zur Laufzeit testen lässt. ### 6.9.1 Automatische und explizite Typanpassung Die Klassen Room und Player modellierten wir als Unterklassen von GameObject. Die eigene Oberklasse GameObject erweitert selbst keine explizite Oberklasse, sodass implizit java.lang.Object die Oberklasse ist. In GameObject gibt es das Attribut name, das Player und Room erben, und der Raum hat zusätzlich size für die Raumgröße. #### Automatische Typanpassung Mit der Ist-eine-Art-von-Beziehung ist eine interessante Eigenschaft verbunden, die wir bemerken, wenn wir die Zusammenhänge zwischen den Typen beachten: Ein Raum ist ein Spielobjekt. Ein Spieler ist ein Spielobjekt. Ein Spielobjekt ist ein java.lang.Object. Ein Spieler ist ein java.lang.Object. Ein Raum ist ein java.lang.Object. Ein Raum ist ein Raum. Ein Spieler ist ein Spieler. Kodieren wir das in Java: Listing 6.60 com/tutego/insel/game/vd/TypeSuptype.java, main()
    Player     playerIsPlayer     = new Player();
    GameObject gameObjectIsPlayer = new Player();
    Object     objectIsPlayer     = new Player();
    Room       roomIsRoom         = new Room();
    GameObject gameObjectIsRoom   = new Room();
    Object     objectIsRoom       = new Room();
Es gilt also, dass immer dann, wenn ein Typ gefordert ist, auch ein Untertyp erlaubt ist. Der Compiler führt eine implizite Typanpassung durch. Wir werden uns dieses so genannte Liskov’sche Substitutionsprinzip im folgenden Abschnitt anschauen. #### Was wissen Compiler und Laufzeitumgebung über unser Programm? Wichtig ist zu beobachten, dass Compiler und Laufzeitumgebung unterschiedliche Dinge wissen. Durch den new-Operator gibt es zur Laufzeit nur zwei Arten von Objekten: Player und Room. Auch wenn es heißt
    GameObject gameObjectIsRoom = new Room();
referenziert gameObjectIsRoom zur Laufzeit ein Room-Objekt. Der Compiler aber »vergisst« dies und glaubt, gameObjectIsRoom wäre nur ein einfaches GameObject. In der Klasse GameObject ist jedoch nur name deklariert, aber kein Attribut size, obwohl das tatsächliche Room-Objekt natürlich eine size kennt. Auf size können wir aber erst einmal nicht zugreifen:
    println( gameObjectIsRoom.name );
    println( gameObjectIsRoom.size ); // gameObjectIsRoom.size cannot be resolved
                                      // or is not a field
Schreiben wir noch einschränkender:
    Object objectIsRoom = new Room();
    println( objectIsRoom.name );      // objectIsRoom.name cannot be resolved
                                       // or is not a field
    println( objectIsRoom.size );      // objectIsRoom.size cannot be resolved or
                                       // is not a field
so steht hinter der Referenzvariablen objectIsRoom ein vollständiges Room-Objekt, aber weder size noch name sind nutzbar; es bleiben nur die Fähigkeiten aus java.lang.Object. #### Explizite Typanpassung Diese Typeinschränkung gilt auch an anderer Stelle. Ist eine Variable vom Typ Room deklariert, können wir die Variable nicht mit einem »kleineren« Typ initialisieren.
    GameObject go         = new Room();    // Es ist ein Raum zur Laufzeit
    Room       cubbyhole  = go;            // Type mismatch: cannot convert from
                                           // GameObject to Room
Auch wenn zur Laufzeit go ein Room referenziert, können wir cubbyhole nicht damit initialisieren. Der Compiler kennt go nur unter dem »kleineren« Typ GameObject, und das reicht nicht zur Initialisierung vom »größeren« Typ Room. Es ist aber möglich, das Objekt hinter go durch eine explizite Typumwandlung für den Compiler wieder zu einem vollwertigen Room mit Größe zu machen:
    Room       cubbyhole = (Room) go;
    System.out.println( cubbyhole.size );  // Room hat Attribut size
#### Unmögliche Anpassung und ClassCastException Dies funktioniert aber lediglich dann, wenn go auch wirklich einen Raum referenziert. Dem Compiler ist das in dem Moment relativ egal, sodass auch Folgendes ohne Fehler kompiliert:
    GameObject go        = new Player();
    Room       cubbyhole = (Room) go;       // Hier folgt die ClassCastException
    System.out.println( cubbyhole.size );
Zur Laufzeit kommt es bei diesem Kuckucksobjekt zu einer ClassCastException:
    Exception in thread "main" java.lang.ClassCastException:
    com.tutego.insel.game.vd.Player cannot be cast to com.tutego.insel.game.vd.Room
     at com.tutego.insel.game.vd.ClassCastExceptionDemo.main(ClassCastExceptionDemo.java:8)
### 6.9.2 Das Substitutionsprinzip Stellen wir uns vor, Bekannte kommen ausgehungert von einer Wandertour und fragen: »Haste was zu essen?« Die Frage zielt wohl darauf ab, dass es bei Hunger ziemlich egal ist, was wir anbieten, wichtig ist nur etwas Essbares. Daher können wir Eis, aber auch Frittierfett und leckere gegrillte Heuschrecken anbieten. Diese Ausgangslage führt uns zu einem wichtigen Konzept in der Objektorientierung: »Wer wenig will, kann viel bekommen.« Genauer gesagt: Wenn Unterklassen wie Player oder Room die Oberklasse GameObject erweitern, können wir überall, wo GameObject gefordert wird, auch einen Player oder Room übergeben, da beide ja vom Typ GameObject sind und wir mit der Unterklasse nur spezieller werden. Auch können wir weitere Unterklassen von Player und Room übergeben, da auch die Unterklasse weiterhin zusätzlich das »Gen« GameObject in sich trägt. Alle diese Dinge wären vom Typ GameObject und daher typkompatibel. Wenn nun etwa eine Methode eine Übergabe vom Typ GameObject erwartet, kann sie alle Eigenschaften von GameObject nutzen, also das Attribut name, da ja alle Unterklassen die Eigenschaften erben und Unterklassen die Eigenschaften nicht »wegzaubern« können. Derjenige, dem wir »mehr« übergeben, kann damit zwar nichts mit den Erweiterungen anfangen, ablehnen wird er das Objekt aber nicht, weil es alle geforderten Eigenschaften aufweist. Weil anstelle eines Objekts auch ein Objekt der Unterklasse auftauchen kann, sprechen wir von Substitution. Das Prinzip wurde von der Professorin Barbara Liskov [Die Zeitschrift »Discover« zählt sie zu den 50 wichtigsten Frauen in der Wissenschaft.] formuliert und heißt daher auch Liskov’sches Substitutionsprinzip. Hier klicken, um das Bild zu vergrößern Die folgende Klasse QuoteNameFromGameObject nutzt diese Eigenschaft. Sie fordert in der Funktion quote() irgendein GameObject, von dem bekannt ist, dass es ein Attribut name hat. Im Hauptprogramm kann quote() ein Spieler oder Raum übergeben werden. Listing 6.61 com/tutego/insel/game/vd/QuoteNameFromGameObject.java, QuoteNameFromGameObject
    public class QuoteNameFromGameObject
    {
      public static void quote( GameObject go )
      {
        System.out.println( "'" + go.name + "'" );
      }

      public static void main( String[] args )
      {
        Player player = new Player();
        player.name = "Godman";
        quote( player );            // 'Godman'

        GameObject room = new Room();
        room.name = "Hogwurz";
        quote( room );             // 'Hogwurz'
      }
    }
Mit GameObject haben wir eine Basisklasse geschaffen, die verschiedenen Unterklassen Grundfunktionalität beibringt, in unserem Fall das Attribut name. So liefert die Basisklasse einen gemeinsamen Nenner, etwa gemeinsame Attribute oder Methoden, die jede Unterklasse besitzen wird. In der Java-Bibliothek finden sich zahllose weitere Beispiele. Häufigstes Anwendungsfeld sind Datenstrukturen. Eine Liste nimmt zum Beispiel beim Hinzufügen beliebige Objekte entgegen, denn der Parametertyp ist (intern) Object. Die Substitution besagt, dass wir alle Objekte dort einsetzen können, da alle Klassen von Object abgeleitet sind. ### 6.9.3 Typen mit dem binären Operator instanceof testen Der relationale Operator instanceof hilft dabei, Exemplare auf ihre Verwandtschaft mit einem Referenztyp (Klasse oder Schnittstelle) zu prüfen. Er stellt zur Laufzeit fest, ob eine Referenz von einem bestimmten Typ ist. Listing 6.62 com/tutego/insel/game/vd/InstanceofDemo.java, main()
    System.out.println( "Toll" instanceof String );       // true
    System.out.println( "Toll" instanceof Object );       // true
    System.out.println( new Player() instanceof Object ); // true
Alles in doppelten Anführungsstrichen ist ein String, sodass instanceof String wahr ergibt. Für den zweiten und dritten Fall gilt: Alle Objekte gehen irgendwie aus Object hervor und sind somit logischerweise Erweiterungen.
Hinweis Der Operator instanceof testet ein Objekt auf seine Hierarchie. So ist zum Beispiel o instanceof Object für jedes Objekt o wahr, denn jedes Objekt ist immer Kind von java.lang.Object. Die Programmiersprache Smalltalk unterscheidet hier mit zwei Nachrichten isMemberOf (exakt) und isKindOf (wie Javas instanceof). Um den exakten Typ zu testen, lässt sich mit dem Class-Objekt arbeiten, etwa wie im Ausdruck o.getClass().equals (Object.class), der testet, ob o genau ein Object-Objekt ist.
Die bisherigen Beziehungen hätte der Compiler bereits herausfinden können. Vervollständigen wir das, um zu sehen, dass instanceof wirklich zur Laufzeit den Test machen muss. In allen Fällen ist das Objekt zur Laufzeit ein Raum:
    Room       go1 = new Room();
    System.out.println( go1 instanceof Room );       // true
    System.out.println( go1 instanceof GameObject ); // true
    System.out.println( go1 instanceof Object );     // true

    GameObject go2 = new Room();
    System.out.println( go2 instanceof Room );       // true
    System.out.println( go2 instanceof GameObject ); // true
    System.out.println( go2 instanceof Object );     // true
    System.out.println( go2 instanceof Player );     // false

    Object     go3 = new Room();
    System.out.println( go3 instanceof Room );       // true
    System.out.println( go3 instanceof GameObject ); // true
    System.out.println( go3 instanceof Object );     // true
    System.out.println( go3 instanceof Player );     // false
    System.out.println( go3 instanceof String );     // false
Der Compiler lässt aber nicht alles durch. Liegen zwei Typen überhaupt nicht in der Typhierarchie, lehnt der Compiler den Test ab, da die Vererbungsbeziehungen schon inkompatibel sind.
    "Toll" instanceof StringBuilder;
    // Incompatible conditional operand types String and StringBuilder
Der Ausdruck ist falsch, da StringBuilder keine Basisklasse für String ist. Zum Schluss:
    Object ref1 = new int[ 100 ];
    System.out.println( ref1 instanceof String );
Ein instanceof-Test mit einer Referenz, die null ist, gibt immer false zurück.
    Object ref2 = null;
    System.out.println( ref2 instanceof String );
Hinweis Mit instanceof lässt sich der Programmfluss aufgrund der tatsächlichen Typen steuern, etwa mit Anweisungen wie if ( reference instanceof Typ ) tue dies, else tue das. In der Regel zeigt Kontrolllogik dieser Art aber tendenziell ein Designproblem an und kann oft anders gelöst werden. Das dynamische Binden ist so eine Lösung; sie wird später vorgestellt.
### Ihr Kommentar Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen. >> Zum Feedback-Formular
<< zurück | <top> | vor >>
Zum Katalog
Java ist auch eine Insel Jetzt bestellen
Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen? Ihre Meinung
Tipp
| Coding for Fun
Buchempfehlungen
| Objektorientierte Programmierung
| Einstieg in Eclipse 3.4
| Java 6 lernen mit Eclipse
| NetBeans Platform 6
| Java und XML
| Visual C# 2008
| IT-Handbuch für Fachinformatiker
| C++ von A bis Z
Shopping
Versandkostenfrei bestellen in Deutschland und Österreich Info

Copyright © Galileo Press 2009
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.

[Galileo Computing]
Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de