Java 101: Pakete organisieren Klassen und Schnittstellen

Warum das Rad neu erfinden? Dieses Klischee gilt für die Softwareentwicklung, bei der einige Entwickler häufig denselben Code für verschiedene Programme neu schreiben. Zwei Nachteile bei diesem Ansatz sind:

  1. Es verschwendet Zeit
  2. Es führt das Potenzial für Fehler im debuggten Code ein

Als Alternative zum Umschreiben desselben Codes bieten viele Softwareentwicklungsumgebungen ein Bibliothekstool, das häufig verwendeten Code organisiert. Sobald Entwickler das Debuggen von wiederverwendbarem Code abgeschlossen haben, verwenden sie das Tool, um diesen Code in einer Bibliothek zu speichern - eine oder mehrere Dateien, die häufig verwendeten Code zur Verwendung in verschiedenen Programmen enthalten. Während der Programmerstellung greift der Compiler oder das Bibliothekstool auf die Bibliothek zu, um den bibliotheksbezogenen Code des Programms mit dem Programm zu verbinden.

Bibliotheken sind für Java von grundlegender Bedeutung. Sie ermöglichen es dem Classloader der JVM teilweise, Klassendateien zu lokalisieren. (Ich werde in einem zukünftigen Artikel auf Klassenlader eingehen.) Aus diesem Grund werden Javas Bibliotheken allgemein als Klassenbibliotheken bezeichnet. Java bezeichnet Klassenbibliotheken jedoch als Pakete.

Dieser Artikel befasst sich mit Paketen. Ich zeige Ihnen, wie Sie Pakete mit Klassen und Schnittstellen erstellen, gepackte Klassen und Schnittstellen importieren (dh in ein Programm einbinden), Pakete auf der Festplatte verschieben und JAR-Dateien zum Einkapseln von Paketen verwenden.

Hinweis
Das Einzelpaketexperiment dieses Artikels ist Microsoft Windows-spezifisch. Sie sollten in der Lage sein, dieses Experiment problemlos auf Nicht-Windows-Plattformen zu extrapolieren.

Was sind Pakete?

Ein Paket ist eine Sammlung von Klassen und Schnittstellen. Jedes Paket hat seinen eigenen Namen und organisiert seine Klassen und Schnittstellen der obersten Ebene (dh nicht verschachtelt) in einem separaten Namespace oder einer Namenssammlung . Obwohl gleichnamige Klassen und Schnittstellen nicht im selben Paket angezeigt werden können, können sie in verschiedenen Paketen angezeigt werden, da jedem Paket ein separater Namespace zugewiesen wird.

Aus Sicht der Implementierung erweist sich das Gleichsetzen eines Pakets mit einem Verzeichnis als hilfreich, ebenso wie das Gleichsetzen der Klassen und Schnittstellen eines Pakets mit den Klassendateien eines Verzeichnisses. Beachten Sie andere Ansätze - wie die Verwendung von Datenbanken - zur Implementierung von Paketen. Gewöhnen Sie sich daher nicht an, Pakete immer mit Verzeichnissen gleichzusetzen. Da viele JVMs Verzeichnisse zum Implementieren von Paketen verwenden, werden in diesem Artikel Pakete mit Verzeichnissen gleichgesetzt. Das Java 2 SDK organisiert seine umfangreiche Sammlung von Klassen und Schnittstellen in einer baumartigen Hierarchie von Paketen innerhalb von Paketen, die Verzeichnissen innerhalb von Verzeichnissen entspricht. Diese Hierarchie ermöglicht es Sun Microsystems, diese Klassen und Schnittstellen einfach zu verteilen (und mit ihnen zu arbeiten). Beispiele für Java-Pakete sind:

  • java.lang:Eine Sammlung sprachbezogener Klassen wie Objectund String, die im Unterpaket des javaPakets langorganisiert sind
  • java.lang.ref:Eine Sammlung referenzbezogener Sprachklassen wie SoftReferenceund ReferenceQueue, die im refUnterunterpaket des Unterpakets des javaPakets langorganisiert sind
  • javax.swing:Eine Sammlung von Swing-bezogenen Komponentenklassen wie JButtonund Schnittstellen, wie ButtonModelsie im Unterpaket des javaxPakets swingorganisiert sind

Punktzeichen trennen Paketnamen. Beispielsweise javax.swingtrennt in ein javaxPunktzeichen den Paketnamen vom Unterpaketnamen swing. Ein Punktzeichen ist das plattformunabhängige Äquivalent von Schrägstrichzeichen ( /), Schrägstrichzeichen ( \) oder anderen Zeichen, um Verzeichnisnamen in einer verzeichnisbasierten Paketimplementierung, Datenbankverzweigungen in einer hierarchischen datenbankbasierten Paketimplementierung usw. zu trennen .

Trinkgeld
So wie Sie nicht sowohl eine Datei als auch ein Verzeichnis mit identischen Namen im selben Verzeichnis speichern können, können Sie eine Klasse oder Schnittstelle und ein Paket mit identischen Namen nicht im selben Paket speichern. Wenn Sie beispielsweise ein Paket mit dem Namen angeben accounts, können Sie nicht sowohl ein Paket als auch eine Klasse mit dem Namen payablein speichern accounts. Um widersprüchliche Namen zu vermeiden, setzen Sie den ersten Buchstaben der Klassen- und Schnittstellennamen in Großbuchstaben und den ersten Buchstaben der Paketnamen in Kleinbuchstaben. Speichern Sie Payableim vorherigen Beispiel die Klasse im Paket accountsals accounts.Payableund das Paket payableim Paket accountsals accounts.payable. Weitere Informationen zu dieser und anderen Namenskonventionen finden Sie in den Codekonventionen von Sun für die Java-Programmiersprache .

Erstellen Sie ein Paket mit Klassen und Schnittstellen

Die Klassen und Schnittstellen jeder Quelldatei sind in einem Paket organisiert. In packageAbwesenheit der Direktive gehören diese Klassen und Schnittstellen zum unbenannten Paket (das Verzeichnis, das die JVM als aktuelles Verzeichnis betrachtet - das Verzeichnis, in dem ein Java-Programm seine Ausführung über das Windows java.exe-Programm oder ein Betriebssystem-äquivalentes Programm beginnt - und keine Unterpakete enthält). . Wenn die packageDirektive jedoch in einer Quelldatei angezeigt wird, benennt diese Direktive das Paket für diese Klassen und Schnittstellen. Verwenden Sie die folgende Syntax, um eine packageDirektive im Quellcode anzugeben :

'package' packageName ['.' subpackageName ...] ';'

Eine packageDirektive beginnt mit dem packageSchlüsselwort. Eine Kennung , die Namen einer Verpackung packageName, unmittelbar folgt. Wenn Klassen und Schnittstellen in einem Unterpaket (auf einer bestimmten Ebene) innerhalb angezeigt werden sollen packageName, werden danach ein oder mehrere durch Punkte getrennte subpackageNameBezeichner angezeigt packageName. Das folgende Codefragment enthält zwei packageAnweisungen:

Paketspiel; Paket game.devices;

Die erste packageAnweisung identifiziert ein Paket mit dem Namen game. Alle Klassen und Schnittstellen, die in der Quelldatei dieser Direktive enthalten sind, werden im gamePaket organisiert. Die zweite packageAnweisung identifiziert ein Unterpaket mit dem Namen devices, das sich in einem Paket mit dem Namen befindet game. Alle Klassen und Schnittstellen, die in der Quelldatei dieser Direktive gameenthalten sind, werden im devicesUnterpaket des Pakets organisiert. Wenn eine JVM-Implementierung Paketnamen Verzeichnisnamen zuordnet, game.deviceswerden sie einer game\devicesVerzeichnishierarchie unter Windows und einer game/devicesVerzeichnishierarchie unter Linux oder Solaris zugeordnet.

Vorsicht
packageIn einer Quelldatei kann nur eine Direktive erscheinen. Darüber hinaus muss die packageDirektive der erste Code (abgesehen von Kommentaren) in dieser Datei sein. Wenn Sie gegen eine der beiden Regeln verstoßen, meldet der Java-Compiler einen Fehler.

Um Sie mit Paketen vertraut zu machen, habe ich ein Beispiel vorbereitet, das alle Themen in diesem Artikel abdeckt. In diesem Abschnitt erfahren Sie, wie Sie das Paket des Beispiels erstellen. In späteren Abschnitten erfahren Sie, wie Sie eine Klasse und eine Schnittstelle aus diesem Paket importieren, dieses Paket an einen anderen Speicherort auf Ihrer Festplatte verschieben und dennoch von einem Programm aus auf das Paket zugreifen und das Paket in einer JAR-Datei speichern . Listing 1 enthält den Quellcode des Pakets:

Listing 1. A.java

// A.java package testpkg; öffentliche Klasse A {int x = 1; public int y = 2; protected int z = 3; int returnx () {return x; } public int returny () {return y; } protected int returnz () {return z; } öffentliche Schnittstelle StartStop {void start (); void stop (); }} Klasse B {public static void hello () {System.out.println ("hallo"); }}

Listing 1 führt den Quellcode in Ihr zuerst genanntes Paket ein. Die package testpkg;Direktive benennt dieses Paket testpkg. Innerhalb testpkgsind Klassen Aund B. Darin Abefinden sich drei Felddeklarationen, drei Methodendeklarationen und eine innere Schnittstellendeklaration. Innerhalb Bist eine einzelne Methodendeklaration. Der gesamte Quellcode wird gespeichert, A.javada Aes sich um eine öffentliche Klasse handelt. Unsere Aufgabe: Verwandeln Sie diesen Quellcode in ein Paket, das aus zwei Klassen und einer inneren Schnittstelle besteht (oder in ein Verzeichnis, das drei Klassendateien enthält). Die folgenden Windows-spezifischen Schritte führen diese Aufgabe aus:

  1. Open a Windows command window and ensure you are in the c: drive's root directory (the main directory—represented by an initial backslash (\) character). To do that, type the c: command followed by the cd \ command. (If you use a different drive, replace c: with your chosen drive. Also, do not forget to press the Enter key after typing a command.)
  2. Create a testpkg directory by typing md testpkg. Note: When following this article's steps, do not type periods after the commands.
  3. Make testpkg the current directory by typing cd testpkg.
  4. Use an editor to enter Listing 1's source code and save that code to an A.java file in testpkg.
  5. Compile A.java by typing javac A.java. You should see classfiles A$StartStop.class, A.class, and B.class appear in the testpkg directory.

Figure 1 illustrates Steps 3 through 5.

Congratulations! You have just created your first package. Think of this package as containing two classes (A and B) and A's single inner interface (StartStop). You can also think of this package as a directory containing three classfiles: A$StartStop.class, A.class, and B.class.

Note
To minimize package name conflicts (especially among commercial packages), Sun has established a convention in which a company's Internet domain name reverses and prefixes a package name. For example, a company with x.com as its Internet domain name and a.b as a package name (a) followed by a subpackage name (b) prefixes com.x to a.b, resulting in com.x.a.b. My article does not follow this convention because the testpkg package is a throw-away designed for teaching purposes only.

Import a package's classes and interfaces

Once you have a package, you will want to import classes and/or interfaces—actually, class and/or interface names—from that package to your program, so it can use those classes and/or interfaces. One way to accomplish that task is to supply the fully qualified package name (the package name and all subpackage names) in each place where the reference type name (the class or interface name) appears, as Listing 2 demonstrates:

Listing 2. Usetestpkg1.java

// Usetestpkg1.java class Usetestpkg1 implements testpkg.A.StartStop { public static void main (String [] args) { testpkg.A a = new testpkg.A (); System.out.println (a.y); System.out.println (a.returny ()); Usetestpkg1 utp = new Usetestpkg1 (); utp.start (); utp.stop (); } public void start () { System.out.println ("Start"); } public void stop () { System.out.println ("Stop"); } } 

By prefixing testpkg. to A, Usetestpkg1 accesses testpkg's class A in two places and A's inner interface StartStop in one place. Complete the following steps to compile and run Usetestpkg1:

  1. Open a Windows command window and make sure you are in the c: drive's root directory.
  2. Ensure the classpath environment variable does not exist by executing set classpath=. (I discuss classpath later in this article.)
  3. Use an editor to enter Listing 2's source code and save that code to a Usetestpkg1.java file in the root directory.
  4. Compile Usetestpkg1.java by typing javac Usetestpkg1.java. You should see classfile Usetestpkg1.class appear in the root directory.
  5. Type java Usetestpkg1 to run this program.

Figure 2 illustrates Steps 3 through 5 and shows the program's output.

According to Usetestpkg1's output, the main() method's thread successfully accesses testpkg.A's y field and calls the returny() method. Furthermore, the output shows a successful implementation of the testpkg.A.StartStop inner interface.

For Usetestpkg1, prefixing testpkg. to A in three places doesn't seem a big deal. But who wants to specify a fully qualified package name prefix in a hundred places? Fortunately, Java supplies the import directive to import a package's public reference type name(s), so you do not have to enter fully qualified package name prefixes. Express an import directive in source code via the following syntax:

'import' packageName [ '.' subpackageName ... ] '.' ( referencetypeName | '*' ) ';' 

An import directive consists of the import keyword immediately followed by an identifier that names a package, packageName. An optional list of subpackageName identifiers follows to identify the appropriate subpackage (if necessary). The directive concludes with either a referencetypeName identifier that identifies a specific class or interface from the package, or an asterisk (*) character. If referencetypeName appears, the directive is a single-type import directive. If an asterisk character appears, the directive is a type-on-demand import directive.

Caution
As with the package directive, import directives must appear before any other code, with three exceptions: a package directive, other import directives, or comments.

The single-type import directive imports the name of a single public reference type from a package, as the following code fragment demonstrates:

import java.util.Date; 

The previous single-type import directive imports class name Date into source code. As a result, you specify Date instead of java.util.Date in each place that class name appears in source code. For example, when creating a Date object, specify Date d = new Date (); instead of java.util.Date d = new java.util.Date ();.

Seien Sie vorsichtig mit importAnweisungen vom Typ . Wenn der Compiler eine importDirektive vom Typ Single erkennt , die einen Referenztypnamen angibt, der auch in einer Quelldatei deklariert ist, meldet der Compiler einen Fehler, wie das folgende Codefragment zeigt:

import java.util.Date; Klasse Datum {}

Der Compiler betrachtet das Codefragment als einen Versuch, zwei gleichnamige Referenztypen einzuführen Date: