5.3.1 Pakete (Java Packages) im Detail

Duke mit Paketen

Javapakete (englisch packages) erlauben das Zusammenfassen von Klassen und "Interfaces" (Schnittstellen) in logischen Gruppen.

Das Gruppieren von Klassen und Schnittstellen (Interfaces) in Pakete erlaubt:

  • Bildung von eigenen Namensräumen für Klassen und Schnittstellen (Interfaces)
  • Definierter Export von Klassen, bzw. "Information Hiding" von Klassen die nicht exportiert werden sollen
  • Definierter Import von Klassen von Fremdpaketen in eigene Klassen

Javapakete werden mit dem Schlüsselwort package deklariert und mit Hilfe des Schlüsselworts import importiert.

Jede Javaklasse die zu einem gegebenen Paket gehören soll, muss in einer Quelldatei stehen die mit dem Schlüsselwort package beginnt:

package DemoPackage;
...
class Demonstration1 {
...
}
...
class Demonstration2 {
...
}
Struktur von *.java Quelldateien
  • Datei beginnt optional mit Schlüsselwort package und Paketname
    • Alle Klassen und Interfaces in dieser Datei gehören zum entsprechenden Paket
  • gefolgt von einer oder mehreren Java Klassen oder "Interfaces"
  • genau eine Klasse darf als public deklariert sein.

 

Hinweis: Der Javaübersetzer javac wird für jede einzelne Klasse eine Datei mit dem Klassennamen und der Dateierweiterung .class erzeugen.

Alle Javaklassen die mit dem gleichen Paketnamen versehen sind gehören logisch zusammen. Dies bedeutet, dass diese Klassen typischerweise gemeinsam verwendet werden und das für sie ähnliche Zugriffsrechte gelten.

Alle Klassen für die kein Paket spezifiziert wurde, gehören automatisch dem namenlosen Standardpaket an.

Javapakete und Verzeichnisstrukturen

Bei der Verwendung von Javapaketen ist darauf zu achten, dass das Javalaufzeitsystem (Kommando java) standardmäßig davon ausgeht, dass eine *.class Datei in einem Unterverzeichnis zu finden ist, welches den Paketnamen besitzt. Der Javaübersetzer (javac) ignoriert jedoch in seinen Standardeinstellungen Unterverzeichnisse und speichert Javaklassen im aktuellen Verzeichnis.

Hierzu das folgende Beispiel mit der Datei HelloWorld.java:

package Demo;

class A {
   public static void main(String[] args) {
      B.printHelloWorld();
   }
}

class B {
   public static void printHelloWorld() {
      System.out.println("Hello World!");
   }
}

Es empfiehlt sich im Rahmen des Kurses die .java Dateien in einem Verzeichnis zu pflegen welches den Namen des verwendeten Pakets besitzt. Der Javaübersetzer javac sollte in dem Paketverzeichnis aufgerufen werden. Das Java Laufzeitsystem java sollte im darüber liegenden (allgemeineren) Verzeichnis aufgerufen werden.

Wichtig: In einer Quelldatei dürfen zwar mehrere Klassen vorhanden sein, es darf jedoch nur maximal eine Klasse als public deklariert sein.

Empfehlung: Im Normalfall ist es üblich in einer Datei nur eine Klasse zu implementieren. Die Datei sollte genau den Namen der Klasse tragen.

Steuern der Paketsuche durch "classpath"

Java findet die Standardklassen der SE Edition immer automatisch. Zum Finden von anwenderspezifischen Paketen wird in Java der classpath verwendet. classpath ist eine Umgebungsvariable des Betriebsystems die man setzen kann. Die Javakommandos werden dann diese Variable zum Suchen der Pakete verwenden. Die Art und Weise des Setzens dieser Variable sind Betriebssystem spezifisch. Unter Windows geschieht dies beispielsweise durch das folgende Kommando:

set classpath=.;D:\Paket1;D:\Paket2

Hier wird zuerst im aktuellen Verzeichnis (.) gesucht dann in den Verzeichnissen Paket1 und Paket2 des Laufwerks D.

Die Javakommandos verfügen auch über eine Option -classpath mit der man die Suchpfade für einen bestimmten Aufruf vorgeben kann. Die Syntax der -classpath Option kann man mit Hilfe des Kommandos java -help erfragen:

java -help
...
-classpath <class search path of directories and zip/jar files>
                  A : separated list of directories, JAR archives,
                  and ZIP archives to search for class files. ...

Namensräume

Klassen und Schnittstellen (Interfaces) in einem Paket bilden einen Namensraum. Dies bedeutet:

  • Nur Klassen aus dem gleichen Paket können ohne weiteres verwendet werden
  • Klassen und Interfacenamen innerhalb eines Pakets müssen eindeutig sein
  • Zur Verwendung von Klassen aus anderen Paketen muss
    • die Klasse für die gesamte Quelldatei importiert werden (expliziter Import). Beispiel
      • import Demo.B;
    • oder die Klasse muss mit dem Paket genau bei der Benutzung spezifiziert werden (impliziter Import). Beispiel:
      • Demo.B.printHelloWorld();
  • Es können Klassen mit dem gleichen Namen in unterschiedlichen Paketen vorkommen.

Export von Klassen und Schnittstellen(Interfaces)

Nur das Paket selbst bestimmt welche Klassen exportiert werden. Dies bedeutet, dass die entsprechenden Klassen von Aussen sichtbar und benutzbar sind.

Die Benutzung einer Klasse außerhalb eines Pakets wird duch das Schlüsselwort public vor dem Schlüsselwort class deklariert. Beispiel:

package Demo;

...
 public class A {
...

}

Import von Klassen und Schnittstellen(Interfaces)

Expliziter Import

Der explizite Import von Klassen eines anderen Pakets geschieht durch das Schlüsselwort import gefolgt vom Paketnamen und dem Klassennamen der vom Paketnamen durch den Punktoperator getrennt wird.

package DemoConsumer;
...
import Demo.A;
...
class Consumer {
...
   A.eineMethode(); // Aufruf der Methode eineMethode() der Klassse A
...
}

Neben der Möglichkeit bestimmte Klassen eines Pakets zu importieren, kann man auch alle Klassen eines Pakets importieren. Beispiel:

package DemoConsumer;
...
import Demo.*;
...
class Consumer {
...
   A.eineMethode();   // Aufruf der Methode Demo.A.eineMethode()
   B.andereMethode(); // Aufruf der Methode Demo.B.andereMethode()
...
}

Impliziter Import

Fremde Klassen können auch adhoc verwendet werden indem man den Klassennamen mit vorangestelltem Paketnamen und dem Punktoperator verwendet. Beispiel;

package DemoConsumer;
...
class Consumer {
...
   Demo.A.main(); Aufruf der Methode main() der Klassse A
   Demo.B.printHelloWorld();
...
}

Der implizite Import ist nützlich wenn zwei Klassen mit identischem Namen aus zwei verschiedenen Paketen benutzt und unterschieden werden müssen. Beim freiwilligen, impliziten Import muss man zwischen den folgenden Vor- und Nachteilen abwägen:

  • Vorteil: Man benutzt nur die deklarierte Klasse, die Herkunft der Klasse ist für den Leser des Quellcodes direkt sichtbar
  • Nachteil: Bei jeder Verwendung muss der Paketnamen vorgestellt werden welches den "Textverbrauch" die langen Klassen- und Paketnamen erheblich steigern kann. Eine Programmzeile sollte nicht mehr als 80 Zeichen haben!

Statischer Import

Die bisher vorgestellten Importvarianten erlauben das Importieren einer oder mehrerer Klassen. Beim Benutzen von statischen Attributen, Konstanten und Methoden muss man bei Java jedoch immer den Klassennamen voranstellen (Beispiel: Math.cos() ).

Statische Importe erlauben diese statischen Elemente einer Klasse im Namensraum einer Datei direkt bekannt zu machen. Hiermit kann man auf statische Attribute, Konstanten, Methoden einer Klasse zugreifen wie auf die lokalen Objektelemente einer Klasse. Man erspart sich das explizite Nennen der Klasse.

Implementierung mit explizitem Import:

package test;
import java.lang.Math;
...
class calc {
   public void main(String[] args) {
      float x = Math.PI;
      float y = Math.cos(2*x);
   }
}

Mit Hilfe des des statischen Imports kann man z.Bsp. die Klasse Math importieren um deren statischen Elemente direkt benutzen zu können. Durch den statischen Import ergibt sich für das vorhergehende Beispiel die folgende Implementierung:

package test;
import static java.lang.Math.*;
...
class calc {
   public void main(String[] args) {
      float x = PI;
      float y = cos(2*x);
   }
}

Wichtig:

  • beim statischen Import müssen Klassen immer explizit angegeben werden.
  • Namenskonflikte werden vom Übersetzer beanstandet. Sie müssen dann durch einen impliziten Import aufgelöst werden.

Zugriffsrechte auf Methoden und Attribute

Klassen außerhalb eines Pakets können auf Methoden und Attribute von Klassen eines Pakets nur zugreifen insofern es sich selbst mit den Schlüsselworten public class selbst zum Export freigibt. Falls das der Fall ist kann auf Methoden und Attribute abhängig von der Schlüsselwörten public, protected, private zugregriffen werden. Hierfür gilt:

  • public: Zugriff innerhalb der Klasse, außerhalb der Klasse, inner- und außerhalb des Pakets
  • protected: Zugriff nur durch Klassen und Methoden des eigenen Pakets oder Methoden von Unterklassen
  • private: Zugriff nur innerhalb der Klasse
  • keine Angabe: Zugriff nur innerhalb des Pakets (Im folgenden Diagramm "package" genannt)
Zugriffsrechte abhängig von der Deklaration im Paket
Exportierendes Schlüsselwort Konsument außerhalb des Pakets Konsument innerhalb des Pakets Konsument in einer Unterklasse Konsument innerhalb der Klasse
private - - - Benutzung erlaubt
"kein Schlüsselwort" (package) - Benutzung erlaubt Benutzung erlaubt Benutzung erlaubt
protected Benutzung nur erlaubt wenn Klasse Unterklasse ist Benutzung erlaubt Benutzung erlaubt Benutzung erlaubt
public Benutzung erlaubt Benutzung erlaubt Benutzung erlaubt Benutzung erlaubt

Dies ergibt in Bezug auf die Benutzbarkeit von anderen Klassen die folgenden geschachtelten Mengen:

Reichweite der Benutzbarkeit in Abhängigkeit vom Schlüsselwort

 

 

Anonymous (not verified)

Mon, 11/18/2019 - 00:12

Ich glaube, beim package test unter "statische importe" für statische Importe beim beispiel expliziter import fehlt ein .* hinter import java.lang.Math
:)

Ich glaube der explizite Import is in Ordnung. Er importiert eine Klasse. Testen Sie doch mal den Code in der Entwicklungsumgebung.