Inhaltsverzeichnis

  • Vererbung und abstrakte Klassen

    • Problematik

    • Vererbung

    • Vom UML-Diagramm zum Java-Code

    • Sichtbarkeitsmodifizierer protected und package private

    • Packages

    • Zugriff auf Loewe und Papagei aus anderen Packages

    • Anmerkungen zur Wahl von Sichtbarkeitsmodifikatoren und finale Klassen

    • Gesamter Code dieses Kapitels


Vererbung und abstrakte Klassen

== Vererbung und abstrakte Klassen

Problematik

Nachdem unser Zoohandlungsprogramm bereits die Klasse Loewe kennt, ist es an der Zeit eine weitere Tier-Klasse zu erstellen. Eine neue Lieferung mit Papageien steht an, also müssen wir eine Klasse Papagei erstellen:

=== Problematik Nachdem unser Zoohandlungsprogramm bereits die Klasse _Loewe_ kennt, ist es an der Zeit eine weitere Tier-Klasse zu erstellen. Eine neue Lieferung mit Papageien steht an, also müssen wir eine Klasse _Papagei_ erstellen:

Wie beim Löwen beschränken wir uns auf ein paar wesentliche Eigenschaften und Fähigkeiten:

Auch ein Papagei benötigt die Felder name, gewicht, futterzeit und anzahlBeine. Weitere für den Kunden wichtige Merkmale sind das lieblingswort und die spannweite.

Als Methoden verwenden wir neben laufe(), setAnzahlBeine(int anzahl) und getAnzahlBeine() noch die zusätzlichen Methoden fliege() und sprich(String wort).

Das zugehörige UML-Diagramm siehst du hier (mehrfach anklicken zum Vergleich mit der Klasse Loewe):

Wie beim Löwen beschränken wir uns auf ein paar wesentliche Eigenschaften und Fähigkeiten: Auch ein Papagei benötigt die Felder *name*, *gewicht*, *futterzeit* und *anzahlBeine*. Weitere für den Kunden wichtige Merkmale sind das *lieblingswort* und die *spannweite*. Als _Methoden_ verwenden wir neben `laufe()`, `setAnzahlBeine(int anzahl)` und `getAnzahlBeine()` noch die zusätzlichen Methoden `fliege()` und `sprich(String wort)`. Das zugehörige UML-Diagramm siehst du hier (_mehrfach anklicken zum Vergleich mit der Klasse Loewe_):

Man erkennt deutlich, wie viele Dinge ein Objekt vom Typ Papagei mit einem Objekt vom Typ Loewe gemeinsam hat. Natürlich könnte man eine Klasse Papagei so erstellen, wie wir es bei der Klasse Loewe gemacht hatten, allerdings ist schon auffällig, wie viele Dinge man doppelt eingeben müsste, nämlich name, gewicht, futterzeit, anzahlBeine, laufe(), setAnzahlBeine(int anzahlBeine) und getAnzahlBeine().

Das ist nicht nur aufwändig, sondern im Falle einer notwendigen Fehlerkorrektur auch fehleranfällig: man müsste sich jedes Mal beim Beheben eines Fehlers in der Klasse Loewe daran erinnern, diesen möglicherweise auch in der Klasse Papagei auszubessern.

Zum Glück gibt es für dieses sehr häufig auftretende Szenario eine Lösung:

Man erkennt deutlich, wie viele Dinge ein Objekt vom Typ _Papagei_ mit einem Objekt vom Typ _Loewe_ gemeinsam hat. Natürlich könnte man eine Klasse _Papagei_ so erstellen, wie wir es bei der Klasse _Loewe_ gemacht hatten, allerdings ist schon auffällig, wie viele Dinge man doppelt eingeben müsste, nämlich *name*, *gewicht*, *futterzeit*, *anzahlBeine*, *laufe()*, *setAnzahlBeine(int anzahlBeine)* und *getAnzahlBeine()*. Das ist nicht nur aufwändig, sondern im Falle einer notwendigen Fehlerkorrektur auch fehleranfällig: man müsste sich jedes Mal beim Beheben eines Fehlers in der Klasse _Loewe_ daran erinnern, diesen möglicherweise auch in der Klasse _Papagei_ auszubessern. Zum Glück gibt es für dieses sehr häufig auftretende Szenario eine Lösung:

Vererbung

=== Vererbung

In unserem Beispiel sind beide Tiere, Papagei und Löwe, Tiere. Und jedes Tier hat einen Namen, ein Gewicht, Futterzeiten, usw.

Deshalb führen wir eine Klasse Tier ein, die alle Felder, Methoden und Konstruktoren, die Löwe und Papagei gemeinsam haben, beinhaltet:

In unserem Beispiel sind beide Tiere, Papagei und Löwe, _Tiere_. Und jedes _Tier_ hat einen Namen, ein Gewicht, Futterzeiten, usw. Deshalb führen wir eine Klasse _Tier_ ein, die alle Felder, Methoden und Konstruktoren, die Löwe und Papagei gemeinsam haben, beinhaltet:

Wenn man Java klarmachen könnte, dass die Klassen Loewe und Papagei alle Eigenschaften und Methoden von Tier erben, müsste man nur noch die fehlenden Felder und Methoden in den Klassen Loewe und Papagei komplettieren.

Loewe und Papagei würden dann so aussehen:

Wenn man Java klarmachen könnte, dass die Klassen _Loewe_ und _Papagei_ alle Eigenschaften und Methoden von _Tier_ erben, müsste man nur noch die fehlenden Felder und Methoden in den Klassen _Loewe_ und _Papagei_ komplettieren. _Loewe_ und _Papagei_ würden dann so aussehen:

Das sieht jetzt schon recht übersichtlich aus.

Um im UML zu veranschaulichen, dass Papagei und Loewe etwas von Tier erben, verwendet man einen nicht ausgefüllten Pfeil, der von Papagei zu Tier und von Loewe zu Tier zeigt.

Man kann den Pfeil lesen als "ist": Ein Loewe ist ein Tier und ein Papagei ist auch ein Tier.

Das sieht jetzt schon recht übersichtlich aus. Um im UML zu veranschaulichen, dass _Papagei_ und _Loewe_ etwas von _Tier_ erben, verwendet man einen nicht ausgefüllten Pfeil, der von _Papagei_ zu _Tier_ und von _Loewe_ zu _Tier_ zeigt. Man kann den Pfeil lesen als "ist": Ein _Loewe_ *ist* ein _Tier_ und ein _Papagei_ *ist* auch ein _Tier_.

Loewe ist hier eine Unterklasse bzw. subclass von Tier und umgekehrt ist Tier die Oberklasse bzw. superclass von Loewe und von Papagei.

Eine Klasse kann mehrere Unterklassen, aber nur eine Oberklasse haben.
_Loewe_ ist hier eine *Unterklasse* bzw. *subclass* von _Tier_ und umgekehrt ist _Tier_ die *Oberklasse* bzw. *superclass* von _Loewe_ und von _Papagei_. [NOTE] Eine Klasse kann mehrere Unterklassen, aber nur eine Oberklasse haben.

Vom UML-Diagramm zum Java-Code

Da wir drei Klassen benötigen, müssen wir auch drei Dateien erstellen: Tier.java, Loewe.java und Papagei.java.

Beginnen wir mit Tier.java: der Code sieht eigentlich so aus wie bisher:

=== Vom UML-Diagramm zum Java-Code Da wir drei Klassen benötigen, müssen wir auch drei Dateien erstellen: *Tier.java*, *Loewe.java* und *Papagei.java*. Beginnen wir mit *Tier.java*: der Code sieht eigentlich so aus wie bisher:
public class Tier {

    private String name;
    private double gewicht;
    private String futterzeit;
    private int anzahlBeine;

    public Tier(String name, double gewicht) {
        this.name = name;
        this.gewicht = gewicht;
    }

    public void laufe() {
        System.out.println("Ich laufe");
    }

    public void setAnzahlBeine(int anzahlBeine) {
        this.anzahlBeine = anzahlBeine;
    }

    public int getAnzahlBeine() {
        return anzahlBeine;
    }
}
Java
[source,java,indent=0] ---- public class Tier { private String name; private double gewicht; private String futterzeit; private int anzahlBeine; public Tier(String name, double gewicht) { this.name = name; this.gewicht = gewicht; } public void laufe() { System.out.println("Ich laufe"); } public void setAnzahlBeine(int anzahlBeine) { this.anzahlBeine = anzahlBeine; } public int getAnzahlBeine() { return anzahlBeine; } } ----

Interessanter wird es aber in der Klasse Loewe:

Interessanter wird es aber in der Klasse _Loewe_:
public class Loewe extends Tier{

    private String maehnenShampooMarke;

    public Loewe(String name, double gewicht) {
        super(name,gewicht);
    }

    public int bruelle(int dezibel) {
        System.out.println("Ich brülle mit "+dezibel+ " dB");
        return dezibel;
    }
}
Java
[source,java,indent=0] ---- public class Loewe extends Tier{ private String maehnenShampooMarke; public Loewe(String name, double gewicht) { super(name,gewicht); } public int bruelle(int dezibel) { System.out.println("Ich brülle mit "+dezibel+ " dB"); return dezibel; } } ----

Hier fällt in der ersten Zeile extends Tier auf: damit wird die bestehende Klasse Tier erweitert. Das heißt, dass Loewe im Prinzip alles kann (also die gleichen Felder und Methoden hat), was Tier kann und darüber hinaus noch brüllen kann und eine Mähnen-Shampoo-Marke hat.

Beim Konstruktor fällt noch etwas auf:

Die übergebenen Argumente werden direkt mit super(name,gewicht) verarbeitet. Dabei ruft super den Konstruktor der Oberklasse auf (auf englisch super class), also den Konstruktor Tier(String name, double gewicht).

Hier fällt in der ersten Zeile `extends Tier` auf: damit wird die bestehende Klasse _Tier_ erweitert. Das heißt, dass _Loewe_ im Prinzip alles kann (also die gleichen Felder und Methoden hat), was _Tier_ kann und darüber hinaus noch brüllen kann und eine Mähnen-Shampoo-Marke hat. Beim Konstruktor fällt noch etwas auf: Die übergebenen Argumente werden direkt mit `super(name,gewicht)` verarbeitet. Dabei ruft `super` den Konstruktor der Oberklasse auf (auf englisch *super* class), also den Konstruktor `Tier(String name, double gewicht)`.

Sichtbarkeitsmodifizierer protected und package private

Der Konstruktor der Oberklasse muss nicht aus dem Konstruktor von Loewe aufgerufen werden, ebenso gut könnte man

=== Sichtbarkeitsmodifizierer protected und package private Der Konstruktor der Oberklasse muss nicht aus dem Konstruktor von _Loewe_ aufgerufen werden, ebenso gut könnte man
public Loewe(String name, double gewicht) {
    this.name = name;
    this.gewicht = gewicht;
}
Java
[source,java,indent=0] ---- public Loewe(String name, double gewicht) { this.name = name; this.gewicht = gewicht; } ----

aufrufen. Schließlich wurde weiter oben beschrieben, dass alle Felder und Methoden von Tier im Prinzip auch der Unterklasse Loewe zur Verfügung stehen.

Will man die Klasse Loewe mit dem neuen Konstruktor compilieren, so scheitert dies an der Tatsache, dass this.name einen Fehler verursacht:

aufrufen. Schließlich wurde weiter oben beschrieben, dass alle Felder und Methoden von _Tier_ _im Prinzip_ auch der Unterklasse _Loewe_ zur Verfügung stehen. Will man die Klasse _Loewe_ mit dem neuen Konstruktor compilieren, so scheitert dies an der Tatsache, dass `this.name` einen Fehler verursacht:

name has private access in Tier

`name has private access in Tier`

Stimmt! Wir hatten ja alle Felder mit dem Sichtbarkeitsmodifikator private versehen und das bedeutet, dass nur aus der Klasse Tier heraus darauf zugegriffen werden kann.

Eine mögliche Lösung bestünde darin, dass man in Tier die öffentliche Methode

Stimmt! Wir hatten ja alle Felder mit dem Sichtbarkeitsmodifikator `private` versehen und das bedeutet, dass nur aus der Klasse _Tier_ heraus darauf zugegriffen werden kann. Eine mögliche Lösung bestünde darin, dass man in _Tier_ die öffentliche Methode

In Tier:

In _Tier_:
public void setName(String name) {
     this.name = name;
}
Java
[source,java,indent=0] ---- public void setName(String name) { this.name = name; } ----

einführt und im Konstruktor von Loewe diesen Setter mittels

einführt und im Konstruktor von _Loewe_ diesen Setter mittels

In Loewe:

In _Loewe_:
setName(name);
Java
[source,java,indent=0] ---- setName(name); ----

aufruft.

Das erscheint aber sehr umständlich.

Schneller ginge es, wenn man den Sichtbarkeitsmodifikator von name int Tier auf public setzt. Aber dadurch kann jeder das Feld name von außerhalb direkt verändern, was wir eigentlich vermeiden wollten!

Abhilfe schaffen hier weitere Sichtbarkeitsmodifikatoren:

  • protected String name; in Tier erlaubt allen Unterklassen von Tier, also beispielsweise Loewe und Papagei den direkten Zugriff auf ein Feld. Das wäre genau das, was wir hier benötigen. Im UML-Diagramm wird diese Sichtbarkeit mit einem Hash (#) dargestellt.

  • Eine weitere Variante besteht darin, keinen Sichtbarkeitsmodifikator vor ein Feld zu setzen, also statt private String name; einfach nur String name;. Dadurch wird name package private, ist also nur innerhalb des aktuellen Packages (Paket) sichtbar (im UML-Diagramm durch eine Tilde (~) symbolisiert). Was bedeutet das?

aufruft. Das erscheint aber sehr umständlich. Schneller ginge es, wenn man den Sichtbarkeitsmodifikator von `name` int _Tier_ auf `public` setzt. Aber dadurch kann jeder das Feld `name` von außerhalb direkt verändern, was wir eigentlich vermeiden wollten! Abhilfe schaffen hier weitere Sichtbarkeitsmodifikatoren: * `protected String name;` in _Tier_ erlaubt allen Unterklassen von _Tier_, also beispielsweise _Loewe_ und _Papagei_ den direkten Zugriff auf ein Feld. Das wäre genau das, was wir hier benötigen. Im UML-Diagramm wird diese Sichtbarkeit mit einem Hash (#) dargestellt. * Eine weitere Variante besteht darin, *keinen* Sichtbarkeitsmodifikator vor ein Feld zu setzen, also statt `private String name;` einfach nur `String name;`. Dadurch wird `name` _package private_, ist also nur innerhalb des aktuellen Packages (Paket) sichtbar (im UML-Diagramm durch eine Tilde (~) symbolisiert). Was bedeutet das?

Übersicht über die Sichtbarkeit von Feldern und Methoden

Modifikator Schlüsselwort UML-Symbol sichtbar in Klasse sichtbar im Package sichtbar in jeder Unterklasse Überall sichtbar

private

private

-

package private

ohne

~

protected

protected

#

public

public

+

*Übersicht über die Sichtbarkeit von Feldern und Methoden* |=== |Modifikator|Schlüsselwort| UML-Symbol|sichtbar in Klasse|sichtbar im Package|sichtbar in _jeder_ Unterklasse|Überall sichtbar |private|`private`|-|icon:check[role=green]|icon:times [role=red] |icon:times[role=red] |icon:times[role=red] |package private|_ohne_|~|icon:check[role=green]|icon:check[role=green] |icon:times[role=red] |icon:times[role=red] |protected|`protected`|#|icon:check[role=green]|icon:check[role=green] |icon:check[role=green] |icon:times[role=red] |public|`public`|+|icon:check[role=green]|icon:check[role=green] |icon:check[role=green] |icon:check[role=green] |===

Packages

Man kann mehrere Java-Klassen dem gleichen Package mit dem Namen zoohandlung zuordnen, indem

  1. die jeweiligen Dateien sich alle im Unterordner Zoohandlung befinden und

  2. in der ersten Zeile der jeweiligen Java-Klassen package zoohandlung; steht.

Sollte es mehrere Ebenen von Unterverzeichnissen geben, so dass man beispielsweise die Verzeichnisse firmenImperium/Zoohandlung und firmenImperium/Autokonzern so würden die zugehörigen Dateien in den jeweiligen Verzeichnissen mit den Zeilen

package firmenImperium.zoohandlung;

und

package firmenImperium.autokonzern;

beginnen.

=== Packages Man kann mehrere Java-Klassen dem gleichen *Package* mit dem Namen `zoohandlung` zuordnen, indem . die jeweiligen Dateien sich alle im Unterordner _Zoohandlung_ befinden *und* . in der ersten Zeile der jeweiligen Java-Klassen `package zoohandlung;` steht. [NOTE] ==== Sollte es mehrere Ebenen von Unterverzeichnissen geben, so dass man beispielsweise die Verzeichnisse _firmenImperium/Zoohandlung_ und _firmenImperium/Autokonzern_ so würden die zugehörigen Dateien in den jeweiligen Verzeichnissen mit den Zeilen `package firmenImperium.zoohandlung;` und `package firmenImperium.autokonzern;` beginnen. ====

Alle Felder und Methoden ohne Sichtbarkeitsmodifikator (also ohne private oder public oder protected) sind für andere Klassen desselben Packages sichtbar bzw. erreichbar, von außerhalb des Packages jedoch unsichtbar.

Für unser Beispiel sollen alle unsere Klassen dem gleichen Package zoohandlung zugehören, so dass auch alle Dateien im gleichen Ordner liegen.

Alle Felder und Methoden ohne Sichtbarkeitsmodifikator (also ohne `private` oder `public` oder `protected`) sind für andere Klassen desselben Packages sichtbar bzw. erreichbar, von außerhalb des Packages jedoch unsichtbar. Für unser Beispiel sollen alle unsere Klassen dem gleichen Package `zoohandlung` zugehören, so dass auch alle Dateien im gleichen Ordner liegen.

Übersicht über die Verzeichnisstruktur:

*Übersicht über die Verzeichnisstruktur:*

Durch die neue Zugehörigkeit zu Packages könnten wir den Quellcode von Tier, Loewe und Papagei folgendermaßen umschreiben:

Durch die neue Zugehörigkeit zu Packages könnten wir den Quellcode von _Tier_, _Loewe_ und _Papagei_ folgendermaßen umschreiben:

Zoohandlung/Tier.java:

package zoohandlung;

public abstract class Tier {

    String name;
    double gewicht;
    String futterzeit;
    int anzahlBeine;

    public Tier(){}

    public Tier(String name, double gewicht) {
        this.name = name;
        this.gewicht = gewicht;
    }

    public void laufe() {
        System.out.println("Ich laufe");
    }

    public void setAnzahlBeine(int anzahlBeine) {
        this.anzahlBeine = anzahlBeine;
    }

    public int getAnzahlBeine() {
        return anzahlBeine;
    }

    @Override
    public String toString(){
        return "Ich heiße "+name+" und wiege "+gewicht+" kg";
    }
}
Java
*Zoohandlung/Tier.java:* [source,java,indent=0] ---- package zoohandlung; public abstract class Tier { String name; double gewicht; String futterzeit; int anzahlBeine; public Tier(){} public Tier(String name, double gewicht) { this.name = name; this.gewicht = gewicht; } public void laufe() { System.out.println("Ich laufe"); } public void setAnzahlBeine(int anzahlBeine) { this.anzahlBeine = anzahlBeine; } public int getAnzahlBeine() { return anzahlBeine; } @Override public String toString(){ return "Ich heiße "+name+" und wiege "+gewicht+" kg"; } } ----

Das abstract vor class sorgt dafür, dass man kein Objekt vom Typ Tier erzeugen kann.

Tier t = new Tier();
Java

wird also zu einer Fehlermeldung führen.

Das ist auch tatsächlich nicht sinnvoll, denn was soll in einer Zoohandlung das Tier sein? Wir haben Löwen und Papageien, aber das Tier?

Es handelt sich bei Tier um eine Klasse, die wir benötigen, um daraus durch Vererbung die Klassen Loewe und Papagei abzuleiten. Tier selbst ist also nur für die Strukturierung unserer Klassen notwendig, ein Erstellen eines Objekts aus dieser Klasse soll deshalb nicht möglich sein.

Ein Objekt vom Typ Papagei und Loewe soll aber erstellt werden können. Deswegen sind die Klassen Loewe und Papagei auch nicht abstrakt.

Somit gilt:

Aus einer abstrakten Klasse kann kein Objekt erzeugt werden, wohl aber aus ihren Unterklassen (außer auch diese sind abstrakt).

Das `abstract` vor `class` sorgt dafür, dass man kein Objekt vom Typ _Tier_ erzeugen kann. [source,java] ---- Tier t = new Tier(); ---- wird also zu einer Fehlermeldung führen. Das ist auch tatsächlich nicht sinnvoll, denn was soll in einer Zoohandlung _das Tier_ sein? Wir haben Löwen und Papageien, aber _das Tier_? Es handelt sich bei _Tier_ um eine Klasse, die wir benötigen, um daraus durch Vererbung die Klassen _Loewe_ und _Papagei_ abzuleiten. _Tier_ selbst ist also nur für die Strukturierung unserer Klassen notwendig, ein Erstellen eines Objekts aus dieser Klasse soll deshalb nicht möglich sein. Ein Objekt vom Typ _Papagei_ und _Loewe_ soll aber erstellt werden können. Deswegen sind die Klassen _Loewe_ und _Papagei_ auch nicht abstrakt. Somit gilt: [NOTE] ==== Aus einer abstrakten Klasse kann kein Objekt erzeugt werden, wohl aber aus ihren Unterklassen (außer auch diese sind abstrakt). ====

Zoohandlung/Loewe.java:

package zoohandlung;

public class Loewe extends Tier {

    String maehnenShampooMarke;

    public Loewe(String name, double gewicht) {
        this.name = name;
        this.gewicht = gewicht;
    }

    public Loewe(String name, double gewicht, String msm) {
        this.name = name;
        this.gewicht = gewicht;
        this.maehnenShampooMarke = msm;
    }

    public int bruelle(int dezibel) {
        System.out.println("Ich brülle mit " + " dB");
        return dezibel;
    }
}
Java
*Zoohandlung/Loewe.java:* [source,java,indent=0] ---- package zoohandlung; public class Loewe extends Tier { String maehnenShampooMarke; public Loewe(String name, double gewicht) { this.name = name; this.gewicht = gewicht; } public Loewe(String name, double gewicht, String msm) { this.name = name; this.gewicht = gewicht; this.maehnenShampooMarke = msm; } public int bruelle(int dezibel) { System.out.println("Ich brülle mit " + " dB"); return dezibel; } } ----

Hier haben wir noch einen weiteren Konstruktor hinzugefügt, der neben Name und Gewicht auch noch die Mähnen-Shampoo-Marke akzeptiert.

Konstruktoren wie in diesem Beispiel, also ohne expliziten Aufruf des Konstruktors der Oberklasse (z. B. super(name, gewicht)), sind nur möglich, wenn in der Oberklasse der Standard-Konstruktor, hier also Tier(), definiert ist.

Hier haben wir noch einen weiteren Konstruktor hinzugefügt, der neben Name und Gewicht auch noch die Mähnen-Shampoo-Marke akzeptiert. [WARNING] ==== Konstruktoren wie in diesem Beispiel, also ohne expliziten Aufruf des Konstruktors der Oberklasse (z. B. `super(name, gewicht)`), sind nur möglich, wenn in der Oberklasse der Standard-Konstruktor, hier also `Tier()`, definiert ist. ====

Zoohandlung/Papagei.java:

package zoohandlung;

public class Papagei extends Tier {

    String lieblingswort;
    double spannweite;

    public Papagei(String name, double gewicht, String lieblingswort, double spannweite) {
        super(name,gewicht);
        this.lieblingswort=lieblingswort;
        this.spannweite=spannweite;
    }

    public void fliege() {
        System.out.println("Ich fliege");
    }

    public String sprich(String wort) {
        System.out.println(wort);
        return wort;
    }
}
Java
*Zoohandlung/Papagei.java:* [source,java,indent=0] ---- package zoohandlung; public class Papagei extends Tier { String lieblingswort; double spannweite; public Papagei(String name, double gewicht, String lieblingswort, double spannweite) { super(name,gewicht); this.lieblingswort=lieblingswort; this.spannweite=spannweite; } public void fliege() { System.out.println("Ich fliege"); } public String sprich(String wort) { System.out.println(wort); return wort; } } ----

Zugriff auf Loewe und Papagei aus anderen Packages

Um aus einem anderen Package als zoohandlung heraus auf unser Package zoohandlung und all seine Klassen zuzugreifen, muss man das Package zoohandlung importieren:

=== Zugriff auf _Loewe_ und _Papagei_ aus anderen Packages Um aus einem anderen Package als _zoohandlung_ heraus auf unser Package _zoohandlung_ und all seine Klassen zuzugreifen, muss man das Package _zoohandlung_ importieren:

externesPackage/ExterneMainKlasse.java:

package externesPackage;

import zoohandlung.Papagei;

public class ExterneMainKlasse {
    public static void main(String[] args) {
        Papagei p=new Papagei("Ara", 12.32,"Hallo", 40);
        p.sprich("Hallo");
        p.setAnzahlBeine(2);
        System.out.println(p);
    }
}
Java
*externesPackage/ExterneMainKlasse.java:* [source,java,indent=0] ---- package externesPackage; import zoohandlung.Papagei; public class ExterneMainKlasse { public static void main(String[] args) { Papagei p=new Papagei("Ara", 12.32,"Hallo", 40); p.sprich("Hallo"); p.setAnzahlBeine(2); System.out.println(p); } } ----

Durch die zweite Zeile wird die Klasse Papagei importiert.

Möchte man alle Klassen eines Packages auf einmal importieren, also nicht einzeln

Durch die zweite Zeile wird die Klasse _Papagei_ importiert. Möchte man alle Klassen eines Packages auf einmal importieren, also nicht einzeln
import zoohandlung.Papagei;
import zoohandlung.Loewe;
Java
[source,java,indent=0] ---- import zoohandlung.Papagei; import zoohandlung.Loewe; ----

so kann man dies folgendermaßen erreichen:

so kann man dies folgendermaßen erreichen:
import zoohandlung.*;
Java
[source,java] ---- import zoohandlung.*; ----

Anmerkungen zur Wahl von Sichtbarkeitsmodifikatoren und finale Klassen

Im letzten Kapitel wurde empfohlen, alle Sichtbarkeitsmodifikatoren von Feldern auf private zu setzen. Das ist auch korrekt, wenn es sich um einzelne Klassen handelt, die nicht abgeleitet werden können bzw. nicht in einem Verbund mit anderen Klassen arbeiten sollen.

Sollen Felder oder Methoden wirklich nur für für eine Klasse selbst und deren Unterklassen zur Verfügung stehen, so ist dafür das Schlüsselwort protected die beste Wahl. Allerdings ist zu beachten, dass damit auch abgeleitete Unterklassen außerhalb des Packages Zugriff bekommen.

Sobald mehrere Klassen eines Packages Zugriff auf Felder anderer Klassen dieses Packages haben, ohne dass dieses Klassen untereinander in einem Vererbungsverhältnis stehen ist package private die pragmatischste Lösung.

Die Wahl des Sichtbarkeitsmodifikators ist vom Einsatzszenario abhängig. Sinnvoll ist es aber auf jeden Fall, diesen so restriktiv wie nötig, aber auch so pragmatisch wie möglich zu wählen.

Ist der Sichtbarkeitsmodifikator einese Feldes als protected gewählt, so kann jede von der zugehörigen Klasse abgeleitete Klasse - auch außerhalb des Packages der Oberklasse - auch auf dieses Feld zugreifen:

public class Bergloewe extends Loewe {}

wird von Loewe abgeleitet und muss nicht im gleichen Package wie Loewe liegen. Die Klasse Bergloewe könnte auf ein Feld protected String name in Tier oder Loewe zugreifen und dieses verändern.

Will man ein solches Ableiten von Klassen innerhalb oder außerhalb des eigenen Packages verhindern, so gelingt dies mit dem Schlüsselwort final am Beginn einer Klasse:

final public class Loewe extends Tier { …​ }

verbietet es, eine Unterklasse von Loewe zu bilden.

=== Anmerkungen zur Wahl von Sichtbarkeitsmodifikatoren und finale Klassen Im letzten Kapitel wurde empfohlen, alle Sichtbarkeitsmodifikatoren von Feldern auf private zu setzen. Das ist auch korrekt, wenn es sich um einzelne Klassen handelt, die nicht abgeleitet werden können bzw. nicht in einem Verbund mit anderen Klassen arbeiten sollen. Sollen Felder oder Methoden wirklich nur für für eine Klasse selbst und deren Unterklassen zur Verfügung stehen, so ist dafür das Schlüsselwort `protected` die beste Wahl. Allerdings ist zu beachten, dass damit auch abgeleitete Unterklassen _außerhalb_ des Packages Zugriff bekommen. Sobald mehrere Klassen eines Packages Zugriff auf Felder anderer Klassen dieses Packages haben, ohne dass dieses Klassen untereinander in einem Vererbungsverhältnis stehen ist `package private` die pragmatischste Lösung. ==== Die Wahl des Sichtbarkeitsmodifikators ist vom Einsatzszenario abhängig. Sinnvoll ist es aber auf jeden Fall, diesen so restriktiv wie nötig, aber auch so pragmatisch wie möglich zu wählen. ==== [WARNING] ==== Ist der Sichtbarkeitsmodifikator einese Feldes als `protected` gewählt, so kann jede von der zugehörigen Klasse abgeleitete Klasse - *auch außerhalb des Packages der Oberklasse* - auch auf dieses Feld zugreifen: `public class Bergloewe extends Loewe {}` wird von _Loewe_ abgeleitet und *muss nicht im gleichen Package wie Loewe liegen*. Die Klasse _Bergloewe_ könnte auf ein Feld `protected String name` in _Tier_ oder _Loewe_ zugreifen und dieses verändern. Will man ein solches Ableiten von Klassen innerhalb *oder außerhalb* des eigenen Packages verhindern, so gelingt dies mit dem Schlüsselwort `final` am Beginn einer Klasse: `final public class Loewe extends Tier { ... }` verbietet es, eine Unterklasse von _Loewe_ zu bilden. ====

Gesamter Code dieses Kapitels

Den oben gezeigten Code kannst du nochmals hier einsehen oder als Netbeans-Projekt herunterladen:

=== Gesamter Code dieses Kapitels Den oben gezeigten Code kannst du nochmals <<tags_b,hier einsehen>> oder als Netbeans-Projekt herunterladen:

Dateiname

Zoohandlung.zip

Größe in Bytes

17159

Einfügezeitpunkt

3.2.2018, 11:24:06

Zurück zur Hauptseite

<<tags_@, Zurück zur Hauptseite>>