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.10 Methoden überschreiben 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. ### 6.10.1 Methoden in Unterklassen mit neuem Verhalten ausstatten Besitzt eine Unterklasse eine Methode mit dem gleichen Methodennamen und der exakten Parameterliste (also der gleichen Signatur) wie schon die Oberklasse, so überschreibt die Unterklasse die Methode der Oberklasse. Ist der Rückgabetyp void oder ein primitiven Typ, so muss er in der überschreibenden Methode der gleiche sein. Bei Referenztypen kann der Rückgabetyp etwas variieren, doch das werden wir später 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 überlagert werden. Attribute werden auch nicht dynamisch gebunden – eine Eigenschaft, die später 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.63 java/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.64 com/tutego/insel/game/ve/GameObject.java, GameObject
    public class GameObject
    {
      private String name;

      public String getName()
      {
        return name;
      }

      public void setName( String name )
      {
        this.name = name;
      }
    }
Es liefert toString() auf einem GameObject-Objekt eine etwas kryptische Kennung:
    GameObject go = new GameObject();
    out.println( go.toString() ); // com.tutego.insel.game.ve.GameObject@e48e1b
Es ist also eine gute Idee, toString() in den Unterklassen zu überschreiben. Eine Stringkennung sollte den Namen der Klasse und die Zustände eines Objektes beinhalten. Für einen Raum, der einen (geerbten) Namen und eine eigene Größe hat, kann dies wie folgt aussehen: Listing 6.65 com/tutego/insel/game/ve/Room.java, Room
    public class Room extends GameObject
    {
      private int size;

      public void setSize( int size )
      {
        if ( size > 0 )
          this.size = size;
      }

      public int getSize()
      {
        return size;
      }

      @Override public String toString()
      {
        return String.format( "Room[name=%s, size=%d]", getName(), getSize() );
      }
    }
Hier klicken, um das Bild zu vergrößern Und der Test: Listing 6.66 com/tutego/insel/game/ve/Playground.java, main()
    Room winterfield = new Room();
    winterfield.setName( "Winterfield" );
    winterfield.setSize( 2040000 );
    System.out.println( winterfield ); // Room[name=Winterfield, size=2040000]
Zur Erinnerung: Ein println() auf einem beliebigen Objekt ruft die toString()-Methode von diesem Objekt auf. #### Die Annotation @Override Unser Beispiel nutzt die Annotation @Override bei der Methode toString() und macht auf diese Weise deutlich, dass die Unterklasse eine Methode der Oberklasse ü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 sie den Vorteil, dass der Compiler überprüft, ob wir tatsächlich eine Methode aus der Oberklasse überschreiben – haben wir uns im Methodennamen verschrieben und würde die Unterklasse auf diese Weise eine neue Methode hinzufügen, so würde der Compiler das als Fehler melden. Fehler wie to s tring() 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.
Hinweis Seit Java 6 ist die Annotation auch an Methoden gültig, die Operationen aus Schnittstellen implementieren; unter Java 5 war das noch ein Fehler.
### 6.10.2 Mit super an die Eltern 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 gibt es Räume mit einer Größe. Die Größe lässt sich mit setSize() setzen und mit getSize() erfragen. Eine Konsistenzprüfung in setSize() erlaubt nur Größen echt größer null. Wenn nun eine Garage als besonderer Raum eine gewisse Größe nicht überschreiten darf – sonst wäre es keine Garage –, lässt sich setSize() überschreiben und immer dann das setSize() der Oberklasse zum tatsächlichen Setzen des Attributs aufrufen, wenn die Größe im richtigen Bereich lag. Listing 6.67 com/tutego/insel/game/ve/Garage.java, Garage
    public class Garage extends Room
    {
      private static final int MAX_GARAGE_SIZE = 40;

      @Override public void setSize( int size )
      {
        if ( size <= MAX_GARAGE_SIZE )
          super.setSize( size );
      }
    }
Stünde statt super.setSize(size) nur setSize(size), würde ein Methodenaufruf in die Endlosrekursion führen. Hier klicken, um das Bild zu vergrößern #### Eigenschaften der super-Referenz Nicht nur kann die super-Referenz in überschriebenen Methoden sinnvoll eingesetzt werden, auch ist eine Anwendung 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.68 ToStringFromSuper.java
    public class ToStringFromSuper
    {
      public ToStringFromSuper()
      {
        System.out.println( super.toString() );
      }

      @Override public String toString()
      {
        return "No";
      }

      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 überlagertes 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. ### 6.10.3 Finale Klassen und finale Methoden 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, StringBuffer, StringBuilder Wrapper-Klassen: Integer, Double ... Math System Font, Color
Tipp Eine protected Eigenschaft in einer 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() 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 ausheben und selbst die protected-Variable name beschreiben, auf die die Unterklasse Zugriff hat. Listing 6.69 com/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.length() > 0 )
          this.name = name;
      }
    }
Bei dem Versuch, in einer Unterklasse die Methode zu überschreiben, meldet der Compiler einen Fehler. Listing 6.70 com/tutego/insel/game/vg/Player.java, Player
    public class Player extends GameObject
    {
      @Override
      public void setName( String name )
                  ^ Cannot override the final method from GameObject
      {
        this.name = name;
      }
    }
Hinweis Auch private Methoden können final sein, aber private Methoden lassen sich ohnehin nicht überschreiben (sie werden überlagert), sodass final überflüssig ist.
### 6.10.4 Kovariante Rückgabetypen Ü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 Typanpassungen 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.71 BigBassLoudspeaker.java
    class Loudspeaker
    {
      Loudspeaker getThis()
      {
        return this;

      }
    }

    class BigBassLoudspeaker extends Loudspeaker
    {
      @Override
      BigBassLoudspeaker getThis()
    //  Loudspeaker getThis()
      {
        return this;
      }
    }
Die Unterklasse BigBassLoudspeaker überschreibt die Methode getThis(), auch wenn der Rückgabetyp nicht Loudspeaker, sondern BigBassLoudspeaker heißt. Hier klicken, um das Bild zu vergrößern 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.
Hinweis Merkwürdig in diesem Zusammenhang ist, 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 beziehungsweise ganz andere Ausnahmen als die Methode der Oberklasse erzeugen.
### 6.10.5 Array-Typen und Kovarianz Die Aussage »Wer wenig will, kann viel bekommen« gilt auch für Arrays, denn wenn eine Klasse U Unterklasse einer Klasse O ist – und dann gilt Exemplar von U instanceof O –, 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. Listing 6.72 ArrayCovariance.java, main()
    Object[] objectArray = new Object[ 1 ];
    String[] stringArray = new String[ 1 ];
    System.out.println( "It's time for change" instanceof Object );  // true
    System.out.println( stringArray instanceof String[] );           // true
    objectArray[ 0 ] = "It's time for change";
Bauen wir uns eine Funktion set() und schauen wir, was passiert. Listing 6.73 ArrayCovariance.java, set()
    public static void set( Object[] array, Object element ) {
      array[ 0 ] = element;
    }
Das element soll einfach an die erste Stelle ins Feld gesetzt werden. Rufen wir die Funktion mit den soeben deklarierten Variablen auf:
    set( stringArray, "It's time for change" );
    set( objectArray, "It's time for change" );
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-Feld speichern, da ein Object ja ein Basistyp ist. Ein Dilemma wäre es jedoch, wenn ein Feld nicht den richtigen Typ bekäme.
    set( stringArray, new StringBuilder("It's time for change") );
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-Feld speichern. Selbst ein new Object() hätte zu einem Problem geführt. Das Typsystem von Java kann diese Spitzfindigkeit nicht prüfen. Erst zur Laufzeit ist ein Test mit dem bitteren Ergebnis einer ArrayStoreException möglich. ### 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