Paquetes en Java

Intermedio

En la programación, a menudo es útil agrupar piezas relacionadas de un programa. En Java, esto se puede lograr mediante el uso de paquetes.

Un paquete tiene dos propósitos:

  1. En primer lugar, proporciona un mecanismo mediante el cual las partes relacionadas de un programa se pueden organizar como una unidad. Se debe acceder a las clases definidas dentro de un paquete a través del nombre de su paquete. Por lo tanto, un paquete proporciona una forma de nombrar una colección de clases.
  2. En segundo lugar, un paquete participa en el mecanismo de control de acceso de Java. Las clases definidas dentro de un paquete se pueden hacer privadas (private) para ese paquete y no se puede acceder por código fuera del paquete. Por lo tanto, el paquete proporciona un medio por el cual las clases pueden ser encapsuladas. Examinemos cada característica un poco más de cerca.

1. Qué son Paquetes en Java

Los paquetes sirven para agrupar clases relacionadas y definen un espacio de nombres (namespace) para las clases que contienen.

En general, cuando nombra una clase, está asignando un nombre del namespace. Un namespace define una región declarativa. En Java, no hay dos clases que puedan usar el mismo nombre del mismo namespace. Por lo tanto, dentro de un namespace dado, cada nombre de clase debe ser único.

Los ejemplos que se muestran en las publicaciones anteriores han utilizado el espacio de nombres predeterminado (global). Si bien esto está bien para los programas de muestra cortos, se convierte en un problema a medida que los programas crecen y el namespace predeterminado se llena.

En programas grandes, encontrar nombres únicos para cada clase puede ser difícil. Además, debe evitar las colisiones de nombres con código creado por otros programadores que trabajan en el mismo proyecto y con la biblioteca de Java. La solución a estos problemas es el paquete porque le da una manera de particionar el espacio de nombres(namespace). Cuando se define una clase dentro de un paquete, el nombre de ese paquete se adjunta a cada clase, evitando así las colisiones de nombres con otras clases que tienen el mismo nombre, pero están en otros paquetes.

Dado que un paquete generalmente contiene clases relacionadas, Java define derechos de acceso especiales para el código dentro de un paquete. En un paquete, puede definir código al que pueda acceder otro código dentro del mismo paquete pero no mediante código fuera del paquete. Esto le permite crear grupos independientes de clases relacionadas que mantienen su operación privada.

2. Definiendo un paquete

Todas las clases en Java pertenecen a algún paquete. Cuando no se especifica ninguna declaración de paquete, se usa el paquete predeterminado (global). Además, el paquete predeterminado no tiene nombre, lo que hace que el paquete predeterminado sea transparente. Es por eso que no ha tenido que preocuparse por los paquetes anteriormente.

Si bien el paquete predeterminado está bien para programas de muestra cortos, es inadecuado para aplicaciones reales. La mayoría de las veces, definirá uno o más paquetes para su código.

Para crear un paquete, coloque un comando de paquete en la parte superior de un archivo fuente de Java. Las clases declaradas dentro de ese archivo pertenecerán al paquete especificado. Como un paquete define un espacio de nombres, los nombres de las clases que colocas en el archivo formaran parte del espacio de nombres de ese paquete.

Esta es la forma general de la declaración del paquete:

package pkg;

Aquí, pkg es el nombre del paquete. Por ejemplo, la siguiente declaración crea un paquete llamado mipaquete:

package mipaquete

Java usa el sistema de archivos para administrar paquetes, con cada paquete almacenado en su propio directorio. Por ejemplo, los archivos .class para cualquier clase que declare que forma parte de mipaquete deben almacenarse en un directorio llamado mipaquete.

Al igual que el resto de Java, los nombres de los paquetes distinguen entre mayúsculas y minúsculas. Esto significa que el directorio en el que se almacena un paquete debe ser exactamente el mismo que el nombre del paquete.

Si tiene problemas para probar los ejemplos de este tema, recuerde verificar cuidadosamente los nombres de su paquete y directorio. En minúsculas a menudo se usa para nombres de paquetes.

2.1. Varios paquetes

Más de un archivo puede incluir la misma declaración de paquete. La declaración del paquete simplemente especifica a qué paquete pertenecen las clases definidas en un archivo. No excluye que otras clases en otros archivos sean parte de ese mismo paquete. La mayoría de los paquetes del mundo real están distribuidos en muchos archivos.

Puede crear una jerarquía de paquetes. Para hacerlo, simplemente separe cada nombre de paquete del que está encima de él mediante el uso de un punto. La forma general de una declaración de paquete multinivel se muestra aquí:

package pack1.pack2.pack3...packN;

Por supuesto, debe crear directorios que admitan la jerarquía de paquetes que crea. Por ejemplo,

package alpha.beta.gamma;

debe almacenarse en …/alpha/beta/gamma, donde especifica la ruta a los directorios especificados.

3. Encontrar paquetes y CLASSPATH

Como se acaba de explicar, los paquetes se reflejan en los directorios. Esto plantea una pregunta importante: ¿cómo sabe el sistema de tiempo de ejecución de Java dónde buscar los paquetes que usted crea? En lo que respecta a los ejemplos de este artículo, la respuesta tiene tres partes.

  1. En primer lugar, de forma predeterminada, el sistema de tiempo de ejecución Java utiliza el directorio de trabajo actual como punto de partida. Por lo tanto, si tu paquete está en un subdirectorio del directorio actual, se encontrará.
  2. En segundo lugar, puede especificar una ruta o rutas de directorio estableciendo la variable de entorno CLASSPATH.
  3. En tercer lugar, puede usar la opción -classpath con java y javac para especificar la ruta a sus clases.

Es útil señalar que, comenzando con JDK 9, un paquete puede ser parte de un módulo y, por lo tanto, puede encontrarse en la ruta del módulo. Sin embargo, la discusión sobre módulos y rutas de módulos se verá más adelante. Por ahora, usaremos solo rutas de clase. Por ejemplo, asumiendo la siguiente especificación de paquete:

package mipaquete

Para que un programa encuentre mipaquete, el programa se puede ejecutar desde un directorio inmediatamente arriba de mipaquete, o CLASSPATH debe configurarse para incluir la ruta a mipaquete, o la opción -classpath debe especificar la ruta a mipaquete cuando se ejecuta el programa a través de Java.

La forma más fácil de probar los ejemplos que se muestran a partir de aquí es simplemente crear los directorios de paquetes debajo de tu directorio de desarrollo actual, colocar los archivos .class en los directorios apropiados y luego ejecutar los programas desde el directorio de desarrollo. Este es el enfoque utilizado para los siguientes ejemplos.

Un último punto: para evitar problemas, es mejor mantener todos los archivos .java y .class asociados a un paquete en el directorio de ese paquete. Además, compile cada archivo desde el directorio sobre el directorio del paquete.

4. Un ejemplo de paquete corto

Teniendo en cuenta la discusión precedente, prueba este breve ejemplo de paquete. Crea una base de datos de libros simple que está contenida dentro de un paquete llamado packlibros.

package packlibros;

class Libros {
    private String titulo;
    private String autor;
    private int anio;

    Libros(String t, String a, int d){
        titulo=t;
        autor=a;
        anio=d;
    }

    void mostrar(){
        System.out.println(titulo);
        System.out.println(autor);
        System.out.println(anio);
        System.out.println();
    }
}
package packlibros;

class DemoLibro {
    public static void main(String[] args) {
        Libros libros[]=new Libros[5];

        libros[0]= new Libros("Aprendiendo a Programar en Java","Sonia Jaramillo",2015 );
        libros[1]= new Libros("Estructuras de Datos en Java","Mark Allen Weis",2013 );
        libros[2]= new Libros("Fundamentos de programación Java","Ricardo Marcelo Villalobos",2012);
        libros[3]= new Libros("Introducción a la Programación Orientada A Objetos","Franciso Aragón Mesa",2014 );
        libros[4]= new Libros("Java Cómo Programar","Paul Deitel",2017);

        for (int i=0; i<libros.length;i++) libros[i].mostrar();
    }
}

Salida:

Aprendiendo a Programar en Java
Sonia Jaramillo
2015
....

Llame a este archivo DemoLibro.java y póngalo en un directorio llamado packlibros. A continuación, compila el archivo. Puedes hacer esto especificando:

javac packlibros/DemoLibro.java

desde el directorio directamente antes de packlibros. Luego intente ejecutar la clase, usando la siguiente línea de comando:

java bookpack.BookDemo
Ejecutar archivo java con paquetes
Ejecutar archivo java con paquetes

Recuerde, necesitará estar en el directorio anterior de packlibros cuando ejecute este comando. (O bien, use una de las otras dos opciones descritas en la sección anterior para especificar la ruta de acceso al packlibros).

Como se explicó, DemoLibro y Libros ahora son parte del paquete packlibros. Esto significa que DemoLibro no se puede ejecutar solo. Es decir, no puede usar esta línea de comando:

java DemoLibro

En cambio, DemoLibro debe calificarse con su nombre de paquete.

5. Paquetes y control de acceso

En publicaciones anteriores se han introducido los fundamentos del control de acceso, incluidos los modificadores privados y públicos (Leer más), pero no han contado toda la historia. Una razón para esto es que los paquetes también participan en el mecanismo de control de acceso de Java, y este aspecto del control de acceso tuvo que esperar hasta que se cubrieron los paquetes.

Antes de continuar, es importante tener en cuenta que la nueva característica de módulos añadida por JDK 9 también ofrece otra dimensión a la accesibilidad, pero aquí nos enfocamos estrictamente en la interacción entre paquetes y clases.

La visibilidad de un elemento se ve afectada por su especificación de acceso -privada, pública, protegida o predeterminada (private, public, protected, o default)- y el paquete en el que reside. Por lo tanto, en lo que se refiere a clases y paquetes, la visibilidad de un elemento está determinada por su visibilidad dentro de una clase y su visibilidad dentro de un paquete.

5.1. Nivel de acceso para paquetes

Este enfoque de varias capas para el control de acceso admite una gran variedad de privilegios de acceso. La siguiente tabla resume los diversos niveles de acceso.

RelaciónPrivateDefaultProtectedPublic
Visible dentro de la misma clase
Visible dentro del mismo paquete por subclaseNO
Visible dentro del mismo paquete por no-subclaseNO
Visible dentro de diferente paquete por subclaseNONO
Visible dentro de diferente paquete por no-subclaseNONONO

Examinemos cada opción de acceso individualmente.

  • Si un miembro de una clase no tiene un modificador de acceso explícito, entonces es visible dentro de su paquete pero no fuera de su paquete. Por lo tanto, utilizará la especificación de acceso predeterminada (default) para los elementos que desea mantener en privado para un paquete pero público dentro de ese paquete.
  • Los miembros explícitamente declarados públicos (public) son los más visibles, y se puede acceder desde diferentes clases y diferentes paquetes.
  • Un miembro privado (private) es accesible solo para los otros miembros de su clase. Un miembro privado no se ve afectado por su membresía en un paquete.
  • Se puede acceder a un miembro especificado como protegido (protected) dentro de su paquete y a subclases en otros paquetes.

Una clase de nivel superior tiene solo dos niveles de acceso posibles: predeterminado y público. Cuando una clase se declara como pública, se puede acceder fuera de su paquete. Si una clase tiene acceso predeterminado, solo se puede acceder por otro código dentro de su mismo paquete. Además, una clase que se declara pública debe residir en un archivo con el mismo nombre.

Recuerde, la nueva característica de módulos agregada por JDK 9 también puede afectar la accesibilidad. Los módulos se discuten posteriormente.

6. Ejemplo de acceso en paquete

En el ejemplo del paquete mostrado anteriormente, tanto Libros como DemoLibro estaban en el mismo paquete, por lo que no había ningún problema con DemoLibro al utilizar Libros porque el privilegio de acceso predeterminado (default) otorga acceso a todos los miembros del mismo paquete.

Sin embargo, si Libros estuviera en un paquete y DemoLibro en otro, la situación sería diferente. En este caso, el acceso a Libros sería denegado. Para que Libros esté disponible para otros paquetes, debe realizar tres cambios.

  1. Primero, Libros debe ser declarado público. Esto hace que el Libro sea visible fuera de packlibros.
  2. En segundo lugar, su constructor debe hacerse público y,
  3. finalmente, su método mostrar() debe ser público. Esto les permite ser visibles también fuera de packlibros,. Por lo tanto, para que Libros pueda ser utilizado por otros paquetes, debe ser recodificado como se muestra aquí:
//Recodificando Libros para acceso público
package packlibros;

public class Libros {
    private String titulo;
    private String autor;
    private int anio;

    //Ahora público
    public Libros(String t, String a, int d){
        titulo=t;
        autor=a;
        anio=d;
    }

    //Ahora público
    public void mostrar(){
        System.out.println(titulo);
        System.out.println(autor);
        System.out.println(anio);
        System.out.println();
    }
}

Para usar Libros desde otro paquete, debe usar la declaración import que se describe en la siguiente sección, o debe calificar completamente su nombre para incluir su especificación de paquete completo.

Por ejemplo, aquí hay una clase llamada LibroLeido, que está contenida en el paquete packextlibros. Y que califica completamente Libros para usarlo:

//Esta clase se encuentra en packextlibros
package packextlibros;

public class LibroLeido {

        public static void main(String[] args) {
            //Llamamos a Libros desde su paquete packlibros
            packlibros.Libros libros[]=new packlibros.Libros[5];

            libros[0]= new packlibros.Libros("Aprendiendo a Programar en Java","Sonia Jaramillo",2015 );
            libros[1]= new packlibros.Libros("Estructuras de Datos en Java","Mark Allen Weis",2013 );
            libros[2]= new packlibros.Libros("Fundamentos de programación Java","Ricardo Marcelo Villalobos",2012);
            libros[3]= new packlibros.Libros("Introducción a la Programación Orientada A Objetos","Franciso Aragón Mesa",2014 );
            libros[4]= new packlibros.Libros("Java Cómo Programar","Paul Deitel",2017);

            for (int i=0; i<libros.length;i++) libros[i].mostrar();
        }
}

Observe cómo cada Libros está precedido por el calificador packlibros. Sin esta especificación, no se encontraría Libros cuando intentó compilar LibroLeido.

7. Comprender los miembros protegidos

Los recién llegados a Java a veces se confunden con el significado y el uso de protected. Como se explicó, el modificador protegido crea un miembro al que se puede acceder dentro de su paquete y a subclases en otros paquetes.

Por lo tanto, un miembro protegido está disponible para todas las subclases para usar, pero aún está protegido del acceso arbitrario por código fuera de su paquete. Para comprender mejor los efectos de la protected, trabajemos a través de un ejemplo. Primero, cambie la clase Libros para que sus variables de instancia estén protegidas (protected), como se muestra aquí:

//Cambiar las variables de instancia a protected
package packlibros;

public class Libros {

    //Ahora están protegidas
    protected String titulo;
    protected String autor;
    protected int anio;

    public Libros(String t, String a, int d){
        titulo=t;
        autor=a;
        anio=d;
    }

    public void mostrar(){
        System.out.println(titulo);
        System.out.println(autor);
        System.out.println(anio);
        System.out.println();
    }
}

Luego, cree una subclase de Libros, llamada LibrosExt, y una clase llamada DemoProtected que use LibrosExt. LibrosExt agrega un campo que almacena el nombre del editor y varios métodos de acceso. Ambas clases estarán en su propio paquete llamado packextlibros. Ellos se muestran aquí:

package packextlibros;

public class LibrosExt extends packlibros.Libros {
    private String editorial;


    public LibrosExt(String t, String a, int d,String e) {
        super(t, a, d);
        editorial=e;
    }

    public void mostrar(){
        super.mostrar();
        System.out.println(editorial);
        System.out.println();
    }

    public String getEditorial(){return editorial;}
    public void setEditorial(String e){editorial=e;}

    //Esto está OK porque las subclases pueden acceder a miembros protegidos
    public String getTitulo(){return  titulo;}
    public void setTitulo(String t){titulo=t;}
    public String getAutor(){return autor;}
    public void setAutor(String a){autor=a;}
    public int getAnio(){return anio;}
    public void setAnio(int d){anio=d;}
}

class DemoProtected{
    public static void main(String[] args) {
        LibrosExt libros []= new LibrosExt[5];

        libros[0]= new LibrosExt("Aprendiendo a Programar en Java","Sonia Jaramillo",2015,"ELIZCOM" );
        libros[1]= new LibrosExt("Estructuras de Datos en Java","Mark Allen Weis",2013,"Addison Wesley" );
        libros[2]= new LibrosExt("Fundamentos de programación Java","Ricardo Marcelo Villalobos",2012,"Macro");
        libros[3]= new LibrosExt("Introducción a la Programación Orientada A Objetos","Franciso Aragón Mesa",2014 ,"Desconocido");
        libros[4]= new LibrosExt("Java Cómo Programar","Paul Deitel",2017,"Pearson");

        for (int i=0; i<libros.length;i++) libros[i].mostrar();

        //Encontrado autores por libros
        System.out.println("Mostrando todos los libros para Paul Deitel:");
        for (int i=0 ; i<libros.length;i++)
            if (libros[i].getAutor()=="Paul Deitel")
                System.out.println(libros[i].getTitulo());

        //libros[0].titulo="Titulo de prueba"; //ERROR - NO ACCESIBLE
    }
}

Salida:

Aprendiendo a Programar en Java
Sonia Jaramillo
2015
...
...
Mostrando todos los libros para Paul Deitel:
Java Cómo Programar

Mire primero el código dentro de LibrosExt. Debido a que LibrosExt extiende (extends) de Libros, tiene acceso a los miembros protegidos (protected) de Libros, a pesar de que LibrosExt se encuentra en un paquete diferente.

Por lo tanto, puede acceder a título, autor y anio directamente, como lo hace en los métodos de acceso que crea para esas variables. Sin embargo, en DemoProtected, se deniega el acceso a estas variables porque DemoProtected no es una subclase de Libros. Por ejemplo, si elimina el símbolo de comentario de la siguiente línea, el programa no se compilará.

//libros[0].titulo="Titulo de prueba"; //ERROR - NO ACCESIBLE

8. Importar paquetes

Cuando utiliza una clase de otro paquete, puede calificar completamente el nombre de la clase con el nombre de su paquete, como lo han hecho los ejemplos anteriores. Sin embargo, un enfoque de este tipo podría convertirse fácilmente en tedioso e incómodo, especialmente si las clases que califica están profundamente anidadas en una jerarquía de paquetes.

Como Java fue inventado por los programadores para los programadores -y a los programadores no les gustan las construcciones tediosas- no debería sorprender que exista un método más conveniente para usar los contenidos de los paquetes: la declaración import. Al usar import, puede mostrar uno o más miembros de un paquete. Esto le permite usar esos miembros directamente, sin la calificación explícita del paquete.

Aquí está la forma general de la declaración import:

import paquete.nombredeclase;

Aquí, paquete es el nombre del paquete, que puede incluir su ruta completa, y nombredeclase es el nombre de la clase que se está importando. Si desea importar todo el contenido de un paquete, use un asterisco (*) para el nombre de la clase. Aquí hay ejemplos de ambas formas:

import mipaquete.MiClase
import mipaquete.*;

En el primer caso, la clase MiClase se importa de mipaquete. En el segundo, todas las clases en mipaquete son importadas.

En un archivo fuente de Java, las instrucciones de importación se producen inmediatamente después de la declaración del paquete -si existe- y antes de cualquier definición de clase.

Puede usar import para traer el paquete packlibros de manera que la clase Libros se pueda usar sin calificación. Para hacerlo, simplemente agregue esta instrucción de importación a la parte superior de cualquier archivo que use Libros.

import packlibros.*;

Por ejemplo, aquí está la clase de LibroLeido recodificada con el uso de import.

//Demostración de import
package packextlibros;
import packlibros.*;

public class LibroLeido {

        public static void main(String[] args) {
            //Ahora nos referimos directamente a Libros
            //Sin calificación
            Libros libros[]=new Libros[5];

            libros[0]= new Libros("Aprendiendo a Programar en Java","Sonia Jaramillo",2015 );
            libros[1]= new Libros("Estructuras de Datos en Java","Mark Allen Weis",2013 );
            libros[2]= new Libros("Fundamentos de programación Java","Ricardo Marcelo Villalobos",2012);
            libros[3]= new Libros("Introducción a la Programación Orientada A Objetos","Franciso Aragón Mesa",2014 );
            libros[4]= new Libros("Java Cómo Programar","Paul Deitel",2017);

            for (int i=0; i<libros.length;i++) libros[i].mostrar();
        }
}

Tenga en cuenta que ya no necesita calificar Libros con su nombre de paquete.

9. La biblioteca de clases de Java está contenida en paquetes

Como se explicó anteriormente en este curso, Java define una gran cantidad de clases estándar que están disponibles para todos los programas. Esta biblioteca de clase a menudo se denomina API de Java (Application Programming Interface/interfaz de programación de aplicaciones). La API de Java se almacena en paquetes. En la parte superior de la jerarquía del paquete está java. Descendiendo de Java hay varios subpaquetes. Aquí están algunos ejemplos:

Tabla de sub-paquetes en Java.
SubpaqueteDescripción
java.langContiene una gran cantidad de clases de uso general
java.ioContiene clases de E/S
java.netContiene clases que admiten redes
java.utilContiene una gran cantidad de clases de utilidad, incluido Collections Framework
java.awtContiene clases que son compatibles con Abstract Window Toolkit

Desde el comienzo de este curso, has estado usando java.lang. Contiene, entre muchos otros, la clase System, que ha estado utilizando al realizar la salida utilizando println().

El paquete java.lang es único porque se importa automáticamente en cada programa Java. Esta es la razón por la que no tuvo que importar java.lang en los programas de ejemplo anteriores. Sin embargo, debe importar explícitamente los otros paquetes. Examinaremos varios paquetes en posteriores publicaciones.

Paquetes en Java
  • Introducción a Paquetes

Sobre el Autor:

Hey hola! Yo soy Alex Walton y tengo el placer de compartir conocimientos hacía ti sobre el tema de Programación en Java, desde cero, Online y Gratis.

Deja una Respuesta

*

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.