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.3 Privatsphäre und Sichtbarkeit  downtop

Innerhalb einer Klasse sind alle Funktionen und Attribute für die Methoden sichtbar. Damit die Daten einer Klasse vor externem Zugriff geschützt sind und Methoden nicht von außen aufgerufen werden können, verbietet das Schlüsselwort private allen von außen zugreifenden Klassen den Zugriff.

In der UML werden private Eigenschaften mit einem führenden Minus gekennzeichnet.

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

Abbildung 6.3   Ein privates Attribut pass


Beispiel   Eine Klasse Password mit dem privaten Attribut pass

Listing 6.6   PassDemo.java, Teil 1

class Password
{
    private String pass = "";
    void setPassword( String oldpass, String newpass )
  {
    if ( oldpass != null && oldpass.equals(pass) )
    {
      pass = newpass;
      System.out.println( "Passwort gesetzt." );
    }
    else
      System.out.println( "Passwort konnte nicht gesetzt werden." );
  }
}

Wir sehen, dass öffentliche Objektmethoden ganz selbstverständlich auf das private-Element zugreifen können.

Eine Klasse PassDemo will nun auf das Passwort von außen zugreifen.

Listing 6.7   PassDemo.java, Teil 2

public class PassDemo
{
  public static void main( String[] args )
  {
    Password pwd = new Password();
    pwd.setPassword( """TeutoburgerWald" );
    pwd.setPassword( "TeutoburgerWald""Doppelkeks" );
    pwd.setPassword( "Dopplerkeks""panic" );
    //    System.out.println( pwd.pass );   // Compilerfehler
  }
}

Die Klasse Password enthält den privaten String pass, und dieser kann nicht referenziert werden. Der Compiler erkennt zur Übersetzungs- beziehungsweise Laufzeit Verstöße und meldet diese.

Eclipse

Bei einem durch die falsche Sichtbarkeit verursachten Fehler bietet Eclipse mit (Strg)+(1) eine Änderung der Sichtbarkeit an.

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

Allerdings wäre es manchmal besser, wenn der Compiler uns nicht verraten würde, dass das Element privat ist, sondern einfach nur melden würde, dass es dieses Element nicht gibt.


Galileo Computing

6.3.1 Wieso nicht freie Methoden und Variablen für alle?  downtop

Private Funktionen und Variablen dienen in erster Linie dazu, den Klassen Modularisierungsmöglichkeiten zu geben, die von außen nicht sichtbar sein müssen. Zwecks Strukturierung werden Teilaufgaben in Funktionen gegliedert, die aber von außen nie alleine aufgerufen werden dürfen. Da die Implementierung versteckt wird und der Programmierer vielleicht nur eine Zugriffsfunktion sieht, wird auch der Terminus »Data Hiding« verwendet. Nehmen wir zum Beispiel ein Radio. Von außen bietet es die Funktionen an(), aus(), lauter() und leiser() an, aber welche physikalischen Vorgänge ein Radio zum Musikspielen bringt, ist eine ganz andere Frage, für die wir uns als gewöhnliche Benutzer eines Radios nicht interessieren. Wer wird schon einem Fremden die Geheimzahl der Kreditkarte geben oder verraten, mir wem er die letzte Nacht verbracht hat?

Dem unerlaubten Zugriff steht der freie Zugriff auf Funktionen und Variablen entgegen. Eingeleitet wird dieser durch das Schlüsselwort public. Damit ist von außen jederzeit ein Zugriff möglich. Wird weder public, protected noch private verwendet, so ist ein Zugriff von außen nur innerhalb des Pakets möglich. Damit können Gruppen von Klassen gebildet werden, die gegenseitig Teile ihres Innenlebens kennen. Von außerhalb des Pakets ist der Zugriff auf diese Teile dann untersagt, analog zu private. Anmerkung zur Disko: Die Disko beschäftigt illegale Mexikanerinnen. Der Gast sieht aber nur, dass er seine Getränke von einer Bedienung bekommt und weiß nicht, in welchem Beschäftigungsverhältnis sie steht.

Der Einsatz von private zeigt sich besonders in Unterklassen, denn wenn in einer Oberklasse Eigenschaften mit private gekennzeichnet werden, ist der Zugriff auf diese Funktionen in der Unterklasse nicht erlaubt. Interessieren diese Informationen auch manche Unterklassen, lassen sich die Methoden und Attribute protected deklarieren. Damit räumt eine Oberklasse den Unterklassen spezielle Privilegien ein. Mit protected sind die Mitglieder einer Klasse für die Unterklassen sichtbar und ebenso im gesamten Paket.


Galileo Computing

6.3.2 Privat ist nicht ganz privat: Es kommt darauf an, wer’s sieht  downtop

Wir wollen sehen, dass es in einem Spezialfall für eine Referenz ref doch möglich ist, auf ein privates Attribut oder eine private Methode des referenzierten Objekts zuzugreifen. Dazu muss dieser Zugriff in der Methode einer Klasse stattfinden, in der die private-Eigenschaft oder -Methode selbst definiert ist. Das sehen wir am besten an einem Beispiel.

Listing 6.8   WhoMayUsePrivate.java

public class WhoMayUsePrivate
{
  public static void main( String[] args )
  {
    DiskoVergleicher m = new DiskoVergleicher();
    System.out.println( m.compare( new DiskoVergleicher() ) );
  }
}
class DiskoVergleicher
{
  private int priv = (int) Math.random();
  public boolean compare( DiskoVergleicher comp )
  {
    // Zugriff auf ein privates Element über comp.priv
    return   priv == comp.priv;
    }
}

Interessant ist die Zeile unter dem Kommentar. Die Methode compare() der Klasse DiskoVergleicher vergleicht das eigene Attribut priv des aufrufenden Objekts mit dem Attribut des als Parameter übergebenen Objekts comp. An dieser Stelle sehen wir, dass der Zugriff auf comp.priv zulässig ist, obwohl priv privat ist. Dieser Zugriff ist aber erlaubt, da die compare()-Methode in der DiskoVergleicher-Klasse definiert und der Parameter ebenfalls vom Typ DiskoVergleicher ist. Mit Unterklassen funktioniert das schon nicht mehr. Private Attribute und Methoden sind also gegen Angriffe von außerhalb der definierenden Klasse geschützt.


Galileo Computing

6.3.3 Zugriffsmethoden für Attribute definieretoptop

Bisher sind wir davon ausgegangen, dass Attribute eine tolle Sache sind und dass es für den Nutzer eines Objekts nur Vorteile hat, wenn dieser über die Attribute auf den Zustand des Objekts zugreifen kann. Leider ist das nicht immer ohne Probleme möglich, wie die nachfolgenden Fälle zeigen:

1. Bei manchen Variablen gibt es Wertebereiche, die einzuhalten sind. Das Alter einer Person kann nicht kleiner null sein, und Menschen, die älter als zweihundert Jahre sind, werden nur in der Bibel genannt. Wenn wir das Alter privat machen, kann eine Zugriffsfunktion wie setzeAlter(int) mit Hilfe einer Bereichsprüfung nur bestimmte Werte in die Variable eintragen und den Rest ablehnen. Die öffentliche Methode holeAlter() gibt dann Zugriff auf die Variable. Im Fall einer Disko sollte sie sicherlich keine negative Anzahl von Leuten haben.1
       
2. Mit einigen Variablen sind Abhängigkeiten verbunden. Wenn zum Beispiel ein Disko-Objekt eine Anzahl Personen speichert, kann die Disko gleichzeitig eine Wahrheitsvariable Leer deklarieren. Ist keine Person in der Disko, soll die Wahrheitsvariable auf true gesetzt werden. Diese Abhängigkeit lässt sich mit zwei öffentlichen Variablen nicht wirklich erzwingen. Eine Methode setzeAnzahlPersonen(int) kann jedoch bei privaten Attributen diese Konsistenz einhalten.
       
3. Wie das Beispiel mit dem Radio zeigt, gibt es bei Klassen ein Geheimnisprinzip. Obwohl es vorrangig für Methoden gilt, sollte es auch für Variablen gelten. Möchten Entwickler etwa ihr internes Attribut von int auf BigInteger ändern – auch in der Hölle ist die Mega-Disko – und damit beliebig große Ganzzahlen verwalten, hätten wir ein beträchtliches Problem, denn an jeder Stelle des Vorkommens müsste ein Objekt eingesetzt werden. Wollten wir zwei Variablen einführen, ein int, damit die alte, derzeit benutzte Software ohne Änderung auskommt, und ein neues BigInteger, hätten wir ein Konsistenzproblem.
       

Namensgebung für Zugriffsmethoden/Setter und Getter nach der JavaBeans-Spezifikation

Wir sehen an diesen Beispielen, dass es gute Gründe dafür gibt, Attribute zu privatisieren und öffentliche Methoden zum Lesen und Schreiben anzubieten. Weil diese Methoden auf die Attribute zugreifen, nennen sie sich auch Zugriffsmethoden. Für jedes Attribut wird eine Schreib- und Lesemethode definiert, für die es auch ein Namensschema laut JavaBean-Konvention gibt. Lesemethoden beginnen mit get-, Schreibmethoden mit set-. Hinter die Vorsilbe wird der Name des Attributs gesetzt. Bei boolean-Attributen darf es (und muss es in manchen Fällen auch) statt getXXX() (auch) isXXX() heißen. (Da die Programmentwicklung in der Regel mit englischen Bezeichnernamen erfolgt, kommt es nicht zu unschönen Bezeichnernamen à la getAnzahlPersonen() – wie in diesem Tutorial.)

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

Das folgende Beispiel soll eine Disko implementieren, in der die Anzahl der Personen und die Quadratmeterzahl privat und hübsch durch Zugriffsfunktionen abgesichert sind. Im Fall der Quadratmeter gibt es setQuadratmeter() und getQuadratmeter(), im Fall der Personenzahl nur getAnzahlPersonen(), da die Anzahl Personen nur über die Inkrement- Dekrement-Funktion zu modifizieren ist. Eine Konsistenzprüfung soll verhindern, dass es eine Quadratmeteranzahl kleiner null gibt.

Listing 6.9   v3/Disko.java

package v3;
/**
 * Führt Zugriffsmethoden ein.
 */
public class Disko
{
  private int quadratmeter;    // Größe der Disco
  private int anzahlPersonen;  // Anzahl Personen in der Disco
  /**
   * Liefert Anzahl Quadratmeter der Disko.
   *
   * @return Quadratmeter.
   */
  public int getQuadratmeter()
  {
    return quadratmeter;
  }
  /**
   * Setzt die Anzahl Quadratmeter der Disko.
   *
   * @param qm  Neue Größe in Quadratmetern.
   */
  public void setQuadratmeter( int qm )
  {
    if ( qm > 0 )
      quadratmeter = qm;
    else
      System.out.print( "Quadratmeter kann nicht <= 0 sein!" );
  }
  /**
   * Person kommt in die Disko.
   */
  public void personRein()
  {
    anzahlPersonen++;
  }
  /**
   * Person verlässt die Disko.
   */
  public void personRaus()
  {
    if ( anzahlPersonen > 0 )
    anzahlPersonen--;
  }
  /**
   * Liefert Anzahl Personen in der Disco.
   *
   * @return Anzahl Personen.
   */
  public int getAnzahlPersonen()
  {
    return anzahlPersonen;
  }
}

An den Methoden wird eine weitere Konvention sichtbar.

gp  Die Hole-Methode getXXX() besitzt keine Parameterliste. Der Typ vom Rückgabewert bei unserer Methode getQuadratmeter() ist der gleiche wie der von der Variablen quadratmeter.
gp  Die set-Methode hat keinen Rückgabewert, aber genau einen Parameter vom Typ des Attributs.

Wird später bei der Weiterentwicklung des Programms eine Änderung nötig, beispielsweise muss die Quadratmeteranzahl ja keine ganzzahligen Werte annehmen, kann der Typ der internen Variablen geändert werden, und die Welt draußen bekommt davon nichts mit. Lediglich eine kleine Typanpassung muss in der Implementierung von setQuadratmeter() und getQuadratmeter() vorgenommen werden. Sicherlich ist es keine gute Idee, sich bei ungültigen Werten taub zu stellen; besser wäre es, eine Fehlermeldung zu produzieren, was etwa in Form einer Ausnahme (Exception) geschehen könnte.

Eclipse

Eclipse bietet zwei Möglichkeiten, Setter und Getter automatisch zu generieren. Das Kontextmenü unter Source/Generate Getters and Setters … fördert ein Dialogfenster zu Tage, mit dem Eclipse automatisch die setXXX()- und getXXX()-Methoden einfügen kann. Die Attribute, für die eine Zugriffsmethode gewünscht ist, werden selektiert. Die zweite Möglichkeit funktioniert nur für genau ein Attribut. Steht der Cursor auf der Variablen, liefert Refactor/Encapsulate Field … einen Dialog, mit dem Setter und Getter zum einen generiert und zum anderen die direkten Zugriffe auf das Attribut in Funktionsaufrufe umgewandelt werden.




1  Na ja, Zombies laufen ja oft genug in einer Disko rum.

 << 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