Pacotes Java
Agrupe classes Java relacionadas em pacotes, siga convenções de nomenclatura e estruture projetos para facilitar a manutenção.
Java não tem um único grande pool de nomes de classes. Toda classe vive dentro de um pacote — um espaço nomeado que funciona tanto como unidade de organização quanto como namespace em nível Java. Duas classes chamadas Logger podem coexistir sem conflito, desde que estejam em pacotes diferentes, e o nome do pacote aparece em todo lugar: em declarações import, em nomes totalmente qualificados, no sistema de arquivos e até em manifestos JAR. Conhecer bem os pacotes é o que permite ler a estrutura de um projeto alheio de relance.
Esta página aborda o que é um pacote, como nomeá-lo, o caso especial do pacote padrão, como os nomes de pacotes se mapeiam para diretórios e como declarar um pacote no seu próprio arquivo-fonte.
O que é um pacote, de fato
Um pacote serve a três propósitos ao mesmo tempo:
- Um namespace.
java.util.Dateejava.sql.Datesão classes diferentes; o nome do pacote as separa. - Um limite de acesso. Sem um modificador, os membros são visíveis apenas dentro do mesmo pacote — o nível de acesso "package-private". É uma forma real e estrutural de encapsulamento; veja Modificadores de acesso.
- Um diretório. O nome do pacote mapeia um-para-um para um caminho de pasta.
com.example.app.utilfica emcom/example/app/util/.
O mesmo nome é usado em três lugares — declaração, caminho de arquivo e import — e todos precisam estar de acordo.
Convenções de nomenclatura
A convenção do Java é a nomenclatura em DNS inverso baseada em um domínio que você controla:
- Tudo em minúsculas:
com.example, nãoCom.Example. - Ordem de domínio invertida: um projeto em
w3docs.comusacom.w3docscomo raiz. - Segmentos de projeto, módulo e funcionalidade seguem:
com.w3docs.learnjava.parser. - Evite palavras reservadas do Java como segmentos (
int,class,new). Se o seu domínio incluir uma delas, modifique-a:com.example.int_ou divida de forma diferente.
Essas convenções importam além da estética. A regra de DNS inverso é o que torna seguro colocar JARs de diferentes organizações no mesmo classpath sem conflitos de nomes.
O pacote padrão
Um arquivo .java sem declaração package pertence ao pacote padrão (sem nome). Duas coisas acontecem como resultado:
- Você não pode usar
importdo pacote padrão em um pacote nomeado. Qualquer coisa nele fica efetivamente isolada do código real. - Ferramentas de build, IDEs e sistemas de módulos tratam o pacote padrão como um caso degenerado — muitos simplesmente se recusam a compilar contra ele.
Use-o para arquivos Hello.java isolados. Não distribua nada a partir dele.
Como os pacotes se mapeiam para diretórios
Se você declarar package com.w3docs.learnjava.parser; no topo de Tokenizer.java, o arquivo precisa estar em:
com/w3docs/learnjava/parser/Tokenizer.javarelativo à raiz do código-fonte. O compilador não infere o pacote a partir do caminho — ele lê a declaração e confia em você. Mas o runtime (e a maioria das ferramentas) não ficará satisfeito se os dois não coincidirem.
Essa raiz do código-fonte é onde começa o labirinto do classpath: a JVM precisa saber onde a árvore de pacotes começa, ou não encontrará nada.
Declarando um pacote
A instrução package é o que atribui uma classe a um pacote. Ela deve ser a primeira instrução no arquivo — antes de qualquer import, antes da própria classe. Apenas comentários e linhas em branco podem precedê-la.
// 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();
}
}De qualquer outro lugar, esta classe agora é conhecida pelo seu nome totalmente qualificado, com.w3docs.learnjava.parser.Tokenizer. Código no mesmo pacote pode referenciá-la apenas como Tokenizer; código em outros pacotes precisa importá-la ou escrever o nome completo.
package, e apenas uma classe public de nível superior — cujo nome deve corresponder ao nome do arquivo. Colocar package em qualquer lugar que não seja o topo faz o compilador rejeitar o arquivo.Um exemplo prático: dois Loggers
O argumento mais claro para pacotes é a colisão que eles previnem. O programa a seguir usa duas classes chamadas Logger em essência — o java.util.logging.Logger do JDK pelo seu nome totalmente qualificado — e mostra como o pacote de uma classe faz parte de sua identidade em tempo de execução.
Duas conclusões ao executá-lo: as classes conhecem seu próprio pacote em tempo de execução (via Class.getName() e Class.getPackage()), e o nome totalmente qualificado é o que identifica um tipo de forma inequívoca — Logger sozinho é ambíguo; java.util.logging.Logger não é.
O que vem a seguir
Nomear um pacote é uma coisa; trazer seus tipos para o seu próprio código é outra. O próximo capítulo aborda a instrução import — importações de tipo único, importações com curinga, importações estáticas e quando cada uma é a escolha certa.