El método main() primero verifica que se haya especificado un único argumento de línea de comandos. Si la verificación es exitosa, pasa este argumento a Audio.newAudio() y asigna la referencia del objeto Audio devuelto a una variable local llamada audio. Luego, main() procede a verificar que audio no sea nulo y (en este caso) interroga al objeto Audio, generando los valores de muestra del clip de audio junto con su frecuencia de muestreo. Copie el Listado 3 en un archivo llamado UseAudio.java y coloque este archivo en el mismo directorio que el directorio ca que creó anteriormente. Luego, ejecute el siguiente comando para compilar UseAudio.java: javac UseAudio.java Si todo va bien, debería observar UseAudio.class en el directorio actual. Ejecute el siguiente comando para ejecutar UseAudio contra un archivo WAV ficticio llamado audio.wav: java UseAudio audio.wav Debería observar la siguiente salida: Muestras Frecuencia de muestreo: 0 Suponga que UseAudio.java no estaba ubicado en el mismo directorio que ca. ¿Cómo compilaría este archivo fuente y ejecutaría la aplicación resultante? La respuesta es usar la ruta de clases. La ruta de clases de Java La ruta de clases de Java es una secuencia de paquetes que la máquina virtual Java (JVM) busca para los tipos de referencia. Se especifica mediante la opción -classpath (o -cp) utilizada para iniciar la JVM o, cuando no está presente, la variable de entorno CLASSPATH. Formato de la ruta de clases Ya sea que use la opción -classpath/-cp o la variable de entorno CLASSPATH para especificar una ruta de clases, hay un formato específico que se debe seguir. En Windows, este formato se expresa como path1;path2;…, donde path1, path2, etc. son las ubicaciones de los directorios de paquetes. En Mac OS X, Unix y Linux, este formato cambia a path1:path2:…. Supongamos (en una plataforma Windows) que la biblioteca de audio está almacenada en C:audio y que UseAudio.java está almacenado en C:UseAudio, que es el actual. Especifique los siguientes comandos para compilar el código fuente y ejecutar la aplicación: javac -cp ../audio UseAudio.java java -cp ../audio;. UseAudio audio.wav El carácter de punto en la línea de comandos con prefijo java representa el directorio actual. Debe especificarse para que la JVM pueda localizar UseAudio.class. Temas de paquetes adicionales El lenguaje Java incluye una palabra clave protected, que es útil en un contexto de paquete. Además, los paquetes se pueden distribuir en archivos JAR. Además, la JVM sigue un orden de búsqueda específico al buscar paquetes para tipos de referencia (independientemente de si estos paquetes están almacenados o no en archivos JAR). Exploraremos estos temas a continuación. Acceso protegido La palabra clave protected asigna el nivel de acceso protegido a un miembro de clase, como un campo o método (como ejemplo, protected void clear()). Declarar un miembro de clase protected hace que el miembro sea accesible a todo el código en cualquier clase ubicada en el mismo paquete y a las subclases independientemente de sus paquetes. Joshua Bloch explica la razón de dar a los miembros de la clase acceso protegido en su libro, Effective Java Second Edition (“Item 17: Design and document for heritage or else prohibit it”). Son ganchos en el funcionamiento interno de una clase para permitir a los programadores “escribir subclases eficientes sin un dolor indebido”. Consulte el libro para obtener más información. Archivos JAR Distribuir un paquete especificando instrucciones para crear la estructura de directorio necesaria junto con los archivos de clase del paquete (e instrucciones sobre qué archivos de clase almacenar en qué directorios) sería una tarea tediosa y propensa a errores. Afortunadamente, los archivos JAR ofrecen una alternativa mucho mejor. Un archivo JAR (Java archive) es un archivo ZIP con una extensión .jar (en lugar de la extensión .zip). Incluye un directorio META-INF especial que contiene manifest.mf (un archivo especial que almacena información sobre el contenido del archivo JAR) y una estructura de directorio jerárquica que organiza los archivos de clase. Utilice la herramienta jar del JDK para crear y mantener un archivo JAR. También puede ver la tabla de contenido del archivo JAR. Para mostrarte lo fácil que es usar esta herramienta, crearemos un archivo audio.jar que almacena el contenido del paquete ca.javajeff.audio. Luego accederemos a este archivo JAR cuando ejecutemos UseAudio.class. Crea audio.jar de la siguiente manera: Primero, asegúrate de que el directorio actual contenga la jerarquía de directorios ca / javajeff / audio creada anteriormente, y que audio contenga audio.class y WavReader.class. Segundo, ejecuta el siguiente comando: jar cf audio.jar cajavajeffaudio*.class La opción c significa «crear nuevo archivo» y la opción f significa «especificar nombre de archivo de archivo». Ahora deberías encontrar un archivo audio.jar en el directorio actual. Demuéstrate a ti mismo que este archivo contiene los dos archivos de clase ejecutando el siguiente comando, donde la opción t significa «listar tabla de contenidos»: jar tf audio.jar Puedes ejecutar UseAudio.class agregando audio.jar a su classpath. Por ejemplo, suponiendo que audio.jar se encuentra en el mismo directorio que UseAudio.class, puede ejecutar UseAudio en Windows mediante el siguiente comando: java -classpath audio.jar;. UseAudio Para mayor comodidad, puede especificar el -cp más corto en lugar del -classpath más largo. Búsqueda de paquetes para tipos de referencia Los recién llegados a los paquetes Java a menudo se frustran por el error «no se encontró ninguna definición de clase» y otros errores. Esta frustración se puede evitar en parte entendiendo cómo la JVM busca tipos de referencia. Para entender este proceso, debe darse cuenta de que el compilador es una aplicación Java especial que se ejecuta bajo el control de la JVM. Además, hay dos formas de búsqueda: búsqueda en tiempo de compilación y búsqueda en tiempo de ejecución. Búsqueda en tiempo de compilación Cuando el compilador encuentra una expresión de tipo (como una llamada de método) en el código fuente, debe localizar la declaración de ese tipo para verificar que la expresión sea legal. Como ejemplo, podría verificar que exista un método en la clase del tipo, cuyos tipos de parámetros coincidan con los tipos de los argumentos pasados en la llamada de método. El compilador primero busca los paquetes de la plataforma Java (en rt.jar y otros archivos JAR), que contienen los tipos de biblioteca de clases estándar de Java (como la clase System de java.lang). Luego busca los paquetes de extensión para los tipos de extensión. Si se especifica la opción -sourcepath al iniciar javac, el compilador busca los archivos de origen de la ruta indicada. El mecanismo de extensión Java admite un mecanismo de extensión que «proporciona una forma estándar y escalable de hacer que las API personalizadas estén disponibles para todas las aplicaciones que se ejecutan en la plataforma Java». Los paquetes de extensión de Java (también conocidos como paquetes opcionales) se almacenan en un directorio de extensiones especial llamado ext. Este directorio se encuentra en el subdirectorio lib del subdirectorio jre del directorio de inicio de Java. Oracle dejó obsoleto el mecanismo de extensión en Java 8 y lo eliminará en una versión futura. De lo contrario, el compilador busca en la ruta de clase (en orden de izquierda a derecha) el primer archivo de clase o archivo de origen que contiene el tipo. Si no hay ninguna ruta de clase, se busca en el directorio actual. Si no hay ningún paquete que coincida o el tipo sigue sin poder encontrarse, el compilador informa un error. De lo contrario, registra la información del paquete en el archivo de clase. Búsqueda en tiempo de ejecución Cuando se ejecuta el compilador o cualquier otra aplicación Java, la JVM encontrará tipos y debe cargar sus archivos de clase asociados a través de un código especial conocido como cargador de clases. La JVM usará la información del paquete almacenada previamente que está asociada con el tipo encontrado en una búsqueda del archivo de clase de ese tipo. La JVM busca los paquetes de la plataforma Java, seguidos de los paquetes de extensión, seguidos de la ruta de clase o el directorio actual (cuando no hay una ruta de clase) para el primer archivo de clase que contiene el tipo. Si no coincide ningún paquete o no se puede encontrar el tipo, se informa un error de «no se encontró la definición de clase». De lo contrario, el archivo de clase se carga en la memoria. Importación estática de miembros estáticos En Effective Java Second Edition, Item 19, Joshua Bloch menciona que los desarrolladores de Java solo deben usar interfaces para declarar tipos. No debemos usar interfaces para declarar interfaces constantes, que son interfaces que solo existen para exportar constantes. La interfaz de constante conmutable del Listado 4 proporciona un ejemplo. Listado 4. Una interfaz constante (Switchable.java) public interface Switchable { boolean OFF = false; boolean ON = true; } Los desarrolladores recurren a interfaces constantes para evitar tener que anteponer el nombre de la constante con el nombre de su tipo de referencia (por ejemplo, Math.PI). Por ejemplo, considere la clase Light del Listado 5, que implementa la interfaz Switchable para que el desarrollador sea libre de especificar las constantes OFF y ON sin tener que incluir prefijos de clase (si se declararon en una clase). Listado 5. Light implementa Switchable (Light.java, versión 1) public class Light implements Switchable { private boolean state = OFF; public void printState() { System.out.printf(«state = %s%n», (state == OFF) ? «OFF» : «ON»); } public void toggle() { state = (state == OFF) ? ON : OFF; } } Una interfaz constante proporciona constantes que están destinadas a usarse en la implementación de una clase. Como detalle de implementación, no debería filtrar constantes en la API exportada de la clase porque podrían confundir a otros que usen su clase. Además, para preservar la compatibilidad binaria, se compromete a admitirlas, incluso cuando la clase ya no las use. Importaciones estáticas Para satisfacer la necesidad de interfaces constantes y evitar los problemas que impone su uso, Java 5 introdujo las importaciones estáticas. Esta característica del lenguaje se puede usar para importar los miembros estáticos de un tipo de referencia. Se implementa mediante la declaración import static cuya sintaxis aparece a continuación: import static packagespec . typename . ( staticmembername | * ); Colocar static después de import distingue esta declaración de una declaración import normal. La sintaxis es similar a la declaración import normal en términos de la lista estándar separada por puntos de nombres de paquetes y subpaquetes. Puede importar un solo nombre de miembro estático o todos los nombres de miembros estáticos (gracias al asterisco). Considere los siguientes ejemplos: import static java.lang.Math.*; // Importar todos los miembros estáticos de Math. import static java.lang.Math.PI; // Importar solo la constante estática PI. import static java.lang.Math.cos; // Importa únicamente el método estático cos(). Una vez que los hayas importado, puedes especificar miembros estáticos sin tener que anteponerles sus nombres de tipo. Por ejemplo, después de especificar la primera o la tercera importación estática, podrías especificar cos directamente, como en[> double cosine = cos(angle); Para arreglar el Listado 5 de modo que ya no dependa de implements Switchable, podemos insertar una importación estática, como se muestra en el Listado 6. Listado 6. Una importación estática mejora la implementación de Switchable (Light.java, versión 2) paquete foo; import static foo.Switchable.*; public class Light { private boolean state = OFF; public void printState() { System.out.printf(«state = %s%n», (state == OFF) ? «OFF» : «ON»); } public void toggle() { state = (state == OFF) ? ON : OFF; } } El Listado 6 comienza con un paquete foo; declaración porque no puede importar miembros estáticos de un tipo ubicado en el paquete sin nombre. Este nombre de paquete aparece como parte de la importación estática posterior: import static foo.Switchable.*; A qué prestar atención al usar importaciones estáticas Hay dos precauciones adicionales con respecto a las importaciones estáticas. Primero, cuando dos importaciones estáticas importan el miembro con el mismo nombre, el compilador informa un error. Por ejemplo, suponga que el paquete physics contiene una clase Math que es idéntica a la clase Math de java.lang en que implementa la misma constante PI y métodos trigonométricos. Cuando se enfrenta al siguiente fragmento de código, el compilador informa errores porque no puede determinar si se está accediendo a la constante PI de java.lang.Math o physics.Math y se está llamando al método cos(): import static java.lang.Math.cos; import static physics.Math.cos; double angle = PI; System.out.println(cos(angle)); En segundo lugar, el uso excesivo de importaciones estáticas contamina el espacio de nombres del código con todos los miembros estáticos que importa, lo que puede hacer que su código sea ilegible e imposible de mantener. Además, cualquier persona que lea su código podría tener dificultades para averiguar de qué tipo proviene un miembro estático, especialmente al importar todos los nombres de miembros estáticos de un tipo. Conclusión Los paquetes le ayudan a crear bibliotecas reutilizables de tipos de referencia con sus métodos. Si debe llamar a un método (ya sea empaquetado en una biblioteca o no) con un argumento ilegal (como un índice negativo para una matriz), probablemente se encontrará con una excepción de Java.
Deja una respuesta