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

 << zurück
Java ist auch eine Insel von Christian Ullenboom
Programmieren für die Java 2-Plattform in der Version 5
Java ist auch eine Insel

Java ist auch eine Insel
5., akt. und erw. Auflage
1454 S., mit CD, 49,90 Euro
Galileo Computing
ISBN 3-89842-747-1
gp Kapitel 6 Eigene Klassen schreiben
  gp 6.1 Eigene Klassen definieren
    gp 6.1.1 Methodenaufrufe und Nebeneffekte
    gp 6.1.2 Argumentübergabe mit Referenzen
    gp 6.1.3 Die this-Referenz
    gp 6.1.4 Überdeckte Objektvariablen nutzen
  gp 6.2 Assoziationen zwischen Objekten
    gp 6.2.1 Gegenseitige Abhängigkeiten von Klassen
  gp 6.3 Privatsphäre und Sichtbarkeit
    gp 6.3.1 Wieso nicht freie Methoden und Variablen für alle?
    gp 6.3.2 Privat ist nicht ganz privat: Es kommt darauf an, wer’s sieht
    gp 6.3.3 Zugriffsmethoden für Attribute definieren
  gp 6.4 Statische Methoden und statische Attribute
    gp 6.4.1 Warum statische Eigenschaften sinnvoll sind
    gp 6.4.2 Statische Eigenschaften mit static
    gp 6.4.3 Statische Eigenschaften über Referenzen nutzen?
    gp 6.4.4 Warum die Groß- und Kleinschreibung wichtig ist
    gp 6.4.5 Statische Eigenschaften und Objekteigenschaften
    gp 6.4.6 Statische Variablen zum Datenaustausch
    gp 6.4.7 Statische Blöcke als Klasseninitialisierer
  gp 6.5 Konstanten und Aufzählungen
    gp 6.5.1 Konstanten über öffentliche statische final-Variablen
    gp 6.5.2 Problem mit finalen Klassenvariablen
    gp 6.5.3 Typsicherere Konstanten
    gp 6.5.4 Aufzählungen mit enum in Java 5
    gp 6.5.5 enum-Konstanten in switch
  gp 6.6 Objekte anlegen und zerstören
    gp 6.6.1 Konstruktoren schreiben
    gp 6.6.2 Einen anderen Konstruktor der gleichen Klasse aufrufen
    gp 6.6.3 Initialisierung der Objekt- und Klassenvariablen
    gp 6.6.4 Finale Werte im Konstruktor und in statischen Blöcken setzen
    gp 6.6.5 Exemplarinitialisierer (Instanzinitialisierer)
    gp 6.6.6 Zerstörung eines Objekts durch den Müllaufsammler
    gp 6.6.7 Implizit erzeugte String-Objekte
    gp 6.6.8 Private Konstruktoren, Utility-Klassen, Singleton und Fabriken
  gp 6.7 Vererbung
    gp 6.7.1 Vererbung in Java
    gp 6.7.2 Einfach- und Mehrfachvererbung
    gp 6.7.3 Gebäude modelliert
    gp 6.7.4 Konstruktoren in der Vererbung
    gp 6.7.5 Sichtbarkeit protected
    gp 6.7.6 Das Substitutionsprinzip
    gp 6.7.7 Automatische und explizite Typanpassung
    gp 6.7.8 Typen testen mit dem binären Operator instanceof
    gp 6.7.9 Array-Typen und Kovarianz
    gp 6.7.10 Methoden überschreiben
    gp 6.7.11 Mit super eine Methode der Oberklasse aufrufen
    gp 6.7.12 Kovariante Rückgabetypen
    gp 6.7.13 Finale Klassen
    gp 6.7.14 Nicht überschreibbare Funktionen
    gp 6.7.15 Zusammenfassung zur Sichtbarkeit
    gp 6.7.16 Sichtbarkeit in der UML
    gp 6.7.17 Zusammenfassung: Konstruktoren und Methoden
  gp 6.8 Object ist die Mutter aller Oberklassen
    gp 6.8.1 Klassenobjekte
    gp 6.8.2 Objektidentifikation mit toString()
    gp 6.8.3 Objektgleichheit mit equals() und Identität
    gp 6.8.4 Klonen eines Objekts mit clone()
    gp 6.8.5 Hashcodes
    gp 6.8.6 Aufräumen mit finalize()
    gp 6.8.7 Synchronisation
  gp 6.9 Die Oberklasse gibt Funktionalität vor
    gp 6.9.1 Dynamisches Binden als Beispiel für Polymorphie
    gp 6.9.2 Keine Polymorphie bei privaten, statischen und finalen Methoden
    gp 6.9.3 Polymorphie bei Konstruktoraufrufen
  gp 6.10 Abstrakte Klassen und abstrakte Methoden
    gp 6.10.1 Abstrakte Klassen
    gp 6.10.2 Abstrakte Methoden
  gp 6.11 Schnittstellen
    gp 6.11.1 Ein Polymorphie-Beispiel mit Schnittstellen
    gp 6.11.2 Die Mehrfachvererbung bei Schnittstellen
    gp 6.11.3 Erweitern von Interfaces – Subinterfaces
    gp 6.11.4 Vererbte Konstanten bei Schnittstellen
    gp 6.11.5 Vordefinierte Methoden einer Schnittstelle
    gp 6.11.6 Abstrakte Klassen und Schnittstellen im Vergleich
    gp 6.11.7 CharSequence als Beispiel einer Schnittstelle
    gp 6.11.8 Die Schnittstelle Iterable
  gp 6.12 Innere Klassen
    gp 6.12.1 Statische innere Klassen und Schnittstellen
    gp 6.12.2 Mitglieds- oder Elementklassen
    gp 6.12.3 Lokale Klassen
    gp 6.12.4 Anonyme innere Klassen
    gp 6.12.5 this und Vererbung
    gp 6.12.6 Implementierung einer verketteten Liste
    gp 6.12.7 Funktionszeiger
  gp 6.13 Generische Datentypen
    gp 6.13.1 Einfache Klassenschablonen
    gp 6.13.2 Einfache Methodenschablonen
    gp 6.13.3 Umsetzen der Generics, Typlöschung und Raw-Types
    gp 6.13.4 Einschränken der Typen
    gp 6.13.5 Generics und Vererbung, Invarianz
    gp 6.13.6 Wildcards
  gp 6.14 Die Spezial-Oberklasse Enum
    gp 6.14.1 Methoden auf Enum-Objekten
    gp 6.14.2 enum mit eigenen Konstruktoren und Methoden
  gp 6.15 Dokumentationskommentare mit javaDoc
    gp 6.15.1 Einen Dokumentationskommentar setzen
    gp 6.15.2 Mit javadoc eine Dokumentation erstellen
    gp 6.15.3 HTML-Tags in Dokumentationskommentaren
    gp 6.15.4 Generierte Dateien
    gp 6.15.5 Weitere Dokumentationskommentare
    gp 6.15.6 javaDoc und Doclets
    gp 6.15.7 Veraltete (deprecated) Klassen, Konstruktoren und Methoden


Galileo Computing

6.5 Konstanten und Aufzählungedowntop

In Programmen gibt es Variablen, die sich ändern wie ein Schleifenzähler, aber auch andere, die sich beim Ablauf eines Programms nicht ändern. Dazu gehört etwa die Anzahl der Buchstaben in »Nichts als die Wahrheit« – zumindest in der ersten Ausgabe – oder die Ausmaße einer DIN-A4-Seite. Die Werte sollten nicht wiederholt im Quellcode stehen, sondern über ihren Namen angesprochen werden. Dazu werden Variablen definiert, denen genau der konstante Wert zugewiesen wird; die Konstanten heißen dann symbolische Konstanten.

In Java gibt es zur Definition von Konstanten zwei Möglichkeiten:

gp  In öffentliche statische finale Variablen werden konstante Werte abgelegt.
gp  Mit einem enum-Konstrukt werden Aufzählungen seit Java 5 realisiert.

Galileo Computing

6.5.1 Konstanten über öffentliche statische final-Variablen  downtop

Statische Variablen werden auch verwendet, um symbolische Konstanten zu definieren. Damit die Variablen unveränderlich bleiben, gesellt sich der Modifizierer final hinzu. Dem Compiler wird auf diese Weise mitgeteilt, dass dieser Variablen nur einmal ein Wert zugewiesen werden darf. Für Variablen bedeutet dies: Es sind Konstanten, jeder spätere Schreibzugriff wäre ein Fehler.

Listing 6.14   Genre.java

public class Genre
{
  public static   final   int CHRISTIAN_GANGSTA_RAP  = 0;
  public static   final   int COUNTRY                = 1;
  public static   final   int GANGSTA                = 2;
  public static   final   int JUNGLE                 = 3;
  public static   final   int JPOP                   = 4;
  public static   final   int GOTHIC_TECHNO          = 5;
  public static   final   int POLKA                  = 6;
  public static   final   int PSYCHEDELIC_CLASSIC    = 7;
  public static   final   int TECHNO                 = PSYCHEDELIC_CLASSIC + 1;
}

In der Klasse Genre werden mehrere Konstanten definiert. Für Konstanten ist es günstig, sie relativ zum Vorgänger zu wählen, um das Einfügen in der Mitte zu vereinfachen. Das sehen wir bei der letzten Variable TECHNO. Da im Quellcode das Vorkommen der Zahlen 2, 4, 5 undurchsichtig wäre, sind symbolische Namen zwingend. Stehen dennoch Zahlen ohne öffentliche Bedeutung im Quellcode, so werden sie magische Zahlen (engl. magic numbers) genannt.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 6.4   Die Genre-Klasse in einer alternativen »Eclipse-Darstellung«


Tipp   Es ist eine gute Idee, die Namen von Konstanten durchgehend groß zu schreiben, um ihre Bedeutung hervorzuheben.


Galileo Computing

6.5.2 Problem mit finalen Klassenvariablen  downtop

Finale Klassenvariablen können in der Entwicklung mit einer größeren Anzahl von Klassen zu einem Problem werden. Das liegt an der Eigenschaft der finalen Werte, dass sie sich nicht ändern können und sich daher sicher an der Stelle einsetzen lassen, wo sie gebraucht werden. Ein Beispiel:

class Finanz
{
  public static final int MWST = 16;
}

Greift eine andere Klasse auf die Variable MWST zu, ist das im Quellcode nicht als direkter Variablenzugriff Finanz.MWST kodiert, sondern das Literal 16 wird direkt an der Aufrufstelle eingesetzt. Das ist eine Optimierung des Compilers, die er laut Java-Definition machen kann.

Das ist zwar nett, bringt aber gewaltige Probleme mit sich, etwa dann, wenn sich die Konstante einmal ä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 Bezug nehmenden Klassen neu zu übersetzen und sich am besten anzugewöhnen, bei einer Änderung einer Konstante gleich alles neu zu compilieren. Ein anderer Weg transformiert die finale Variable in eine später initialisierte Form:

class Finanz
{
  public static final int MWST = Integer.valueOf( 16 );
}

Die Initialisierung findet in jedem Konstruktor statt, und die Konstante mit dem Literal 16 ist zunächst einmal verschwunden. Der Compiler wird also beim Zugriff auf Finanz.MWST nicht die 16 vorfinden und daher das Literal an den Aufrufstellen nicht einbauen können. In der Klassendatei wird der Bezug Finanz.MWST weiterhin zu finden sein, und eine Änderung der Konstante erzwingt keine neue Übersetzung der Klassen.

Im Übrigen gibt es die Fabrikmethode valueOf() erst seit Java 5; vorher war zu schreiben:

public static final int MWST = new Integer( 16 ).intValue();

Galileo Computing

6.5.3 Typsicherere Konstanten  downtop

Konstanten sind eine wertvolle Möglichkeit, den Quellcode aussagekräftiger zu machen. Der herkömmliche Weg geht über Ganzzahl-Konstanten:

public static final int KONSTANTE1 = 0;
public static final int KONSTANTE2 = 1;
public static final int KONSTANTE3 = 2;

Dieser Weg bringt den Nachteil mit sich, dass die Konstanten nicht unbedingt von jedem angewendet werden müssen und ein Programmierer die Zahlen oder Zeichenketten eventuell direkt einsetzt. Dieses Problem ergibt sich zum Beispiel dann, wenn ein Font-Objekt für die grafische Oberfläche angelegt werden soll, aber unser Gedächtnis versagt, in welcher Reihenfolge die Parameter zu füllen sind. Ein Fallbeispiel:

Font f = new Font( "Dialog"12Font.BOLD );

Leider ist dies falsch, denn die Argumente für die Zeichensatzgröße und den Schriftstil wurden vertauscht. Das Problem ist, dass die Konstanten nur Namen für Werte eines frei zugänglichen Grundtyps sind und nur der Wert an die Funktion übergeben wird. Niemand kann verbieten, dass die Werte direkt eingetragen werden. Das führt dann zu Fehlern wie im oberen Fall. In diesem ist 12 die Ganzzahl für den Schriftstil, obwohl es dafür nur die Werte 0, 1, 2 geben sollte. Mit Zeichenketten als Werten der Konstanten kommen wir der Lösung auch nicht näher.

Eine gute Möglichkeit, von Ganzzahlen wegzukommen, ist, Objekte einer Klasse als Konstanten einzusetzen. Folgendes bietet sich an:

Listing 6.15   TypsicherGenre.java

public final class TypsicherGenre
{
  public static final TypsicherGenre COUNTRY = new TypsicherGenre();
  public static final TypsicherGenre TECHNO  = new TypsicherGenre();
  private static int nextId;
  private int id;
  private TypsicherGenre() // von aussen lassen sich keine weiteren
  {                        // Objekte erzeugen
    id = nextId++;
  }
  public int getOrdinal()
  {
    return id;
  }
}

Die Klasse TypsicherGenre definiert die Konstanten als statische Attribute vom Typ TypsicherGenre. Da die Objekte für die Konstanten aber nur einmal vorliegen, lassen sie sich einfach mit ==, wie im folgenden Beispiel mit func() gezeigt, vergleichen. Die Methode getOrdinal() liefert einen Zahlwert, um später die Reihenfolge der Definitionen nutzen zu können.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Listing 6.16   TypsichereKonstantenDemo.java

public class TypsichereKonstantenDemo
{
  static void func( TypsicherGenre k )
  {
    if ( k == TypsicherGenre.COUNTRY )
      System.out.println( "COUNTRY" );
    else if ( k == TypsicherGenre.TECHNO )
      System.out.println( "TECHNO" );
  }
  public static void main( String[] args )
  {
    func( TypsicherGenre.TECHNO );
  }
}

Galileo Computing

6.5.4 Aufzählungen mit enum in Java 5  downtop

Wie wir gesehen haben, muss etwas Programmieraufwand getrieben werden, um typsichere Aufzählungen in Java zu realisieren – ein einfaches int reicht nicht aus. Seit Java 5 sind richtige Aufzählungen möglich, denn die Sprache nutzt nun das Schlüsselwort enum. In der Schreibweise für Aufzählungen ersetzt das Schlüsselwort enum das Schlüsselwort class und ist auch ähnlich zu nutzen. (Eine enum-Definition verhält sich wie eine Klassendefinition, nur dass keine Unterklassen und Oberklassen möglich sind. Sonst erlaubt enum auch die Definition von Methoden und Variablen.)


Beispiel    Erzeuge Konstanten für die Wochentage.
public enum Wochentage
{
  MONTAG, DIENSTAG, MITTWOCH, DONNERSTAG, FREITAG, SAMSTAG, SONNTAG
}
Die Konstantennamen werden wie üblich groß geschrieben.

Hinter den Elementen stehen einzelne Objekte, die sich – wie alle anderen auch – weiterverarbeiten lassen, das heißt, etwa in eine Datenstruktur einsetzen lassen. Intern erstellt der Compiler normalen Bytecode für eine Klasse Wochentage und mit ihr sieben Objekte. Es gibt »normale« Aufzählungen, die an normale Klassen erinnern, und auch »innere« Aufzählungen, die an innere Klassen erinnern. Mit anderen Worten: »Wochentage« kann auch innerhalb einer anderen Klasse definiert werden.


Galileo Computing

6.5.5 enum-Konstanten in switch  toptop

enum-Konstanten sind in switch-Anweisungen möglich, da sie intern über eine Ganzzahl als Identifizierer verfügen, der vom Compiler für die Aufzählung eingesetzt wird.

Wochentage tage = Wochentage.MONTAG;
switch ( tage )
{
  case SAMSTAG:
  case SONNTAG: System.out.println( "Wochenende. Party!" );
}

Die erzeugten enum-Objekte bekommen standardmäßig eine Reihe von zusätzlichen Eigenschaften. Sie überschreiben sinnvoll toString(), hashCode() und equals() aus Object und implementieren zusätzlich Serializable und Comparable. toString() ist so umgesetzt, dass nicht der Ganzzahlwert zu sehen ist, sondern der Name der Konstanten, etwa sonntag. Aufzählungsobjekte können nicht geklont werden. Zusätzlich erbt jedes Aufzählungs-Objekt von der Spezialklasse Enum, die später erklärt wird.

Statische Imports werden auch von enum sinnvoll genutzt

Dazu ein Beispiel: Nehmen wir eine enum-Klasse, die Konstanten definiert, im Paket musik an:

package musik;
public enum Stil { ROCKPOPTECHNO }

Um jetzt die Konstanten ohne den Aufzählungsnamen zu nutzen, können wir unterschiedliche import-Varianten nehmen, um auf die Konstanten ROCK zuzugreifen.


Import-Anweisung Zugriff
import musik.Stil; Stil.ROCK
import musik.*; Stil.ROCK
import static musik.Stil.*; ROCK

Nehmen wir als zweites Beispiel eine innere Aufzählung hinzu:

package musik;
class Musikstil
{
  public enum Stil { ROCKPOPTECHNO; }
}

Import-Anweisung Zugriff
import musik.Musikstil; Musikstil.Stil.ROCK
import musik.Musikstil.Stil; Stil.ROCK
import static musik.Musikstil.Stil.* ROCK




1  Ein DIN-A4-Blatt ist 29,7 Zentimeter hoch und 21,0 Zentimeter breit.

2  Die Ordnung der Konstanten ist die Reihenfolge, in der sie geschrieben sind.

 << zurück




Copyright © Galileo Press GmbH 2005
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 GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de