Menu
Programación en Java SE 6 desde cero: Parte 2

Programación en Java SE 6 desde cero: Parte 2

 

Programación en Java SE 6 desde cero, parte 2

Control de Flujo en JAVA

 

  1. Sentencias Selección

  2. Bucles

  3. Assertions

  4. Exceptions

 

 

En este capítulo mostraremos la sintaxis adecuada y el correcto uso de las estructuras de control que proporciona el lenguaje para controlar los flujos del programa.

 

Objetivos:

 

  • Conocer las sentencias de selección disponibles: if, else y switch.

  • Manejar los distintos bucles del lenguaje: for, foreach, while y do-while.

  • Gestionar  completamente  las Exceptions.

 

Sentencias de selección en JAVA

 

Estructura de control en JAVA (IF):

 

Una sentencia if, también conocida como if – else o if – then – else es la más básica estructura de control en Java para la toma de decisiones. El siguiente ejemplo muestra la sintaxis de una sentencia if:

 

If (expresion_boleana) {

  //Programa bloque if

}else {

  //Programa bloque else

}

 

Reglas estructura if - else en JAVA

 

Las siguiente reglas aplican a la estructura if – else:

 

La expresión que se debe evaluar necesariamente tiene que devolver un valor lógico, es decir true o false. De lo contrario el compilador generará un error.

Si la expresión se evalúa como verdadera, es decir devuelve true, se ejecutará el bloque de código if.

Si por el contrario se evalúa como falsa, se ejecutará el bloque de código else (si existe).

El bloque de código else es opcional.

Las llaves no son necesarias, sin embargo si no las ponemos sólo se ejecutará la primera línea de código que se encuentre el bloque que se ejecute. No obstante por claridad para la comprensión del código siempre es una buena idea ponerlas.

 

En el siguiente código se puede ver el uso de una estructura de este tipo:

 

int x = (int) (Math.random() * 10 +1);

if(x< = 5) {

System.out.println("Menor de cinco");

}

 

Sentencia ELSE en JAVA

 

El valor de x es asignado de forma aleatoria en un número entre 1 y 10, si el valor es menor o igual que 5, entonces mostramos por consola el texto: “Menor de cinco”. Si por el contrario el valor que se asigna a x es mayor de 5 la línea de código que imprime por la salida estándar no es ejecutada en el programa.

 

Una sentencia else se puede añadir a cualquier if, como puede verse en la imagen de la derecha.

 

int x = (int) (Math.random() * 10 +1);

if(x< = 5) {

System.out.println("Menor de cinco");

} else {

System.out.println("Mayor de cinco");

}

 

El switch en JAVA

 

El switch es una estructura de toma de decisión basado en una igualdad para un valor entero de entre una lista de sentencias case. Un switch es similar en declaración a las estructuras if – else, se muestra a continuación  un ejemplo de la sintaxis:

 

 

switch (variable_entera) {

  case constante:

    //líneas de código

  case constante:

    //líneas de código

  default:

    //líneas de código

}

 

Reglas estructura switch

 

Las siguientes reglas se aplican a las sentencias switch:

 

La variable_entera debe ser compatible con el tipo int, esto santifica que únicamente se pueden utilizar los siguiente tipos: byte, short, char, int, Short, Character, Integer, o un tipo enum.

Puede aparecer cualquier número de sentencias case. Aunque en el ejemplo sólo se vean dos.

El valor constante, debe ser un valor literal que pueda adoptar la variable_entera.

El bloque default es opcional y debe aparecer después de escribir todas las sentencias case deseadas. Si no coincide ninguno de los case anteriores con el valor de variable_entera, entonces se ejecutara las sentencias contenidas en el default (por defecto).

Cuando se verifica como true la comparación de variable_entera y constante de alguno de los case, el resto de case no se evalúan y se ejecutan todas las sentencias desde el que devolvió true hasta el final del switch a no ser que se encuentre una sentencia break.

 

En el próximo ejemplo analizaremos varias operativas para aclarar el uso de switch:

 

int x = 0;

switch(x) {

  case 0:

  case 1:

    System.out.println("0 o 1");

    break;

  case 2:

    System.out.println("2");

  case 3: 

    System.out.println("2 o 3");

    break;

  default:

     System.out.println("por defecto");

}

System.out.println("Pasado el Switch");

 

Se define la variable entera x y se asigna valor 0. 

 

El case 0 se hace verdadera en la tercera línea, pero al no existir break, sigue ejecutando el switch. No escribe nada porque no hay código. A partir de aquí no se evalúan más "case" pero sí se ejecutan todas las sentencias hasta el final, a no ser que encontremos un "break".

Se ejecuta el System.out de "case 1" y se imprime por pantalla 0 o 1. 

En la siguiente línea se ejecuta un break y por lo tanto se sale del switch.

Se imprime la sentencia por consola una vez finalizado el switch. 

 

Bucles en JAVA

 

Los bucles son la forma de crear estructuras repetitivas para poder definir flujos de código repetitivos para nuestros algoritmos. En este apartado estudiaremos los diferentes tipos de bucles que se permiten utilizar en Java.

 

Bucle for básico en JAVA

 

El primero que estudiaremos es el bucle for básico. Que cumple las siguientes propiedades:

 

  • Dos punto y coma (;) son requeridos para crear tres secciones en la definición de la sentencia, una para inicialización, otra para verificar la expresión y ver si continua el bucle o no, y una última para actualizar variables.

  • La sección de inicialización sólo se ejecuta una vez al comienzo del bucle.

  • La verificación de expresión, se debe evaluar a true o false y se ejecuta por cada iteración del bucle. Esta parte del bucle es la toma de decisión que define si el bucle realiza otra iteración o no.

  • La parte de actualización se ejecuta tras la última sentencia contenida en el ámbito del bucle y se ejecuta en cada iteración.

  • Las secciones de inicialización y actualización permiten agrupar más de una sentencia en su interior y se deben indicar separándolas por comas (,).

for ( int x=1; x<=10; x++) {

System.out.println("El valor de x es " + x);

}

 

Vamos a hacer un ejemplo un poco más complicado en el que podemos ver el uso de bucles anidados y además veremos la ejecución a partir de variables de tipo char para obtener un punto de vista algo direfente.

 

for ( char car="a": car<="f"; car++) {

  for ( int i=1; i=<=3; i++) {

  System.out.println(" " + car + i);

  }

System.out.println();

}

 

La salida correspondiente a la ejecución del código que acabamos de ver es la siguiente:

a1 a2 a3

b1 b2 b3

c1 c2 c3

d1 d2 d3

e1 e2 e3

f1 f2 f3

 

 

Bucle for mejorado en JAVA

 

Desde la versión de Java 5.0 el lenguaje cuenta con un bucle for mejorado, que permite escribir sentencias for de forma un poco más sencilla en la iteración de elementos de tipo objeto. Es decir este bucle mejorado está orientado a iterar listas o arrays de objetos.

  

En este for mejorado se pone primero el tipoObjeto que corresponde a cada elemento que viene en la lista (collection). Elemento corresponde a la variable que podemos utilizar dentro de las sentencias del bucle y que en cada iteración tomará el valor correspondiente al elemento de la lista que se este iterando en el momento.

 

for (tipoObjeto elemento: collection) {

//Sentencias de codigo dentro del bucle

}

 

Bucle for mejorado con un array en JAVA

 

Vamos a ver un ejemplo de  bucle for mejorado con un array:

 

char[] caracteres = {'1','2','3','4','5'}; 

for ( char car : caracteres ) {

System.out.println("Numero en la iteración del carácter: " + car); 

}

 

La salida de la ejecución que acabamos de estudiar es la siguiente:

 

Numero en la iteración:  1

Numero en la iteración:  2

Numero en la iteración:  3

Numero en la iteración:  4

Numero en la iteración:  5

 

 

Variable num en JAVA

 

Como se puede observar se ha recorrido todo el array de principio a fin, en cada iteración la variable num ha ido cambiando de valor al de cada elemento del array.

 

Debemos remarcar que el ámbito de la variable num, es el interior del bucle, por lo que fuera de las llaves que delimitan este ámbito no podremos hacer uso de la variable num.

En el siguiente ejemplo vamos a mostrar un programa completo que podréis ejecutar:

 

 

import java.util.Arraylist;

public class Preferidas {

  private ArrayList<String> urls =

  new ArrayList<String>();

  public void showPreferidas() {

    for(String url: urls) {

      if(url.startsWith("http://")) {

        System.out.println(url);

      } else {

        Syste.out.println("http://" + url);

      }

  }

}

  public void addPreferida(String url) {

    url.add(url);

}

  public static void main(String [] args){

    Preferidas f = new Preferidas();

 

    f.addPreferida("http://www.google.es");

    f.addPreferida("http://www.votatuprofesor.com");

    f.showPreferidas();

  }

}

 

Analicemos el código que acabamos de mostrar. Tenemos una clase Preferidas, que cuenta con un método main, por lo tanto es posible ejecutar esta clase. No tiene constructor por lo tanto contamos con el constructor por defecto y tiene dos métodos addPreferida y showPreferidas, el primero añade una cadena a un atributo de tipo ArrayList (es una lista que contiene Objetos) y el segundo muestra el contenido de este atributo con un bucle for mejorado.

 

Si ejecutamos el programa, obtenemos la siguiente línea:

http://www.google.es

http://www.votatuprofesor.com

 

Bucle While en JAVA

 

Continuando con los tipos de bucles disponibles en el lenguaje, encontramos el bucle while. Es muy útil para definir bloques de código repetitivos que se deben ejecutar un número indeterminado de veces.

 

Expresion_boleana será el resultado de la evaluación de una expresión que necesariamente debe devolver true o false. Siempre que se devuelva true la sentencias del bucle se ejecutarán, y cuando devuelva false el bucle dejará de ejecutarse y continuarán la ejecución de las siguientes líneas del programa.

En este ejemplo hemos repetido el mismo algoritmo utilizado en el bucle for cuando lo aprendimos, es un contador que muestra los valores de x entre uno y diez.

 

while ( expresion_boleana) {

  //Sentencias de código dentro del bucle

}

 

int x = 1;

while (x<=10) {

  System.out.println("El valor de x es" +x");

  x++

}

 

Expresión Booleana en Java

 

Cuando utilizamos este tipo de bucles debemos tener cuidado, ya que con facilidad podemos generar un bucle infinito.

 

Bucle Do - While en JAVA

 

Tenemos dentro del lenguaje un bucle do – while que es prácticamente igual al while solo que muestra una pequeña diferencia en cuanto al concepto de ejecución, ya que la evaluación de la expresión_boleana se hace una vez ejecutado el bucle por primera vez. Por lo tanto este bucle siempre se ejecuta al menos una vez.

 

Podemos ver la sintaxis del bucle en el siguiente ejemplo:

 

do {

 //Sentencias de código dentro del bucle

} while(expresion_boleana);

 

Ahora vamos a reproducir el mismo bucle que para los ejemplos anteriores que muestre los valores para x entre uno y diez.

 

int x = 1;

do {

  System.out.println(x);

  x++;

} while (x<=10);

 

Hasta el momento nada nuevo ni reseñable, aunque se hacemos una pequeña modificación sobre el código anterior y ejecutamos de nuevo, si que vemos algo que hasta el momento no podíamos hacer con los tipos de bucle vistos hasta el momento.

 

En este bucle, la primera vez que se evalúa la condición es falta, en cambio si ejecutamos el código veremos que el cuerpo del bucle se ejecuta la primera vez, porque a diferencia de los tipos de bucle vistos anteriormente, primero se ejecuta el cuerpo y luego se evalúa la condición.

 

Assertions en JAVA

 

Una assertion en Java es una expresión que deberá devolver verdadero o falso, que permite en tu código incluir aseveraciones que siempre se deben cumplir, por lo tanto el resultado de la expresión incluida en el assert debe ser siempre true.

En el siguiente ejemplo, estamos seguros que el valor de x siempre será mayor de 0, e introducimos un assert para indicar que en caso contrario hay un error.

int a = 3, b = 5;

int x = a * b;

assert x > 0;

 

Estas assertions permiten chequear nuestro código en busca de errores, así evitaremos errores que no son notificados. Podemos incluir este tipo de sentencias en todo nuestro código, y debemos activarlas mientras estemos en fases de desarrollo y pruebas, y debemos desconectarlas cuando pasemos nuestros programas a producción.

 

La sentencia assert la podemos escribir de dos formas diferentes:

assert expresión_boleana;

assert expresión_boleana : mensaje_error;

 

Siempre que el resultado de la expresión_boleana sea verdadero nuestro código se ejecutará normalmente, en cambio cuando sea falsa se lanzará un excepción de tipo java.lang.AssertionError, con la diferencia entre las dos sentencias escritas que en el segundo caso, se devolvera en la excepción la cadena contenida en mensaje_error.

Por defecto los assert son ignorados por la JVM en tiempo de ejecución. Para que funcionen como esperamos, es necesario activarlos utilizando los el modificador –enableassertions o –ea cuando lancemos nuestro programa.

 

Exceptions o las Excepciones en JAVA

 

Las excepciones son eventos que ocurren en tiempo de ejecución en nuestro programa que nos permiten establecer flujos para nuestro código, normalmente son flujos alternativos que se activan en el momento que se lanza una excepción y se corta el flujo normal del programa.

 

Habitualmente se lanzan excepciones ante comportamientos raros del programa, que suelen ser debidos a errores. Dentro de una excepción podremos acceder a la pila de ejecución del programa y así podremos ver donde se inició la excepción, y detalles derivados del error que ha ocurrido.

La excepción viaja desde el punto en que se genera o es lanzada y va volviendo por la pila de llamadas del código hasta que encuentre algún segmento de código que sea capaz de recuperar la excepción, bloque try – catch, o sino parara el programa si no encuentra ningún bloque de este tipo en su camino.

 

 

En Java las excepciones se dividen en dos tipos, las programadas y las de tiempo de ejecución, que las lanza automáticamente la JVM cuando se realiza alguna operación que no está permitida, como por ejemplo invocar a un método de una clase que no se ha inicializado, lo que generará una NullPointerException o al realizar un división por cero, que se recoge una ArithmeticException. En el siguiente código se puede observar una excepción de tiempo de ejecución al dividir por cero.

 

public class ExceptionDemo {

    public void method1() {

         System.out.println(“Dentro de method1”);

         method2();

    }

    public void method2() {

         System.out.println(“Dentro de method2”);

         method3();

    }

    public void method3() {

         System.out.println(“Dentro de method3”);

         int x = 5, y = 0;

        int z = x/y; //Esta línea lanza una ArithmeticException

        System.out.println(“z = “ + z);

    }

    public static void main(String [ ] args) {

         System.out.println(“Inicio main”);

         new ExceptionDemo().method1();

        System.out.println(“Final main”);

    }

}

 

El programa ExceptionDemo se puede ejecutar porque tiene un main, si seguimos la ejecución del programa podemos observar como desde el método main, se genera un objeto de tipo ExceptionDemo y se invoca al method1, este invoca al method2 y a su vez este al method3 que lanza una ArithmeticException, que como ya hemos estudiado se produce al realizar una división por cero. Podemos observar que la traza Final main no aparece porque la ejecución del programa se corta al lanzarse la excepción y no estar incluida en un bloque try – catch.

Podemos estudiar el resultado de la excepción:

 

Exception in thread “main” java.lang.ArithmeticException: / by zero

at ExceptionDemo.method3(ExceptionDemo.java:15)

at ExceptionDemo.method2(ExceptionDemo.java:9)

at ExceptionDemo.method1(ExceptionDemo.java:4)

at ExceptionDemo.main(ExceptionDemo.java:21)

Después de las trazas propias de nuestro programa podemos observar la traza que ha dejado la excepción. Donde es muy interesante observar como se ve la pila de llamadas de Java entre unos métodos y otros, que si hubiera ocurrido entre diferentes tipos de objeto también podríamos verlo. En la primera línea cabe destacar como devuelve el error división por cero que es la información relativa al problema que hemos tenido.

 

Bloque Try Catch en JAVA

 

Cuando estamos ejecutando un programa que hemos hecho, normalmente no deseamos que por el hecho de que ocurra un error, se detenga el flujo de nuestro programa, sino que deseamos reconducir el problema, notificarlo y que se pueda seguir trabajando con el programa normalmente. Para esto esta diseñado el bloque try – catch, que permite capturar excepciones y que el programa no se detenga cuando se lanza una excepción.

Veamos a continuación el uso del bloque try – catch. Para ello mejoramos el primer ejemplo aumentando el código para las zonas sensibles de lanzar excepciones.

 

public class ExceptionDemo {

    public void method1() {

         System.out.println(“Dentro de method1”);

         method2();

    }

    public void method2() {

         System.out.println(“Dentro de method2”);

         method3();

    }

    public void method3() {

         System.out.println(“Dentro de method3”);

         int x = 5, y = 0;

         int z = x/y; //Esta línea lanza una ArithmeticException

         System.out.println(“z = “ + z);

    }

    public static void main(String [ ] args) {

         System.out.println(“Inicio main”);

         try {

            new ExceptionDemo().method1();

         }  catch (ArithmeticException ex) {

            ex.printStackTrace();

         }

         System.out.println(“Final main”);

    }

}

 

Ahora la línea que sabemos que lanza la excepción la hemos incrustado en un bloque try – catch que a continuación detallaremos a nivel de reglas sintácticas.

Al ejecutar el código observamos como la ejecución ha variado notablemente, porque vemos todo lo ocurrido anteriormente y además podemos ver la traza Final main, ya que ahora al saltar la excepción el nuevo bloque la ha capturado y automáticamente el flujo de programa se ha conducido al bloque catch.

 

Las excepciones son objetos del API de Java como cualquier otro de los que hemos visto anteriormente, y por lo tanto tienen sus métodos y atributos, en este primer ejemplo, hemos resaltado el uso de método printStackTrace que imprime toda la pila de ejecución Java.

 

Al bloque try le aplican las siguientes reglas de sintaxis:

 

  • Irá seguido de llaves que determinan un ámbito de programación, donde se recogerán las excepciones que se determinen en las sentencias catch que sigan al try.

  • Dentro de un bloque de este tipo de pueden incluir todas las líneas de código Java que sean necesarias. Cuando salta una excepción de una de las líneas de un bloque try el resto de líneas del bloque no se ejecutan, directamente se pasa a ejecutar el bloque catch y tras este se continúa con el programa por la siguiente sentencia posterior al bloque try – catch.

  • Un try deberá como mínimo ir seguido de un bloque catch.

  • Se pueden anidar más de un bloque catch a un mismo try. Uno por cada tipo de excepción diferente que se quiera recoger.

 

Nosotros podemos programar nuevas excepciones para completar las que están ya recogidas por Java, como las que hemos visto hasta el momento. Para programar una excepción es como programar una clase normal sólo que debemos extender de la clase java.lang.Exception. Este hecho implica que al estar en la cadena de herencia de la clase Exception nos hemos convertido nosotros también, como vimos en la herencia, en una Exception, solo que podemos mejorarla con las reglas que aprendimos, especializandola o modificando su comportamiento.

Imaginemos que nuestro programa exige que se lance en determinadas ocasiones una excepción cuando detectamos que, antes de realizar una operación de negocio, uno de los números es negativo. Para ello determinamos hacerlo con una excepción, primero debemos programarla:

 

public class NumeroNegativoException extends Exception {

    protected String mensaje;

    public NumeroNegativoException(){

         this.mensaje="";

 

    }

 

    public NumeroNegativoException(String mensaje){

         this.mensaje=mensaje;

 

    }

    public String getMensaje() {

         return mensaje;

    }

 

}

 

Reglas para lanzar una excepción en JAVA

 

Una vez programada la excepción, la podemos utilizar en nuestro programa donde corresponda. Para lanzar una excepción es necesario tener en cuenta las siguientes reglas:

Para la lanzar una excepción utilizaremos la palabra reservada throw, que indica que elevamos una excepción.

Se debe lanzar un objeto, y no una clase, por lo tanto para poder lanzar una excepción deberemos utilizar el operador new y crear un objeto a partir de la clase de excepción que queramos lanzar.

Aquel método que lance una excepción debe indicarlo en su definición con la palabra reservada throws seguido del nombre de la clase de las excepciones que lance el método. Si lanza más de una, se enumerarán entre comas.

 

 

En el siguiente código podemos ver como lanzamos la excepción programada anteriormente en el caso que el parámetro que espera nuestra clase de negocio sea negativo:

 

public class ClaseNegocio {

     public void ejecutaNegocio(int numero) throws NumeroNegativoException{

        if (numero<0) {

           NumeroNegativoException ex = 

              new NumeroNegativoException("EXCEPCION: El numero " + numero + " es negativo.")

           throw ex;

        }

        // aqui sentencias de codigo de negocio

     }

}

 

En el momento que se lanza una excepción las siguientes sentencias de código Java no se ejecutan, por lo tanto las sentencias de negocio, sólo se ejecutaran en el caso que no se lance la excepción.

 

Declaración de excepciones en JAVA

 

Cuando se utiliza un método de un objeto Java que en su declaración indica que lanza excepciones, obligatoriamente el compilador nos obliga a que nuestro método o bien capture la excepción mediante un bloque try – catch o indique en un definición que se lanza esa excepción mediante la clausula throws. En el siguiente ejemplo se muestra como se utiliza la ClaseNegocio desde otra clase.

 

public class Principal {

     public static void main(String[ ] args) {

        ClaseNegocio miNegocio = new ClaseNegocio();

        try {

            miNegocio.ejecutaNegocio(5);

            System.out.println(“Primera ejecución de negocio”);

            miNegocio.ejecutaNegocio(-3);

            System.out.println(“Segunda ejecución de negocio”);

 

        } catch (NumeroNegativoException ex) {

            ex.printStackTrace();

        }

        // continua nuestro programa con normalidad

     }

}

 

 

Flujo de ejecución:

 

Si seguimos el flujo de la ejecución de nuestro código podemos ver como la segunda traza no se ve por la consola ya que se lanza antes la excepción que es capturada y luego vemos la pila de misma y luego continua nuestro código.

 

public class PrincipalExcepcionFinally {

    public static void main (String args[ ]) {

       int i = 0;

       String cadenas[ ] = {

        "Cadena 1", "Cadena 2", "Cadena 3", "Cadena 4"

       };

       try {

          for (i=0; i<=4; i++) 

             System.out.println(cadenas[i]);

       } catch( ArrayIndexOutOfBoundsException ex ) {

           ex.printStackTrace();

       } catch( Exception e ) {

           // Captura cualquier otra excepción

           e.printStackTrace();

       } finally {

           // Bloque que siempre se ejecuta

           System.out.println( "Esto se imprime siempre." );

       }

    }

}

 

En el programa que acabamos de ver mostramos como ocurre un error al intentar acceder a una posición que no existe dentro de un array, eso produce una excepción en Java llamada java.lang.ArrayIndexOutOfBoundsException, la cual capturamos en el primer catch que hemos asociado a nuestro bloque try, aunque en previsión de que pudiera ocurrir algún otro error hemos anidado un segundo catch que recoge cualquier excepción ya que se indica que recoja Exception que es el padre de las excepciones.

 

Por último podemos observar un bloque finally que no hemos visto hasta el momento. Es un bloque opcional que se permite anidar uno por bloque try – catch después del último catch que hayamos escrito. Este bloque es un bloque que siempre se ejecuta independientemente de que el flujo del programa vaya por el try o por cualquier de los catch que tiene programados. Es un bloque que se pone cuando es necesario realizar determinadas tareas de finalización independientemente del resultado de negocio obtenido en nuestra ejecución, como son liberación de recursos tales como conexiones a bases de datos, o cerrar los flujos de entrada salida que tenemos con ficheros.

 

Seguimos con el curso de Java en el siguiente enlace:

Programación en Java SE 6 desde cero: Parte 3 

Las dudas que os puedan comentarlas justo aquí debajo e iremos respondiendo. 

¡Deja un comentario!