W3docs

Crear paquetes personalizados en Java

Crea tus propios paquetes Java: estructura de directorios, declaración de paquete y cómo compilarlos y usarlos.

Leer los paquetes de otras personas es una cosa. Crear los tuyos propios requiere tres pequeños pasos: elegir un nombre, organizar el directorio y colocar la declaración package correcta al inicio de cada archivo. El compilador y la JVM siguen una convención estricta, y una vez que lo hayas hecho dos veces se convierte en memoria muscular. Si eres nuevo en el concepto de paquetes, comienza primero con la introducción a los paquetes Java; este capítulo se centra en crear los tuyos propios.

Las tres reglas

Todo lo que escribas dentro de un paquete debe cumplir exactamente tres reglas:

  1. La primera declaración que no sea un comentario en el archivo es la declaración package. No puede haber otra declaración antes de ella.
  2. La ruta del archivo refleja el nombre del paquete. com.example.util.Strings debe estar en com/example/util/Strings.java dentro de alguna raíz de código fuente.
  3. El archivo lleva el nombre de su clase pública de nivel superior. Strings.java para public class Strings.

Eso es todo. Todo lo demás es convención.

Elegir un nombre de paquete

El compilador solo aplica las tres reglas anteriores, pero unas pocas convenciones de nomenclatura mantienen tus paquetes libres de colisiones e idiomáticos:

  • Todo en minúsculas. com.w3docs.greet, nunca com.W3Docs.Greet. Las letras mayúsculas en los nombres de paquetes funcionan, pero rompen la convención que esperan todos los demás desarrolladores de Java y causan problemas en sistemas de archivos que no distinguen mayúsculas de minúsculas.
  • Invierte el nombre de tu dominio. Si posees w3docs.com, tus paquetes comienzan con com.w3docs. Esto garantiza unicidad global para que tu Greeter nunca colisione con el Greeter de alguien más en el classpath.
  • Sin palabras reservadas de Java. Un segmento de paquete no puede ser una palabra reservada. Si tu dominio fuera int.example.com, no podrías escribir com.example.int — tendrías que escaparlo, por ejemplo, com.example.int_.
  • Sin dígitos al inicio de un segmento. com.3m.tape es ilegal porque 3m no es un identificador válido; una solución común es com._3m.tape.

Si no declaras ningún paquete, tus clases quedan en el paquete por defecto — está bien para un fragmento desechable, pero las clases allí no pueden ser importadas por código empaquetado, así que evítalo en proyectos reales.

Un ejemplo mínimo

Supón que quieres un pequeño paquete de utilidades llamado com.w3docs.greet. Aquí está la estructura:

project/
└── src/
    └── com/
        └── w3docs/
            └── greet/
                ├── Greeter.java
                ├── Greeting.java
                └── Main.java

Greeting.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());
  }
}

Los tres archivos comienzan con la misma línea package com.w3docs.greet;. Como comparten un paquete, Greeter y Main pueden usar Greeting sin ningún import — solo necesitas import para los tipos de otros paquetes.

Compilar y ejecutar desde la línea de comandos

Desde la raíz de project/, los comandos canónicos son:

# 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 le indica a javac dónde colocar los archivos .class compilados; preserva la estructura de directorios del paquete. -cp out (classpath) es cómo java los encuentra en tiempo de ejecución — el próximo capítulo sobre el classpath de Java entra en los detalles.

Si la declaración del paquete no coincide con la ubicación del archivo relativa a la raíz del código fuente, javac la acepta (el paquete es lo que está declarado, no lo que se infiere de la ruta) — pero java fallará en tiempo de ejecución con NoClassDefFoundError. Mantén siempre los dos sincronizados.

Los subpaquetes no están anidados

Es tentador pensar que com.example "contiene" a com.example.util, pero para el compilador de Java son dos paquetes no relacionados que comparten un prefijo de nombre. Una clase en com.example no tiene acceso especial a los miembros de acceso de paquete de com.example.util. No existe herencia de paquetes.

Lo que los subpaquetes te dan es un árbol de directorios fácil de navegar y un lugar lógico para agrupar código relacionado. La mayoría de los proyectos reales usan subpaquetes por funcionalidad (auth, billing, parser) o por capa (controller, service, repository) — pero ninguna elección le da al subpaquete acceso adicional.

Unidades de compilación y package-info.java

Un archivo .java también se denomina unidad de compilación, y puede contener:

  • Una declaración package (o ninguna, para el paquete por defecto).
  • Cualquier número de declaraciones import.
  • Cualquier número de declaraciones de tipos, de los cuales como máximo uno puede ser public, y ese debe coincidir con el nombre del archivo.

También existe un archivo especial llamado package-info.java cuyo único propósito es llevar Javadoc a nivel de paquete y anotaciones:

/**
 * Greeting utilities for the W3Docs Java book.
 */
@NullMarked
package com.w3docs.greet;

Sin tipos — solo el comentario, anotaciones opcionales y la línea package. Lo verás en bibliotecas bien documentadas.

Un ejemplo desarrollado

Este programa declara dos tipos que vivirían en el mismo paquete en un proyecto real y los usa juntos. El widget ejecutable aquí aplana la estructura de archivos para que puedas ver el funcionamiento en un único listado de código fuente, pero en un proyecto real cada tipo público viviría en su propio archivo .java bajo la estructura de directorios mostrada anteriormente.

java— editable, runs on the server

¿Qué sigue?

Los paquetes personalizados cubren el código que escribes tú mismo. La mayoría de las veces, sin embargo, te apoyarás en los paquetes del JDK — las clases de la biblioteca estándar para colecciones, E/S, tiempo, entre otros. El próximo capítulo es un recorrido guiado por los que usarás con más frecuencia. Continúa con paquetes integrados de Java.

Práctica

Práctica
Un archivo fuente Java declara `package com.example.util;` pero se compila y luego se ejecuta desde un `.class` ubicado en `out/com/example/Strings.class`. ¿Qué sucede?
Un archivo fuente Java declara `package com.example.util;` pero se compila y luego se ejecuta desde un `.class` ubicado en `out/com/example/Strings.class`. ¿Qué sucede?
Was this page helpful?