Objektorientiert programmieren mit Java (2)

1 Schlüsselwort "static"

1.1 "Statische" Attribute

Bisher:

Neu:

1.2 "Statische" Methoden

Bisher:

Neu:

1.3 "Statische" Initialisierer

Bisher:

Neu:

2 Verwendung eigener entwickelter Klassen

2.1 Eine Anwendung verwaltet Objekte von einer Klasse

"siehe alle bisherigen Testanwendungen"

2.2 Eine weitere Klasse enthält Attribute vom Typ einer schon vorhandenen Klasse

Begriffe:
  • zwischen der "neuen" Klasse und der bereits vorhandenen besteht eine "has-a"-Beziehung
    (d.h. die "neue" Klasse hat/besitzt eine Komponente vom Typ der bereits vorhandenen Klasse)
  • auch Aggregation genannt
Beispiel:
die Klasse Marathon besitzt (u.a.) ein Attribut vom Typ Zeit
Besonderheiten:
  • JEDES Marathon-Objekt benötigt für sein Attribut laufZeit ein eigenes "neues" Zeit-Objekt!
    ansonsten:
    • Änderungen an einem "gemeinsam" genutzten Zeit-Objekt würden sich direkt auf die Marathon-Objekte auswirken Tafelbild einbauen!!!
    Lösung:
    • im Konstruktor der Marathon-Klasse MUSS dem Attribut laufZeit die Adresse eines NEUEN Zeit-Objektes zugewiesen werden
  • in der Implementierung von Marathon-Methoden können bzw. müssen (nur) öffentliche Methoden der Zeit-Klasse genutzt werden,
    (d.h. ein direkter Zugriff auf die Zeit-Attribute ist NICHT möglich.
    Die Marathon-Klasse ist "Außenwelt" - wie eine Anwendung - der Zeit-Klasse)
  • Getter-Methoden sollen "nur" einen LESENDEN Zugriff auf Marathon- Objekt-"Inhalte" ermöglichen;
    insbesondere darf hier NICHT die "Original"-Adresse des laufZeit-Objektes abgeliefert werden, da so Veränderungen im Marathon-Objekt über Setter-Methoden der Zeit-Klasse möglich würden
    Lösung:
    • die getLaufZeit()-Methode liefert die Adresse eines NEUEN Zeit-Objektes ab, welches mit den Informationen des laufZeit-Objektes initialisiert wird (d.h. es wird eine Kopie des laufZeit-Objektes abgeliefert!)

2.3 Eine neue Klasse erweitert eine schon vorhandene Klasse

Begriffe:
  • zwischen der "neuen" Klasse und der bereits vorhandenen besteht eine "is-a"-Beziehung
    (d.h. die "neue" Klasse ist eine erweiterte/spezielle Ausprägung der schon vorhandenen Klasse)
  • diese Beziehung wird auch Vererbungs- oder Ableitungs-Beziehung genannt
Beispiel:
die Klasse GenauereZeit erweitert die Klasse Zeit um ein zusätzliches Attribut milliSekunden
Vorgehensweise:
  1. Klassenkopf muss erweitert werden:
    public class NeueKlasse extends VorhandeneKlasse
    {
        ...
    }

    Wirkung: die neue Klasse "erbt" alle öffentlichen Methoden UND auch die privaten Attribute, die allerdings NICHT direkt ansprechbar sind!

  2. anschließend die ZUSÄTZLICHEN Attribute definieren
  3. Konstruktor-Methoden definieren
  4. Akzessor-Methoden definieren
  5. Ausgabe-Methoden definieren
Genauere Zeit
Besonderheiten:
  • Implementierung von Konstruktoren:
    • die geerbten Attribute können NICHT direkt initialisiert werden, vielmehr muss ein (passender) Konstruktor der Elternklasse mit der Initialisierung beauftragt werden.
    • der Aufruf eines solchen Elternklassen-Konstruktors erfolgt in der Form:
      super(Argumentliste);
      über die Argumentliste wird gesteuert, WELCHER Konstruktor aufgerufen werden soll;
      dies muss die 1. Anweisung in der Konstruktor-Definition(der Kind-Klasse) sein!
    • in der Implementierung des Copy-Konstruktors (der Kind-Klasse) muss typischerweise der Copy-Konstruktor der Eltern-Klasse aufgerufen werden;
      bei diesem Aufruf darf als Argument die Adresse eines Kind-Klassen-Objektes angegeben werden
      allg.:
      public KindKlasse(Kindklasse p)
      {
          super(p); //ruft den Copy-Konstruktor der Elternklasse auf,
                    //der einen Parameter vom Typ der Elterklasse besitzt
          ...
      }
      verallgemeinert: einer Elternklassenreferenz darf AUCH die Adresse eines Kindklassen-Objektes zugewiesen werden.
    • die neu hinzugekommenen Attribute werden wie üblich belegt ...
  • Implementierung der Akzessoren:
    • die Akzessoren für die von der Elternklasse geerbten Attribute können direkt (auch über Kindklassen-Objekte) genutzt werden!
    • in der Kindklasse müssen also "nur" Akzessoren für die neu hinzugekommenen Attribute definiert werden
  • Implementierung weiterer Methoden:
    • speziell: toString()-Methode:

      wird automatisch (falls benötigt) aufgerufen und besitzt einen vorgeschriebenen Methoden-Kopf

      in einer Kindklasse muss also eine Methode mit DEMSELBEN Methoden-Kopf "neu" definiert werden, wie eine von der Elternklasse geerbte Methode

      die Kindklassen-Methode überschreibt die geerbte Elternklassen-Methode

      auf diese Art und Weise "verdeckt" die Kindklass die von der Elternklasse geerbte Methode, somit wird DIESE Methode aufgerufen, wenn das aktuelle Objekt ein Kindklasse-Objekt ist

      DENNOCH kann für die Implementierung der Kindklassen-Methode die geerbte Elternklasse-Methode genutzt werden: durch Qualifizieren mit der super-Referenz z.B.:

      public String toString()
      {
          super.toString();
      }
    • allgemein:

      für die Implementierung von Kindklasse-Methode SOLLTE (soweit sinnvoll) auf geerbte Elternklasse-Methoden zurückgegriffen werden

      dabei muss gegebenenfalls wieder die super-Referenz eingesetzt werden

      auf die hinzugekommenen Attribute kann direkt zugegriffen werden, der Zugriff auf die geerbten Attribute ist NUR über die geerbten (public) Methoden möglich

3 Arbeiten mit Basisklassen-Referenzen

3.1 Hintergrund

3.2 Methodenaufrufe über Basisklassen-Referenzen

Beispiel:
in einer Anwendung zum "Mitarbeiter"-Szenario werden in einem Array verschiedene konkrete Mitarbeiter-Objekte "zusammengefasst"
Mitarbeiter[] alle = new Mitarbeiter[7];
//anschließend Wertzuweisung für JEDES Array-Element
//mit Adressen von Objekten abgeleiteter Klassen
Aufruf der display() bzw. toString() Methode über Mitarbeiter -Referenzen in einer foreach Schleife:
Zur Laufzeit:
  • es wird NICHT die jeweilige Methode der Basisklasse Mitarbeiter, sondern die jeweils zum aktuellen (abgeleiteten) Objekt gehörende geerbte bzw. überschriebene Methode aufgerufen ("Polymorphie")
Zur Übersetzungszeit:
  • der Compiler "sieht" in der Basisklasse Mitarbeiter beide Methoden und ist damit zufrieden
Aufruf der toString() bzw. display() -Methode über Object - Referenzen in einer foreach Schleife:
Abänderung des Beispiels:
Object[] alle = new Mitarbeiter[7]; //Besonderheit wegen Object!
//...
for (Object o : alle)
    ...
Aufruf der toString() -Methode über eine Object -Referenz (innerhalb der foreach Schleife) problemlos!):
Zur Übersetzungszeit:
  • auch in der Klasse Object gibt es eine toString()-Methode
Zur Laufzeit:
  • wegen "Polymorphie" wird die jeweils passende toString()-Methode einer abgeleiteten Klasse aufgerufen
Aufruf der display() -Methode über eine Object -Referenz:
Zur Übersetzungszeit:
  • da es in der Klasse Object KEINE display()-Methode gibt, weigert sich der Compiler, einen entsprechenden Aufruf zuzulassen!
    um den Compiler zu beruhigen, können wir einen EXPLIZITEN Down-Cast nach Mitarbeiter vornehmen;
    Voraussetzung: ALLE Array-Elemente verweisen auf "Mitarbeiter"-Objekte (konkrete aus abgeleiteten Klassen)
    Umsetzung des Down-Casts:
entweder:
									
for (Object o : alle)
{
    Mitarbeiter m = (Mitarbeiter) o;
    o.display();
}
oder (zusammengefasst)

for (Object o : alle)
    ((Mitarbeiter) o).display();
Ergänzung:
  • Um sicher zu gehen, dass der Down-Cast vom Typ Object in den Typ Mitarbeiter problemlos funktioniert, kann (vorab) der instanceof-Operator eingesetzt werden;
allgemein:
Referenzvariable instanceof Klassenname
liefert true falls das Objekt, auf das die Referenzvariable verweist, problemlos einer Variablen vom Typ der Klasse zugewiesen werden kann ("Objekt ist eine Instanz der Klasse"), ansonsten false
konkret:
for (Object o : alle)
    if (o instanceof Mitarbeiter)
        ((Mitarbeiter)o).display();
					
Zur Laufzeit:
wegen "Polymorphie" auch hier Aufruf der passenden display() -Methode einer abgeleiteten Klasse
Aufruf von Methoden, für die es in der Basisklasse keine (fachlich) sinnvollen Implementierungen gibt:
  • z.B.: die getVerdienst()-Methode ist zwar in allen abgeleiteten Klassen sinnvoll implementiert, in der Basisklasse liegen aber keine Informationen zur Verdienstberechnung vor.
  • Lösung:
    1. Dennoch (auch) in der Basisklasse diese Methode definieren, um den Compiler zu beruhigen...
    2. dem Compiler reicht es, wenn er nicht die Definition, sondern lediglich eine Deklaration (Beschreibung) dieser Methode in der Basisklasse "sieht";
      Hierbei wird auf Angaben im Methoden-Block verzichtet und stattdessen "nur" ein Strichpunkt gesetzt;
      Außerdem muss dem Compiler mitgeteilt werden, dass die Methode eine "abstrakte" Methode ist... (siehe Schlüsselwort abstrat)

4 Das Schlüsselwort abstract

4.1 Abstrakte Methoden

4.1.1 Syntax

public abstract Ergebnistyp Methodenname(Parameterliste);
			

Beispiel: innerhalb der Basisklasse Mitarbeiter

public abstract double getVerdienst();
			

4.1.2 Konsequenzen

z.B.: in Angestellter:
public double getVerdienst()
{
    return gehalt;
}

4.2 Abstrakte Klassen

4.2.1 Syntax

public abstract class Klassenname

4.2.2 Beispiel

public abstract class Mitarbeiter
{
    ...
}

Konsequenz: eine abstrakte Klasse ist NICHT instantiierbar

5 Das Schlüsselwort final

5.1 5.1 final Attribute

5.1.1 Syntax

Zugriffsberechtigung final Datentyp Bezeichner;
bzw.
Zugriffsberechtigung final Datentyp Attributname = Wert;
bzw.
Zugriffsberechtigung final static Datentyp Klassenattributname;
bzw.
Zugriffsberechtigung final static Datentyp Klassenattributname = Wert;

5.1.2 Konsequenzen

5.1.3 Beispiel

public class Demo
{
    ...
    private final int NR;
    private final static int MAXNR = 5000;
    ...
    public Demo()
    {
        NR = 123;
    }
    ...
}

"Konstanten" werden in Java üblicherweise in Großbuchstaben benannt!

5.2 final Klassen

5.2.1 Syntax

Zugriffsberechtigung final class Klassenname

Konsequenz: final Klassen sind NICHT ableitbar

die meisten "vordefinierten" Java-Klassen sind final (z.B. String)

5.2.2 Beispiel

public final class Demo2
{
    ...
}

5.3 final Methoden

5.3.1 Syntax

Zugriffsberechtigung final Ergebnistyp Methodenname(Parameterliste)
{
    ...
}

Konsequenz: final Methoden können in abgeleiteten Klassen NICHT überschrieben werden!

5.3.2 Beispiel

public class Demo3
{
    ...
    public final void nurSoUndNichtAnders()
    {
        ...
    }
    ...
}

auch final-Klassenmethoden sind erlaubt

6 "Objektorientierte" Operatoren

6.1 Der Punkt Operator

6.1.1 Syntax

6.1.2 Beispiel

A a = ...;  a.methode() ...
              a.attribut ...
innerhalb einer Methode  this.attribut ...        super.methode() ...
                           this.methode() ...       super.attribut ...
new A().methode()!
m1().m2()

6.2 instanceof Operator

6.2.1 Syntax

6.2.2 Beispiel

A a = ...;
a instanceof Klassenname

7 Arbeiten mit Interfaces

7.1 Wesen

7.2 Syntax: für die Definition eines Interface

Zugriffsberechtigung interface Interfacename
{
    ... Deklaration öffentlicher abstrakter Methoden
    ... optional: Definition öffentlicher Klassen-Konstanten
}

7.2.1 Beispiel

public interface IBsp1
{
    public abstract int im1();
    int im2(); // automatisch public abstract
}
public interface IBsp2
{
    ...
}

7.3 Syntax: für die Zuordnung eines Interface in einer Klassendefinition

Zugriffsberechtigung class Klassenname implements Interfacename
{
    ...
}
Zugriffsberechtigung class NeueKlasse extends VorhandeneKlasse implements Interfacename1,
	                                                                  Interfacename2
			

7.3.1 Beispiel

public class A implements IBsp1
{
    ...
}

public class B extends A implements IBsp2
{
    ...
}
interface
In einer Anwendung dann möglich: o = new A();
Object O;                        a = new A();
A a;                             i1 =  new A();
B b;                             -----------
IBsp1;                           o = new B();
IBsp2;                           a = new B();
                                 b = new B();
                                 i1 = new B();
                                 i2 = new B();

7.4 Vergleich Interfaces - abstrakte Klassen

Interface:
  • KEINE Methodendefinitionen, sondern -deklarationen
  • eine Klasse kann mehrere Interfaces implementieren
  • kann auch Klassen-Konstanten enthalten (nur solche Attribute)
  • als Basisklassen-Referenztypen geeignet
abstrakte Klassen:
  • KANN Methodendefinitionen enthalten
  • eine Klasse kann nur von 1 Klasse "erben"
  • können beliebige Klassen- und/oder Instanz-Variablen bzw. -Konstanten enthalten
  • (ebenfalls) als Basisklassen-Referenztypen geeignet

7.5 instanceof und Interfaces

7.5.1 Syntax

Referenzvariable instanceof Interfacename
			

Ergebnis:

liefert (hier) true , wenn die Klasse, der das Objekt, dessen Adresse im linken Operanden angegeben ist, angehört, das Interface, dessen Name im rechten Operanden steht, implementiert hat (direkt oder indirekt!)

7.6 Zusammenhänge Klassen und Interfaces

7.7 das vordefinierte Interface Comparable

7.7.1 Wesen

7.7.2 Beispiel

das Mitarbeiter -Klassensystem soll erweitert werden, so dass Mitarbeiter auf der Basis ihres Verdienstes vergleichbar sind

8 Überschreiben und Überladen von Methoden

8.1 Überschreiben

8.2 Überladen