Java 101: i pacchetti organizzano classi e interfacce

Perché reinventare la ruota? Questo cliché si applica allo sviluppo di software in cui alcuni sviluppatori spesso riscrivono lo stesso codice per programmi diversi. Due svantaggi di questo approccio sono:

  1. Fa perdere tempo
  2. Introduce la possibilità di bug nel codice sottoposto a debug

In alternativa alla riscrittura dello stesso codice, molti ambienti di sviluppo software forniscono uno strumento di libreria che organizza il codice utilizzato di frequente. Una volta che gli sviluppatori hanno terminato il debug di un codice riutilizzabile, utilizzano lo strumento per archiviare tale codice in una libreria, uno o più file che contengono codice utilizzato di frequente da utilizzare in vari programmi. Durante la creazione del programma, il compilatore o lo strumento libreria accede alla libreria per connettere il codice di riferimento alla libreria del programma al programma.

Le librerie sono fondamentali per Java. Consentono, in parte, al programma di caricamento classi della JVM di individuare i file di classe. (Esplorerò i classloader in un prossimo articolo.) Per questo motivo, le librerie di Java sono comunemente note come librerie di classi. Tuttavia, Java fa riferimento alle librerie di classi come pacchetti.

Questo articolo esplora i pacchetti; Ti mostro come creare pacchetti di classi e interfacce, come importare (cioè portare in un programma) classi e interfacce pacchettizzate, come spostare pacchetti sul disco rigido e come usare file jar per incapsulare pacchetti.

Nota
L'esperimento a pacchetto singolo di questo articolo è specifico di Microsoft Windows. Dovresti essere in grado di estrapolare facilmente questo esperimento su piattaforme non Windows.

Cosa sono i pacchetti?

Un pacchetto è una raccolta di classi e interfacce. Ogni pacchetto ha il proprio nome e organizza le classi e le interfacce di primo livello (cioè non annidate) in uno spazio dei nomi separato o raccolta di nomi. Sebbene le classi e le interfacce con lo stesso nome non possano essere visualizzate nello stesso pacchetto, possono apparire in pacchetti diversi perché uno spazio dei nomi separato viene assegnato a ciascun pacchetto.

Dal punto di vista dell'implementazione, si rivela utile equiparare un pacchetto a una directory, così come equiparare le classi e le interfacce di un pacchetto con i file di classe di una directory. Tieni a mente altri approcci, come l'uso di database, per implementare i pacchetti, quindi non prendere l'abitudine di equiparare sempre i pacchetti alle directory. Ma poiché molte JVM utilizzano le directory per implementare i pacchetti, questo articolo identifica i pacchetti con le directory. Java 2 SDK organizza la sua vasta collezione di classi e interfacce in una gerarchia ad albero di pacchetti all'interno di pacchetti, che è equivalente alle directory all'interno delle directory. Questa gerarchia consente a Sun Microsystems di distribuire facilmente (e di lavorare con facilità) quelle classi e interfacce. Esempi di pacchetti Java includono:

  • java.lang:Una raccolta di classi relative al linguaggio, come Objecte String, organizzate nel sottopacchetto javadel pacchettolang
  • java.lang.ref:Una raccolta di corsi di lingua di riferimento legati, ad esempio SoftReferencee ReferenceQueue, organizzata nel refsub-sottopackage del javadel pacchetto di langsottopackage
  • javax.swing:Una raccolta di classi di componenti correlate a Swing, come JButton, e interfacce, come ButtonModel, organizzate nel sottopacchetto javaxdel pacchettoswing

I caratteri del punto separano i nomi dei pacchetti. Ad esempio, in javax.swing, un carattere punto separa il nome del pacchetto javaxdal nome del sottopacchetto swing. Un carattere punto è l'equivalente indipendente dalla piattaforma di caratteri barra ( /), caratteri barra rovesciata ( \) o altri caratteri per separare i nomi di directory in un'implementazione di pacchetto basata su directory, rami di database in un'implementazione di pacchetto basata su database gerarchica e così via .

Mancia
Proprio come non è possibile memorizzare sia un file che una directory con nomi identici nella stessa directory, non è possibile memorizzare una classe o un'interfaccia e un pacchetto con nomi identici nello stesso pacchetto. Ad esempio, dato un pacchetto denominato accounts, non è possibile memorizzare sia un pacchetto sia una classe denominata payablein accounts. Per evitare conflitti tra i nomi, mettere in maiuscolo la prima lettera dei nomi di classe e interfaccia e minuscola la prima lettera dei nomi di pacchetto. Utilizzando l'esempio precedente, memorizza la classe Payablein package accountsas accounts.Payablee package payablein package accountsas accounts.payable. Ulteriori informazioni su questa e altre convenzioni di denominazione dalle Convenzioni del codice di Sun per il linguaggio di programmazione Java .

Crea un pacchetto di classi e interfacce

Le classi e le interfacce di ogni file sorgente si organizzano in un pacchetto. In packageassenza della direttiva, quelle classi e interfacce appartengono al pacchetto senza nome (la directory che la JVM considera come la directory corrente - la directory in cui un programma Java inizia la sua esecuzione tramite il programma Windows java.exe, o OS equivalente - e non contiene sottopacchetti) . Ma se la packagedirettiva appare in un file sorgente, quella direttiva nomina il pacchetto per quelle classi e interfacce. Utilizzare la seguente sintassi per specificare una packagedirettiva nel codice sorgente:

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

Una packagedirettiva inizia con la packageparola chiave. Un identificatore che i nomi di un pacchetto, packageName, segue immediatamente. Se le classi e le interfacce devono apparire in un sottopacchetto (a un certo livello) all'interno packageName, uno o più subpackageNameidentificatori separati da punti appaiono dopo packageName. Il seguente frammento di codice presenta una coppia di packagedirettive:

pacchetto di gioco; pacchetto game.devices;

La prima packagedirettiva identifica un pacchetto denominato game. Tutte le classi e le interfacce che compaiono nel file sorgente di quella direttiva vengono organizzate nel gamepacchetto. La seconda packagedirettiva identifica un sottopacchetto denominato devices, che risiede in un pacchetto denominato game. Tutte le classi e le interfacce che compaiono nel file sorgente di quella direttiva vengono organizzate nel sottopacchetto gamedel pacchetto devices. Se un'implementazione JVM mappa i nomi dei pacchetti sui nomi delle directory, game.devicesesegue la mappatura su una game\devicesgerarchia di directory in Windows e su una game/devicesgerarchia di directory in Linux o Solaris.

Attenzione
Solo una packagedirettiva può essere visualizzata in un file di origine. Inoltre, la packagedirettiva deve essere il primo codice (a parte i commenti) in quel file. La violazione di una delle regole causa la segnalazione di un errore da parte del compilatore Java.

Per aiutarti a familiarizzare con i pacchetti, ho preparato un esempio che copre tutti gli argomenti di questo articolo. In questa sezione imparerai a creare il pacchetto dell'esempio. Nelle sezioni successive imparerai come importare una classe e un'interfaccia da questo pacchetto, come spostare questo pacchetto in un'altra posizione sul tuo disco rigido e continuare ad accedere al pacchetto da un programma e come memorizzare il pacchetto in un file jar . Il listato 1 presenta il codice sorgente del pacchetto:

Listato 1. A.java

// Pacchetto A.java testpkg; classe pubblica A {int x = 1; public int y = 2; protetto int z = 3; int returnx () {return x; } public int returny () {return y; } protected int returnz () {return z; } interfaccia pubblica StartStop {void start (); void stop (); }} classe B {public static void hello () {System.out.println ("hello"); }}

Il Listato 1 introduce il codice sorgente nel primo pacchetto denominato. La package testpkg;direttiva nomina quel pacchetto testpkg. All'interno testpkgsono classi Ae B. All'interno ci Asono tre dichiarazioni di campo, tre dichiarazioni di metodo e una dichiarazione di interfaccia interna. All'interno Bc'è una singola dichiarazione di metodo. L'intero codice sorgente viene memorizzato in A.javaperché Aè una classe pubblica. Il nostro compito: trasformare questo codice sorgente in un pacchetto che consiste di due classi e un'interfaccia interna (o una directory che contiene tre file di classe). I seguenti passaggi specifici di Windows eseguono tale attività:

  1. Apri una finestra di comando di Windows e assicurati di essere nella c:directory principale dell'unità (la directory principale, rappresentata da una barra rovesciata iniziale ( \)). Per farlo, digita il c:comando seguito dal cd \comando. (Se utilizzi un'unità diversa, sostituiscila c:con l'unità scelta. Inoltre, non dimenticare di premere il tasto Invio dopo aver digitato un comando.)
  2. Crea una testpkgdirectory digitando md testpkg. Nota: quando si seguono i passaggi di questo articolo, non digitare punti dopo i comandi.
  3. Crea testpkgla directory corrente digitando cd testpkg.
  4. Usa un editor per inserire il codice sorgente del Listato 1 e salva quel codice in un A.javafile in formato 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 ();.

Prestare attenzione con le importdirettive di tipo unico . Se il compilatore rileva una importdirettiva di tipo singolo che specifica un nome di tipo di riferimento dichiarato anche in un file di origine, il compilatore segnala un errore, come dimostra il seguente frammento di codice:

import java.util.Date; Data corso {}

Il compilatore considera il frammento di codice come un tentativo di introdurre due tipi di riferimento con lo stesso Datenome: