Apuntes Java: Packages

Cláusula package

Un package es una agrupación de clases afines. Equivale al concepto de librería existente en otros lenguajes o sistemas. Una clase puede definirse como perteneciente a un package y puede usar otras clases definidas en ese o en otros packages.

Los packages delimitan el espacio de nombres (space name). El nombre de una clase debe ser único dentro del package donde se define. Dos clases con el mismo nombre en dos packages distintos pueden coexistir e incluso pueden ser usadas en el mismo programa.

Una clase se declara perteneciente a un package con la cláusula package, cuya sintaxis es:

package nombre_package;

La cláusula package debe ser la primera sentencia del archivo fuente. Cualquier clase declarada en ese archivo pertenece al package indicado.

Por ejemplo, un archivo que contenga las sentencias:

package miPackage;
. . .
class miClase {
. . .
}

declara que la clase miClase pertenece al package miPackage.

La cláusula package es opcional. Si no se utiliza, las clases declaradas en el archivo fuente no pertenecen a ningún package concreto, sino que pertenecen a un package por defecto sin nombre.

La agrupación de clases en packages es conveniente desde el punto de vista organizativo, para mantener bajo una ubicación común clases relacionadas que cooperan desde algún punto de vista. También resulta importante por  la implicación que los packages tienen en los modificadores de acceso, que se explican en un capítulo posterior.

Cláusula import

Cuando se referencia cualquier clase dentro de otra se asume, si no se indica otra cosa, que ésta otra está declarada en el mismo package. Por ejemplo:

package Geometria;
. . .
class Circulo {
    Punto centro;
    . . .
}

En esta declaración definimos la clase Circulo perteneciente al package Geometria. Esta clase usa la clase Punto. El compilador y la JVM asumen que Punto pertenece también al package Geometria, y tal como está hecha la definición, para que la clase Punto sea accesible (conocida) por el compilador, es necesario que esté definida en el mismo package.

Si esto no es así, es necesario hacer accesible el espacio de nombres donde está definida la clase Punto a nuestra nueva clase. Esto se hace con la clausula import. Supongamos que la clase Punto estuviera definida de esta forma:

package GeometriaBase;
class Punto {
    int x , y;
}

Entonces, para usar la clase Punto en nuestra clase Circulo deberiamos poner:

package GeometriaAmpliada;
import GeometriaBase.*;
class Circulo {
   Punto centro;
   . . .
 }

Con la cláusula import GeometriaBase.*; se hacen accesibles todos los nombres (todas las clases) declaradas en el package GeometriaBase. Si sólo se quisiera tener accesible la clase Punto se podría declarar: import GeometriaBase.Punto;

También es posible hacer accesibles los nombres de un package sin usar la cláusula import calificando completamente los nombres de aquellas clases pertenecientes a otros packages. Por ejemplo:

package GeometriaAmpliada;
class Circulo {
    GeometriaBase.Punto centro;
    . . .
 }

Sin embargo si no se usa import es necesario especificar el nombre del package cada vez que se usa el nombre Punto.

La cláusula import simplemente indica al compilador donde debe buscar clases adicionales, cuando no pueda encontrarlas en el package actual y delimita los espacios de nombres y modificadores de acceso. Sin embargo, no tiene la implicación de 'importar' o copiar código fuente u objeto alguno. En una clase puede haber tantas sentencias import como sean necesarias. Las cláusulas import se colocan después de la cláusula package (si es que existe) y antes de las definiciones de las clases.

Nombres de los packages

Los packages se pueden nombrar usando nombres compuestos separados por puntos, de forma similar a como se componen las direcciones URL de Internet. Por ejemplo se puede tener un package de nombre misPackages.Geometria.Base. Cuando se utiliza esta estructura se habla de packages y subpackages. En el ejemplo misPackages es el Package base, Geometria es un subpackage de misPackages y Base es un subpackage de Geometria.

De esta forma se pueden tener los packages ordenados según una jerarquía equivalente a un sistema de archivos jerárquico.

El API de java está estructurado de esta forma, con un primer calificador (java o javax) que indica la base, un segundo calificador (awt, util, swing, etc.) que indica el grupo funcional de clases y opcionalmente subpackages en un tercer nivel, dependiendo de la amplitud del grupo. Cuando se crean packages de usuario no es recomendable usar nombres de packages que empiecen por java o javax.

Ubicación de packages en el sistema de archivos

Además del significado lógico descrito hasta ahora, los packages también tienen un significado físico que sirve para almacenar los módulos ejecutables (ficheros con extensión .class) en el sistema de archivos del ordenador.

Supongamos que definimos una clase de nombre miClase que pertenece a un package de nombre misPackages.Geometria.Base. Cuando la JVM vaya a cargar en memoria miClase  buscará el módulo ejecutable (de nombre miClase.class) en un directorio en la ruta de acceso misPackages/Geometria/Base. Está ruta deberá existir y estar accesible a la JVM para que encuentre las clases. En el capítulo siguiente se dan detalles sobre compilación y ejecución de programas usando el compilador y la máquina virtural .

Si una clase no pertenece a ningún package (no existe cláusula package )  se asume que pertenece a un package por defecto sin nombre, y la JVM buscará el archivo .class en el directorio actual.

Para que una clase pueda ser usada fuera del package donde se definió debe ser declarada con el modificador de acceso public, de la siguiente forma:

package GeometriaBase;
public class Punto {
    int x , y;
}

Nota: Los modificadores de acceso se explicarán detalladamente en un capítulo posterior.

Si una clase no se declara public sólo puede ser usada por clases que pertenezcan al mismo package.

Última actualización: 11/11/2016