OCP7 12 – Conexión JDBC en Java 7

Introducción 

JDBC (Java Database Connectivity) es un acrónimo que identifica la API mediante la cuál las aplicaciones Java pueden conectarse a sistemas gestores de bases de datos (BBDD).
Esta conexión se obtiene por la utilización de interfícies de conexión llamadas controladores JDBC (o conocidos también como drivers).
Estas bases de datos acostumbran a ser en general relacionales, aunque también existen otros drivers para otros tipos de BBDD (nosql, ficheros planos, hojas de cálculo, etc).

API JDBC en Java 7 (Paquetes principales)

La API está compuesta por dos paquetes principales:
Ambos están ya incluidos dentro del SDK estándar de Java cuándo se descarga en su versión 7.
En las notas técnicas puede encontrarse información más detallada, este post pretende ser una introducción solamente.

Uso de JDBC

El paquete java.sql consiste en ejecutar sentencias SQL de tipo consulta, aunque también permite leer y escribir datos  mediante operaciones de modificación realizando su conexión des de cualquier fuente de datos utilizando un formato tabular (en forma de tupla).

La URL del JDBC se construye mediante la plantilla siguiente:

 jdbc : subprotocolo : subnombre

El esquema sobre la operación del controlador JDBC para ejecutar una sentencia SQL en Java:
El algoritmo de ejecución detallado en la imagen anterior es el siguiente:
1.-Mediante el DriverManager se utiliza el método getConnection(…) para disponer de una conexión al SGBD.
2.-Si se ha realizado la conexión con el SGBD de forma correcta, se crea la consulta mediante la API del JDBC utilizando el método createStatement(…).
Además en este paso se parametrizan los elementos que así lo requieran (valores adicionales en los elementos WHERE, posibles alias utilizados, etc)

3.-El último paso consiste en ejecutar la sentencia SQL y gestionar la tupla obtenida como resultado de su ejecución parametrizando la información según se requiera.

Por otro lado el paquete javax.sql,  proporciona una API en la capa del servidor para el acceso a la fuente de datos y procesado del lenguaje de programación Java. Incorpora los siguientes añadidos:

  • La interfície de DataSource  como una alternativa al DriverManager para establecer la conexión con una fuente de datos.
  • Pooling de conexiones y sentencias de SQL.
  • Transacciones distribuidas.
  • Conjuntos de filas.
  • Aplicaciones para utilizar directamente las API’s DataSource y RowSet,  aunque las API’s del pooling de conexiones y de ls transacciones distribuidas se utilizan internamente por intermediarios.
Cuándo se cierran los recursos del JDBC una vez han sido utilizados se sigue el siguiente proceso:
Fig. 2. Cierre de la conexión
Fig. 2. Cierre de la conexión
  1. El cierre de Connection cierra automáticamente todos los recursos.
  2. El cierre del objeto ResultSet debe realizarse explícitamente siempre que no se utilice dado que si se deja automáticamente sólo se cerrará cuándo sea analizado por el recolector de basura. Es una buena práctica siempre cerrarlo explícitamente.
  3. Por último siempre cerrar cualquier recurso externo que sea capaz de mantener activa la conexión del SGBD.
En Java 7 para cerrar correctamente todos los recursos JDBC debe utilizarse una herramienta introducida en llamada try-with-resources:
try (Connection con = DriverManager.getConnection(url, username, password));
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(query);
{
 // Using resources
}catch(Exception ex) {
}
Esto permite cerrar todos los recursos al final del bloque de código.
Si se produce alguna excepción el bloque try antes de ser capturada la excepción, en el bloque catch se cierran los recursos en el orden inverso al utilizado.
Para utilizar correctamente la herramienta try-with-resources entre los paréntesis que lo implementan debemos utilizar los objetos que implementen la interfaz AutoCloseable.

Sql y JDBC

La API del JDBC:

  • No restringe las sentencias que se pueden utilizar en una BBDD.
  • No controla que las sentencias enviadas a la BBDD estén correctamente formuladas.
  • Suministra tres clases y tres métodos respectivamente para el envío de sentencias SQL:
Fig. 3. Creacion de objetos SQL
Fig. 3. Creacion de objetos SQL
  • Statement: Utiliza el método de createStatement y incluye los métodos de executeQuery (para consultas) y executeUpdate(para operaciones de modificación).
  • PreparedStatement: Se utiliza para enviar consultas SQL que tengan uno o más parámetros como argumentos de entrada. Cuenta con métodos propios que nos ayudan a dar valor a estos parámetros.   Se muestra el siguiente código fuente como ejemplo:
PreparedStatement ps = con.prepareStatement(
 "select * from OWNER where ID=? AND NAME=? AND CODE=?");
ps.setInt(1,id-employee);
ps.setString(2,name);
ps.setInt(3,code);
  • CallableStatement:  Se usan para ejecutar procedimientos almacenados SQL (Stored Procedures). Éstos son un grupo de sentencias SQL que son llamados mediante un nombre. Un objeto CallableStatement hereda de PreparedStatement los métodos para el manejo de parámetros y además añade métodos para el manejo de estos parámetros.  Se muestra el siguiente código fuente como ejemplo:
String createProcedure = "create procedure SHOW_SUPPLIERS "
 + "as "+"select SUPPLIERS.SUP_NAME, COFFEES.COF_NAME "
 + "from SUPPLIERS, COFFEES "
 + "where SUPPLIERS.SUP_ID=COFFEES.SUP_ID "
 + "order by SUP_NAME";
CallableStatement cs = con.prepareCall("{call SHOW_SUPPLIERS}");

Toda acción que se realice sobre la BBDD de datos abrir/cerrar conexión, ejecutar una sentencia SQL, etc pueden lanzar una excepción del tipo SQLException que se deberá capturar o propagar.

Este error puede resultar crítico para la integridad de los datos de la BBDD: la clase SQLException hereda de Iterable lo que permite recorrer dicha cadena de fallos. Así es posible recorrer todos los objetos de tipo Throwable que haya en una excepción SQLException.

Transacción

Mecanismo para manejar grupos de operaciones como si fueran una acción realizada de forma única.

Cada transacción debe tener las propiedades ACID:

  • Atomicidad (Atomicity). Una operación se hace o se deshace por completo.
  • Consistencia (Consistency). Transformación de un estado consistente a otro estado consistente.
  • Aislamiento (Isolation). Cada transacción se produce con independencia de otras transacciones que se produzcan al mismo tiempo.
  • Permanencia (Durability). Propiedad que hace que las transacciones realizadas sean definitivas.

Con JDBC se ejecuta un COMMIT automático  (autoCOMMIT) tras cada insert, update o delete (con excepción de si se trata de un procedimiento almacenado).

Para indicar que una sentencia SQL no se ejecuta de forma automática se utilizará el método setAutommit(boolean); de la interfaz Connection pasándole como parámetro el valor false.

De este modo se pueden agrupar varias sentencias SQL en una misma transacción siendo el programador el que gestiona el momento de realizar el COMMIT de la ejecución.

ROLLBACK permite deshacer las transacciones que se hayan ejecutado dejando la BBDD en un estado consistente.
Si se cierra la conexión sin hacer COMMIT o ROLLBACK, explícitamente se ejecuta un COMMIT automático aunque el autoCOMMIT esté asignado a false.

Api RowSet

Fig. 4. RowSet
Fig. 4. RowSet Api Description
Las interfaces que componen la API son las siguientes:

 

CachedRowSet
Permite obtener una conexión des de un DataSource, además de permitir la actualización y desplazamiento de datos sin necesidad de disponer de la conexión a BBDD abierta.

FilterRowSet
Deriva de RowSet y añade la posibilidad de aplicar criterios de filtros para hacer visible cierta porción de datos de un resultado global.

JdbcRowSet
Clase que engloba el funcionamiento básico de un ResultSet y añade capacidades de desplazamiento y actualización de datos.

JoinRowSet
Deriva de WebRoseSet y añade capacidades similares al JOIN de SQL pero sin necesidad de estar conectado a la fuente de datos.

WebRowSet
Deriva de CachedRowSet y añade funcionalidad para la lectura y escritura de documentos XML.

Las clases que componen la API son las siguientes:

BaseRowSet
Clase base abstracta que provee un objeto RowSet junto a su funcionalidad básica.

RowSetMetadaImpl
Clase que proporciona implementaciones para los métodos que establecen y recuperan información de los metadatos de las columnas del objecto RowSet.

Un objeto RowSetMetaDataImpl realiza un seguimiento del número de columnas del RowSet y mantiene un Array interno de los atributos de la columna para cada una de las columnas.

Un objeto RowSet crea internamente un objeto RowSetMetaDataImpl con el fin de establecer y recuperar información sobre sus columnas.

RowSetProvider
Se utiliza para crear un objeto RowSetFactory.  A su vez, RowSetFactory se utiliza para crear instancias de implementaciones de RowSet (que deriva de ResultSet y por lo tanto contiene todas las capacidades de ResultSet pero añadiendo nuevas funcionalidades).

 

OCP7 08 – Excepciones (I)

Tipos de Excepciones

Excepciones comprobadas (checked).

Representan errores producidos durante la ejecución de un programa por condiciones inválidas en el flujo esperado. Estos errores pueden ser previsibles o esperables y por eso se puede definir un flujo alternativo para tratarlos. Es el caso, por ejemplo, de errores de conexión de red, errores en la localización de ficheros, conexión a base de datos, etc. En estos casos, se puede aplicar una política de re-intentos o bien informar al usuario del error de forma controlada si se trata de un entorno interactivo.

Los métodos están obligados a tratar de alguna manera las excepciones de este tipo producidas en su implementación, ya sea relanzándolas, apilándolas o tratándolas mediante un bloque try/catch.

Exception y subclases, excepto RuntimeException y subclases.

Excepciones no comprobadas (unchecked).

Representan errores producidos durante la ejecución de un programa de los que no se espera una posible recuperación o no se pueden tratar. Se incluyen entre estos casos errores aritméticos, cómo divisiones entre cero, excepciones en el tratamiento de punteros, cómo el acceso a referencias nulas (NullPointerException) u errores en el tratamiento de índices, cómo por ejemplo el acceso a un índice incorrecto de un array.

Este tipo de errores pueden ocurrir en cualquier lugar de la aplicación y no se requiere su especificación en la firma de los métodos correspondientes o su tratamiento a través de bloques try/catch (aunque es posible hacerlo) lo que facilita a la legibilidad del código.

RuntimeException, Error y subclases de éstas.

En concreto la excepción no comprobada Error representa errores producidos por condiciones anormales en la ejecución de una aplicación que nunca deberían darse. En su mayoría se trata de errores no recuperables y por esta razón, este tipo de excepciones no extienden de Exception y si de Throwable con el propósito que no sean capturadas accidentalmente por ningún bloque try/catch que pueda impedir la finalización de la ejecución. A nivel de compilación, estos se tratan de igual forma que las excepciones no comprobadas, por lo que no hay la obligación de declarar su lanzamiento en las firmas de los métodos.

Ejemplos:

  • VirtualMachineError: Indica que se ha producido un error que impide a la máquina virtual seguir con la ejecución, sea porque se ha roto o porque no puede conseguir los recursos necesarios para hacerlo, cómo por ejemplo, por falta de memoria (OutOfMemoryError), porque se haya producido un desborde de la pila (StackOverflowError) o porque se haya producido un error interno (InternalError).
  • LinkageError: Indica incompatibilidades con una dependencia (clase) que ha sido modificada después de la compilación.
  • AssertionError: Indica un error en una aserción.

Jerarquía de Excepciones

La jerarquía de las excepciones en Java 7 puede visualizarse en el siguiente esquema:

Fig. 1. Jerarquía de excepciones
Fig. 1. Jerarquía de excepciones

La superclase de todas las excepciones es Throwable.

La clase Exception sirve como superclase para crear excepciones de propósito específico, es decir, adaptado a nuestras necesidades.

La clase Error está relacionada con errores de compilación, del sistema o de la JVM. Normalmente estos errores son irrecuperables.

RuntimeException (Excepciones Implícitas): Excepciones muy frecuentes relacionadas con errores de programación. Existen pocas posibilidades también de recuperar situaciones anómalas de este tipo.

Lanzamiento de Excepciones

Para el lanzamiento de una excepción debe ejecutarse el siguiente código:
//  Crear una excepcion
MyException me = new MyException("Myexception message");

//  Lanzamiento de la excepción
throw me;

Bloque try-catch

El bloque que puede lanzar una excepción se coloca dentro de un bloque try.  Se escribe un bloque catch para cada excepción que se quiera capturar. Ambos bloques se encuentran ligados en ejecución por lo que no debe existir una separación entre ellos formando una estructura try-catch conjunta e indivisible. Pueden asociarse varios bloques catch a un mismo bloque try.

import java.util.*;

public class ExTryCatch {
 public static void main(String[] args){
  
  int i=-3;
  
  try{
   String[] array = new String[i];
   System.out.println("Message1");
  }
  
  catch(NegativeArraySizeException e){
   System.out.println("Exception1");
  }
  
  catch(IllegalArgumentException  e){
   System.out.println("Exception2");
  }
  
  System.out.println("Message2");
  
 }
}

Bloque Finally
Cuándo se agrupan excepciones al acceder a uno de los catch el resto de catch no se ejecutarán y puede provocar un error en la liberación de recursos utilizados en un programa. Java cuenta con un mecanismo para evitar esto de forma consistente en el bloque de código finally el cuál siempre se ejecuta.

import java.util.*;

public class ExFinally {
 public static void main(String[] args){
  
  int i=5;
  
  try{
   String[] array = new String[i];
  }
  
  catch(NegativeArraySizeException e){
   System.out.println("Exception1");
  }
  
  catch(IllegalArgumentException  e){
   System.out.println("Exception2");
  }
  
  finally{
   System.out.println("This always executes");
  }
  
  
 }
}

 

OCP7 04 – Diseño avanzado de una clase JAVA

Clases

Las clases abstractas sirven para indicar el comportamiento general que deben tener sus subclases sin implementar algunos métodos. Dichas subclases son las que se encargan de implementar estos métodos.
abstract class FiguraGeometrica {
     abstract void dibujar();
}

class Circulo extends FiguraGeometrica {
    void dibujar() {
     // codigo para dibujar Circulo
   }
}
Una clase que tenga uno o más métodos abstractos se llama clase abstracta. Debe contener en su declaración el termino abstract. A su vez, esta clase puede también contener métodos que no sean abstractos.
Un método abstracto contiene el termino abstract en su declaración y no dispone de ninguna implementación:  abstract type nom_metodo();

Es obligatorio que todas las subclases que heredan de la clase abstracta realicen la implementación de todos los métodos abstractos de la superclase dentro de su especificidad.

Aunque una clase abstracta puede ser utilizada como tipo de referencia, ésta no puede ser instanciada ya que su implementación está completa y si se intenta se producirá un error de compilación.

Palabras Reservadas 

Palabra Reservada Static

Es útil disponer de una variable compartida por todas las instancias de una clase mediante el modificador static.

Los campos que tienen static en su declaración se llaman campos estáticos o variables de clase.

Si existen por ejemplo tres objetos definidos que tienen el campo definido como estático, todas sus instancias pueden modificar el valor de dicho campo dado que está compartido por todos los objetos de la misma clase.
Para referenciar el campo estático es suficiente utilizar el nombre de la clase para acceder al campo.
Un método estático también tiene el modificador static en su declaración. También puede ser invocado sólo con el nombre de clase sin necesidad de instanciar el tipo.
Cualquier de acceso a campos o métodos no estáticos provocará un error de compilación.
Una clase también puede contener bloques de código estáticos que no forman parte de los métodos normales. Estos bloques estáticos se encuentran encerrados entre llaves. El código dentro de los bloques estáticos sólo se ejecuta una única vez, cuándo la clase se carga por primera vez. Si aparecen varios bloques dentro de la clase, éstos se ejecutan por orden de aparición en la clase.
Importaciones staticPueden utilizarse cuándo queramos importar campos o métodos estáticos de una clase sin anteponer el nombre de la misma:       
import static java.lang.Math.random
y al no ser necesario anteponer el nombre de la clase al utilizar la importación estática su implementación queda de la siguiente manera:
         double d = random();
         System.out.println(d);

Final: Las clases, los métodos y las variables pueden ser finales.

-Final en las clases. No se pueden generar subclases a partir de esta clase.

-Final en los métodos. No pueden ser sobrescritos.

-Final en las variables: campos, parámetros o variables locales.

Una variable de referencia como final no puede hacer referencia a otro objeto (aunque puede modificarse el estado del mismo)
Un campo puede posponer su inicialización (inicializarse mediante un constructor de la clase) o inicializarse en su declaración. Una vez está inicializado no es posible modificar su valor. El intento de modificarlo provoca un error de compilación.
Si se marca una variable como local se le podrá asignar el valor en cualquier momento dentro del cuerpo del método pero sólo una vez.
Dentro de los parámetros formales de un método si marcamos uno de los parámetros como final, el valor recibido en el método no podrá ser modificado dentro del cuerpo del método dando un error de compilación si se intenta.

Si se utilizan static y final conjuntamente como en el caso de ejemplo    

private static final int field;   

se puede considerar el campo como una constante.  Las constantes no se pueden reasignar y se producirá un error de compilación si se intenta.

Enumeraciones

public enum Day {
             SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY,
             SATURDAY;
}
Un tipo enumerado es un tipo cuyos campos son un conjunto de constantes. Proporciona una comprobación de rango en tiempo de compilación.
El compilador interpreta la Enum como una clase Java por lo que es posible declararla fuera de una clase o dentro de ella como una clase interna.
Todas las enumeraciones extienden implícitamente de la clase Enum.
Las enumeraciones pueden ser importadas estáticamente:
import static package.Day.*;

Esto permite no ser necesario que el nombre de la enumeración preceda la constante.

Una enumeración proporciona una comprobación de rango en tiempo de compilación. Si se intenta asignar un valor que no se encuentra en la enumeración se produce un error de compilación.
Debido a que los tipos enumerados son como una clase Java además de declarar constantes, un tipo enumerado puede contener:
  • campos
  • métodos
  • constructores privados. El constructor de una enumeración es privado por lo que no es posible crear instancias de una enumeración.  Los argumentos del constructor se suministran después de cada valor declarado:
public enum Day {

    SUNDAY("Sunday"),
    MONDAY("Monday"),
    TUESDAY("Tuesday"),
    WEDNESDAY("Wednesday"),
    THURSDAY("Thursday"),
    FRIDAY("Friday"),
    SATURDAY("Saturday");

    private final String name;

    private Day(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }   
}

Cuándo hay campos, métodos o constructores, la lista de constantes de la enumeración debe terminar con un punto y coma.

Las enumeraciones pueden implementar interfaces pero no pueden extender otras enumeraciones. Una posible utilidad de esto es «esconder» la enumeración referenciando a la interfaz:

public enum Day implements IDayOfWeek
{
             SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY,
             SATURDAY;
}

...

IDayOfWeek dow = Day.SUNDAY;

Clases Anidadas

Es una clase declarada en el cuerpo de otra clase.

import java.util.ArrayList;

public class Coordenadas {

    private class Punto {
        private int x, y;

        public Punto(int x, int y) {
            fijarX(x);
            fijarY(y);
        }

        public void fijarX(int x) {
            this.x = x;
        }

        public void fijarY(int y) {
            this.y = y;
        }

        public int retornarCuadrante() {
            if (x > 0 && y > 0)
                return 1;
            else if (x < 0 && y > 0)
                return 2;
            else if (x < 0 && y < 0)
                return 3;
            else if (x > 0 && y < 0)
                return 4;
            else
                return -1;
        }
    }

    private ArrayList<Punto> puntos;

    public Coordenadas() {
        puntos = new ArrayList<Punto>();
    }

    public void agregarPunto(int x, int y) {
        puntos.add(new Punto(x, y));
    }

    public int cantidadPuntosCuadrante(int cuadrante) {
        int cant = 0;
        for (Punto pun : puntos)
            if (pun.retornarCuadrante() == cuadrante)
                cant++;
        return cant;
    }

}

Pueden dividirse en dos categorías:

1.-Clases Internas.

  1. Clases miembro. Están declaradas dentro de una clase y fuera de cualquier método. Esta clase tiene acceso a los campos y a los métodos de la clase anterior, así como de los campos y los métodos de la superclase de la que herede.  No es posible declarar ninguna clase miembro estático debido a que una clase miembro es cargada sólo dentro del contexto de una instancia de su clase exterior.
    public class DataStructure {
        
        // Create an array
        private final static int SIZE = 15;
        private int[] arrayOfInts = new int[SIZE];
        
        public DataStructure() {
            // fill the array with ascending integer values
            for (int i = 0; i < SIZE; i++) {
                arrayOfInts[i] = i;
            }
        }
        
        public void printEven() {
            
            // Print out values of even indices of the array
            DataStructureIterator iterator = this.new EvenIterator();
            while (iterator.hasNext()) {
                System.out.print(iterator.next() + " ");
            }
            System.out.println();
        }
        
        interface DataStructureIterator extends java.util.Iterator<Integer> { } 
    
        // Inner class implements the DataStructureIterator interface,
        // which extends the Iterator<Integer> interface
        
        private class EvenIterator implements DataStructureIterator {
            
            // Start stepping through the array from the beginning
            private int nextIndex = 0;
            
            public boolean hasNext() {
                
                // Check if the current element is the last in the array
                return (nextIndex <= SIZE - 1);
            }        
            
            public Integer next() {
                
                // Record a value of an even index of the array
                Integer retValue = Integer.valueOf(arrayOfInts[nextIndex]);
                
                // Get the next even element
                nextIndex += 2;
                return retValue;
            }
        }
        
        public static void main(String s[]) {
            
            // Fill the array with integer values and print out only
            // values of even indices
            DataStructure ds = new DataStructure();
            ds.printEven();
        }
    }
  2. Clases locales. Declarada dentro de un bloque de código en el cuerpo de un método y sólo es visible dentro del bloque de código en el que se ha definido. En las clases internas locales primero se define la clase y luego se crean uno o más objetos según la necesidad.
    public class LocalClassExample {
      
        static String regularExpression = "[^0-9]";
      
        public static void validatePhoneNumber(
            String phoneNumber1, String phoneNumber2) {
          
            final int numberLength = 10;
            
            // Valid in JDK 8 and later:
           
            // int numberLength = 10;
           
            class PhoneNumber {
                
                String formattedPhoneNumber = null;
    
                PhoneNumber(String phoneNumber){
                    // numberLength = 7;
                    String currentNumber = phoneNumber.replaceAll(
                      regularExpression, "");
                    if (currentNumber.length() == numberLength)
                        formattedPhoneNumber = currentNumber;
                    else
                        formattedPhoneNumber = null;
                }
    
                public String getNumber() {
                    return formattedPhoneNumber;
                }
                
                // Valid in JDK 8 and later:
    
    //            public void printOriginalNumbers() {
    //                System.out.println("Original numbers are " + phoneNumber1 +
    //                    " and " + phoneNumber2);
    //            }
            }
    
            PhoneNumber myNumber1 = new PhoneNumber(phoneNumber1);
            PhoneNumber myNumber2 = new PhoneNumber(phoneNumber2);
            
            // Valid in JDK 8 and later:
    
    //        myNumber1.printOriginalNumbers();
    
            if (myNumber1.getNumber() == null) 
                System.out.println("First number is invalid");
            else
                System.out.println("First number is " + myNumber1.getNumber());
            if (myNumber2.getNumber() == null)
                System.out.println("Second number is invalid");
            else
                System.out.println("Second number is " + myNumber2.getNumber());
    
        }
    
        public static void main(String... args) {
            validatePhoneNumber("123-456-7890", "456-7890");
        }
    }
  3. Clases anónimas. Se usan para definir clases sin nombre. Cómo la clase anónima no tiene nombre sólo se puede crear un único objeto ya que las clases anónimas no pueden definir constructores.
    public class HelloWorldAnonymousClasses {
      
        interface HelloWorld {
            public void greet();
            public void greetSomeone(String someone);
        }
      
        public void sayHello() {
            
            class EnglishGreeting implements HelloWorld {
                String name = "world";
                public void greet() {
                    greetSomeone("world");
                }
                public void greetSomeone(String someone) {
                    name = someone;
                    System.out.println("Hello " + name);
                }
            }
          
            HelloWorld englishGreeting = new EnglishGreeting();
            
            HelloWorld frenchGreeting = new HelloWorld() {
                String name = "tout le monde";
                public void greet() {
                    greetSomeone("tout le monde");
                }
                public void greetSomeone(String someone) {
                    name = someone;
                    System.out.println("Salut " + name);
                }
            };
            
            HelloWorld spanishGreeting = new HelloWorld() {
                String name = "mundo";
                public void greet() {
                    greetSomeone("mundo");
                }
                public void greetSomeone(String someone) {
                    name = someone;
                    System.out.println("Hola, " + name);
                }
            };
            englishGreeting.greet();
            frenchGreeting.greetSomeone("Fred");
            spanishGreeting.greet();
        }
    
        public static void main(String... args) {
            HelloWorldAnonymousClasses myApp =
                new HelloWorldAnonymousClasses();
            myApp.sayHello();
        }            
    }

2.-Clases anidadas estáticas.
Se definen mediante el modificador static y sólo pueden ser creadas dentro de otra clase al máximo nivel, es decir, directamente en el bloque de definición de la clase contenedora y no en un bloque más interno.

clase ClaseExterior
{ 
... 
    clase ClaseAnidada
    { 
        ... 
    } 
}

OCP7 02 – Repaso general de la sintaxis Java

Estructura de una clase

El package se coloca siempre al principio de un archivo java antes de cualquier declaración dentro de la clase.
El import permite realizar la importación de las clases ubicadas en otros paquetes.
El término class se corresponde con un bloque de código que contiene la declaración de una clase. Precede siempre al nombre de la clase. El nombre de la clase siempre debe coincidir con el nombre del fichero .java.
Los comentarios en Java para bloques de texto se utilizan los carácteres  /* */. En caso de querer comentar sólo una línea de código se utilizan los caracteres //.

Las variables miembro de una clase se declaran siempre dentro de una clase pero de forma externa al constructor de la clase. Java es un lenguaje fuertemente tipado y esto obliga a que las variables deban inicializarse antes de ser utilizadas. En Java todos los parámetros son pasados por valor.

 

Constructores

  1. El nombre del constructor debe coincidir con el nombre de la clase.
  2. El constructor no devuelve ningún valor solamente inicializa (si así se desea) las variables miembro de la clase.
  3. Existe un constructor por defecto instanciado por el compilador, pero también existe el constructor por parámetro recibiendo como parámetros los posibles valores para las variables miembro de la clase y siendo asignadas a éstas en el cuerpo del constructor por parámetro.
  4. Toda implementación dentro de un constructor es estrictamente opcional dado que ya existe uno proporcionado por defecto por parte del compilador.
El punto de partida para cualquier programa Java que se ejecute des de una línea de comando es el bloque main:
public static void main(String[] args)

 

Tipos de datos primitivos y operadores

En Java existen varios tipos de datos primitivos.  El siguiente cuadro los ilustra:
Tipos Primitivos en Java
Fig.1 Tipos de Datos Primitivos en Java
Debe tenerse en cuenta que el compilador  no asigna un valor predeterminado a una variable local por lo que puede producirse un error de compilación si no se asigna manualmente.
Dentro del rango de operaciones que pueden realizarse se contemplan las operaciones matemáticas y las lógicas. El siguiente cuadro ilustra los operadores que se pueden utilizar:
Fig. 2 Operadores en Java
Fig. 2 Operadores matemáticos/lógicos Java 7

 

Sentencias de control de flujo

Pueden encontrarse las sentencias for, if…else, do…while, while.

La sentencia switch

Fig. 3 Implementación de Switch en Java 7

evalúa el valor de una variable de tipo int, char o String.  Permite una acción u otra según el resultado de una comparación o un valor lógico. Permite también agrupar un mismo resultado para varios case.

 

Programación orientada a objetos

Paso por valor

En Java siempre que se trabaja con paso de parámetros por valor con argumentos de tipo primitivo o referencia siempre se trabaja con copias de los valores, nunca con los valores originales. De este modo, se asegura la integridad del valor con el que se desea trabajar. En el caso de referencias a objetos, se entiende como valor la propia referencia, no el objeto en si.

Encapsulación

Consiste en la acción de ocultar los datos dentro de una clase y hacerlos disponibles mediante ciertos métodos. Para obtener la encapsulación correctamente se hace necesario aplicar los modificadores de visibilidad a la clase, a los campos de la clase y a las declaraciones de los métodos.

En general se llaman métodos asignadores y recuperadores a los métodos que disponen de los valores encapsulados dentro de una clase.

Herencia

Consiste en un mecanismo de relación entre clases. Aunque cada clase sólo tiene una SUPERCLASE,  existe una clase que se encuentra en la parte superior de la estructura de herencia.

Es la clase Object, clase de la que derivan TODAS las clases Java y a la que TODOS sus hijos pueden acceder a sus variables miembro y métodos miembro.

La herencia se implementa mediante la palabra clave:

extends <nom_class_padre>
El mecanismo de herencia permite a los programadores poner miembros comunes en una clase y hacer que otras clases los hereden.

 

Polimorfismo

Consiste en la capacidad para hacer referencia a un objeto mediante su forma actual o mediante su superclase.

Sobrecarga de métodos (overloading)

En Java puede haber varios métodos con el mismo nombre dentro de una misma clase pero con distintos parámetros formales en los métodos. A esto se llama overloading.

Dentro de una misma clase es posible agrupar varias sobrecargas en Java para disponer de un sólo método pero que puede recibir distintas cargas o parámetros.

El mecanismo que permite la sobrecarga se llama varargs (o argumentos variables) y permite escribir un método más génerico del tipo siguiente

public int sum (int… nums)
dónde nums es un objeto array de tipo entero.

A nivel de JVM, el compilador siempre prefiere utilizar un método que contenga tipos de parámetros a los cuáles podamos convertir los tipos de argumentos utilizados en la llamada que utilizar un método que acepte argumentos variables.

 

 

OCP7 03 – Diseño de una clase Java

En este apartado se introducen conceptos relacionados con el diseño de una clase.

Modificadores de Visibilidad

En Java existen distintos modificadores que afectan a la visibilidad de distintos elementos Java.
La siguiente tabla define los modificadores de acceso existentes para los campos y los métodos que forman una clase.
Fig.1 Visibilidad según campo o método de una clase
Fig. 1. Modificadores para un campo o método de un clase
En este caso, la tabla muestra los modificadores de acceso existentes de una clase.
Modificadores del acceso de una clase
Fig. 2. Modificadores del acceso de una clase

 

Sobrescribir un método: uso y normas

Sobrescribir un método consiste en redefinirlo en una subclase de tal forma que su nombre, número y tipo de parámetros sean iguales a los de la superclase.
Debe coincidir el tipo de retorno o el subtipo de retorno con el del método de la superclase.
En el caso de miembros estáticos de la clase, si se redefine en una subclase éste no es sobrescrito, sino que su implementación se oculta (hidden method). 
Se puede encontrar bastante información detallada en la documentación oficial de Oracle:       http://docs.oracle.com/javase/tutorial/java/IandI/override.html

InstanceOf  (Conversión de Objetos)

El operador instanceOf  permite identificar el tipo de objeto que se está tratando.
Las conversiones en dirección ascendente en la jerarquía de clases siempre están permitidas y no precisan de operador de conversión.
En el caso de las conversiones descendentes el compilador debe considerar que la conversión al menos es posible.
Si se intenta realizar una conversión de clases que no tienen relación de herencia se produce un error de compilación en la JVM.

Sobreescritura de métodos de la clase Object

La clase Object es la superclase de todas las clases Java y no necesita ser indicada la herencia mediante el operador extends de forma explícita.
Contiene tres métodos muy importantes:
-método toString:
este método es llamado si una instancia de nuestra clase es pasada a un método que toma un string  pasándolo al método println y devuelve el nombre de la clase y su dirección de referencia.
Se puede sobrescribir el método para proporcionar información de mayor utilidad.
-método equals:
este método de un objeto devuelve true únicamente si las dos referencias comparadas se refieren al mismo objeto.
Comparar el contenido de dos objetos siempre que sea posible, razón por la que se sobrescribe con frecuencia este método con implementaciones más específicas por parte del programador para demostrar la igualdad entre dos objetos ubicados en memoria.
-método hashcode:
este método se utiliza principalmente para la optimización de colecciones basadas en hash y su sobrescritura debe ir a la par que la sobrescritura del método equals.
No es recomendable crearlo manualmente a no ser que sea absolutamente necesario.
Ya existen generadores que permiten obtener de forma automática el cuerpo de este método.

OCP7 13 – Localización

Localización

La localización o regionalización es el proceso mediante el cual un producto internacionalizado se configura para una determinada región, aprovechando las opciones que la internacionalización previa de este producto ha permitido (i18n). 

Por ejemplo, la internacionalización puede permitir utilizar distintos formatos de fecha, y la localización consiste en escoger el adecuado para una región específica.

Puede encontrase información adicional en los siguientes enlaces: aquí y aquí.

Ventajas

  • Permite que se adapte un software para una región o idioma específicos añadiendo componentes específicos locales y texto traducido a la región o idioma.
  • La mayor parte de la tarea consiste en la traducción del idioma pero existen también otras tareas como formatos de fechas, cambio de moneda,  tipo de calendario, y cualquier otro elemento distintivo de la región.
El objetivo principal de la localización persigue la adaptación a la referencia cultural sin realizar ninguna modificación en el código fuente que implementa la aplicación.
Una aplicación con localización se divide en dos bloques principales:
1) Elementos de la interfaz de usuario localizable para la referencia cultural como textos, menús, etc…
2) Código Ejecutable:  Código de la aplicación a ser utilizado por todas las referencias culturales.

Ejemplos prácticos de localización: Establecer el idioma al inglés, salir de la aplicación, etc…

 

Paquete de Recursos en Java

La clase ResourceBundle aísla los casos específicos de los locales.
Esta clase devuelve parejas clave-valor de forma independiente que pueden ser programadas en una clase que extienda de ResourceBundle en un archivo de propiedades.
Para utilizarla debe crearse el archivo de paquetes y después llamar a cada localización específica des de nuestra aplicación. Cada clave identifica un componente específico de la aplicación.

Ejemplo clave-valor:

my.hello=Hello

my.goodbye=Bye

Cada fichero del paquete de recursos, sea un fichero de properties o una clase, dispone de un nombre que sigue la siguiente estructura:

Para un fichero de properties:    Message_Bundle_<language>_<country>.properties

Para una clase:   Message_Bundle_<language>_<country>.java

 

Ejemplo de los ficheros de properties:

MessageBundle_en_EN.properties

my.hello=Hello
my.goodbye=Bye
my.question=Do you speak English?

MessageBundle_es_ES.properties

my.hello=Hola
my.goodbye=Adios
my.question=u00bfHablas inglu00e9s?

MessageBundle_sv_SE.properties

my.hello=Hejsan
my.goodbye=Hejd?
my.question=Pratar du engelska?

 

Ejemplo de implementación mediante clases

 

En el ejemplo que se muestra a continuación se implementan 3 clases correspondientes a los mismos casos de uso que en el ejemplo de los ficheros properties. 

Los puntos importantes a resaltar son:

  • Las clases implementadas deben extender de la clase ResourceBundle
  • En el nombre de la clase se debe incluir el identificador del ResourceBundle así como el idioma y el código de país.
  • Se deben implementar el método handleGetObject para acceder a los mensajes a través de su clave
  • También se debe implementar el método getKeys para poder acceder al índice de todas las claves que proporciona la implementación del ResourceBundle
  • En los ejemplos, se ha utilizado un Map como backend de los textos internacionalizados, pero este mecanismo hace posible obtener los textos des de otros soportes, como por ejemplo base de datos. Para ello sólo se tendría que modificar el método populateData para que obtuviera los datos a través de una conexión jdbc. Este método no forma parte de la API de la clase ResourceBundle y sólo sirve a afectos de mostrar un posible mecanismo para indicarle a la implementación realizada la lista de mensajes que se soporta.

MessageBundle_en_EN.java

public class MessageBundle_en_EN extends ResourceBundle 
{
 HashMap data; 
   
   
 public MessageBundle_en_EN()
 {
  data = new HashMap(); 
  populateData();
 }
 
 protected void populateData()
 {
  data.put("my.hello", "Hello");                               
  data.put("my.goodbye", "Bye");                               
  data.put("my.question", "Do you speak English?");        
 }
 
 @Override
 protected Object handleGetObject(String key) 
 {
  return data.get(key);
 }

 @Override
 public Enumeration getKeys() 
 {
  return Collections.enumeration(data.keySet());
 }
}

MessageBundle_es_ES.java

public class MessageBundle_es_ES extends ResourceBundle 
{
 HashMap data; 
   
   
 public MessageBundle_es_ES()
 {
  data = new HashMap(); 
  populateData();
 }
 
 protected void populateData()
 {
  data.put("my.hello", "Hola");                                
  data.put("my.goodbye", "Adios");                             
  data.put("my.question", "¿Hablas inglés?");                    
 }
 
 @Override
 protected Object handleGetObject(String key) 
 {
  return data.get(key);
 }

 @Override
 public Enumeration getKeys() 
 {
  return Collections.enumeration(data.keySet());
 }
}

MessageBundle_sv_SE.properties

public class MessageBundle_sv_SE extends ResourceBundle 
{
 HashMap data; 
   
   
 public MessageBundle_sv_SE()
 {
  data = new HashMap(); 
  populateData();
 }
 
 protected void populateData()
 {
  data.put("my.hello", "Hejsan");                              
  data.put("my.goodbye", "Hejd?");                             
  data.put("my.question", "Pratar du engelska?");       
 }
 
 @Override
 protected Object handleGetObject(String key) 
 {
  return data.get(key);
 }

 @Override
 public Enumeration getKeys() 
 {
  return Collections.enumeration(data.keySet());
 }
}

Formateo especial números y fechas

Existen clases específicas para formatear fechas y números:

Declaraciones de las variables para estos formatos:

NumberFormat currency;

Double money = new Double(1000000.00);

Date date = new Date();

DateFormat dtf;   
A continuación se detalla el código fuente a modo de ejemplo para la inicialización de algunas variables declaradas con anterioridad.
  • DateFormat: El Javadoc puede encontrarse aquí.
  • public void showDate() {
    
         df = DateFormat.getDateInstance(DateFormat.DEFAULT, currentLocale);
    
         pw.println(df.format(today)+" "+currentLocale.toString());
    
    }
  • NumberFormat: El Javadoc puede encontrarse aquí.
  • public void showMoney() {
    
         currency = NumberFormat.getCurrencyInstance(currentLocale);
    
         pw.println(currency.format(money)+" "+currentLocale.toString());
    
    }

A continuación se detalla un caso de ejemplo de localización que muestra mensajes de texto en distintos idiomas (español, inglés y sueco) .

La clase se llama TestLocaleI18N y su implementación es la siguiente:

package i18n; 
 
import java.util.Locale;
import java.util.ResourceBundle;
 
public class TestLocaleI18N { 
 
 /*  
 La carga del ResourceBundle puede hacerse des de fichero, des del contexto de una aplicación,  
 des del propio entorno en el que se esté ejecutando esta clase Java. 
  
 En este caso de ejemplo se utiliza el acceso al fichero properties ubicado dentro  
 del mismo paquete que la clase principal.  
 En un futuro, se realizaran pruebas con el resto de mecanismos de acceso.    
 */ 
 public static void main(String[] args) throws Exception {
 
  ResourceBundle bundle1 = ResourceBundle.getBundle("i18n.TestResourceBundle");
  visualizar(bundle1, null);
 
  Locale deLocale = Locale.getDefault();
  ResourceBundle bundle2 = ResourceBundle.getBundle("i18n.TestResourceBundle", deLocale);
  visualizar(bundle2, deLocale);
 
  Locale svLocale = new Locale("sv", "SE");
  ResourceBundle svBundle = ResourceBundle.getBundle("i18n.TestResourceBundle", svLocale);
  visualizar(svBundle, svLocale);
 
  Locale spLocale = new Locale("es", "ES");
  ResourceBundle spBundle = ResourceBundle.getBundle("i18n.TestResourceBundle", spLocale);
  visualizar(spBundle, spLocale);
   
  Locale enLocale = new Locale("en", "US");
  ResourceBundle enBundle = ResourceBundle.getBundle("i18n.TestResourceBundle", enLocale);
  visualizar(enBundle, enLocale);
   
 } 
 
 public static void visualizar(ResourceBundle bundle, Locale lo) {
  if(lo == null) { lo = Locale.getDefault();}
  System.out.println("Idioma: "+lo.getLanguage()); 
  System.out.println(".........................................................");
  System.out.println("Contenido Mensaje -->hello: " +" "+ bundle.getString("my.hello"));
  System.out.println("Contenido Mensaje -->goodbye: " +" "+ bundle.getString("my.goodbye"));
  System.out.println("Contenido Mensaje -->question: " +" "+ bundle.getString("my.question"));
  System.out.println("=========================================================");
 } 
}

 

El resultado de la ejecución se mostraría mediante la consola y sería algo así:

Idioma: es
…………………………………………………
Contenido Mensaje –>hello:  Hola
Contenido Mensaje –>goodbye:  Adios
Contenido Mensaje –>question:  ¿Hablas inglés?
=========================================================
Idioma: es
…………………………………………………
Contenido Mensaje –>hello:  Hola
Contenido Mensaje –>goodbye:  Adios
Contenido Mensaje –>question:  ¿Hablas inglés?
=========================================================
Idioma: sv
…………………………………………………
Contenido Mensaje –>hello:  Hejsan
Contenido Mensaje –>goodbye:  Hejd?
Contenido Mensaje –>question:  Pratar du engelska?
=========================================================
Idioma: es
…………………………………………………
Contenido Mensaje –>hello:  Hola
Contenido Mensaje –>goodbye:  Adios
Contenido Mensaje –>question:  ¿Hablas inglés?
=========================================================
Idioma: en
…………………………………………………
Contenido Mensaje –>hello:  Hello
Contenido Mensaje –>goodbye:  Bye
Contenido Mensaje –>question:  Do you speak English?
=========================================================

OCP7 05 – La herencia en las interfaces Java

Uso de las interfaces Java

Una interfaz representa una alternativa a la herencia multiple de objetos.
Son similares a las clases abstractas ya que contienen únicamente métodos públicos y abstractos.
Ninguno de sus métodos pueden ser implementados (ni siquiera con un conjunto vacío de llaves).
La declaración de una interfaz es similar a la declaración de una clase.
Se usa la palabra reservada interface.
Para la implementación de una interface se añade implements a la clase que implementa la interfaz.
Una interfaz puede utilizarse como un tipo de referencia. Puede utilizarse el operador instanceof con las interfaces para detectar si un objeto es del tipo de referencia indicado por la interfície implementada.
Interfaces de Marcador: definen un tipo concreto pero no describen  los métodos que deben ser implementados por una clase, sólo sirven para la comprobación de tipos.
Existen dos tipos:
* java.io.Serializable és una interfaz de marcador utilizado por la biblioteca de E/S de Java para determinar si un objeto puede tener su estado serializado.

Como convertir de un tipo de dato al tipo de la interfaz

Antes de generar una excepción en la conversión de tipos de unos objetos a otros objetos se comprueba que dicha conversión sea posible mediante la utilización del operador instanceof (ya comentado anteriormente).
En general, cuándo se utilicen referencias, éstas deben utilizar el tipo más genérico posible, es decir, que sirvan para cualquier tipo de interfaz o clase padre. Así la referencia no se vincula a una clase particular.
Una clase puede heredar de una clase padre e implementar una o varias interfaces pero siempre en este orden: primero hereda – extends – y después implementa – implements separando las interfaces mediante comas.
Una interfaz puede heredar de otra interfaz.
Java no permite la herencia múltiple de clases pero sí la herencia múltiple de interfaces:
Fig. 1 Herencia múltiple de interfaces

Si escribimos una clase que hereda de una clase que implementa una interfaz, entonces la clase que estamos escribiendo hereda también de dicha interfaz. La refactorización consiste en realizar modificaciones en el código para mejorar su estructura interna sin alterar su comportamiento externo.

Composición

Este patrón de diseño permite la creación de objetos más complejos a partir de objetos más simples. Se crea una nueva clase con referencias a otras clases. A esta nueva clase le agregaremos los mismos métodos que tienen las demás clases.

 

public class ComplexClass {
	private Single1 c1 = new Single1();
	private Single2 c2 = new Single2();
	private Single3 c3 = new Single3();
}
Fig. 4 Ejemplo de patrón Composition

Referencia al polimorfismo

Vamos a describir un ejemplo de polimorfismo. Si existe una clase nueva llamada Hombre que dispone  de un método addCoche con la configuración de clases establecida en la siguiente imagen:

Fig. 7 Ejemplo de polimorfismo erróneo  en la Composición
no se le puede pasar como argumento al método addCoche de Hombre cualquier tipo de coche.
Solución: Para soportar el polimorfismo en las composiciones cada clase usada en la composición debe disponer de una interfaz definida y así se le podrá pasar cualquier tipo de coche al método en cuestión.
Fig. 8 Ejemplo de polimorfismo correcto  en la Composición

 

OCP7 11 – Hilos (01) – Introducción

 

En este apartado se exponen los conceptos básicos referentes del diseño de programas concurrentes y paralelos y los mecanismos que la especificación estándar de Java pone a disposición del programador para conseguirlo.

 
Se tratarán los siguientes conceptos:

 

  • Cómo el sistema operativo gestiona los procesos e hilos.
  • Ciclo de vida de un hilo de ejecución.
  • Sincronización y comunicación de datos entre hilos de ejecución.

 

Gestión de procesos e hilos

La Multitarea se describe cómo la habilidad de ejecutar varias tareas aparentemente al mismo tiempo compartiendo uno o varios procesadores. 
Existen dos tipos de tareas:
  • Procesos: Conjunto de instrucciones de código que se ejecutan secuencialmente y que tiene asociados un estado y recursos de sistema (espacio de memoria, punteros a disco, recursos de red…).
  • Hilos de ejecución: También llamado proceso ligero, es un flujo de ejecución secuencial dentro de un proceso. En un proceso se pueden estar ejecutando uno o más hilos de ejecución a la vez. Los hilos permiten evitar «los cuellos de botella» en el rendimiento del sistema. Su origen puede venir determinado por varias razones: bloqueo de operaciones de E/S, bajo uso de CPU o debido al recurso contencioso, consistente en que dos o más tareas queden a la espera del uso exclusivo de un recurso.

 

El planificador es el componente de los sistemas multitarea y multiproceso encargado de repartir el tiempo de ejecución de un procesador entre los diferentes procesos que estén disponibles para su ejecución.
En los sistemas operativos de propósito general, existen tres tipos de planificadores:
  • Planificador a corto plazo: Planificador encargado de repartir el tiempo de proceso entre los procesos que se encuentran en memoria principal en un momento determinado.
  • Planificador a mediano plazo. Relacionado con aquellos procesos que no se encuentran en memoria principal. Se encarga de mover procesos entre memoria principal y la memoria de Swap (Disco).
  • Planificador a largo plazo:Planificador encargado del ciclo de vida de los procesos, desde que son creados en el sistema hasta su finalización.

 

Existen diferentes políticas de planificación con multitud de variaciones y especializaciones que permiten ser utilizadas para diferentes propósitos. En el apartado de referencias se encuentran enlaces a las explicaciones de algunas de ellas.
Paralelismo
Se definen como procesos paralelos aquellos que se ejecutan en el mismo instante de tiempo, debido a esto, este tipo de computación sólo es posible en sistema multiprocesador.
Concurrencia
Se definen como procesos concurrentes aquellos que se ejecutan en un mismo intervalo de tiempo pero no necesariamente de forma simultánea. A diferencia del paralelismo, este tipo de computación se puede realizar en sistema monoprocesador alternando la ejecución de las 2 tareas. En la siguiente imagen se puede observar esta diferencia.
Existe un método en java que permite obtener el número de procesadores disponibles en un sistema:
1
int countProcessors = Runtime.getRuntime().availableProcessors();

Ciclo de vida de un hilo de ejecución

El ciclo de vida de un hilo de ejecución representa los estados por los que este pasa desde que es creado hasta que completa su tarea o finaliza su por otra razón, cómo por ejemplo porque se produce un error que lo interrumpe.
Se pueden enumerar los siguientes estados:
  • Nuevo (new): En el momento en que se crea un nuevo Thread, este se sitúa en estado nuevo hasta que el programa inicia su ejecución. En este estado el hilo no se encuentra activo.
  • Ejecutable (runnable): En el momento en que se inicia el hilo mediante el método start() se considera que este se encuentra activo. En este momento el control de su ejecución pasa a ser del planificador que decidirá si se ejecuta inmediatamente o se mantiene a la espera en un pool hasta que decida ponerlo en ejecución.
  • En ejecución (running): En el momento en que el planificador escoge un hilo del pool para ser ejecutado, este pasa a estar en ejecución. Una vez en este estado, el hilo puede volver al estado de espera (ejecutable) si el planificador decide que su tiempo asignado de CPU ha finalizado aunque no haya completado su tarea. En este supuesto, deberá esperar a que el planificador vuelva a escogerlo para devolverlo a ejecución. Otras causas por las que un hilo puede abandonar este estado son los bloqueos o esperas o por su finalización.
  • Bloqueado/esperando (bloqued/waiting): Un hilo activo puede entrar en un estado de espera finito, por ejemplo durante operaciones de entrada/salida en las que debe esperar para obtener datos de un recurso. Cuando esta espera finaliza, el hilo vuelve al estado de ejecución. Este estado también se puede producir en caso de que el hilo de ejecución deba esperar a la realización de una tarea por parte de otro hilo. En este caso volverá al estado activo cuando el otro hilo envíe le una señal al hilo en espera para que siga su ejecución.
  • Finalizado (dead): Un hilo activo entra en este estado cuando completa su tarea o finaliza por otra causa, como por ejemplo que se produzca un error o se le envíe una señal de finalización.

Problemas de concurrencia

Cuando 2 o más hilos se ejecutan al mismo tiempo y tienen que competir por los mismos recursos o colaborar para producir un resultado, es posible que se produzcan situaciones no deseadas que alteren este resultado o incluso produzcan problemas de rendimiento considerables en el sistema.
Entre los problemas más corrientes y conocidos se encuentran los siguientes:
  • Condición de carrera (Race condition): Este problema se produce cuando 2 o más hilos de ejecución modifican un recurso compartido en un orden diferente al esperado, provocando un estado erróneo de este recurso. Por ejemplo, 2 hilos de ejecución leen es valor de una variable compartida, realizan un cálculo sobre este y actualizan de nuevo la variable con el resultado. Si no se sincroniza adecuadamente el acceso a dicha variable es posible que los hilos hayan realizado los cálculos en base a un valor obsoleto porque el otro hilo lo haya actualizado antes. En este caso, la solución pasa por disponer de mecanismos para sincronizar el acceso a los recursos compartidos de manera que la lectura y posterior actualización sean atómicas y no puedan producirse de forma concurrente.
  • Bloqueo mutuo (Deadlock): Este problema se produce cuando 2 o más hilos de ejecución compiten por un recurso o se comunican entre ellos y no pueden acceder al recurso quedando indefinidamente a la espera de que sea liberado pero esto no se produce nunca. Un ejemplo clásico sería el de 2 hilos A y B que tienen asignados 2 recursos R1 y R2 respectivamente. Si A require R2 y B requiere R1 pero estos no son liberados por sus poseedores en ese momento, tanto A como B se encuentran bloqueados a la espera de poder acceder a los recursos. En este caso, la solución pasa por impedir situaciones en que un hilo de ejecución quede bloqueado esperando un recurso compartido sin liberar antes los que tiene él tiene ocupados.

Referencias

Políticas de planificación de tareas

OCP7 07 – Manejo de Cadenas

Argumentos y formatos de cadenas

El método main contiene el parámetro String[] args. Puede recibir zero o más argumentos.
La implementación de la clase es la siguiente.
public class Echo {
 public static void main(String[] args) {
  for (String s: args) {
   System.out.println(s);
  }
 }
}

La clase Echo puede recibir los siguientes parámetros por línea de consola de comandos.

java Echo Enero Febrero Marzo Abril

Pasándole como argumentos en la línea de comandos los cuatro primeros meses del año.

Cada uno de los meses separados por un salto de línea. Esto es así debido a que el carácter espacio se utiliza para separar los parámetros unos de otros mediante un salto de línea entre cada uno.

Si se quiere mostrar un frase o texto completo, por ejemplo

            java Echo «Enero, Febrero, Marzo, Abril son los cuatro primeros meses…»

se debe indicar la apertura y el cierre del texto mediante comillas dobles («»).

Debe tenerse en cuenta que los arrays SIEMPRE empiezan con el valor 0 nunca con el 1. Para recuperar los parámetros recibidos mediante el método main se utilizan los indices del vector. Así con el código args[0] se recupera el primer parámetro, con args[1] se recupera el segundo, etc.

Adicionalmente, si una aplicación necesita recibir argumentos de tipo numérico, debe convertirse el argumento de tipo String a un argumento que represente un número, como por ejemplo el «34», a su valor numérico equivalente, el 34.

Este snippet transforma un argumento de consola de comandos en un tipo entero:

int primerArg;
if (args.length > 0) {
    try {
        primerArg = Integer.parseInt(args[0]);

System.out.println("Se ha convertido la cadena "+args[0]+" en el número siguiente -> "+primerArg);
    } catch (NumberFormatException e) {
        System.err.println("Argumento " + args[0] + " debe ser un número entero.");
        System.exit(1);
    }
}

primerArg lanza una excepción del tipo NumberFormatException si el formato de args[0] no es valido. Todas las clases de tipo Number Integer, Float, Double y demás — disponen de métodos de conversión que transforman un String representando un número en un objeto de su tipo específico.

Formatos de cadena

Los distintos argumentos de conversión que podemos utilizar para modificar el aspecto de una objeto String son los siguientes:
Fig. 1. Argumentos_conversion_Strings
Fig.1 Argumentos de conversión de String

Cómo ejemplo inicial para limitar el número de caracteres que se visualizan por pantalla es suficiente con utilizar %2.2x dónde x corresponde al argumento de conversión que se haya pasado. 

PrintWriter

PrintWriter es una nueva clase de impresión bastante similar a Printf perteneciente a la librería java.io.  Puede encontrarse su Javadoc aquí.

PrintWriter pw = new PrintWriter(System.io, true);

pw.printf("Texto escrito mediante Pw");

 

Procesamiento de Cadenas

Dentro del JDK en su versión del Java 7 existen varias clases Java que permiten manipular y tratar los elementos de tipo cadena. Son clases ya existentes en versiones anteriores del JDK.

1.-String.

Representa una cadena de caracteres inalterable. Al modificar un objeto String lo que estamos haciendo en realidad es crear otro objeto String. No es la más eficiente ni la mejor. Ideal para tratar con cadenas de texto cuyo valor sabemos que no se modificará: mensajes de alerta, texto, informativo, etc.

 

2.-StringBulider/StringBuffer.
Debe utilizarse cuándo debamos trabajar con cadenas de texto que deban modificar su contenido en tiempo de ejecución. Ambas disponen del mismo API de desarrollo.
Como norma general utilizaremos StringBuilder en lugar de StringBuffer.
Razón: Los métodos de StringBuffer son sincronizados por lo que pueden ser utilizados de forma segura en un ambiente multihilo.

Los métodos de StringBuilder no son sincronizados por lo que su uso implica un mejor rendimiento cuándo se usan localmente.

En general, la concatenación de Strings ocurre con variables locales a un método por lo que es recomendable utilizar de forma general StringBuilder en lugar de StringBuffer.

Cuan rápido es StringBuilder sobre StringBuffer?
StringBuilder puede resultar un 50% más rápido para concatenar String.
Para esta implementación.
public class StringBuilder_vs_StringBuffer {

    public static void main(String[] args) {
        StringBuffer sbuffer = new StringBuffer();
        long inicio = System.currentTimeMillis();

        for (int n = 0; n < 1000000; n++) {
            sbuffer.append("zim");
        }

        long fin = System.currentTimeMillis();
        System.out.println("Time using StringBuffer: " + (fin - inicio));

        StringBuilder sbuilder = new StringBuilder();

        inicio = System.currentTimeMillis();

        for (int i = 0; i < 1000000; i++) {
            sbuilder.append("zim");
        }

        fin = System.currentTimeMillis();
        System.out.println("Time using StringBuilder: " + (fin - inicio));


    }
}
obteniendo casi una mejora del 50% utilizando StringBuilder.
Fig. 2. Tiempos de ejecución
Fig. 2 Tiempo en concatenar Strings

Clases Auxiliares en la Manipulación de cadenas

StringTokenizer

Extrae información (en forma de tokens) de una cadena de texto cuyos caracteres están separados por un carácter o símbolo especial o separador. Recorre la cadena de texto y obtiene las cadenas obtenidas (tokens) a partir del símbolo especial o separador indicado en la llamada al método.

// Inicialización
StringTokenizer  st = new StringTokenizer("this is a test");

// Utilización
while(st.hasMoreTokens())  {
      System.out.println(st.nextToken())
}

Puede encontrarse información más detallada dentro de la API de Java 7 en la siguiente dirección.

Scanner

Extrae información de una cadena o flujo de datos como StringTokenizer. Cambia el  tipo de dato a las divisiones mientras se itera sobre ellas (StringTokenizer no puede), es decir, ante un flujo de datos podremos capturar enteros, decimales, etc en función de nuestro método de iteración utilizando Scanner.

Error de codificación Unicode en un mensaje Javascript

Descripción del Problema

Cuando se publica un mensaje que contiene un carácter en formato Unicode no muestra el texto correctamente. No se renderiza el carácter Unicode en Javascript sino que se muestra directamente la codificación

Análisia del Problema

En el fichero JSP contenedor se encuentra la siguiente llamada:

        <a  
                onclickreturn(confirm(«Mensaje de llamada previo al mu00E9todo?«));» 
                href=»<ruta_web_navegacion>»
        >

Cuando el navegador renderiza el botón y procesa la funcionalidad del atributo onclick no renderiza correctamente el texto contenido dentro de los confirm como carácter Unicode desde el Javascript, sino que lo interpreta como un string que forma parte de la cadena contenida dentro del confirm .

Solución

Hay que añadir en el JSP una sección Javascript donde se implementa la siguiente función:

        <script>
function metodo() {
return confirm(«Mensaje de llamada previo al mu00E9todo?»);
}
</script>

Esta nueva función renderiza directamente el texto contenido en el mensaje desde Javascript no desde HTML.

En el código HTML del enlace <a> hay que indicar en el atributo onclick el siguiente código para ejecutar correctamente la ventana confirm y posteriormente el envío del resultado al onclick del botón:      

       <a   
                onclickreturn(metodo());» 
                href=»<ruta_web_navegacion>»
        >

Esto permite que la codificación Unicode sea interpretada correctamente y se obtiene el carácter é lugar de la codificación ue00E9 cuando se muestra el mensaje por pantalla.

Detalle

El método confirm devuelve el resultado de la opción elegida cuando se abre la ventana mediante Javascript.

Lo que se está haciendo se redirigir este regreso (sea true o false) hacia el botón onclick de manera que se ejecute directamente o no según la selección de las opciones del confirm Javascript que se haya efectuado.