Java Classpath


El objetivo de esta práctica es mostrar el concepto del Java classpath y cómo afecta a la compilación y ejecución de los programas Java. El classpath es el concepto más básico pero esencial, que consiste en la ubicación de los ficheros. Si el classpath no se configura correctamente, se experimentará con la excepción (error) "java.lang.NoClassDefFoundError: <nombre del fichero class>".


Recursos


Ejercicios


Ejercicio 1: Experimentar con classpath con el programa Hello en la línea de comandos

Este ejercicio se realiza en una ventana de comandos y muestra el uso de class path con un programa simple que tiene una clase.


(1.0) Compilar el programa Hello usando "javac" y ejecutando con "java" en la línea de comando

Este paso ya se ha realizado como parte del Ejercicio 1 del Laboratorio: Conociendo el entorno de programación Java.  Se repite aquó por si no se ha visto antes.

1. Crear un directorio donde se va a poner los programas Java.


C:\>mkdir c:\misprogramasjava
C:\>cd \misprogramasjava

2. Escribir Hello.java con un editor de texto (pe. jedit) como se muestra en Código-1.10. 

C:\misprogramasjava>jedit Hello.java

public class Hello {
   
    /**
     * Mi primer programa Java
     */
    public static void main( String[] args ){
       
        // Print the string "Hello world" on screen
        System.out.println("Hello world");
       
    }

}
Código-1.10: Hello.java

3. Compilar Hello.java usando el compilador javac. El compilador javac viene con el J2SE SDK descargado e instalado. Se encuentra en el directorio %JAVA_HOME%\bin (Windows). El resultado de la compilación será la creación del fichero de clase Hello.class.

C:\misprogramasjava>javac Hello.java

4. Hay que asegirarse que se ha creado el fichero Hello.class. El fichero Hello.class contiene la representación en bytecode de la clase Hello.

C:\misprogramasjava>dir Hello.class
 
Volume in drive C is ACER
 Volume Serial Number is 58CE-B0DC

 Directory of C:\misprogramasjava

01/19/2009  12:16 AM               415 Hello.class
               1 File(s)            415 bytes
               0 Dir(s)  21,578,907,648 bytes free

5. Ejecutar el programa Hello usando el comando java.

C:\misprogramasjava>java Hello
Hello world 

                                                                                                                        Volver al inicio del ejercicio


(1.1) Ejercicio con la opción "-classpath" o "-cp" cuando se ejecuta con el comando "java"

La opción "-classpath <path>" o "-cp <path>" especifica la ubicación donde residen los ficheros de clase (en el ejemplo, sólo se tiene un fichero de clase llamado Hello.class).  Cuando no se especifica la opción classpath, el comando java trata de encontrar los ficheros de clase en los directorios especificados en la variable de entorno CLASSPATH o en el directorio en curso. Así, el intérprete Java trata de encontrar los ficheros de clase en el siguiente orden:

1. Ejecutar el programa Hello usando el comando java con la opción -cp .  (hay un espacio y un punto después de -cp seguido por un espacio) o la opción -classpath . (hay un espacio y un punto después de -classpath seguido por un espacio).

C:\misprogramasjava>java -cp . Hello
Hello world
C:\misprogramasjava>java -classpath . Hello
Hello world

2. Ejecutar "java -classpath .. Hello" para ver si funciona. (en lugar de un punto simple ., se especifican dos puntos .. entre -classpath y Hello. El doble punto .. especifica el directorio padre, C:\ en este ejemplo, mientra que el punto simple . especifica el directorio en curso, c:\misprogramasjava en este ejemplo.) Debe dar un error como se muestra a continuación, debido a que se asigna como classpath el directorio padre, C:\, que no contiene el fichero Hello.class.

C:\misprogramasjava>java -classpath .. Hello
Exception in thread "main" java.lang.NoClassDefFoundError: Hello

3. Copiar el fichero Hello.class al directorio padre e intentar el comando anterior otra vez. Se verá el siguiente resultado:

C:\misprogramasjava>move Hello.class ..  (mover Hello.class al directorio padre)
C:\misprogramasjava>java -classpath .. Hello (ejecuta el programa usando la classpath asignada al directorio padre)
Hello world
C:\misprogramasjava>java -classpath C:\ Hello (igual al anterior haciendo classpath explícito)
Hello world
C:\misprogramasjava>java -classpath . Hello (esto da error porque Hello.class ya no se encuentra en el directorio en curso)
Exception in thread "main" java.lang.NoClassDefFoundError: Hello

4. Se puede especificar varios directorios en la opción -classpath y -cp.

C:\misprogramasjava>java -classpath .;..;c:\tmp Hello (si el fichero Hello.class se encuentra en uno de los tres directorios, funciona .)
Hello world

                                                                                                                       Volver al inicio del ejercicio


(1.2) Ejercicio con la variable de entorno CLASSPATH

Se puede asignar un classpath en el sistema mediante la variable de entorno CLASSPATH. Este classpath se aplica en todo el sistema con lo que significa que se aplica a todos los programas Java que se ejecutan en la plataforma.  Se puede sobreescribir mediante la opción -classpath o -cp cuando se ejecuta un programa particular.

1. Asignación de la variable de entorno CLASSPATH primero con el directorio que contiene el fichero Hello.class y después se ejecuta el programa.

C:\misprogramasjava>set CLASSPATH=c:\misprogramasjava (Asignación de la variable de entorno CLASSPATH)
C:\misprogramasjava>java Hello  (se produce una excepción porque no se tiene el fichero Hello.class no está en el directorio en curso sino en el directorio padre, porque se movió en el paso anterior )
Exception in thread "main" java.lang.NoClassDefFoundError: Hello
C:\misprogramasjava>move ..\Hello.class . (para mover Hello.class nuevamente al directorio en curso)
C:\misprogramasjava>java Hello (ahora funciona )
Hello world

2. Asignar la variable de entorno CLASSPATH a un directorio temporal y ejecutar el programa.

C:\misprogramasjava>set CLASSPATH=c:\tmp (asignar la variable de entorno CLASSPATH a un directorio temporal, c:\tmp)
C:\misprogramasjava>java Hello (ocurre un error porque el directorio c:\tmp no contiene el fichero Hello.class)
Exception in thread "main" java.lang.NoClassDefFoundError: Hello
C:\misprogramasjava>java -cp . Hello (se reasigna CLASSPATH con un classpath propio)
Hello world

3. Asignar la variable de entorno CLASSPATH a un conjunto de directorios y ejecutar el programa.

C:\misprogramasjava>set CLASSPATH=c:\tmp;c:\misprogramasjava (cada directorio se busca en el orden especificado )
C:\misprogramasjava>java Hello (funciona porque el directorio c:\misprogramasjava tiene el fichero Hello.class)
Hello world

Como se ha visto antes, se puede asignar la variable de entorno CLASSPATH a un conjunto de directorios. En Windows se usa punto y coma ; para separarlos.

                                                                                                                       Volver al inicio del ejercicio

Resumen

Este ejercicio ha mostrado el uso de la opción -classpath y cómo configurar la variable de entorno CLASSPATH.
   
                                                                                                                                   Volver al inicio

Ejercicio 2: Experimentar con classpath con el programa StudentRecordExample en la línea de comandos

Este ejercicio también se realiza en una terminal de comandos (Símbolo del sistema). El ejercicio muestra el uso de class path con el programa StudentRecordExample primero sin utilizar la sentencia package y después con la sentencia package. Notar que uno de los beneficios de usar un IDE como NetBeans es que el IDE gestiona todos los class path de tal manera que uno no debe preocuparse por esta opción. 

  1. Escribir StudentRecord.java y StudentRecordExample.java sin la sentencia package
  2. Añadir la sentencia package en StudentRecord.java y StudentRecordExample.java
  3. Añadir una clase de un package diferente

(2.1) Escribir StudentRecord.java y StudentRecordExample.java sin la sentencia package


1. Escribir StudentRecord.java usando un editor de textos (pe. jedit) como se muestra en Código-2.10. 


C:\misprogramasjava>jedit StudentRecord.java

public class StudentRecord {
   
    // Declare instance variables.
    private String name;
    private double mathGrade;
    private double englishGrade;
    private double scienceGrade;
    private double average;
   
    // Declare static variables.
    private static int studentCount = 0;
   
    public String getName(){
        return name;
    }
   
    public void setName(String temp ){
        name =temp;
    }
   
    public double getAverage(){
        double result =0;
        return result;
    }
   
    public static int getStudentCount(){
        return studentCount;
    }
   
    public static void increaseStudentCount(){
        studentCount++;
    }
   
}
Código-2.10: StudentRecord.java

2. Escribir StudentRecordExample.java usando un editor de textos (pe. jedit) como se muestra en Código-2.11. 

C:\misprogramasjava>jedit StudentRecordExample.java

public class StudentRecordExample {
   
    public static void main(String[] args) {
       
        // Create an object instance of StudentRecord class.
        StudentRecord annaRecord =new StudentRecord();
       
        // Increament the studentCount by invoking a static method.
        StudentRecord.increaseStudentCount();
       
        // Create another object instance of StudentRecord class.
        StudentRecord beahRecord =new StudentRecord();
       
        // Increament the studentCount by invoking a static method.
        StudentRecord.increaseStudentCount();
       
        // Create the 3rd object instance of StudentRecord class.
        StudentRecord crisRecord =new StudentRecord();
       
        // Increament the studentCount by invoking a static method.
        StudentRecord.increaseStudentCount();
       
        // Set the names of the students.
        annaRecord.setName("Anna");
        beahRecord.setName("Beah");
        crisRecord.setName("Cris");
       
        // Print anna's name.
        System.out.println("Name = " + annaRecord.getName());
       
        // Print number of students.
        System.out.println("Student Count = "+StudentRecord.getStudentCount());
       
    }
   
}
Código-2.11: StudentRecordExample.java

3. Borrar los ficheros class previamente creados para empezar desde un estado limpio.

C:\misprogramasjava>del *.class (eliminación de todos los ficheros class.)

4. Compilar el código especificando los dos ficheros con javac.

C:\misprogramasjava>javac StudentRecord.java StudentRecordExample.java (esto funciona)
C:\misprogramasjava>dir Student*.class
 Volume in drive C is ACER
 Volume Serial Number is 58CE-B0DC

 Directory of C:\misprogramasjava

01/27/2007  07:29 PM             1,266 StudentRecord.class
01/27/2007  07:29 PM             1,065 StudentRecordExample.class
            
5. Ejecutar el programa.

C:\misprogramasjava>java -cp . StudentRecordExample (esto funciona)
Name = Anna
Student Count = 3


(2.2) Añadir la sentencia package en StudentRecord.java y StudentRecordExample.java


En este paso, se explora la relación entre class path y la estuctura de package. 

1. Modificar StudentRecord.java como se muestra en Código-2.20. El fragmento de código que es necesario añadir se realta en color azul y negrita. 

// This class now belongs to studentpackage package.
package studentpackage;

public class StudentRecord {
   
    // Declare instance variables.
    private String name;
    private double mathGrade;
    private double englishGrade;
    private double scienceGrade;
    private double average;
   
    // Declare static variables.
    private static int studentCount = 0;
   
    public String getName(){
        return name;
    }
   
    public void setName(String temp ){
        name =temp;
    }
   
    public static int getStudentCount(){
        return studentCount;
    }
   
    public static void increaseStudentCount(){
        studentCount++;
    }
   
}
Código-2.20: adición de la sentencia package

2. Modificar StudentRecordExample.java como se muestra en Código-2.21. El fragmento de código que es necesario añadir se realta en color azul y negrita. 

// This class now belongs to studentpackage package.
package studentpackage;

public class StudentRecordExample {
   
    public static void main(String[] args) {
       
        // Create an object instance of StudentRecord class.
        StudentRecord annaRecord =new StudentRecord();
       
        // Increament the studentCount by invoking a static method.
        StudentRecord.increaseStudentCount();
       
        // Create another object instance of StudentRecord class.
        StudentRecord beahRecord =new StudentRecord();
       
        // Increament the studentCount by invoking a static method.
        StudentRecord.increaseStudentCount();
       
        // Create the 3rd object instance of StudentRecord class.
        StudentRecord crisRecord =new StudentRecord();
       
        // Increament the studentCount by invoking a static method.
        StudentRecord.increaseStudentCount();
       
        // Set the names of the students.
        annaRecord.setName("Anna");
        beahRecord.setName("Beah");
        crisRecord.setName("Cris");
       
        // Print anna's name.
        System.out.println("Name = " + annaRecord.getName());
       
        // Print number of students.
        System.out.println("Student Count = "+StudentRecord.getStudentCount());
       
    }
   
}
Código-2.21: adición de la sentencia package

3. Eliminar los ficheros class previamente creados para empezar desde un estado limpio.

C:\misprogramasjava>del *.class (Delete all class files.)

4. Compilar el código especificando los dos ficheros con javac.

C:\misprogramasjava>javac StudentRecord.java StudentRecordExample.java (esto funciona)
C:\misprogramasjava>dir Student*.class
 Volume in drive C is ACER
 Volume Serial Number is 58CE-B0DC

 Directory of C:\misprogramasjava

01/27/2007  07:29 PM             1,266 StudentRecord.class
01/27/2007  07:29 PM             1,065 StudentRecordExample.class
            
5. Ejecutar el programa.  Aparece una excepción java.lang.NoClassDefFoundError: StudentRecordExample.

C:\misprogramasjava>java -cp . StudentRecordExample (This should fail.)
Exception in thread "main" java.lang.NoClassDefFoundError: StudentRecordExample
(wrong name: studentpackage/StudentRecordExample)
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(Unknown Source)
        at java.security.SecureClassLoader.defineClass(Unknown Source)
        at java.net.URLClassLoader.defineClass(Unknown Source)
        at java.net.URLClassLoader.access$100(Unknown Source)
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClassInternal(Unknown Source)

La excepción ocurre porque Java trata de encontrar la clase StudentRecordExample.class en el directorio studentpackage (empezando desde el class path en curso, que está asignado al directorio en curso) puesto que StudentRecordExample.java tiene ahora una sentencia package studentpackage; , que indica que el fichero de clase reside en el directorio studentpackage.

6.  Crear un nuevo directorio llamado studentpackage y después mover StudentRecord.java y StudentRecordExample.java a él .

C:\misprogramasjava>mkdir studentpackage (Crea un subdirectorio llamado studentpackage.)
C:\misprogramasjava>move StudentRecord.java studentpackage\StudentRecord.java
C:\misprogramasjava>move StudentRecordExample.java studentpackage\StudentRecordExample.java

Los ficheros *.java que tienen la sentencia package tienen que estar ubicados dentro de la estructura de directorios reflejando el directorio especificado en la sentencia package.

7. Compilar el código.  Se obtiene un error porque se intenta compilar los ficheros Java que no están presentes en el directorio en curso.

C:\misprogramasjava>del *.class
C:\misprogramasjava>javac StudentRecord.java StudentRecordExample.java
error: cannot read: StudentRecord.java
1 error

8. Compilar el código usando el path de los ficheros *.java.  La compilación es correcta.  Notar que los ficheros class se crean ahora bajo el directorio studentpackage y no en el directorio en curso.

C:\misprogramasjava>javac studentpackage/StudentRecord.java studentpackage/StudentRecordExample.java
C:\misprogramasjava>dir studentpackage
 Directory of C:\misprogramasjava\studentpackage

01/27/2007  08:09 PM    <DIR>          .
01/27/2007  08:09 PM    <DIR>          ..
01/27/2007  08:02 PM               694 StudentRecord.java
01/27/2007  08:01 PM             1,343 StudentRecordExample.java
01/27/2007  08:14 PM               738 StudentRecord.class
01/27/2007  08:14 PM               978 StudentRecordExample.class

9. Ejecutar el programa usando la opción "-classpath .". 

C:\misprogramasjava>java -classpath . StudentRecordExample
Exception in thread "main" java.lang.NoClassDefFoundError: StudentRecordExample

Se obtiene una excepción java.lang.NoClassDefFoundError: StudentRecordExample. Esto ocurre porque Java tarta de encontrar la clase StudentRecordExample.class en el directorio (or directorios) especificado en el class path. Sólo hay un directorio en el class path, que es el directorio en curso que no tiene la clase StudentRecordExample.class.

10. Ejecutar el programa usando la opción "-classpath studentpackage". 

C:\misprogramasjava>java -classpath studentpackage StudentRecordExample
Exception in thread "main" java.lang.NoClassDefFoundError: StudentRecordExample
(wrong name: studentpackage/StudentRecordExample)
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(Unknown Source)
        at java.security.SecureClassLoader.defineClass(Unknown Source)
        at java.net.URLClassLoader.defineClass(Unknown Source)
        at java.net.URLClassLoader.access$100(Unknown Source)
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClassInternal(Unknown Source)

Se obtiene una excepción java.lang.NoClassDefFoundError: StudentRecordExample. Esta vez, la excepción dice (wrong name: studentpackage/StudentRecordExample) en la segunda línea. Esto indica que la JVM encuentra el fichero de clase pero la estructura de directorio del package especificada en el fichero de clase no coincide con la estructura de directorio en curso. En este ejemplo la JVM trata de encontrar el fichero StudentRecordExample.class en el class path primero. (La class path se asigna al directorio studentpackage). Lo encuentra. 

Una vez que el fichero se clase se encuentra JVM verifica que la jerarquía de package especificado en el fichero de clase, que es studentpackage a partir del directorio en curso. Es decir, la JVM trata de encontrar el fichero ./studentpackage/StudentRecordExample.class en el class path. Como el class path se asigna al directorio studentpackage, busca en ./studentpackage/studentpackage/StudentRecordExample.class, que no existe.

11. Ejecutar el programa usando la opción "-classpath ." pero con el path apropiado al fichero class. 

C:\misprogramasjava>java -classpath . studentpackage.StudentRecordExample
Name = Anna
Student Count = 3

No hay error porque el JVM enciuentra el fichero StudentRecordExample.class bajo el directorio studentpackage empezando desde el class path. 

12. Ejecutar el programa con la opción "classpath -studentpackage".

C:\misprogramasjava>java -classpath studentpackage studentpackage.StudentRecordExample
Exception in thread "main" java.lang.NoClassDefFoundError: studentpackage/StudentRecordExample

13. Ejecutar el programa en el directorio C:\misprogramasjava\studentpackage.

C:\misprogramasjava>cd studentpackage
C:\misprogramasjava\studentpackage>java -classpath . StudentRecordExample
Exception in thread "main" java.lang.NoClassDefFoundError: StudentRecordExample
(wrong name: studentpackage/StudentRecordExample)
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(Unknown Source)
        at java.security.SecureClassLoader.defineClass(Unknown Source)
        at java.net.URLClassLoader.defineClass(Unknown Source)
        at java.net.URLClassLoader.access$100(Unknown Source)
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClassInternal(Unknown Source)


14. Ejecutar el programa en el directorio C:\misprogramasjava\studentpackage con el classpath apropiado y el path al fichero de clase.

C:\misprogramasjava\studentpackage>java -classpath .. studentpackage.StudentRecordExample
Name = Anna
Student Count = 3

15. Ejecutar el programa en el directorio C:\ dcon el classpath apropiado y el path al fichero de clase.

C:\misprogramasjava>cd \
C:\>java -classpath misprogramasjava studentpackage.StudentRecordExample
Name = Anna
Student Count = 3



(2.3) Añadir una clase de un package diferente


1. Crear otro directorio llamado anotherpackge donde se va a escribir un nuevo programa Java.

C:\misprogramasjava>mkdir anotherpackage

2. Escribir DummyClass.java como se muestra en Código-2.31 bajo el directorio anotherpackage.

C:\misprogramasjava>cd anotherpackage
C:\misprogramasjava\anotherpackage>jedit DummyClass.java

package anotherpackage;

public class DummyClass {
   public String sayHello(String name){
        return "Hello, I am dummy.  Your name is " + name;
    }
}
Código-2.31: DummyClass.java

3.  Modificar StudentRecord.java para usar DummyClass como se muestra en Código-2.32. El fragmento de código a añadir se muestra en azul y negrita.

// This class now belongs to studentpackage package.
package studentpackage;

// Import packages
import anotherpackage.*;

public class StudentRecord {
   
    // Declare instance variables.
    private String name;
    private double mathGrade;
    private double englishGrade;
    private double scienceGrade;
    private double average;
   
    // Declare static variables.
    private static int studentCount = 0;
   
    public String getName(){
        DummyClass dummy = new DummyClass();
        System.out.println(dummy.sayHello(name));
        return name;
    }
   
    public void setName(String temp ){
        name =temp;
    }
   
    public static int getStudentCount(){
        return studentCount;
    }
   
    public static void increaseStudentCount(){
        studentCount++;
    }
   
}
Código-2.32: StundentRecord.java modificado

4. Compilar el código y verificar que los ficheros de clase están presentes.

          C:\misprogramasjava\anotherpackage>cd \misprogramasjava
C:\misprogramasjava>javac studentpackage/*.java
C:\misprogramasjava>dir studentpackage

01/27/2007  09:19 PM    <DIR>          .
01/27/2007  09:19 PM    <DIR>          ..
01/27/2007  09:27 PM             1,345 StudentRecordExample.java
01/28/2007  08:28 AM               827 StudentRecord.java
01/28/2007  08:30 AM               973 StudentRecord.class
01/28/2007  08:30 AM               978 StudentRecordExample.class
             
C:\misprogramasjava>dir anotherpackage

01/28/2007  08:03 AM    <DIR>          .
01/28/2007  08:03 AM    <DIR>          ..
01/28/2007  08:27 AM               165 DummyClass.java
01/28/2007  08:30 AM               503 DummyClass.class

5. Ejecutar el programa.

C:\misprogramasjava>java -classpath . studentpackage.StudentRecordExample
Hello, I am dummy.  Your name is Anna
Name = Anna
Student Count = 3



Resumen

En este ejercicio se ha explorado la relación entre la opción classpath y la estructura de los package.

                                                                                                                        Volver al inicio


Tarea


1. La tarea es desarrollar y ejecutar un programa Java en la línea de comandos usando los comandos "javac" y "java".
El objetivo de esta tarea es experimentar con packages d estructura de dos niveles.  Así,"foodpackage" es un package padre del package"fruitpackage".Cada uno debe tener su propio directorio y la estructura reflejar esa dependencia.  Los programas Food.java y FoodMain.java están bajo el directorio fruitpackage.