Package Java
Raggruppa le classi Java in package, segui le convenzioni di naming e struttura i progetti per la manutenibilità.
Java non ha un unico grande pool di nomi di classe. Ogni classe vive all'interno di un package — uno spazio con nome che funziona sia come unità di organizzazione sia come namespace a livello Java. Due classi chiamate Logger possono coesistere senza conflitti finché si trovano in package diversi, e il nome del package compare ovunque: nelle istruzioni import, nei nomi completamente qualificati, nel file system, persino nei manifest JAR. Una buona conoscenza dei package è ciò che ti permette di leggere la struttura di un progetto altrui a colpo d'occhio.
Questa pagina tratta cosa sia un package, come denominarne uno, il caso speciale del package predefinito, come i nomi dei package si mappano sulle directory e come dichiarare un package nel proprio file sorgente.
Cosa è effettivamente un package
Un package serve tre scopi contemporaneamente:
- Un namespace.
java.util.Dateejava.sql.Datesono classi diverse; il nome del package le tiene separate. - Un confine di accesso. Senza un modificatore, i membri sono visibili solo all'interno dello stesso package — il livello di accesso "package-private". È una forma reale e strutturale di incapsulamento; vedi Modificatori di accesso.
- Una directory. Il nome del package si mappa uno a uno su un percorso di cartella.
com.example.app.utilsi trova incom/example/app/util/.
Lo stesso nome viene usato in tre punti — dichiarazione, percorso del file e import — e devono tutti corrispondere.
Convenzioni di naming
La convenzione di Java è il naming in DNS inverso basato su un dominio che controlli:
- Tutto in minuscolo:
com.example, nonCom.Example. - Ordine di dominio inverso: un progetto su
w3docs.comusacom.w3docscome radice. - I segmenti di progetto, modulo e funzionalità seguono:
com.w3docs.learnjava.parser. - Evita le parole riservate di Java come segmenti (
int,class,new). Se il tuo dominio ne include una, modificala:com.example.int_o suddividi diversamente.
Queste convenzioni contano oltre l'estetica. La regola DNS inverso è ciò che rende sicuro inserire JAR di organizzazioni diverse nel medesimo classpath senza conflitti di nomi.
Il package predefinito
Un file .java senza dichiarazione package appartiene al package predefinito (senza nome). Di conseguenza accadono due cose:
- Non puoi fare
importdal package predefinito in uno nominato. Tutto ciò che si trova in esso è praticamente isolato dal codice reale. - Gli strumenti di build, gli IDE e i sistemi di moduli trattano il package predefinito come un caso degenere — molti rifiutano categoricamente di compilare contro di esso.
Usalo per file Hello.java occasionali. Non distribuire nulla da esso.
Come i package si mappano sulle directory
Se dichiari package com.w3docs.learnjava.parser; in cima a Tokenizer.java, il file deve trovarsi in:
com/w3docs/learnjava/parser/Tokenizer.javarelativo alla source root. Il compilatore non deduce il package dal percorso — legge la dichiarazione e si fida di te. Ma il runtime (e la maggior parte degli strumenti) non sarà contento se i due non coincidono.
Quella source root è dove inizia il percorso del classpath: la JVM deve sapere dove inizia l'albero dei package, altrimenti non trova nulla.
Dichiarare un package
L'istruzione package è ciò che assegna una classe a un package. Deve essere la prima istruzione nel file — prima di qualsiasi import, prima della classe stessa. Solo commenti e righe vuote possono precederla.
// File: com/w3docs/learnjava/parser/Tokenizer.java
package com.w3docs.learnjava.parser;
import java.util.List;
public class Tokenizer {
public List<String> tokenize(String source) {
// ...
return List.of();
}
}Da qualsiasi altro punto, questa classe è ora nota con il suo nome completamente qualificato, com.w3docs.learnjava.parser.Tokenizer. Il codice nello stesso package può riferirsi ad essa semplicemente come Tokenizer; il codice in altri package o la importa o scrive il nome completo.
package, e solo una classe public di primo livello — il cui nome deve corrispondere al nome del file. Metti package in qualsiasi posizione diversa dalla cima e il compilatore rifiuterà il file.Un esempio pratico: due Logger
L'argomento più chiaro a favore dei package è il conflitto che prevengono. Il seguente programma usa due classi chiamate Logger in senso figurato — il java.util.logging.Logger del JDK con il suo nome completamente qualificato — e mostra come il package di una classe diventi parte della sua identità a runtime.
Due conclusioni dall'esecuzione: le classi conoscono il proprio package a runtime (tramite Class.getName() e Class.getPackage()), e il nome completamente qualificato è ciò che identifica un tipo in modo univoco — Logger da solo è ambiguo; java.util.logging.Logger non lo è.
Cosa c'è dopo
Denominare un package è una cosa; portare i suoi tipi nel proprio codice è un'altra. Il capitolo successivo tratta l'istruzione import — import di tipo singolo, import con wildcard, import statici e quando ciascuno è la scelta giusta.