Introducción a Java para Programadores
Introducción a Java para Programadores
objetosos
por
Leo Gassman
Versión 2.1.1
Agosto 2009
La presente obra se distribuye bajo los términos de la licencia creative commons by-nc-sa versión 4.0.
([Link] Lo cual permite a terceros distribuir,
remezclar, retocar, y crear a partir de esta obra de modo no comercial, siempre y cuando den crédito
al autor y licencien sus nuevas creaciones bajo las mismas condiciones.
Algoritmos 2 Java Para programadores Objetosos
Indice
OBJETIVO...........................................................................................................................................................3
INTRODUCCIÓN A JAVA..................................................................................................................................3
INSTALACIÓN DE LA JDK.......................................................................................................................................3
INSTALACIÓN DE ECLIPSE......................................................................................................................................3
ARMADO DE UN PROYECTO JAVA..........................................................................................................................4
ESCRIBIENDO NUESTRAS CLASES..............................................................................................................4
MODIFICADORES DE VISIBILIDAD..........................................................................................................................5
AGREGANDO COMPORTAMIENTO...........................................................................................................................5
ESCRIBIENDO UN MAIN.........................................................................................................................................6
HERENCIA..............................................................................................................................................................7
AGREGANDO NUEVOS PACKAGES, ATRIBUTOS Y CONSTRUCTORES.......................................................................9
INTERFACES.........................................................................................................................................................10
COLECCIONES......................................................................................................................................................13
TOSTRING............................................................................................................................................................14
EQUALS...............................................................................................................................................................16
HASHCODE..........................................................................................................................................................18
ANNOTATIONS.....................................................................................................................................................18
COMENTARIOS Y JAVADOC..................................................................................................................................22
EXCEPCIONES.................................................................................................................................................26
MIS PRIMEROS BUGS...........................................................................................................................................26
ACCIONES SOBRE EXCEPCIONES..........................................................................................................................29
PROPAGACIÓN DE LA EXCEPCIÓN........................................................................................................................31
MANEJO DEL ERROR............................................................................................................................................31
Excepciones anidadas.......................................................................................................................................33
2
Algoritmos 2 Java Para programadores Objetosos
Objetivo
El presente documento pretende facilitar la transición de un programador hacia
el mundo de java. Se requiere tener nociones sobre programación orientada a
objetos. No pretende ser un manual de referencia de java. No abarca el lenguaje
completamente. La utilidad de este documento radica en que al finalizar la
lectura del mismo, el programador estará en condiciones de sentarse a realizar
un programa sencillo. El aprendizaje se debe complementar con un posterior
estudio con material adicional, dónde ciertos temas se traten en mayor
profundidad. Algunas de las soluciones propuestas en este documento no son las
mejores en cuanto a su diseño de software, sin embargo fueron elegidas por su
contenido didáctico.
Introducción a java
Java es un lenguaje orientado a clases y tipado. Para que un programa realizado
en java pueda correr, se necesita algo llamado máquina virtual java (java virtual
machine o JVM, o simplemente VM). La JVM es el ambiente dónde viven los
objetos, y tiene ciertas cositas, como un garbage collector, que nos simplificará
el uso de la memoria. Éste se encarga de detectar los objetos que ya no pueden
ser usados porque nadie los referencia y los elimina de la memoria
automáticamente.
Instalación de la jdk.
Además de la VM, para poder desarrollar en java, se necesita algunas cositas
más, todo esto viene en algo llamado JDK (java developer kit). Entonces, lo
primero que se debe realizar es instalar el jdk en la máquina que se usará para
desarrollo. La jdk se puede bajar del sitio de Sun:
[Link]
Al escribir este documento, la última versión estable es la jdk 6 update 7.
Bajar e instalar para el sistema operativo que corresponda.1
Instalación de eclipse
Teóricamente, con la jdk y un editor de texto, ya alcanza para desarrollar
programas. Sin embargo, realizarlo de este modo implica mucho esfuerzo a
pulmón. Nosotros vamos a usar un IDE que nos simplificará las cosas. El IDE
elegido es el Eclipse, uno de los mejores y más populares del mercado. Esto se
baja de [Link]
Hay varias distribuciones distintas de eclipse. Lo que varía entre las mismas son
los plugins que traen incorporados. El eclipse está preparado para que se le
puedan anexar plugins extendiendo su funcionalidad. Vamos a bajar el eclipse
classic, que es una distribución básica, sin embargo el documento podría ser
seguido con otra distribución.
1
Actualmente las dos implementaciones más comunes de la JVM son la de oracle
([Link] y la de OpenJDK
([Link] )
3
Algoritmos 2 Java Para programadores Objetosos
Ahora hay que decirle al eclipse cual es la VM que va a utilizar. Para eso, se debe
ir a preference -> java -> Installed jre , hay que agregar la VM de la jdk.
Si se instaló en Windows, y no se modificó ninguna opción, sería: C:\Archivos de
programas\Java\jdk1.6.0_07.
Si hay alguna otra, se recomienda removerla.
Modificadores de visibilidad.
Si observamos el código que se escribió automáticamente, podemos deducir
cómo es la declaración del package y de la clase. Vemos también que cuando
declaramos la clase, se le agrega la palabra “public”. ¿Qué es eso?, es un
modificador de visibilidad. Las clases, métodos y atributos tienen un alcance y no
pueden ser vistas desde cualquier parte del programa. Algo “private”, sólo se
puede ver desde esa clase. Si no ponemos nada (default) se puede ver desde todo
el package al cual pertenece. La visibilidad “protected” es similar a la de
package, y además puede ser visto por las subclases.
Es una buena práctica que todos los atributos de los objetos sean “private”.
Agregando comportamiento.
Vamos a hacer que nuestro recepcionista pueda saludar al mundo. Entonces
vamos a hacer que Recepcionista entienda el mensaje saludar(Mundo). Mundo
sabrá devolver su nombre con el mensaje getNombre(). Nuestro recepcionista
escribirá el saludo por consola. ¿Cómo quedarían nuestras clases?
[Link]:
package [Link];
[Link]
package [Link];
Escribiendo un main
Para probar lo que estamos haciendo, vamos a hacer una clase de prueba, que
tendrá un método main que el eclipse podrá correr (el eclipse o directamente
cualquier JVM, pues el eclipse no hace más que enviarle el pedido a la JVM que
configuramos).
Para evitar que se nos mezcle nuestra clase de test con nuestras clases de
negocio, agregaremos una nueva carpeta de sources para escribir test. Entonces,
hacemos clic derecho sobre el proyecto en la vista projects, new -> source folder
(no confundir con folder). La llamaremos test. Y aceptamos.
Vamos a crear el mismo package en la carpeta test, y luego la clase Test en dicho
package.
Fíjese que si en la vista java browser, hacen clic en el proyecto, y luego en el
package [Link], van a ver las tres clases, porque a
pesar de que se encuentran en distintas carpetas físicas, los tres pertenecen al
mismo package. Entonces, podemos hacer referencia a las cosas que sólo tienen
visibilidad de package desde nuestra clase Test, y cuando haya que hacer el
deploy de la aplicación será fácil dejar afuera las clases creadas para testing.
Nuestra clase Test, deberá tener un método Main de la siguiente forma.
package [Link];
7
Algoritmos 2 Java Para programadores Objetosos
package [Link];
package [Link];
Herencia
Vamos a hacer cambios para que haya dos tipos de Recepcionistas. Uno que sea
el Clásico Hola Mundo, y otro que sea más formal.
Ambos tienen el comportamiento en común de escribir por consola, pero difieren
en cómo armar el mensaje. Entonces, dejaremos el comportamiento en común en
la clase Recepcionista, y escribiremos las clases RecepcionistaClasico y
RecepcionistaFormal, que armaran el String de saludo de formas distintas. La
clase Recepcionista la convertiremos abstracta porque no podemos tener
recepcionistas que no sean de alguno de los dos tipos que nombramos, pues no
sabríamos cómo armar el mensaje. Entonces, nuestra clase Recepcionista
quedaría así
[Link]
package [Link];
8
Algoritmos 2 Java Para programadores Objetosos
public abstract class Recepcionista {
[Link]
package [Link];
@Override
protected String armarSaludo(Mundo mundo) {
return "hola " + [Link]();
}
[Link]
package [Link];
@Override
protected String armarSaludo(Mundo mundo) {
return "Buen día estimado " + [Link]();
}
9
Algoritmos 2 Java Para programadores Objetosos
package [Link];
Hay dos cosas nuevas aquí. La primera, es que estamos definiendo un Atributo
nombre para la persona. Sigue la misma convención que los métodos.
La segunda, es que hay un método que no es igual a los métodos comunes: no
define que devuelve, y se llama igual que la clase. Ese método es un constructor.
10
Algoritmos 2 Java Para programadores Objetosos
[Link]
package [Link];
public Persona() {
super();
}
Interfaces
Vamos a agregar la posibilidad de que Recepcionista pueda saludar a Mundos y a
Personas. Fíjese que Recepcionista, sólo necesita que el objeto a saludar
entienda el Mensaje getNombre(). Una forma de lograrlo, es armar una
superclase común entre Mundo y Persona. Pero esto no es siempre posible,
porque como java no tiene herencia múltiple, si Mundo y Persona ya
pertenecieran a otras jerarquías, no se podría realizar. La forma de solucionarlo
es armando una interface que defina el mensaje getNombre(), y hacer que
Mundo y Recepcionista, la implementen.
Una interface en java, es un archivo .java similar al que define una clase.
11
Algoritmos 2 Java Para programadores Objetosos
[Link]
package [Link];
[Link]
package [Link];
public Persona() {
super();
}
[Link]
package [Link];
import [Link];
Note que en estas líneas hay un elemento nuevo, la línea que dice:
import [Link];
12
Algoritmos 2 Java Para programadores Objetosos
¿Por qué es esto?. Dijimos al principio, que el nombre de una clase (y también el
de las interfaces), es el nombre de package “.”nombre corto. Cuando desde una
clase se hace referencia a otra que está en el mismo package se puede usar el
nombre corto sin problemas. Cuando la clase o interface que queremos usar está
en otro package (como en este caso, Nombrable está en un package distinto al
de Mundo), para usar el nombre corto debemos importarlo.
Otra forma de hacer que Mundo implemente Nombrable sin usar el import es:
package [Link];
...
Nombrable, al ser una interface, define también un tipo de dato. Veamos como
quedan las clases de Recepcionistas. Para hacer el cambio, podemos cambiar las
tres clases una a una, o podemos usar el refactor de eclipse, para cambiar la
superclase, y que solitas las subclases se den cuenta. Entonces, vamos a la clase
recepcionista, hacemos clic sobre el metodo armarSaludo y apretamos alt + shift
+ c (que es el short cut a “change method signature”) y reemplazamos Mundo
por Nombrable. Después repetimos la operación para el método saludar.
[Link]
package [Link];
import [Link];
[Link]
package [Link];
import [Link];
@Override
protected String armarSaludo(Nombrable nombrable) {
return "hola " + [Link]();
}
[Link]
13
Algoritmos 2 Java Para programadores Objetosos
package [Link];
import [Link];
@Override
protected String armarSaludo(Nombrable nombrable) {
return "Buen día estimado " + [Link]();
}
Y veamos la siguiente clase de Test, dónde nos podemos dar cuenta del uso del
polimorfismo entre Mundo y Persona
package [Link];
import [Link];
import [Link];
Colecciones
Veremos brevemente el uso de una colección, En java hay una interface llamada
Collection, que tiene los métodos add y remove para agregar y sacar objetos.
Tiene un método iterator, que devuelve un objeto que es capaz de recorrerlo.
Una de las implementaciones se llama ArrayList, que implementa en realidad
una subinterface de Collection, llamada List, (la herencia entre interfaces se
escribe igual que entre las clases, con la diferencia de que una interface puede
extender de varias simplemente separándolas por comas).
14
Algoritmos 2 Java Para programadores Objetosos
package [Link];
import [Link];
import [Link];
import [Link];
import [Link];
toString
Vamos a modificar Persona para que tenga una dirección. En lugar de tener un
atributo calle y otro número en la Persona, vamos a generar la abstracción de
Dirección. Entonces las Personas tendrán una dirección, que podrán devolver. Y
vamos a ir agregando cierto comportamiento básico con las direcciones. Por el
momento, le podemos preguntar a una persona dónde vive.
[Link]
package [Link];
2
Se recomienda estudiar con material adicional el tema de colecciones
15
Algoritmos 2 Java Para programadores Objetosos
[Link]
package [Link];
public Persona() {
super();
}
[Link]
package [Link];
import [Link];
import [Link];
16
Algoritmos 2 Java Para programadores Objetosos
¿Por qué el test compiló, si el operador “+” opera entre String y tanto persona
como dirección no lo son? En la clase Object hay un método toString(), lo cual
significa que cualquier objeto sabe responder a ese mensaje. El operador
simplemente opera contra el toString de Juan. Hay varias herramientas en el
mundo java (debugging y logueos) que se basan en el toString, entonces, es
importante que nuestra clase redefina este método para que diga algo sensato,
ya que la implementación de object no es del todo feliz.
@Override
public String toString() {
return [Link]();
}
}
[Link]
package [Link];
@Override
public String toString() {
return [Link] + " " + [Link];
}
}
Aquí también vemos como el operador “+” puede resolver tipos básicos como
enteros.
Ahora la consola del mismo main sería:
La persona Leo vive en Esteban de luca 1322
Equals
Vamos a agregar un mensaje a la persona para que nos cuente si vive en cierto
lugar.
El test con el que lo vamos a probar es:
public static void main(String[] args) {
Direccion direccion = new Direccion("Esteban de Luca", 1322);
Persona persona = new Persona("Leo", direccion);
if([Link](direccion)){
[Link](persona + " vive en " + direccion);
17
Algoritmos 2 Java Para programadores Objetosos
}
else {
[Link](persona + " no vive en " + direccion +", vive en " +
[Link]());
}
}
package [Link];
…
public boolean viveEn(Direccion direccion) {
return [Link] == direccion;
}
}
Al ejecutarlo, la salida es:
Leo vive en Esteban de Luca 1322
Para resolver el problema, lo que hicimos es comparar si la dirección que nos
pasaron por parámetro es la misma instancia que la dirección a la cual tengo
como atributo. Es decir el operador == se fija que dos referencias apunten al
mismo objeto.
3
Estudie sobre “value objects” acerca de estas ocaciones
18
Algoritmos 2 Java Para programadores Objetosos
[Link]
package [Link];
[Link]
package [Link];
…
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Direccion)) {
return false;
}
Direccion otraDireccion = (Direccion) obj;
return [Link]([Link]) && [Link] ==
[Link];
}
}
Con esta implementación la salida es:
Leo vive en Esteban de Luca 1322
4
Para saber un poco más de las características de las clases que necesitan redefinir el equals,
puede consultar el apunte sobre value objects de la cátedra
19
Algoritmos 2 Java Para programadores Objetosos
Cómo obj es una variable de tipo Object, sólo me deja enviar mensajes definidos
en ese tipo. Sin embargo, yo estoy seguro que hace referencia a una instancia de
tipo Direccion (porque más arriba hice el instanceof), entonces, puedo castear el
objeto a ese tipo. El casteo es una indicación que el desarrollador le hace al
compilador para asegurarle que un objeto cumple con cierto tipo y a partir de ahí
poder mandarle mensajes que antes no eran visibles.
¿Por qué usé equals para la calle e “==” para el número? Es correcto al decir
que la igualdad de un objeto depende de la igualdad de algún atributo, usar el
equals en el atributo. Ahora cuando ese atributo es de un tipo básico, la
comparación se realiza con ==.
Para dar por concluido la redefinición del equals, es necesario redefinir otro
método llamado hashCode.
HashCode
El método hashCode está definido en Object y es usado para realizar
optimizaciones. Por este motivo existe una restricción que dice que si dos objetos
son equals, entonces ambos deben devolver el mismo número en el método
hashCode. Un claro ejemplo donde se ve el uso de hashCode es con la clase
HashSet. Esta clase es una colección que tiene internamente una tabla de hash,
entonces al preguntarle si contiene un objeto, primero reduce la cantidad de
objetos a evaluar preguntando el hashCode, para luego preguntar por equals.
Vamos a probar el siguiente ejemplo y veremos el problema de haber redefinido
el equals sin haber redefinido el hashCode.
Para poder redefinir el equals evitando este problema, se sigue una regla simple:
redefinir el hashCode haciendo que dependa de los mismos atributos de los
cuales depende el equals. Si una dirección es igual a otra si son la misma calle y
número, un hashCode válido puede ser:
@Override
public int hashCode() {
return [Link]() + [Link];
}
20
Algoritmos 2 Java Para programadores Objetosos
Annotations
Una annotation es metadata que se le puede agregar a los elementos que
componen una clase, o más genéricamente un tipo. Para ver todos los elementos
que se pueden anotar, revise el Enum ElementType.
Vamos a modificar nuestro programa para informe por pantalla si está saludando
a un mundo o a una persona. Saber si se trata de un mundo o de una persona, se
puede saber preguntando por la clase. Lo que no se puede saber es el género de
lo que representa la clase, es decir si es “un mundo” o “una mundo”. Entonces
vamos a usar una annotation para agregar esta información.
De manera similiar a crear una clase, hacemos clic derecho sobre el package
[Link], new Annotation, y la llamamos Articulo.
En una annotation, solo podemos poder información que sea de tipo básico,
String, Class o enums. (Los enums son un tipo de dato con un conjunto constante
de valores) .
Cómo nosotros queremos agregar un solo valor, llamaremos a ese valor “value”.
Ponerle ese nombre hace que sea más corta la forma de utilizarlo, pues a la hora
de usar la annotation si no se indica ningún nombre java por defecto usará value.
import [Link];
import [Link];
import [Link];
import [Link];
@Retention([Link])
@Target([Link])
public @interface Articulo {
String value();
La annotation del target era fácil deducirlo, pero ¿y la Retention?. Las annotation
tienen un tiempo de vida por el cual estarán disponbles. Hay tres niveles.
Source: son annotation que viven en el .java y el compilador descartará al
generar el binario, por ejemplo la @Override. Class: es el valor default, la
annotation está presente en el archivo .class pero no mientras corre la JVM. Y la
21
Algoritmos 2 Java Para programadores Objetosos
Anotemos ahora Mundo y Persona indicando que articulo debería usarse en cada
caso.
[Link]
package [Link];
import [Link];
import [Link];
@Articulo("un")
public class Mundo implements Nombrable {
[Link]
package [Link];
@Articulo("una")
public class Persona implements Nombrable {
import [Link];
import [Link];
22
Algoritmos 2 Java Para programadores Objetosos
import [Link];
import [Link];
Consola:
saludando a un Mundo
hola Mundo
Para ver como se usa cuando quiero agregar más de un valor a una annotation,
vamos a hace que a las personas le diga “sr”, mientras que al mundo no le diga
nada. (Nótese que es un ejemplo didáctico, no pretendemos discriminar a las
damas).
import [Link];
import [Link];
import [Link];
import [Link];
@Retention([Link])
@Target([Link])
public @interface Articulo {
String cardinal();
String titulo();
23
Algoritmos 2 Java Para programadores Objetosos
[Link]
package [Link];
import [Link];
import [Link];
@Articulo(cardinal="un", titulo="")
public class Mundo implements Nombrable {
…
[Link]
package [Link];
import [Link];
import [Link];
@Override
protected String armarSaludo(Nombrable nombrable) {
return "hola " + getTitulo(nombrable) + " " + [Link]();
}
}
Y ahora nuestra salida por consola es:
saludando a un Mundo
hola Mundo
24
Algoritmos 2 Java Para programadores Objetosos
hola Sr. José
Comentarios y Javadoc
En java existen los clásicos comentarios heredados de c:
//La doble barra comenta la línea entera
Existen herramientas que procesan los comentarios javadoc para generar una
presentación útil de la clase. El más conocido es el comando javadoc que viene
con el jdk que genera documentación html. También en el eclipse al apretar F2
sobre algún componente lo muestra en una ventana.
package [Link];
import [Link];
import [Link];
import [Link];
import [Link];
25
Algoritmos 2 Java Para programadores Objetosos
/**
* Indica cuáles son los artículos que corresponde usar con un
* Nombrable para generar frases en el idioma castellano.
* @author Leo Gassman
* @see Nombrable
*/
@Retention([Link])
@Target([Link])
public @interface Articulo {
/**
* Dependiendo de si es masculino o femenino,
* se admiten las formas "un" o "una"
*/
String cardinal();
/**
* Es un título agregado al nombre
*/
String titulo();
Para ver cómo puede resultar útil el javadoc, podemos ir a la clase Recepcionista
clásico, poner el cursor sobre Articú[Link] y apretar f2
26
Algoritmos 2 Java Para programadores Objetosos
Hasta ahora escribimos comentarios que tienen que ver con la responsabilidad
del componente. Sin embargo también es útil para decir ciertas características
técnicas o contratos que cumple
package [Link];
/**
* Es un Value Object (inmutable) que representa
* una dirección física
* @author Leo Gassman
*/
public class Direccion {
/**
* @param calle Calle o Avenida del domicilio, no puede ser null
* @param numero Altura
*/
public Direccion(String calle, int numero) {
[Link] = calle;
[Link] = numero;
}
Nótese que ahora en estos comentarios se ve un contrato implícito: que calle no
puede ser null, porque la clase no está preparada para funcionar con este valor.
(Si no me cree puede generar una Dirección sin calle y compararla contra otra).
Y las direcciones son Value Objects5 y por lo tanto inmutables. Lo cuál nos
permite tomar ciertas decisiones en la forma de usarlas.
Por otro lado introducimos un tag nuevo que es param, para dar indicaciones
sobre los parámetros.
Otro tag útil es el throws, que indica que un método puede salir por un error e
indica cuál puede ser, en la próxima sección hablaremos un poco de excepciones.
Por último, hay un tag llamado deprecated que se utiliza para indicar que un
componente o método no debe utilizarse porque es algo viejo que existe por
5
Puede consultar el apunte de Value Objects de la cátedra
27
Algoritmos 2 Java Para programadores Objetosos
compatibilidad hacia atrás. Significa que hay una forma mejor de cumplir con la
responsabilidad que hay detrás del componente o método, pero aún no puedo
borrar la forma vieja porque se está usando. Es importante indicar al momento
de deprecar cual es la nueva forma de realizar lo que se necesitaba. Vamos a
deprecar el método viveEn de Persona, porque por convención no queremos
utilizar artículos en los nombres de los mensajes.
/**
* @deprecated use {@link Persona#vive(Direccion)}
*/
public boolean viveEn(Direccion direccion) {
return [Link](direccion);
}
/**
* @return si es la dirección de la Persona
*/
public boolean vive(Direccion direccion) {
return [Link](direccion);
}
Note cómo al deprecar el eclipse tacha el nombre del método para resaltar que
no conviene usarlo. Por otro lado, aparece el tag link que sirve para que las
herramientas generen un vínculo con otro javadoc. También aparece en el
método vive el tag return para decir algo acerca sobre lo que devuelve el método
Para terminar de ver la utilidad de javadoc, es interesante ver los generados para
las mismas clases de java, en particular mire el javadoc de equals y hashcode de
Object, y el de la interface Set, pués no es solamente información útil para quien
va a llamar a esos métodos o usar un Set, si no para aquellos que quieran
extender o implementar un Object o un Set.
Dijimos más atrás que existe un comando que convierte el javadoc en html. El
eclipse sabe utilizar este comando, entonces vamos a generar el html.
Clic derecho sobre el proyecto -> export -> java -> javadoc -> next
28
Algoritmos 2 Java Para programadores Objetosos
Se puede elegir un destino, por default va a crear una carpeta doc dentro del
workspace. Dejamos esta opción y clic en fisish. Y le decimos que sí a la nueva
ventana de confirmación.
Ahora con un browser se puede navegar la documentación del proyecto.
Excepciones6
Mis primeros bugs.
Vamos a correr el siguiente main
public static void main(String[] args) {
Persona persona = new Persona();
Direccion direccion = new Direccion("Esteban de Luca", 1322);
if([Link](direccion)) {
[Link](persona + "vive en " + direccion);
}
else {
[Link](persona + "no vive en " + direccion +", vive en" +
[Link]());
}
}
En teoría este main debería imprimir si una persona vive o no en una dirección,
pero sin embargo por consola pasa lo siguiente:
Exception in thread "main" [Link]
at [Link]([Link])
at [Link]([Link])
6
En esta sección se abarca el tema en forma básica. Para más detalles se puede consultar el
apunte de excepciones de la cátedra
29
Algoritmos 2 Java Para programadores Objetosos
Esto ocurre cuando hubo un error en el programa, y lo que uno ve por pantalla
es lo que se llama stack trace. En un stack trace se ve dos cosas principalmente:
la excepción ocurrida y cuál es el camino de los llamados a métodos que la
provocaron. En nuestro caso la excepción es NullPointerException, lo cual
significa que se envió un mensaje a una referencia que estaba nula
(probablemente falta una inicialización). A veces la excepción viene acompañada
también de un mensaje que agrega información útil para entender que es lo que
ocurre.
Para detectar el lugar dónde está el problema, hay que ver el camino de
llamados. El stack se lee de abajo hacia arriba: el método main de la clase Test
llamó en la línea 15 al método vive de la clase Persona que en la línea 50 lanzó
una NullPointerException.
Entonces, vamos a la línea 50 de Persona (con el eclipse basta al hacer clic en el
stack trace sobre [Link]) y vemos la siguiente línea:
return [Link](direccion);
Sabemos ahora que hay una referencia nula, ¿Quién es? ¿La Persona this? ¿El
atributo direccion? ¿El parámetro direccion? ¿La referencia nula es dentro del
equals?
La Persona que recibe el mensaje no puede ser nula, de lo contrario no se
hubiera empezado a ejecutar el método.
Si el error hubiera sido dentro del equals, debería haber aparecido una línea más
dentro del stack trace.
El problema no puede ser el argumento porque ningún mensaje es enviado a él,
sólo se usa para pasarlo dentro del equals.
El problema está entonces en el atributo [Link]ón. No sólo porque los
demás no pueden ser, si no porque el equals es el único mensaje que se dispara
en esa línea, Recordenos que la excepción NullPointerException significa que el
receptor de un mensaje es nulo.
Para corroborar nuestras sospechas, revisamos que al instanciar la Persona en el
main, no le dijimos en ningún momento cuál es la Dirección, es más ¡tampoco le
asignamos el nombre! Corregimos agregando los seteos y corremos de nuevo:
30
Algoritmos 2 Java Para programadores Objetosos
}
}
Esto se puede ver como que en el main, línea 17, Ocurrió una
ClassCastException, se tenía una referencia a una variable de tipo Mundo y se
quiso ver como Persona. Si revisamos es verdad, porque en la colección de
Nombrables había mezclados mundos y Personas. Arreglar este error ya es un
poco más complicado porque hay que cambiar una decisión de diseño: tener
Personas y Mundos mezclados en esa colección que se usa para verificar si hay
mas cosas que viven en mi casa, o agregar el mensaje vive en la interface
Nombrable para que todos puedan contestar.
Cuando uno quiere correr algo sobre código que no compila, también ocurre una
excepción.
31
Algoritmos 2 Java Para programadores Objetosos
La consola muestra:
Exception in thread "main" [Link]: Unresolved compilation problems:
Esto cannot be resolved to a type
Syntax error on token "compila", ; expected
at [Link]([Link])
[Link]
package [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
[Link]
package [Link];
import [Link];
import [Link];
import [Link];
Se queja de que Writer y FileWriter declara que puede ocurrir una excepción de
tipo IOException y nosotros no estamos tomando ninguna decisión con respecto
a eso.
Hay tres decisiones que uno puede tomar ante la ocurrencia de una excepción.
1) Que se arregle el que me llamó, yo no hago nada, simplemente declaro que
mi método puede tirar excepción y entonces va a subir el problema por el
stack trace hasta que alguien la trate o la máquina virtual nos corte la
8
Al igual que en C el caracter ‘\n’ indica salto de línea
33
Algoritmos 2 Java Para programadores Objetosos
Propagación de la excepción
Consiste en declarar que nuestro método tira una excepción en la firma del
mismo:
public class Cartero {
35
Algoritmos 2 Java Para programadores Objetosos
Hay otro bloque llamado finally que viene de la mano con el try/catch. El mismo
se utiliza cuando hay algo que queremos hacer independientemente de si ocurre
un problema o no. En este documento no lo utilizaremos.
Excepciones anidadas
La tercera opción persigue la idea de ir elevando el nivel de abstracción del
error. Por lo tanto cuando ocurre un error grabando el archivo, no quiero que el
método me diga “tengo un problema de entrada y salida” si no que me diga algo
así cómo “No se puede generar la hoja de ruta”. Entonces, para lo siguiente
vamos a generar una excepción nuestra llamada HojaDeRutaException. Para eso
basta con generar una clase y hacerla extender de Exception (si quiero que sea
chequeada) o de RuntimeException (si quiero que sea no chequeada).
package [Link];
Vamos a forzar el error esta vez usando una dirección inválida de archivo. Y
vamos a ver cómo la excepción se propaga hasta la VM y el stack trace que se
genera.
public static void main(String[] args) {
new Cartero().generarHojaDeRuta(getPersonas(), "estaDireccionEsInvalida");
}
Stack trace:
36
Algoritmos 2 Java Para programadores Objetosos
Exception in thread "main" [Link]: No se pudo generar
la hoja de ruta en la dirección: HH:\estaDireccionEsInvalida
at [Link]([Link])
at [Link]([Link])
Caused by: [Link]: HH:\estaDireccionEsInvalida (The filename,
directory name, or volume label syntax is incorrect)
at [Link](Native Method)
at [Link].<init>([Link])
at [Link].<init>([Link])
at [Link].<init>([Link])
at [Link]([Link])
at [Link]([Link])
... 1 more
¡No asustarse! El stack trace a simple vista parece feo pero si aprendemos a
entenderlo puede ser un gran amigo. ¿Qué es lo que está diciendo?
Primero leemos la excepción sin tener en cuenta los métodos por los que pasó.
Esto nos da una idea del problema sin entrar en el detalle de dónde ocurrió:
No se pudo generar la hoja de ruta en la dirección: HH:\estaDireccionEsInvalida
…
Caused by: [Link]: HH:\estaDireccionEsInvalida (The filename,
directory name, or volume label syntax is incorrect)
…
¡Ahora se entiende un poco mejor! Por suerte los mensajes de las excepciones
fueron escritos a conciencia y dan una buena idea de lo que ocurre: No se pudo
generar la hoja de ruta porque la dirección dónde se quiso generar es
sintácticamente incorrecta. Y además es tan amable de decirnos cual es la
dirección del problema.
Ahora veamos cómo con este stack trace puedo llegar hasta el problema. La
primera línea que hace referencia a un método en cada excepción es el lugar
dónde se lanzó esa excepción, es decir la línea dónde están los throws:
[Link]([Link])
es:
throw new HojaDeRutaException("No se pudo generar la hoja de ruta en la dirección:
" + fileName, e);
y at [Link](Native Method)10
es… ¡Oh! ¡Diantres!! No tengo disponible el código dónde ocurre realmente la
excepción. ¿Qué hago? En ese caso empiezo a bajar en la sección del stack trace
correspondiente a esta excepción hasta encontrar una línea que tenga que ver
con lo que yo escribí, es decir, una clase mía y no de terceros porque ahí no
puedo tomar ninguna acción.
En nuestro caso la primera línea que tiene sentido es:
at [Link]([Link])
correspondiente a:
Writer writer = new FileWriter(fileName);
Bien, de estas dos líneas se deduce que, la excepción de no poder generar la hoja
de ruta es causa de que me explota la aplicación al instanciar un FileWriter. Por
el mensaje de error me doy cuenta que el problema es que ese fileName está mal
10
Un método nativo no está escrito en java, si no que la máquina virtual hace un llamado al
sistema operativo.
37
Algoritmos 2 Java Para programadores Objetosos
formado, por lo tanto tengo que rastrear en el stack hasta ver de dónde sale este
parámetro:
at [Link]([Link])
me lleva a :
[Link](personas, fileName);
Acá también fileName es un parámetro, por lo tanto aún no tengo el punto dónde
arreglar. Cómo es la última línea de esta excepción, sigo navegando en el bloque
de la primera, siguiendo la pista de dónde viene el fileName:
at [Link]([Link])
new Cartero().generarHojaDeRuta(getPersonas(), "HH:\\estaDireccionEsInvalida");
38