Todas las entradas de: Antonio Archilla

Generar esquema XSD a través de las clases de dominio con JAXB

Una operación muy habitual cuando se trabaja con JAXB para generar XML a partir de clases de dominio mediante anotaciones de JAXB es la de generar el esquema XSD asociado a la estructura de datos de dicho XML. Este esquema puede ser utilizado posteriormente para volver a generar las clases de dominio a través de JAXB. Se trata de una operativa muy común cuando se trabaja con servicios web REST para exponer la estructura de los mensajes de respuesta desde el servidor a los clientes para que estos puedan interpretarla, por ejemplo. 

Una manera muy sencilla de generar esta definición XSD cuando ya se tienen modeladas las clases de dominio a partir de las que se creará el XML mediante JAXB es utilizando el método generateSchema del contexto JAXB al que se han especificado las clases de dominio que forman el mensaje XML.

Seguir leyendo Generar esquema XSD a través de las clases de dominio con JAXB

Creación de un Fat Jar con Apache Maven

Hace un tiempo publiqué un post en este mismo blog en el que se explicaba cómo se construir un Fat Jar con Apache Ant para empaquetar toda una aplicación, dependencias incluidas, dentro de un mismo fichero jar. El procedimiento para ello se basa en extraer los ficheros .class compilados que se encuentran dentro de los jars de las dependencias incluirlo dentro del jar principal de la aplicación. En caso de utilizar Maven cómo herramienta de construcción en lugar de Ant, esta acción se puede realizar utilizando el plugin Shade. Para ello será necesario incluir su definición dentro del fichero pom.xml del proyecto y asociar la ejecución de su único goal "shade" a la ejecución de la fase de empaquetado "package":

Seguir leyendo Creación de un Fat Jar con Apache Maven

Creación de un Fat Jar con Apache ANT

Normalmente para construir el distribuible de una aplicación Java se empaqueta el código principal de esta dentro de un fichero jar ejecutable, esto es, que contiene dentro de su fichero de definición MANIFEST.MF una entrada que especifica qué clase contiene el metodo main() y junto a este se crea una carpeta que contenga todas las dependéncias que este (habitualmente se llama a esta carpeta lib).

Seguir leyendo Creación de un Fat Jar con Apache ANT

Obtener la conexión a base de datos a través de Hibernate 4

Si estáis trabajando en una aplicación en la que la persistencia se hace a través de Hibernate 4, os habréis dado cuenta que ha desaparecido la posibilidad de obtener el objecto Connection a través de la Session de Hibernate. Esto sucede porque en el paso de Hibernate 3 a Hibernate 4 ha desaparecido el método connection() a través del que se podía obtener.

Seguir leyendo Obtener la conexión a base de datos a través de Hibernate 4

OCP7 06 – Genéricos y colecciones – Introducción

Tipos de Genéricos

Los objetos genéricos se describen de forma abstracta mediante la siguiente notación:
Objetos Genéricos <T>
Por convención en Java dentro del operador diamante se ha establecido la siguiente convención:
  • T – Tipo
  • E – Elemento
  • K – Key
  • V – Valor
  • S, U – Se emplea si hay 2, 3 o más tipos definidos.
El Operador Diamante es <..> y permite evitar el uso del tipo genérico T en la construcción de un objeto dado que a partir de su declaración se infiere el tipo T asociado. Además, el operador simplifica y mejora la lectura del código fuente.

Tipos de Colecciones

Una colección es un objeto único que maneja un grupo de objetos.  A esta agrupación de objetos también les llamamos elementos, tienen operaciones de inserción, borrado y consulta.
El framework de colecciones en Java es un arquitectura unificada que representa y maneja las colecciones independientemente de los detalles de implementación. Implementan pilas, colas, etc… y sus clases se almacenan en java.util.
Fig. 1. Detalle interface Collection
Fig. 1 Detalle Interfície Colecciones.
List
Una lista es una interfaz que define el comportamiento de una lista genérica. Colección ordenada en que cada elemento ocupa una posición identificada por un índice. Las listas crecen de forma dinámica. Se pueden añadir, eliminar, sobrescribir elementos existentes, y se permiten elementos duplicados.
List<T> lista = new ArrayList<>(3)

Un ArrayList es la implementación más conocida de una Collection, aunque también existen LinkedList y otras implementacions no detalladas en este post. En este enlace se puede profundizar en su conocimiento de forma más exahustiva.

Además de List existe otra interfície que deriva de Collection llamada Set cuyas implementaciones más conocidas son Hashset y TreeSet (implementación ésta de la interfaz SortedSet).

Existe una tercera interfaz que también deriva de Collection llamada Queue. En este enlace puede encontrarse información más detallada.

Para un mayor detalle y nivel de profundidad respecto a la clase Collections de Oracle, puede consultarse su documentación oficial online en este enlace para el JDK 7.

 

Autoboxing & Unboxing

Los tipos primitivos (int , float, double, etc…..) usados en Java no forman parte de su jerarquía de clases por cuestiones de eficiencia.
Java permite un mecanismo de envoltura llamado Wrapper para poder encapsular un tipo primitivo en un objeto. Una vez encapsulado dicho tipo primitivo, su valor puede ser recuperado mediante los métodos asociados a la clase de envoltura.
Nomenclatura de los procesos:
  • AutoBoxing: Encapsular el valor de un tipo primitivo en un objeto Wrapper.
  • Unboxing: Extraer el valor de un tipo primitivo de un objeto Wrapper.
Su utilización simplifica la sintaxis y produce código más limpio y legible para los programadores.
import java.util.ArrayList;
import java.util.List;

public class UnboxingAndAutoboxing {
    public static void main(String[] args) {
        //  Autoboxing
        int inNumber=50; 
        Integer a2 = new Integer(a);  //Boxing 
        Integer a3 = 5;               //Boxing 
        System.out.println(a2+" "+a3);
        // Unboxing
        Integer i = new Integer(-8);
        // 1. Unboxing through method invocation
        int absVal = absoluteValue(i);
        System.out.println("absolute value of " + i + " = " + absVal);
        List<Double> ld = new ArrayList<>();
        ld.add(3.1416);    // Π is autoboxed through method invocation.

        // 2. Unboxing through assignment
        double pi = ld.get(0);
        System.out.println("pi = " + pi);
    }

    public static int absoluteValue(int i) {
        return (i < 0) ? -i : i;
    }
}

Fig. 2 Ejemplo de Autoboxing & Unboxing

OCP7 09 – Principios básicos de E/S

Streams

El término define una corriente de datos de una fuente a un destino.

Todos los datos fluyen a través de un ordenador desde una entrada (fuente) hacia una salida (destino).

Los fuentes y destinos de datos son nodos de los flujos en la comunicación del ordenador. Todos los flujos presentan el mismo modelo a todos los programas Java que los utilizan:

flujo de entrada: para leer secuencialmente datos desde una fuente (un archivo, un teclado por ejemplo). Llamado también como input stream.
flujo de salida: para escribir secuencialmente datos a un destino (una pantalla, archivo, etc). Llamado también como outputstream.
Estos nodos pueden ser representados por una fuente de datos, un programa, un flujo, etc..

 

Flujos de Datos (Bytes y carácteres)

 

La tecnología Java admite dos tipos de datos en los flujos: bytes y carácteres.
Fig. 1 Tipos de Datos admitidos en Java

En el lenguaje Java los flujos de datos se detallan mediante clases que forman jerarquías según sea el tipo de dato char Unicode de 16 bits o byte de 8 bits.

A su vez, las clases se agrupan en jerarquías según sea su función de lectura (Read) o de escritura (Write).

La mayoría de las clases que se utilizan con Streams se encuentran ubicadas en el paquete java.io.  En la cabecera del código fuente debe escribirse el importe del paquete import java.io.*;

  • Métodos básicos de lectura de Streams
    • Clase InputStream (Bytes)
      • int read()
      • int read(byte[] buffer)
      • int read(byte[] buffer, int offset, int length)
    • Clase Reader (Caracteres)
      • int read()
      • int read(char[] buffer)
      • int read(char[] buffer, int offset, int length)
  • Métodos básicos de escritura de Streams 
    • Clase OutputStream (Bytes)
      • void write(int c)
      • void write(byte[] buffer)
      • void write(byte[] buffer, int offset, int length)
    • Clase Writer (Caracteres)
      • void write(int c)
      • void write(char[] buffer)
      • void write(char[] buffer, int offset, int length)
      • void write(String string)
      • void write(String string, int offset, int length)

Lectura/escritura en ficheros

Los tipos fundamentales de nodos o elementos a los que puede entrar y salir un flujo de datos que se pueden encontrar en el JDK 1.7 de Java son los siguientes:

Fig. 2 Tipos fundamentales de elementos en un flujo de datos

 

Todos los flujos deben cerrarse una vez haya finalizado su uso, forzando un close dentro de la cláusula finally.

Flujos en Memoria Intermedia

Para la lectura de archivos cortos de texto es mejor utilizar FileInputStream en conjunción con FileReader. A continuación se añaden algunos ejemplos con código fuente para la memoria intermedia.

1.-Ejemplo TestBufferedStreams

package bufferedstreams;

import java.io.*;

public class TestBufferedStreams {

    public static void main(String[] args) {
        try (
           BufferedReader bufInput = 
       new BufferedReader(new FileReader("src\bufferedstreams\file1.txt"));
           BufferedWriter bufOutput = 
       new BufferedWriter(new FileWriter("src\bufferedstreams\file2.txt"))
        ){
            String line = bufInput.readLine();
            while (line != null) {
               
                bufOutput.write(line, 0, line.length());
                bufOutput.newLine();               
                line = bufInput.readLine();                
            }
        } catch (IOException e) {
            System.out.println("Exception: " + e);
        }
    }
}

2.-Ejemplo TestCharactersStreams

package bufferedstreams;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class TestCharactersStreams {

    public static void main(String[] args) {
        try (FileReader input = new FileReader("src\bufferedstreams\file1.txt");
             FileWriter output = new FileWriter("src\bufferedstreams\file2.txt")) {

            int charsRead;
            while ((charsRead = input.read()) != -1) {
                output.write(charsRead);
            }
        } catch (IOException e) {
            System.out.println("IOException: " + e);
        }
    }
}

 

Ejemplo de entrada y salida estándar

Existen en Java 7 tres flujos estándar principales:
  • System.in. Campo estático de entrada de tipo InputStream lo que permite leer desde la entrada estándar.
  • System.out. Campo estático de salida de tipo PrintStream lo que permite escribir en la salida estándar.
  • System.err. Campo estático de salida de tipo PrintStream lo que permite escribir en el error estándar.
A continuación se indican los métodos principales print y println de la clase PrintStream
  • Métodos print con parámetros distintos
    • void print(boolean b)
    • void print(char c)
    • void print(char[] s)
    • void print(double d)
    • void print(float f)
    • void print(int i)
    • void print(long l)
    • void print(Object obj)
    • void print(String s)
  • Métodos print con parámetros distintos
    • void println()
    • void println(boolean x)
    • void println(char x)
    • void println(char[] x)
    • void println(double x)
    • void println(float x)
    • void println(int x)
    • void println(long x)
    • void println(Object x)
    • void println(String x)
Ambos métodos son métodos sobrecargados de la clase PrintStream. A continuación se añade un ejemplo con código fuente para la entrada y salida estándar.

1.-Ejemplo KeyboardInput
import java.io.*;

public class KeyboardInput {

    public static void main(String[] args) {

        try (BufferedReader in = new BufferedReader(new InputStreamReader(System.in))) {
            String s = "";
            while (s != null) {
                System.out.print("Type xyz to exit: ");
                s = in.readLine().trim();                
                System.out.println("Read: " + s);
                System.out.println("");

                if (s.equals("xyz")) {
                    System.exit(0);
                }
            }
        } catch (IOException e) {
            System.out.println("Exception: " + e);
        }
    }
}

 

Persistencia

La persistencia consiste en el proceso de serialización (secuencia de bytes) y la deserialización (reconstrucción del objeto obteniendo una copia a partir de los bytes) de un objeto en Java.
Un objeto tiene capacidad de persistencia cuándo puede almacenarse en disco o mediante cualquier otro dispositivo de almacenamiento o enviado a otra máquina y mantener su estado actual correctamente.
Dentro de una aplicación Java, cualquier clase que quiera ser serializada debe implementar la interfaz java.io.Serializable, marcador utilizado para indicar que la clase puede ser serializada.
Puede producirse la excepción NotSerializableException cuándo un objeto no se puede serializar.
Los campos marcados con los modificadores static o transient no pueden ser serializados por lo que al deserializar un objeto dichos campos apuntaran a un valor nulo o cero al finalizar la reconstrucción del objeto. A continuación se añade un ejemplo con código fuente para obtener la persistencia de los datos de un estudiante. Se incluyen la definición del objeto Student, y las clases para la persistencia junto a la clase de ejecución.

1.- Ejemplo DeserializeMyClass

package persistence;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class DeserializeMyClass {

    public static void main(String[] args) {
        
        MyClass myclass = null;
        
        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("file1.ser"))) {
            myclass = (MyClass) in.readObject();
            
        } catch (ClassNotFoundException | IOException e) {
            System.out.println("Exception deserializing file1.ser: " + e);
        }
        System.out.println("a = " + myclass.a);
        System.out.println("b = " + myclass.b);
        System.out.println("cad1 = " + myclass.getCad1());
        System.out.println("cad2 = " + myclass.getCad2());
    }
}

2.- Ejemplo MyClass

package persistence;

import java.io.Serializable;

public class MyClass implements Serializable {

    public int a = 0;
    private String cad1 = "";
    static int b = 0;    
    private transient String cad2 = "";
    Student student = new Student();

    public String getCad1() {
        return cad1;
    }

    public void setCad1(String cad1) {
        this.cad1 = cad1;
    }

    public String getCad2() {
        return cad2;
    }

    public void setCad2(String cad2) {
        this.cad2 = cad2;
    }
}

3.- Ejemplo SerializeMyClass

package persistence;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class SerializeMyClass {

    public static void main(String[] args) {
        
        MyClass myclass = new MyClass();
        myclass.a = 100;
        myclass.b = 200;
        myclass.setCad1("Hello World");
        myclass.setCad2("Hello student");

        try (ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("file1.ser"))) {
            o.writeObject(myclass);
            
        } catch (IOException e) {
            System.out.println("Exception serializing file1.ser: " + e);
        }
    }
}

4.- Ejemplo Student

package persistence;

public class Student {

    String name = "Darío";
    int age = 3;
}

 

Recordatorio 

Las clases BufferedReader y BufferedWriter aumentan la eficacia de las operaciones de entrada y salida. Estas clases permiten gestionar el búfer y escribir o leer línea por línea. A continuación se añade un ejemplo sencillo utilizando un BufferedReader para leer la cadena «xyz» y finalizar la ejecución.1.-Ejemplo utilizando BufferedReader

try (BufferedReader in = new BufferedReader(
  new InputStreamReader(system.in)))) {
   String s = "";
   System.out.print("Type xyz to exit");
   s = in.readline().trim();
   System.out.print("Read "+s);

   // ...
}

 

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.