Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 7. Auflage |
<< | < | > | >> | API | Kapitel 14 - Strukturierung von Java-Programmen |
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:
java.util.Date d = new java.util.Date();
import java.util.*; ... Date d = new Date();
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:
import java.util.Date; ... Date d = new Date(); java.util.ArrayList l = new java.util.ArrayList();
import java.util.*; ... Date d = new Date(); ArrayList l = new ArrayList();
Seit der J2SE 5.0 gibt es unter dem Stichwort static import eine weitere Variante der import-Anweisung. Weitere Informationen sowie ein Beispiel dazu finden Sie in Abschnitt 10.4.
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. |
|
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 Compiler-Lauf 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.
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 14.1 und Tabelle 14.2). Sie stieg von acht Standardpaketen im JDK 1.0 auf über 200 seit der Java 7 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 14.1: Wichtige Standardpakete des JDK
Neben den Standardpaketen gibt es eine Reihe von Standarderweiterungen, deren Paketname mit javax. beginnt. Sie stellen erweiterte Funktionalitäten in einer 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:
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 14.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 Pakethierarchien org.xml und org.w3c, die Unterstützung zum Zugriff auf XML-Dateien zur Verfügung stellen (siehe Kapitel 43).
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.
Mit dem JDK 1.2 wurde die Bedeutung der CLASSPATH-Umgebungsvariable so definiert, dass sie nur zur Suche der benutzerspezifischen Klassen verwendet wird. Alle Standardpakete und Standarderweiterungen (beide zusammen werden seit dem JDK 1.2 Bootstrap Classes genannt) werden 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).
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 Internetadressen 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 Standardsprachumfang eines Java-Entwicklungssystems, sondern werden 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.
Der Vollständigkeit halber sollte man anmerken, dass es in der Praxis Ausnahmen zu dem hier beschriebenen System gibt, beispielsweise die Klassen der java.*- und javax.*-Hierarchie. Auch verwenden manche Entwickler ein Schema, bei dem 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, aber die Gefahr von Überschneidungen mit Paketen anderer Anbieter ist natürlich größer. |
|
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 jar-Datei des JUnit-Projekts von Erich Gamma, Kent Beck und anderen (http://www.junit.org) installieren:
set CLASSPATH=.;c:\classes
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, kommt aber in der Praxis glücklicherweise kaum noch vor.
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.
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 entsprechen 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.
001 package demo; 002 003 public class A 004 { 005 public void hello() 006 { 007 System.out.println("Hier ist A"); 008 } 009 } |
demo/A.java |
Sie enthält die Anweisung package demo;, um anzuzeigen, dass die Klasse A zum Paket demo gehört.
001 package demo; 002 003 public class B 004 { 005 public void hello() 006 { 007 System.out.println("Hier ist B"); 008 } 009 } |
demo/B.java |
Auch diese Quelldatei enthält die Anweisung package demo;, um anzuzeigen, dass die Klasse zum Paket demo gehört.
001 package demo.tools; 002 003 public class C 004 { 005 public void hello() 006 { 007 System.out.println("Hier ist C"); 008 } 009 } |
demo/tools/C.java |
Diese Quelldatei enthält die Anweisung package demo.tools;, um anzuzeigen, dass die Klasse zum Paket demo.tools gehört.
001 import demo.*; 002 import demo.tools.*; 003 004 public class PackageDemo 005 { 006 public static void main(String[] args) 007 { 008 (new A()).hello(); 009 (new B()).hello(); 010 (new C()).hello(); 011 } 012 } |
PackageDemo.java |
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
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 Projekts 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-Projekts dort abgelegt werden. |
|
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.
Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 7. Auflage, Addison Wesley, Version 7.0 |
<< | < | > | >> | API | © 1998, 2011 Guido Krüger & Heiko Hansen, http://www.javabuch.de |