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>".
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:
- En los directorios especificados con la opción -classpath o -cp.
- En los directorios especificados con la variable de entorno CLASSPATH (si no se ha especificado con la opción -classpath o -cp)
- En el directorio en curso (si no se ha especificado la opción -classpath o
-cp option y no está asignada la variable de entorno CLASSPATH)
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.
- Escribir StudentRecord.java y StudentRecordExample.java sin la sentencia package
- Añadir la sentencia package en StudentRecord.java y StudentRecordExample.java
- 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".
- Escribir una clase llamada Food bajo el package foodpackage.fruitpackage
- Food.java debe tener la siguiente sentencia package en la parte superior
- package foodpackage.fruitpackage
- Añadir un par de métodos propios a Food.java
- Escribir una clase llamada FoodMain bajo el package
foodpackage.fruitpackage
- La clase FoodMain crea un objeto Food
- La clase FoodMain llama un método del objeto Food
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.