Titel   Inhalt   Suchen   Index   DOC  Handbuch der Java-Programmierung, 5. Auflage
 <<    <     >    >>   API  Kapitel 13 - Strukturierung von Java-Programmen

13.2 Pakete



13.2.1 Verwendung von Paketen

Jede Klasse in Java ist Bestandteil eines Pakets. Der vollständige Name einer Klasse besteht aus dem Namen des Pakets, gefolgt von einem Punkt, dem sich der eigentliche Name der Klasse anschließt. Der Name des Pakets selbst kann ebenfalls einen oder mehrere Punkte enthalten.

Damit eine Klasse verwendet werden kann, muss angegeben werden, in welchem Paket sie liegt. Hierzu gibt es zwei unterschiedliche Möglichkeiten:

Die Verwendung voll qualifizierter Namen hat den Nachteil, dass die Klassennamen sehr lang und unhandlich werden. Bequemer ist daher die Anwendung der zweiten Variante, bei der die benötigten Klassen mit Hilfe einer import-Anweisung dem Compiler bekanntgemacht werden.

Die import-Anweisung gibt es in zwei unterschiedlichen Ausprägungen:

Im Gegensatz zu ähnlichen Konstrukten in anderen Sprachen ist die Verwendung der zweiten Variante der import-Anweisung nicht zwangsläufig ineffizienter als die der ersten. In der Sprachspezifikation wird sie als type import on demand bezeichnet, was bedeutet, dass die Klasse erst dann in den angegebenen Paketen gesucht wird, wenn das Programm sie wirklich benötigt. Keinesfalls muss der Compiler beim Parsen der import-Anweisung zwangsläufig alle Klassendateien des angegebenen Pakets in den Hauptspeicher laden oder ähnlich zeit- und speicheraufwändige Dinge machen. Es schadet also im allgemeinen nichts, die zweite Variante der import-Anweisung zu verwenden.

Allerdings kann es zur Vermeidung von Verwechslungen mitunter nötig sein, die erste Variante zu verwenden. Enthalten nämlich zwei oder mehr Pakete, die mit der *-Notation importiert wurden, Klassen mit demselben Namen, würde der Compiler die Übersetzung mit einer Fehlermeldung »mehrdeutige Referenz auf Klasse...« abbrechen. In diesem Fall muss im Programm entweder der qualifizierte Klassenname verwendet oder die gewünschte Variante der Klasse mit einer expliziten import-Anweisung eingebunden werden.

 Warnung 

Seit der J2SE 5.0 gibt es unter dem Stichwort static import eine weitere Variante der import-Anweisung. Weitere Informationen dazu finden Sie in Abschnitt 9.4.

 JDK1.1-6.0 

Der Import von java.lang

In vielen verschiedenen Beispielen in diesem Buch werden Klassennamen (wie beispielsweise String, Thread oder Object) verwendet, ohne dass eine zugehörige import-Anweisung zu erkennen wäre. In diesem Fall entstammen die Klassen dem Paket java.lang.

Dieses Paket wurde von den Entwicklern der Sprache als so wichtig angesehen, dass es bei jedem Compilerlauf automatisch importiert wird. Man kann sich das so vorstellen, als wenn am Anfang jeder Quelldatei implizit die folgende Anweisung stehen würde:

import java.lang.*;

Ein expliziter Import von java.lang ist daher niemals nötig. Alle anderen Pakete müssen jedoch vor ihrer Verwendung importiert werden, wenn auf die Anwendung voll qualifizierter Klassennamen verzichtet werden soll.

Die vordefinierten Pakete im JDK

Während beim Wechsel der Java-Versionen die Sprachspezifikation relativ stabil geblieben ist, hat sich der Umfang der Laufzeitbibliothek um ein Vielfaches erhöht. Dies zeigt sich unter anderem an der gestiegenen Anzahl an vordefinierten Paketen, die mit dem JDK ausgeliefert werden (siehe Tabelle 13.1 und Tabelle 13.2). Sie stieg von 8 Standardpaketen im JDK 1.0 auf 22 im JDK 1.1, 50 im JDK 1.2, 70 im JDK 1.3, 130 im JDK 1.4 und schließlich knapp 200 seit der Java 5 Standard Edition.

Paket Bedeutung
java.applet Applets
java.awt Das Abstract Windowing Toolkit inkl. diverser Unterpakete
java.beans Java Beans
java.io Bildschirm- und Datei-I/O
java.lang Elementare Sprachunterstützung
java.lang.ref Referenz-Objekte
java.lang.reflect Reflection-API
java.math Fließkomma-Arithmetik
java.net Netzwerkunterstützung
java.nio Das seit dem JDK 1.4 vorhandene New I/O Package
java.rmi Remote Method Invocation (RMI)
java.security Security-Dienste
java.sql Datenbankzugriff (JDBC)
java.text Internationalisierung
java.util Diverse Utilities, Collection-Klassen und Datenstrukturen

Tabelle 13.1: Wichtige Standard-Pakete des JDK

Neben den Standardpaketen gibt es seit der Version 1.2 des JDK eine Reihe von Standarderweiterungen, deren Paketname mit javax. beginnt. Sie stellen erweiterte Funktionalitäten in einem oder mehreren .jar-Dateien zur Verfügung und werden typischerweise im Unterverzeichnis lib\ext des JDK installiert. Im Gegensatz zu den Standardpaketen des JDK sind sie nicht unbedingt Bestandteil jedes Java-Entwicklungssystems und müssen nicht auf allen Plattformen zur Verfügung stehen. Sie stellen häufig gebrauchte Erweiterungen zur Verfügung, deren Umfang die reinen Kernbibliotheken um ein Vielfaches übertrifft:

 JDK1.1-6.0 

Paket Bedeutung
javax.accessibility Unterstützung für Braille-Zeilen und ähnliche Ein-/Ausgabegeräte
javax.crypto Kryptografische Erweiterungen
javax.imageio Lesen und Schreiben von Bilddateien
javax.naming Zugriff auf Namens-Services
javax.print Unterstützung zum Drucken
javax.security.auth Authentifizierung und Autorisierung
javax.sound Das Sound-API
javax.swing Das SWING-Toolkit
javax.xml Zugriff auf XML-Dateien

Tabelle 13.2: Wichtige Standarderweiterungen des JDK

Des weiteren sind einige Pakete im JDK enthalten, deren Inhalt von Dritten hergestellt wurde. Beispiele dafür sind die diversen Pakete unterhalb der org.omg-Hierarchie, mit deren Hilfe CORBA-Support zur Verfügung gestellt wird. Oder die Paket-Hierarchien org.xml und org.w3c, die Unterstützung zum Zugriff auf XML-Dateien zur Verfügungv stellen.

13.2.2 Die Bedeutung der Paketnamen

Paketnamen bestehen in Java aus mehreren Komponenten, die durch Punkte voneinander getrennt sind. Neben der Aufgabe, die Paketnamen visuell zu strukturieren, hat die Unterteilung aber noch eine andere, sehr viel wichtigere Bedeutung.

Jeder Teil eines mehrstufigen Paketnamens bezeichnet nämlich ein Unterverzeichnis auf dem Weg zu der gewünschten Klassendatei. Soll beispielsweise eine Klasse aus dem Paket java.awt.image eingebunden werden, sucht es der Java-Compiler im Unterverzeichnis java\awt\image. Soll dagegen eine Klasse aus dem Paket com.sun.image.codec.jpeg geladen werden, wird es im Unterverzeichnis com\sun\image\codec\jpeg gesucht. Interessant ist in diesem Zusammenhang natürlich die Frage, in welchem Verzeichnis der Compiler mit der Suche beginnt. Bei der Antwort darauf muss zwischen den JDKs 1.2, 1.3 und 1.4 und ihren Vorgängerversionen 1.0 und 1.1 unterschieden werden.

Laden von Klassen im JDK 1.0 und 1.1

Wurde keine Umgebungsvariable CLASSPATH angegeben und der Schalter -classpath beim Compiler-Aufruf nicht verwendet, so suchen der Java-Compiler und die übrigen Tools in einem systemspezifischen Installationsverzeichnis (z.B. c:\java1.1.7\lib beim JDK 1.1.7) und zusätzlich im aktuellen Verzeichnis nach den Klassendateien.

Anders als im JDK 1.0 ist es bei einer Standardinstallation des JDK 1.1 unter Windows 95 nicht erforderlich, den CLASSPATH explizit zu setzen. Alle Tools generieren den CLASSPATH implizit aus der Position des bin-Verzeichnisses (z.B. c:\java1.1.7\bin) nach folgendem Schema:

.;[bin]\..\classes;[bin]\..\lib\classes.zip

[bin] steht hier für den Pfad des bin-Verzeichnisses. Nur wenn die Klassendateien in einem anderen Verzeichnis liegen oder Klassendateien in weiteren Verzeichnissen eingebunden werden sollen, muss die CLASSPATH-Variable manuell geändert werden.

Existiert zum Zeitpunkt des Compiler-Aufrufs die Umgebungsvariable CLASSPATH, beginnt der Compiler die Suche nach den eingebundenen Klassen in allen im CLASSPATH angegebenen Verzeichnissen. Die einzelnen Verzeichnisse werden durch ein Semikolon (bzw. einen Doppelpunkt unter UNIX) voneinander getrennt.

Beim Aufruf des Compilers kann der Schalter -classpath, gefolgt von einer Liste von Verzeichnisnamen, übergeben werden. Er hat dieselbe Bedeutung wie die Umgebungsvariable CLASSPATH und definiert die Verzeichnisse, in denen der Compiler nach den .class-Dateien sucht. Der Compiler-Schalter hat dabei Vorrang gegenüber einer möglichen Umgebungsvariablen.

Laden von Klassen seit dem JDK 1.2

Die Verwendung der CLASSPATH-Variable hat immer wieder zu Schwierigkeiten und Missverständnissen geführt. Es ist insbesondere häufig passiert, dass durch falsches Setzen der Umgebungsvariable die Systemklassen selbst nicht mehr gefunden werden konnten und auf diese Weise das gesamte Laufzeitsystem unbenutzbar wurde.

Seit dem JDK 1.2 wurde daher die Bedeutung der CLASSPATH-Umgebungsvariable dahingehend verändert, dass sie nur noch zur Suche der benutzerspezifischen Klassen verwendet wird. Alle Standardpakete und Standarderweiterungen (beide zusammen werden seit dem JDK 1.2 Bootstrap Classes genannt) werden dagegen unabhängig vom CLASSPATH mit Hilfe der auf das Installationsverzeichnis verweisenden Systemeigenschaft sun.boot.class.path gefunden. Sie wird bei der JDK-Installation automatisch gesetzt und sollte später nicht mehr verändert werden. Der CLASSPATH braucht also nur noch dann explizit gesetzt zu werden, wenn benutzerspezifische Klassen vorhanden sind, die nicht im aktuellen Verzeichnis liegen (letzteres wird ebenfalls automatisch durchsucht).

Falls Sie zuvor eine ältere Versionen des Java Development Kit auf Ihrem Rechner installiert hatten, überprüfen Sie bitte nach der Installation die Umgebungsvariablen. Sorgen Sie dafür, dass nicht ein veralteter CLASSPATH auf Verzeichnisse oder Dateien verweist, aus denen das Laufzeitsystem versehentlich unbrauchbare Klassendateien laden würde.

 Warnung 

Die Dateien classes.zip und rt.jar

Im Installationsverzeichnis von JDKs der Versionen 1.0 und 1.1 findet man meist eine Datei classes.zip anstelle der erwähnten Unterverzeichnisse mit den Klassendateien. Aus Gründen der Performance beim Übersetzen haben sich die Entwickler entschlossen, alle Standardklassendateien in diesem Archiv abzulegen, um einen schnelleren Lesezugriff auf sie zu ermöglichen.

Die Datei classes.zip sollte nicht ausgepackt werden, denn der Compiler verwendet sie in archivierter Form. Obwohl beim Einpacken der Klassen auf die Komprimierung verzichtet wurde, bietet diese Form der Archivierung den Vorteil, dass viele kleine Dateien in einer einzigen großen zusammengefasst werden. Zeitaufwändige Verzeichniszugriffe und -wechsel können so entfallen, wenn der Compiler nach Klassennamen suchen muss oder den Inhalt einer Klassendatei lesen will.

 Warnung 

Um dem Compiler diese Art der Speicherung der Klassendateien bekanntzumachen, muss in der CLASSPATH-Umgebungsvariable nicht nur das Verzeichnis, sondern auch der Name der .zip-Datei angegeben werden, z.B.:

CLASSPATH=.;c:\java\LIB\CLASSES.ZIP

Seit dem JDK 1.2 gibt es die Datei classes.zip nicht mehr. Die Klassenbibliotheken liegen nun als .jar-Dateien (z.B. rt.jar ) vor und befinden sich im Unterverzeichnis jre\lib der JDK-Installation. Wie zuvor erwähnt, werden sie unabhängig vom Inhalt der CLASSPATH-Umgebungsvariable gefunden. Weitere Informationen zu jar-Dateien finden Sie in Kapitel 51.

 JDK1.1-6.0 

Umgekehrte Domain-Namen

Die Entwickler von Java haben sich einen Mechanismus ausgedacht, um auch bei sehr großen Projekten, an denen möglicherweise viele Entwickler beteiligt sind, Namenskollisionen zwischen den beteiligten Klassen und Paketen zu vermeiden. Auch die Verwendung einer großen Anzahl unterschiedlicher Klassenbibliotheken von verschiedenen Herstellern sollte möglich sein, ohne dass Namensüberschneidungen dies schon im Keim ersticken.

Um diese Probleme zu lösen, hat sich das Java-Team eine Vorgehensweise zur Vergabe von Paketnamen überlegt, die an das Domain-Namen-System bei Internet-Adressen angelehnt ist. Danach sollte jeder Anbieter seine Pakete entsprechend dem eigenen Domain-Namen benennen, dabei allerdings die Namensbestandteile in umgekehrter Reihenfolge verwenden.

So sollten beispielsweise die Klassen der Firma SUN, deren Domain-Name sun.com ist, in einem Paket com.sun oder in darunter befindlichen Subpaketen liegen. Da die Domain-Namen weltweit eindeutig sind, werden Namenskollisionen zwischen Paketen unterschiedlicher Hersteller auf diese Weise von vornherein vermieden. Beispiele für derartige Paketnamen liefert die Standardinstallation gleich mit. So stellt das JDK 1.2 diverse Pakete com.sun.* zur Verfügung. Sie gehören nicht zum Standard-Sprachumfang eines Java-Entwicklungssystems, sondern werden von SUN als eigenständige Erweiterung mit dem JDK ausgeliefert.

Unterhalb des Basispakets können Unterpakete beliebig geschachtelt werden. Die Namensvergabe liegt dabei in der Entscheidung des Unternehmens. Gibt es beispielsweise die Abteilungen is, se und tx in einem Unternehmen mit der Domain tl.de, kann es sinnvoll sein, diese Abteilungsnamen auch als Unterprojekte zu verwenden. Die von diesen Abteilungen erstellten Klassen würden dann in den Paketen de.tl.is, de.tl.se und de.tl.tx liegen.

 Hinweis 

Der Vollständigkeit halber sollte man anmerken, dass sich das hier beschriebene System in der Praxis noch nicht komplett durchgesetzt hat und insbesondere die Klassen der java.*- und javax.*-Hierarchie Ausnahmen bilden. Es wird mit der zunehmenden Anzahl allgemein verfügbarer Pakete jedoch an Bedeutung gewinnen. Manche Entwickler verwenden ein leicht abgewandeltes Schema, bei dem nur die Top-Level-Domain ausgelassen wird. Die Paketnamen beginnen in diesem Fall nicht mit org oder com, sondern direkt mit dem zweiten Teil des Domain-Namens (oder einem ähnlichen herstellerspezifischen Kürzel). Dadurch werden sie etwas kürzer und sind leichter zu handhaben.

13.2.3 Einbinden zusätzlicher Pakete

In der Praxis wird man neben der Klassenbibliothek des JDK häufig zusätzliche Pakete von Drittanbietern verwenden wollen. Um den Klassenpfad nicht unnötig lang werden zu lassen, empfiehlt es sich, die Pakete in einem gemeinsamen Verzeichnis abzulegen. Falls sich alle Entwickler von Libraries an das oben besprochene Schema zur Vergabe von Paketnamen halten, kann es keine Überschneidungen geben.

Als Beispiel wollen wir einige Java-Klassen des Autors (zu finden als Datei gkjava.zip unter http://www.gkrueger.com oder auf der DVD zum Buch im Verzeichnis \misc) und die Utilities der »ACME-Labs« von Jef Poskanzer (http://www.acme.com) installieren:

Alle Libraries, die sich an diese Konventionen halten, können in der beschriebenen Weise installiert werden. Probleme gibt es nur, wenn ein Anbieter seine Klassendateien nicht in Paketen ablegt. In diesem Fall müssten die Klassendateien in das aktuelle Verzeichnis oder nach c:\classes kopiert werden. Das würde bei Klassendateien von mehr als einem Anbieter natürlich schnell zum Chaos führen, läßt sich aber nicht so einfach ändern. Es bieten sich zwei Handlungsalternativen an:

Beide Varianten sind unbefriedigend, und es bleibt zu hoffen, dass die Anbieter von Java-Klassenbibliotheken sich verstärkt an die Namenskonventionen des Java-Teams halten werden.

13.2.4 Erstellen eigener Pakete

Benannte Pakete

Bisher wurde nur gezeigt, wie man Klassen aus fremden Paketen verwendet, nicht aber, wie man selbst Pakete erstellt. Glücklicherweise ist das aber keine Aufgabe für Spezialisten, sondern sehr einfach mit Bordmitteln realisierbar.

Um eine Klasse einem ganz bestimmten Paket zuzuordnen, muss lediglich am Anfang des Quelltextes eine geeignete package-Anweisung verwendet werden. Diese besteht (analog zur import-Anweisung) aus dem Schlüsselwort package und dem Namen des Pakets, dem die nachfolgende Klasse zugeordnet werden soll. Die package-Anweisung muss als erste Anweisung in einer Quelldatei stehen, so dass der Compiler sie noch vor den import-Anweisungen findet.

Der Aufbau und die Bedeutung der Paketnamen in der package-Anweisung entspricht exakt dem der import-Anweisung. Der Compiler löst ebenso wie beim import den dort angegebenen hierarchischen Namen in eine Kette von Unterverzeichnissen auf, an deren Ende die Quelldatei steht. Neben der Quelldatei wird auch die Klassendatei in diesem Unterverzeichnis erstellt.

Da der Java-Compiler eingebundene Quelldateien, die noch nicht übersetzt sind, während der Übersetzung einer anderen Klasse automatisch mit übersetzt, ist das Erstellen eines neuen Pakets sehr einfach. Wir wollen uns ein Beispiel ansehen, bei dem zwei Pakete demo und demo.tools angelegt und die darin enthaltenen Klassen A, B und C in einer Klasse PackageDemo verwendet werden. Am einfachsten ist es, wie nachfolgend beschrieben vorzugehen.

Wir gehen davon aus, dass der CLASSPATH das aktuelle Verzeichnis enthält (also beispielsweise den Inhalt ».;c:\classes« hat), und legen im aktuellen Verzeichnis die Unterverzeichnisse demo und demo\tools an.

Ohne vorher die Klassen A, B oder C separat übersetzen zu müssen, kann nun einfach PackageDemo kompiliert werden. Der Compiler erkennt die Verwendung von A, B und C, findet die Paketverzeichnisse demo und demo\tools und erkennt, dass die Quellen noch nicht übersetzt wurden. Er erzeugt dann aus den .java-Dateien die zugehörigen .class-Dateien (in demselben Verzeichnis wie die Quelldateien), bindet sie ein und übersetzt schließlich die Klasse PackageDemo. Die Ausgabe des Programms ist:

Hier ist A
Hier ist B
Hier ist C

Das Default-Paket

Würde nur dann ein eigenes Paket erzeugt werden, wenn die Quelldatei eine package-Anweisung enthält, müsste man sich fragen, zu welchem Paket die Klassen gehören, in deren Quelldatei die package-Anweisung fehlt (was ja erlaubt ist). Die Antwort auf diese Frage lautet, dass es in Java ein Default-Paket gibt, das genau dann verwendet wird, wenn keine andere Zuordnung getroffen wurde.

Das Default-Paket ist ein Zugeständnis an kleinere Programme oder einfache Programmierprojekte, bei denen es sich nicht lohnt, eigene Pakete anzulegen. Ohne Teile des Projektes in Unterverzeichnissen abzulegen und durch import- und package-Anweisungen unnötigen Aufwand zu treiben, ist es auf diese Weise möglich, Quelldateien einfach im aktuellen Verzeichnis abzulegen, dort zu kompilieren und automatisch einzubinden. Klassen des Default-Pakets können ohne explizite import-Anweisung verwendet werden.

Ein Java-Compiler braucht laut Spezifikation nur ein einziges Default-Paket zur Verfügung zu stellen. Typischerweise wird dieses Konzept aber so realisiert, dass jedes unterschiedliche Verzeichnis die Rolle eines Default-Pakets übernimmt. Auf diese Weise lassen sich beliebig viele Default-Pakete erzeugen, indem bei Bedarf einfach ein neues Unterverzeichnis angelegt wird und die Quelldateien eines Java-Projektes dort abgelegt werden.

 Hinweis 

Das public-Attribut

Es gibt noch eine wichtige Besonderheit bei der Deklaration von Klassen, die von anderen Klassen verwendet werden sollen. Damit eine Klasse A eine andere Klasse B einbinden darf, muss nämlich eine der beiden folgenden Bedingungen erfüllt sein:

Wenn also nur Default-Pakete verwendet werden, spielt es keine Rolle, ob eine Klasse vom Typ public ist, denn alle Klassen liegen in demselben Paket. Werden aber Klassen aus externen Paketen eingebunden, so gelingt das nur, wenn die einzubindende Klasse vom Typ public ist. Andernfalls verweigert der Compiler deren Einbindung und bricht die Übersetzung mit einer Fehlermeldung ab.

 Warnung 


 Titel   Inhalt   Suchen   Index   DOC  Handbuch der Java-Programmierung, 5. Auflage, Addison Wesley, Version 5.0.2
 <<    <     >    >>   API  © 1998, 2007 Guido Krüger & Thomas Stark, http://www.javabuch.de