Archivo de la categoría: DEBUN_OCJP7

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 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 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 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.