Eigene Pakete in Java erstellen
Eigene Java-Pakete erstellen: Verzeichnisstruktur, package-Deklaration sowie Kompilieren und Verwenden erklärt.
Pakete anderer Leute zu lesen ist eine Sache. Eigene zu erstellen erfordert drei kleine Schritte: einen Namen wählen, das Verzeichnis anlegen und die richtige package-Deklaration an den Anfang jeder Datei setzen. Der Compiler und die JVM halten sich an eine strikte Konvention, und nach zweimaliger Anwendung wird es zur Routine. Wenn Sie noch keine Erfahrung mit Paketen haben, lesen Sie zuerst die Übersicht zu Java-Paketen; dieses Kapitel konzentriert sich auf das Erstellen eigener Pakete.
Die drei Regeln
Alles, was Sie innerhalb eines Pakets schreiben, muss genau drei Regeln einhalten:
- Die erste Nicht-Kommentar-Anweisung der Datei ist die
package-Deklaration. Keine andere Anweisung darf ihr vorangestellt sein. - Der Dateipfad spiegelt den Paketnamen wider.
com.example.util.Stringsmuss unter einem Quellverzeichnis im Pfadcom/example/util/Strings.javaliegen. - Die Datei wird nach ihrer öffentlichen Top-Level-Klasse benannt.
Strings.javafürpublic class Strings.
Das ist alles. Der Rest ist Konvention.
Einen Paketnamen wählen
Der Compiler erzwingt nur die drei obigen Regeln, aber einige Namenskonventionen halten Ihre Pakete kollisionsfrei und idiomatisch:
- Nur Kleinbuchstaben.
com.w3docs.greet, niemalscom.W3Docs.Greet. Großbuchstaben in Paketnamen funktionieren, verstoßen aber gegen die Konvention, die jeder andere Java-Entwickler erwartet, und verursachen Probleme auf Dateisystemen ohne Groß-/Kleinschreibung. - Domänennamen umkehren. Wenn Sie
w3docs.combesitzen, beginnen Ihre Pakete mitcom.w3docs. Dies garantiert globale Eindeutigkeit, sodass IhrGreeternie mit demGreetereiner anderen Person im Classpath kollidiert. - Keine Java-Schlüsselwörter. Ein Paketsegment darf kein reserviertes Wort sein. Wenn Ihre Domain
int.example.comwäre, könnten Sie nichtcom.example.intschreiben — Sie würden es escapen, z. B.com.example.int_. - Keine führenden Ziffern in einem Segment.
com.3m.tapeist ungültig, weil3mkein gültiger Bezeichner ist; eine übliche Lösung istcom._3m.tape.
Wenn Sie gar kein Paket deklarieren, landen Ihre Klassen im Standardpaket — gut für ein schnelles Wegwerfbeispiel, aber Klassen dort können nicht von paketiertem Code importiert werden; vermeiden Sie es daher in echten Projekten.
Ein minimales Beispiel
Angenommen, Sie möchten ein kleines Hilfspaket namens com.w3docs.greet erstellen. So sieht die Verzeichnisstruktur aus:
project/
└── src/
└── com/
└── w3docs/
└── greet/
├── Greeter.java
├── Greeting.java
└── Main.javaGreeting.java:
package com.w3docs.greet;
public record Greeting(String language, String text) {}Greeter.java:
package com.w3docs.greet;
public class Greeter {
public Greeting greet(String name) {
return new Greeting("en", "Hello, " + name + "!");
}
}Main.java:
package com.w3docs.greet;
public class Main {
public static void main(String[] args) {
System.out.println(new Greeter().greet("Ada").text());
}
}Alle drei Dateien beginnen mit derselben Zeile package com.w3docs.greet;. Da sie dasselbe Paket teilen, können Greeter und Main Greeting ohne import verwenden — Sie importieren nur Typen aus anderen Paketen.
Kompilieren und Ausführen über die Kommandozeile
Vom Verzeichnis project/ aus lauten die kanonischen Befehle:
# Compile every .java file under src/ into a parallel tree under out/
javac -d out $(find src -name "*.java")
# Run a main class by its fully-qualified name, telling the JVM where out/ is.
java -cp out com.w3docs.greet.Main-d out teilt javac mit, wohin die kompilierten .class-Dateien geschrieben werden sollen; dabei wird die Paketverzeichnisstruktur beibehalten. -cp out (Classpath) gibt java an, wo sie zur Laufzeit zu finden sind — das nächste Kapitel über den Java Classpath erklärt die Details.
Wenn die Paketdeklaration nicht mit dem Speicherort der Datei relativ zum Quellverzeichnis übereinstimmt, akzeptiert javac dies (das Paket ist das, was deklariert wurde, nicht was aus dem Pfad abgeleitet wird) — aber java schlägt zur Laufzeit mit NoClassDefFoundError fehl. Halten Sie beide immer synchron.
Unterpakete sind nicht verschachtelt
Es liegt nahe zu denken, com.example „enthält" com.example.util, aber für den Java-Compiler sind das zwei unabhängige Pakete, die zufällig dasselbe Namenspräfix teilen. Eine Klasse in com.example hat keinen besonderen Zugriff auf die paketprivaten Member von com.example.util. Es gibt keine Paketvererbung.
Was Unterpakete tatsächlich bieten, ist eine leicht navigierbare Verzeichnisstruktur und eine logische Stelle, um verwandten Code zu gruppieren. Die meisten echten Projekte verwenden Unterpakete nach Feature (auth, billing, parser) oder nach Schicht (controller, service, repository) — aber keine dieser Entscheidungen gewährt dem Unterpaket zusätzlichen Zugriff.
Compilation Units und package-info.java
Eine .java-Datei wird auch als Compilation Unit bezeichnet und kann Folgendes enthalten:
- Eine
package-Deklaration (oder keine, für das Standardpaket). - Eine beliebige Anzahl von
import-Deklarationen. - Eine beliebige Anzahl von Typdeklarationen, von denen höchstens eine
publicsein darf, und diese muss dem Dateinamen entsprechen.
Es gibt auch eine spezielle Datei namens package-info.java, deren einzige Aufgabe es ist, Javadoc und Annotationen auf Paketebene zu tragen:
/**
* Greeting utilities for the W3Docs Java book.
*/
@NullMarked
package com.w3docs.greet;Keine Typen — nur der Kommentar, optionale Annotationen und die package-Zeile. Man findet diese Datei in gut dokumentierten Bibliotheken.
Ein ausgearbeitetes Beispiel
Dieses Programm deklariert zwei Typen, die in einem echten Projekt im selben Paket liegen würden, und verwendet sie gemeinsam. Das ausführbare Widget zeigt die Dateistruktur hier flach, damit Sie die Verdrahtung in einer einzigen Quelldatei sehen können, aber in einem echten Projekt würde jeder öffentliche Typ in seiner eigenen .java-Datei unter der oben gezeigten Verzeichnisstruktur liegen.
Was kommt als nächstes
Eigene Pakete decken den Code ab, den Sie selbst schreiben. Meistens werden Sie sich jedoch auf die Pakete des JDK stützen — die Standardbibliotheksklassen für Collections, I/O, Zeit und so weiter. Das nächste Kapitel ist eine geführte Tour durch die, die Sie am häufigsten verwenden werden. Weiter zu Java Built-in-Paketen.