Está en la página 1de 16

Tema 6: Sentencias de control (2)

En este tema vamos a presentar dos tipos de sentencias de control: las sentencias
condicionales, y las excepciones. Ambos tipos de sentencias son necesarias en muchos casos
para poder proporcionar algoritmos eficientes y correctos. Explicaremos la semántica asociada a
dichas sentencias, así como su sintaxis en el lenguaje Java.

1. Sentencias condicionales: if
Las sentencias condicionales permiten comprobar el resultado de una expresión booleana (ver
Tema 5, apartado 2), y elegir acciones alternativas dependiendo del resultado de la
comprobación.
Java tiene dos instrucciones de selección:
• la instrucción if, y
• la instrucción switch

Comenzaremos con la primera de ellas. La sintaxis de una instrucción if es la siguiente:

Sentencia IF
if (expresion booleana){ //los paréntesis son obligatorios
sentenciasTrue;
}
else {
sentenciasFalse;
};

En una sentencia if, se evalúa la expresión booleana, que necesariamente tiene que aparecer
entre paréntesis. Si el resultado de dicha evaluación es true, entonces se ejecuta el conjunto de
sentencias que hemos denominado sentenciasTrue. Nos referiremos a estas sentencias
como parte then. Si este conjunto está formado por una única sentencia, entonces se pueden
omitir las llaves. Si el resultado de la expresión booleana es false, entonces se ejecutarán las
sentencias que hemos denominado sentenciasFalse, y que referiremos como parte
else. Dicha parte es opcional.

Las sentencias de las partes then y else de una construcción if pueden ser cualesquiera, incluso
otra sentencia if. A menudo, puede ser necesario anidar varias sentencias if para ejecutar
diferentes acciones en función de varias condiciones, por ejemplo:

Listado 6.1. Trozo de código con varios if anidados


if (source == colorButton1)
currentColor = Color.red;
else
{ //ejecutamos esta parte sólo si source!=colorButton1
if (source == colorButton2)
currentColor = Color.blue;
else {
if (e.target == colorButton3)
currentColor = Color.green;
else currentColor = Color.black;
} //if e.target == colorButton3
} //if source == colorButton2
} //if source == colorButton1
Tema 6. Sentencias de control (2)

Para una mayor claridad a la hora de "leer" el código fuente, y para facilitar la comprensión del
mismo, se suelen incluir espacios delante de las sentencias if, para que así se aprecie
claramente el ámbito de cada una de ellas. Estos espacios en blanco, en cambio, son ignorados
por el compilador de Java.

Supongamos el siguiente ejemplo:


if (x>0)
if (y >0)
t=1; //se ejecuta cuando x>0 & y>0
else
t=2; //¿Cuándo se ejecuta?

Tal y como está escrito el código anterior, se podría suponer que la asignación t=2 se realiza
cuando no se cumple la condición del primer if (x>0), pero se podría haber seguido el criterio de
considerar dicha sentencia como parte else del segundo if. Para evitar que distintos
compiladores tome diferentes criterios, se establece la siguiente norma:

Una sentencia else se asocia siempre con el if más cercano que no tenga parte else..

Así, en el ejemplo anterior, la sentencia t=2 se ejecuta como parte else del segundo if, es
decir, cuando (x>0) & (y≤0).

2. Problemas comunes con los if


Puesto que la sentencia if es más compleja que las sentencias de repetición vistas en el tema
anterior, es frecuente el cometer errores, algunos de los más comunes enunciaremos aquí. Los
errores ocasionados en dicha sentencia son muchas veces difíciles de detectar y corregir1.

Tomemos como ejemplo el siguiente código:


if (x>1e38);
{System.out.println("Este es un número inválido");}

La intención al escribir el código es que si el valor de x es mayor que 1⋅1038 se muestre por
pantalla el mensaje. El problema está en que cuando se ejecuta dicho código, siempre aparece
en pantalla el mensaje, independientemente del valor de x. ¿Dónde está el error? Justamente en
el punto y coma que aparece después de la expresión booleana. Recordemos que el punto y
coma indica la finalización de una sentencia. Por lo tanto el código anterior evaluaría la expresión
y no haría nada, es decir, se trataría de un if sin parte then, ni parte else. La sintaxis del código
es correcta, por lo tanto no detectaremos el error hasta que ejecutemos el programa y
comprobemos el resultado con distintos valores de x. Si casualmente, en varias ejecuciones del
programa la expresión booleana toma el valor cierto, podremos deducir de forma errónea que el
programa es correcto, cuando no lo es. De esta forma resultará difícil la posterior depuración del
programa, cuando dicho error se manifieste (en el caso de que x sea menor o igual que 1⋅1038).
Por lo tanto:

1
Recordad que el proceso de detección y corrección de errores de denomina "depuración". Dicho
concepto se estudió en el Tema 2.

2
Programación para la Gestión

Precaución
La sentencia if no requiere una terminación en punto y coma. La única razón para incluir un
punto y coma en una sentencia if, viene dada por las sentencias individuales controladas por
dicho if.

A continuación modifiquemos ligeramente el código anterior:


if (x>1e38)
System.out.println("Este es un número inválido");
x = 1e38;
Si ejecutamos el programa, comprobaremos que la asignación x = 1e38; siempre se
realiza. En este caso el error se produce porque la única sentencia controlada por el if es la
sentencia de escritura. Si hubiésemos puesto los espacios de forma adecuada, tendríamos:
if (x>1e38)
System.out.println("Este es un número inválido");
x = 1e38;

en donde se aprecia mejor esta situación. No hay que olvidar que la inclusión de espacios en
blanco solamente sirve para las personas que van a leer el código. El compilador siempre quitará
todos los espacios en blanco, tabuladores, o retornos de línea, con lo cual dichos elementos no
tendrán ningún efecto en la ejecución del programa.

Precaución
No debemos olvidarnos de incluir llaves cuando sea necesario. Una buena práctica es incluir una
pareja de llaves inmediatamente después de la expresión booleana del if, o de la palabra
reservada else, y posteriormente escribir el código de las correspondientes partes then y/o else.
De esta forma evitaremos un olvido accidental de los paréntesis.

Recordemos que cuando un método devuelve un valor utiliza la sentencia return como última
sentencia de dicho método. Si dicha sentencia no es la última, las sentencias siguientes al return
no se ejecutarán, puesto que dicho método devolverá el control a la sentencia siguiente a la
llamada a dicho método. Si embargo, es posible incluir varias sentencias return en un mismo
método, de forma que finalice la ejecución del mismo dependiendo de ciertas acciones.

Supongamos, por ejemplo, que estamos escribiendo un programa para calcular ciertas tasas, y
que uno de los métodos, taxOn(), toma como entrada una cantidad, y devuelve la tasa a aplicar a
dicha entrada. Suponemos que, según las normas, entradas en el rango 0...19450,00 son
tasadas con un porcentaje del 15%, las entradas en el rango 19450,01...47050,00 se tasan con
un porcentaje del 28%, las entradas en el rango 47050,01...97620,00 se tasan con un 33%,
finalmente, las entradas superiores a 97620,00 se tasan con un 50%. Según esta definición de
método, una posible implementación sería:

Listado 6.2. Método taxOn que usa else para controlar la ejecución
private double taxOn(double entrada) {
double tasa; //valor de retorno
if (entrada <= 19450.0)
tasa= 0.15 * entrada;
else if (entrada <=47050.0)
tasa= 0.28*entrada;
else if (entrada <=97620.0)
tasa=0.33*entrada;
else tasa= 0.50*entrada;
return tasa;
}

3
Tema 6. Sentencias de control (2)

Otra alternativa podría haber sido utilizar la sentencia return una vez que se ha satisfecho la
condición correspondiente. Esta situación se ilustra en el listado 6.3

Listado 6.3. Método taxOn que usa sentencias return para controlar la ejecución
private double taxOn(double entrada) {
if (entrada <= 19450.0)
return 0.15 * entrada;
if (entrada <=47050.0)
return 0.28*entrada;
if (entrada <=97620.0)
return 0.33*entrada;
else return 0.50*entrada;
}

Esta versión es más simple que la del listado 6.2 puesto que no necesitamos declarar ninguna
variable local para almacenar la cantidad resultante de aplicar la tasa al valor de entrada, pero
requiere tener en cuenta el hecho de que la sentencia return fuerza una terminación
inmediata de la ejecución del método. Además, no ha sido necesario anidar las sentencias if. Si
intentamos combinar la solución de los listados 6.2 y 6.3, obtenemos una solución como la que
se plantea en el listado 6.4, que es incorrecta, puesto que el resultado que se obtiene no es el
deseado.

Listado 6.4. Método taxOn que funciona de forma incorrecta


private double taxOn(double entrada) {
double tasa;
if (entrada <= 19450.0)
tasa= 0.15 * entrada;
if (entrada <=47050.0)
tasa= 0.28*entrada;
if (entrada <=97620.0)
tasa= 0.33*entrada;
else tasa= 0.50*entrada;
return tasa;
}

Si realizamos la traza2 de la ejecución de este método, observaremos que a todas las entradas
con valor inferior o igual a 97620 se les aplica la tasa equivalente a un porcentaje del 33%. Por lo
tanto:

Precaución
Hasta que no estemos lo suficientemente acostumbrados a utilizar la sentencia if, es una buena
práctica no utilizar sentencias return como parte del cuerpo then y/o else de dichas sentencias.
En su lugar, es mejor utilizar secuencias de sentencias if, o sentencias if anidadas.

3. Sentencias condicionales: switch


Resultan usuales los programas que usan la siguiente construcción: "Si variable == constante1,
haz esto, si variable == constante2, haz esto, ..., en cualquier otro caso, haz esto otro". Por ello

2
Una traza consiste en la ejecución de un algoritmo paso a paso, mostrando el estado (valores de todas las
variables) del programa en cada paso.

4
Programación para la Gestión

la mayoría de los lenguajes de programación proporcionan una sentencia que permite


implementar dicho patrón , y Java no es la excepción. La sentencia switch se usa para
seleccionar una de entre varias acciones, dependiendo del valor de alguna expresión. La sintaxis
de la sentencia switch es la siguiente:

Sentencia SWITCH
switch (expresion){ //los paréntesis son obligatorios
case constante1:
sentencias;
case constante2:
sentencias;
...
default:
sentencias;
}

La expresión puede devolver valores de tipo char, byte, short, o int, y las constantes
deben ser literales3 o variables con el modificador final. La etiqueta default puede
omitirse, pero no es una buena idea, puesto que si el resultado de la expresión no coincide con
ninguna constante se producirá un error en tiempo de ejecución (run time error).

Respecto a la semántica de la sentencia switch, cuando dicha sentencia se ejecuta, se


realizan los siguientes pasos:
• Se evalúa la expresión
• El control se traspasa a la sentencia que sigue a la primera etiqueta case cuya
constante sea igual al valor de la expresión.
• Si ninguna de las constantes case es igual al valor de la expresión, el control se
transfiere a la etiqueta defalut.
• En cualquier caso, todas las sentencias entre la etiqueta y el final del switch son
ejecutadas.

Es importante hacer notar que este comportamiento difiere del de una colección de sentencias if
anidadas. Ello es debido a que, a menos que introduzcamos alguna acción que fuerce a terminar
la sentencia switch, todas las sentencias situadas entre la etiqueta case y el final del
switch serán ejecutadas. Si se quiere separar las acciones de una sentencia switch en
unidades discretas y evitar este comportamiento, se deben incluir sentencias break.

Cuando se encuentra una sentencia break, se fuerza la terminación del switch. Normalmente
siempre se utiliza una sentencia break en cada sección case del switch. Solamente
dejaremos de usarla cuando queramos que se evalúen varias secciones case.

El listado 6.5 muestra un trozo de código que contiene una sentencia switch. La variable
currentAlignment contiene un valor asociado a la alineación de la clase Label, y
queremos que este valor cambie según la secuencia LEFT, CENTER y RIGHT. Debido a que las
constantes de la clase que describen las alineaciones de las etiquetas se representan
internamente como constantes de tipo entero, se puede utilizar currentAlignment como
expresón en una sentencia switch.

3
Un literal es una representación del valor de un tipo primitivo, String o null. Por ejemplo 3.08 es un
literal double, y "pepe" es un literal String

5
Tema 6. Sentencias de control (2)

Listado 6.5. Listado con sentencia switch


...
switch (currentAlignment) { {
case Label.LEFT:
miEtiqueta.setAlignment(Label.CENTER);
break;
case Label.CENTER:
miEtiqueta.setAlignment(Label.RIGHT);
break;
case Label.RIGHT:
miEtiqueta.setAlignment(Label.LEFT);
break;
default:
System.out.println("Error. Algo ha fallado");
}

Para ver la importancia de la sentencia break, supongamos que no la hemos utilizado, tal y
como aparece en el listado 6.6.

Listado 6.5. Listado con sentencia switch sin utilizar break


...
switch (currentAlignment) { {
case Label.LEFT:
miEtiqueta.setAlignment(Label.CENTER);
case Label.CENTER:
miEtiqueta.setAlignment(Label.RIGHT);
case Label.RIGHT:
miEtiqueta.setAlignment(Label.LEFT);
default:
System.out.println("Error. Algo ha fallado");
}

En este caso, da lo mismo el valor que tenga la variable currentAlignment, en cualquier


caso, se ejecutará la sentencia correspondiente a la etiqueta default, mostrando el mensaje
de error por pantalla.

Precaución
Olvidar una sentencia break en una sentencia switch es una causa frecuente de errores.

4. Excepciones
Algunas causas que pueden ocasionar un error durante la ejecución del programa posiblemente
no puedan ser detectadas en tiempo de compilación. Supongamos por ejemplo que en un
programa incluimos la sentencia:
offset = x /n;
siendo x y n variables de tipo int. La sentencia es correcta desde el punto de vista del
compilador. Sin embargo, durante la ejecución del programa, podría ocurrir que n tomase un
valor igual a 0. Este es un caso excepcional, que Java detectará indicando que algo problemático
ha ocurrido y "lanzará" una excepción. Eso significa que internamente se activa una alarma,
indicando a la parte del sistema operativo que ejecuta el programa, que se debe lleva a cabo
alguna acción de emergencia. El sistema entonces detiene automáticamente la ejecución normal
del programa y "pide" ayuda. Con suerte, el sistema encontrará alguna parte del código en el
programa que "capturará" la excepción y la tratará, posiblemente visualizando algún mensaje de

6
Programación para la Gestión

error o iniciando alguna acción adecuada, como por ejemplo asignar a offset algún valor por
defecto. Una vez que la excepción ha sido "capturada", la alarma se desactiva y el sistema
retoma la ejecución en el bloque siguiente que contenía la sentencia que provocó el error.

Otra alternativa para manejar excepciones consiste en escribir código para detectar el error
antes de que ocurra, probablemente mediante el uso de guardas (expresiones booleanas). En el
caso anterior podríamos evitar el error producido por la división por cero escribiendo una guarda
como la siguiente:
if (n > 0) //guarda para prevenir la división por cero
offset = x /n;
else
offset = 10;

Mediante el uso de excepciones, sin embargo, se pueden advertir situaciones inusuales y tomar
las acciones adecuadas en cualquier parte del programa. No hay razones concretas para preferir
una estrategia frente a la otra. Cada una de las técnicas para tratar con las situaciones
excepcionales tienen sus ventajas y desventajas, como veremos a lo largo del tema. Más
adelante, presentaremos la sintaxis y semántica de las excepciones en Java.

4.1. Clases en Java relacionadas con las excepciones

En Java existen 16 o más clases que tienen que ver con las excepciones. Todas ellas derivan de
la clase Exception, la cual a su vez es subclase de la clase Throwable. En la Figura 6.1
aparece reflejada una parte de la jerarquía de clases relacionadas con las excepciones.

Object

Throwable

Error

LinkageError

Exception

IllegalAccessException

IOException RunTimeException

Figura 6.1. Parte de la jerarquía de clases de Java relacionadas con excepciones.

Las excepciones se usan para indicar situaciones que generalmente somos capaces de manejar,
como por ejemplo intentar acceder a un carácter de un String en una posición ilegal del
mismo. En la Tabla 6.1 se indican algunas de las subclases de Exception más comunes.

7
Tema 6. Sentencias de control (2)

java.lang.ArithmeticException
Indica que algo ha funcionado mal en una expresión aritmética, como por ejemplo una división
por cero.

java.lang.ArrayIndexOutOfBoundsException
Indica que se ha intentado acceder a un elemento de un array en una posición ilegal (por
ejemplo menor que cero, o mayor que la longitud del array, por ejemplo).

java.lang.ArrayStoreException
Indica que se ha intentado introducir un elemento en un array de un tipo inadecuado.

java.FileNotFoundException
Indica que se ha hecho una referencia a un fichero que no se ha podido encontrar.

java.IllegalArgumentException
Indica que se ha hecho una llamada a un método con un argumento inválido.

java.IOException
Indica un error en sentencias de entrada/salida (éstas se verán en el tema siguiente).

La clase Exception es muy simple. Está formada únicamente por dos constructores:
• Exception()
• Exception(String message)

La segunda de ellas permite especificar un mensaje formado por una cadena de caracteres, por
ejemplo para proporcionar detalles acerca de la naturaleza de la excepción. Para recuperar el
mensaje, se puede utilizar el método de la clase padre Throwable, getMessage(), que
devuelve el mensaje, si existe, asociado con la excepción. Generalmente no crearemos nuevas
instancias de la clase Exception, sino que utilizaremos los constructores de alguna de sus
subclases, con o sin mensaje como argumento. También podemos crear nuevas subclases de
Exception para crear nuestras propias excepciones.

4.2. Métodos para capturar excepciones

Hay muchos métodos en Java capaces de capturar excepciones, vamos a enumerar buena parte
de ellos, (ver Tabla 6.1):

8
Programación para la Gestión

Clase Métodos Excepciones capturadas


Choice void add(String s) Capturan NullPointerException si el
void addItem(String s) argumento es null
String getItem() Capturan IllegalArgumentException si
void insert (String item, int el índice está fuera de rango o si el argumento de
index) remove() no pertenece a este choice
void remove(String item)
void select (int index)I
Container void add (Component c) Captura IllegalArgumentException si
se intenta añadir un componente a sí mismo
Dialog Dialog (Frame parent) Captura IllegalArgumentException si
parent es null
Double static Double valueOf(String s) Captura NumberFormatException si el
argumento no representa un double válido
Float static Float valueOf(String s) Captura NumberFormatException si el
argumento no representa un float válido
Integer static int paseInt (String s) Captura NumberFormatException si el
static Integer valueOf (String s) argumento no representa un int válido
Label void setAlignment(ing alg) Captura IllegalArgumentException si
el argumento no es una de las constantes de clase
Label.LEFT, Label.CENTER, o Label.RIGHT.
List void remove (String item) Captura IllegalArgumentException si
el argumento item no pertenece a la lista.
Long static long parseLong(String s) Captura NumberFormatException si el
static long valueOf(String s) argumento no representa un llong válido
String String (String s) Captura NullPointerException si el
String (char[ ] c) argumento es null
int compareTo (String s)
String concat (String s)
boolean endsWith (String s)
int indexOf (String s)
int lastIndexOf (String s)
boolean startsWith (String s)
char carAt (int index) Captura
void getChars (int start, int end, StringIndexOutOfBoundException
char[ ] c, int cStart) si uno o más de los argumentos representa un
String substring (int index) índice no válido en el String. Además,
String substring(int start,int end) getChars() captura
NullPointerException si el argumento de
tipo array es null.
TextArea void setColumns (int c) Captura IllegalArgumentException si
void setRows (int r) el argumento es negativo.
Vector void copyInto(Object[ ] array) Captura
Object elementAt (int index) StringIndexOutOfBoundException
int indexOf (object obj,int start) si uno o más de los argumentos representa un
void insertElementAt(Object índice no válido en el String, o en el caso de
obj, int index) copyInto(), si el argumento de tipo array es
int lastIndexOf (Object obj, int
menor que el tamaño del vector.
start)
void setElementAt(Object obj,
int index)
void removeElementAt(int index)
Tabla 6.1. Métodos de Java que pueden capturar excepciones

9
Tema 6. Sentencias de control (2)

5. Manejo de excepciones
La idea básica de las excepciones en Java es muy simple: se trata de indicar la intención de
manejar una excepción marcando el bloque en donde la excepción podría ocasionarse, y
proporcionar algún código, que será llamado solamente cuando tenga lugar dicha excepción y se
deba tratar.

Para marcar el bloque de sentencias entre las que presumiblemente se puede producir la
excepción que deberá capturarse, se utiliza la palabra reservada try:

Sintaxis de un bloque TRY


try {
//código que podría originar una excepción
}

Con ello indicamos nuestra intención de manejar alguna o todas las excepciones que se
produzcan dentro del bloque. Si no se produce ninguna excepción mientras se ejecutan las
sentencias del bloque, no se debe ejecutar ninguna acción especial.

El siguiente paso es añadir a continuación del bloque try una o más sentencias catch. Una
sentencia catch consiste en un segmento de código muy parecido a la declaración de un
método, con un argumento formal y un cuerpo de sentencias entre paréntesis.

Sentencia CATCH
catch (TipoExcepcion variable) {
//código para manejar la excepción que usa la variable
//argumento. Dicha variable solamente es conocida dentro
//del bloque
}

Si no se produce ninguna excepción en el bloque try, las sentencias del bloque catch no se
ejecutan. Si por el contrario, sí se produce una excepción, y hay una sentencia catch a
continuación del bloque try que es compatible con el tipo de la excepción que se ha producido,
entonces el control se transfiere automáticamente a la sentencia catch adecuada. Las
sentencias del bloque catch se ejecutan, y el control se transfiere a la primera sentencia
después del bloque try (ignorando cualquier sentencia catch posterior). Por ejemplo, la
división por cero contemplada en el apartado 4 podría haberse implementado como sigue:
try {
offset = x/n;
//cualquier sentencia a partir de aquí será
//ignorada si x=0
}
catch (ArithmeticException e) {
offset = 10;
}
//después de manejar la excepción el control se
//transfiere aquí

Debemos mencionar dos factores importantes:


• Debido a la semántica de try...catch, podemos asumir que si una sentencia en el
bloque try se ejecuta, ninguna sentencia previa ha provocado una excepción debido a

10
Programación para la Gestión

que una vez que el control se abandona por parte del bloque try, éste nunca vuelve a
él4.
• Las sentencias catch se evalúan desde la primera a la última, deteniéndose la
búsqueda en la primera sentencia catch cuyo argumento sea compatible con la
excepción producida, obviando así posteriores sentencias catch. Ello implica que las
sentencias catch deberían ordenarse siguiendo un criterio de mayor a menor
especificidad. Por ejemplo, el siguiente código podría suponer un esfuerzo vano, ya que
la segunda sentencia catch podría no ejecutarse nunca.
try {
...
}
catch (Exception e) //Este argumento es compatible
//con cualquier excepción
{ ... }
catch (ArithmeticException e)
{ ... }

Un bloque try debe seguirse inmediatamente por una o más sentencias catch, y no pueden
exsistir de forma separada, es decir, no podemos escribir un bloque try, sin una sentencia
catch, ni al revés.

5.1 Propagación de excepciones

Supongamos que ocurre una excepción fuera de un bloque try, o dentro de un blque try que
no tiene ninguna sentencia catch asociada con un tipo de excepción compatible. ¿Qué ocurre
en ese caso?

Pues el sistema primero intenta buscar una sentencia catch que pueda tratar dicha excepción.
Si no la encentra, el sistema busca en las setencias try...catch del bloque externo. Por
ejemplo, debido a que en un bloque try pueden aparecer cualquier conjunto de sentencias, el
programa prodría parecerse al siguiente código:
try {
...
try
{
//excepción producida aquí
}
catch (AlgunaExcepcion e)
{
//pero no se captura aquí
}
catch (AlgunaOtraException e)
{
//entonces se capturará aquí
}

Esta búsqueda continúa hasta que se alcanza el bloque más externo. Si no se encuentra
ninguna sentencia catch compatible, entonces la búsqueda se extiende para incluir el bloque
que contiene la llamada al método, como éste:

4
A menos que el control haga que se vuelva a entrar en el bloque try desde el principio, en el curso
normal de ejecución, como podría ocurrir por ejemplo si dicho bloque estuviese dentro de un bucle.

11
Tema 6. Sentencias de control (2)

void oops() {
try
{
//excepción generada
}
catch (AlgunaExcepcion e)
{
//pero no se captura aquí
}
} //final del método oops()
...
//ahora estamos en cualquier otro método, justamente
//en la llamada original a oops(), en donde se origina
//la excepción
try{
oops(); //excepción generada por esta llamada
//no capturada en oops()
}
catch (AlgunaOtraExcepción e)
{
//el siguiente paso es buscar aquí, después
//de haber buscado en oops()
}

Si a pesar de ello no encontramos ninguna sentencia catch compatible, el proceso continúa a


través de las llamadas a métodos, hasta que se encuentre una sentencia catch que pueda
tratar la excepción. El mismo comportamiento se ocasiona si la excepción no se produce dentro
de un bloque try (la búsqueda comienza en el bloque en donde se produce la excepción y se
propaga "hacia fuera" hasta que el sistema encuentre un manejador para la excepción).

Normalmente se garantiza que se va a encontrar algún manejador en el propio sistema Java.


Esto es debido a que tarde o temprano podremos encontrar un método al que llama el propio
Java, ya que es imposible ejecutar un applet o una aplicación a menos que la llamada la realice
el propio Java. Pensemos por ejemplo en que una aplicación comienza su ejecución a través de
un método main(), y un applet comienza su ejecución mediante una llamada a init() (o una
llamada similar, como por ejemplo paint(). ¿Hacemos nosotros dichas llamadas? No, las hace la
máquina virtual de Java.

Por supuesto, si no hemos proporcionado un manejador de excepciones en alguna parte del


código, Java terminará el programa. Esto no siempre es una mala idea. Si un método es llamado
desde un objeto que no ha sido inicializado, por ejemplo, realmente no se puede hacer nada con
la excepción NullPointerException generada, por lo tanto es mejor no capturarla y simplemente
dejar que el programa termine.

5.2 Provocando excepciones

Hay veces que nos interesará generar una excepción bajo nuestro control, en lugar de
permanecer pasivos esperando a que Java la genere por nosotros. En ese caso utilizamos la
sentencia throw.

Sentencia THROW
throw instanciaDeExcepción;

12
Programación para la Gestión

Cuando se ejecuta una sentencia throw, se envía instanciaDeExcepción al soporte


de ejecución5 (run-time environment), de la misma forma que una excepción generada por
Java cuando encuentra alguna anomalía como un acceso a un array fuera de los límites. Nuestro
programa podría entonces usar una colección de sentencias try...catch para tratar con la
excepción de la forma usual. Vamos a suponer, por ejemplo, que tenemos una secuencia de
sentencias que requieren que n sea par para que funcione correctamente. Podríamos tratar esta
precondición generando la excepción correspondiente de la siguiente forma:

// n toma algún valor aquí


try
{
if (n%2 == 1) //n es impar
throw new ArithmeticException();
else
//código que requiere que n sea par
}
catch (ArithmeticException e)
{
//código que trata con los valores impares de n
}

Este es un ejemplo un tanto "artificial", ya que podría haber sido mucho más simple el situar el
código para tratar con números impares en la parte then, y así ahorrarnos la sentencia catch.
Pero, por ejemplo, supongamos que lo habitual va a ser que el número sea par, y que sólo
excepcionalmente tendrá valores impares. En este caso, sí es buena idea utilizar la sentencia
catch, ya que dicho bloque casi nunca se ejecutará, entonces ¿por qué incluirlo dentro de la
sentencia if y emplear mucho tiempo leyéndolo en la parte then del if?

Un buen uso de la excepciones se da cuando se aísla un código complicado que trata


situaciones muy poco probables. Utilizando así las excepciones conseguiremos que el programa
sea más fácil de leer.

Cuando estamos diseñando un método que puede provocar una excepción tenemos dos
posibilidades:
• utilizar las sentencias try..catch para manejar la excepción dentro del método, o
• no capturar la excepción y dejar que el mecanismo de propagación capture la excepción
en alguna otra parte.
En este último caso, podríamos (o en algunos casos deberíamos) indicar al compilador que el
método podría provocar una excepción que no va a capturar por sí mismo. Esto se realiza
utilizando una sentencia throws en la cabecera del método.

Sentencia THROWS
void miMétodo() throws ExceptionTipo1, ExceptionTipo2,...

Por ejemplo, el siguiente código indica que el método puede provocar dos tipos de excepciones,
IllegalArgumentException, que se genera explícitamente en el método, o NullPointerException
que se genera con la sentencia equals(), si la cadena de caracteres que se le pasa como
argumento tiene un valor null.

String wantCookie(String s) throws IllegalArgumentException,


NullPointerException

5
Software que forma parte del sistema operativo y que permite la ejecución de aplicaciones.

13
Tema 6. Sentencias de control (2)

{
if (s.equals("Cookie"))
return "Thanks!"
else
throw new IllegalArgumentException("Quiero galletas!");
//no necesitamos un return, ya que nunca llegaremos
//aquí
}

6. Manejando nuestras propias excepciones


A pesar de las muchos tipos de excepciones que Java proporciona, habrá veces que queramos
inventarnos nuestras propias excepciones para hacer notar situaciones no contempladas en las
clases Exception predefinidas. Esto lo podremos realizar creando subclases de
Exception, si queremos crear excepciones que no vamos a tratar en el bloque en el que se
generan, o subclases de RuntimeException, si queremos inventar una excepción que no
necesitamos tratar en el programa (dejaremos que se encargue de ella el propio sistema Java).

Las excepciones definidas por el programador se tratan igual que las excepciones predefinidas,
excepto, claro está, que debemos ocuparnos nosotros mismos de generarlas. A continuación
mostramos un ejemplo en el que creamos dos tipos de excepciones. Una de ellas,
FaltanDatos, se prevé que sea generada cuando uno o más de los campos de texto de tipo
Textfield están vacíos. La declaración es la siguiente:

class FaltanDatos extends Exception


//generada por el método processSubmitButton()
//cuando uno o más campos de texto están vacíos
{
public FaltanDatos()
{
super();
}
public FaltanDatos(String s)
{
super();
}
}

En ambos constructores necesitamos llamar al correspondiente constructor de la superclase.

La excepción FaltanDatos se genera dentro del siguiente método:


private void processSubmmitButton() throws MissingData
//Se asegura de que todos los datos estén presentes
//antes de aceptar la petición
//cuando uno o más campos de texto están vacíos
{
if ((nombreCliente.getText().equals(EMPTY)) 
((calleCiente.tetText().equals(EMPTY)) 
//resto de sentencias de datos del cliente
pedido.getText().equals(EMPTY)) )
throw new faltanDatos ("Debe introducir sus datos!")
else //enviar la orden
pedido.appendText('\n' + " Pedido realizado!");
}

14
Programación para la Gestión

Finalmente, la excepción que se generó en processSubmitButton() es capturada en el


manejador del applet actionPerformed():

...
else if (source == bSubmit)
{
try {
processSubmitButton();
}
catch (FaltanDatos ex) //falta algún dato
{
pedido.appendText("*** "+ ex.getMessage()+ '\n');
}
}

7. Ejercicios
7.1 Escribe un método denominado calcularNomina, que tiene como argumentos el nombre del
trabajador y el número de horas trabajadas de forma que: si él número de horas es inferior o
igual a 40 se pagan a 5000 pts/hora; si dicho número de horas está entre 40 y 60, entonces
se pagan a 10000 pts/hora las horas que excedan de 40; finalmente si son superiores a 60,
entonces las horas excedidas de 60 se pagan a 20000 pts/hora. El extracto de la nómina
debe aparecer en pantalla de la siguiente forma:
Nombre empleado: Luis
Nª Horas trabajadas (normales): 40 Importe: 80.000 pts
Nª Horas trabajadas (extras): 20 Importe: 200.000 pts
Nª Horas trabajadas (super-extras): 0 Importe: 0 pts
TOTAL NOMINA: 280.000 pts

7.2 Escribe un método denominado calcularNota, que tenga como parámetros el nombre de un
estudiante, y las notas obtenidas en los exámenes teórico y práctico de una asignatura. Se
trata de calcular la nota final teniendo en cuenta que para aprobar la asignatura se debe tener
aprobado cada examen (nota superior o igual a 5). Una nota superior o igual a 7 y menor que
9 tiene una calificación de Notable, si dicha calificación es igual a 9 o inferior a 10 la
calificación es de Sobresaliente, una nota de 10 equivale a una Matrícula de Honor. A la hora
de ponderar la nota se debe tener en cuenta que el examen teórico cuenta el doble que el
práctico. Un posible ejemplo de salida de dicho método sería:
Nombre alumno: Salvador
Nota examen teórico: 4
Nota examen práctico: 6 Calificación: SUSPENSO

7.3 Modifica el ejemplo que trataba de coger un par de calcetines del mismo color de un cajón de
calcetines (apartado 3 del Tema 5), para que contemple los siguientes casos: puede que
inicialmente en el cajón no haya calcetines o solamente haya uno (con lo cual no se puede
obtener un par), si es así, el método cogerUnCalcetin() genera una excepción llamada
NoCalcetines, de forma que se muestra un mensaje por pantalla: "Lo siento, no hay
calcetines suficientes en el cajón". El método cogerUnCalcetín() genera la excepción llamada
ErrorMecánico si el brazo del robot experimenta un fallo mecánico, en cuyo caso se imprime
por pantalla: "Lo siento, fallo mecánico del robot". Los calcetines pueden ser verdes, rojos o
azules: el método que proporciona las órdenes al robot para intentar coger los calcetines se
llama cogerCalcetines y tiene como parámetro el color deseado del par de calcetines. Para
averiguar el color de cada calcetín disponemos de un método "color()" que devuelve 0 si el

15
Tema 6. Sentencias de control (2)

calcetín es rojo, 1 si es verde y 2 si es azul. Si después de 10 intentos no se ha conseguido


coger el primer calcetín del color deseado se genera la excepción NoConsigoColor, que
muestra el mensaje: "Lo siento, parece que no hay calcetines de color x" siendo x el color que
se pasa por parámetro. Una vez conseguido el primer calcetín del color deseado, se dispone
de otros 10 intentos para conseguir el par del mismo color. Si no se consigue se genera la
excepción NoParCalcetines, que muestra el mensaje "Lo siento, otra vez será". El método
cogerCalcetines maneja todas las excepciones generadas. Si se consigue coger el par de
calcetines se muestra por pantalla el número de veces que se ha intentado. Un ejemplo de
ejecución del método sería:
OK: Calcetines de color x conseguidos
Nº intentos primer calcetín: 6
Nº intentos segundo calcetín: 3 TOTAL intentos: 9

7.4 Unos cheques falsificados están en circulación y los bancos han detectado que todos tienen
las mismas propiedades distintivas. Un cheque puede ser falso si en el número de cheque de
10 dígitos hay:
• tres o más ceros en sucesión,
• y/o cuatro o más ceros seguidos.
Implementar un método llamado detectarFalsos(long numero) que detecte que la numeración
que se pasa por parámetro se corresponde con un cheque falsificado. Posibles ejemplos de
ejecución del método serían:
*** Detección de cheques falsificados ***
Número a chequear: 0033300333
OK

*** Detección de cheques falsificados ***


Número a chequear: 4444005500
FALSIFICADO

16

También podría gustarte