El Blog de Ana Buigues » Java http://anabuigues.com Wed, 07 Dec 2011 16:07:47 +0000 es-ES hourly 1 http://wordpress.org/?v=3.4 Cómo configurar Hibernate sin ningún xml http://anabuigues.com/2011/02/07/como-configurar-hibernate-sin-ningun-xml/ http://anabuigues.com/2011/02/07/como-configurar-hibernate-sin-ningun-xml/#comments Mon, 07 Feb 2011 17:56:12 +0000 Ana Buigues http://anabuigues.com/?p=3359 Realizar la configuración de Hibernate con ficheros xml puede llegar a ser bastante engorroso, especialmente en aplicaciones muy grandes. Afortunadamente podemos inicializar la unidad de persistencia de forma programática y realizar los mapeos de las entidades mediante anotaciones, de esta forma conseguimos no tener que utilizar ningún fichero xml para la configuración.

A continuación un ejemplo de cómo hacerlo.

Creación de la unidad de persistencia:

public class PersistenceUnitStarter {
	//Añadimos las propiedades que queramos a la unidad de persistencia
	private static Properties getProperties() {
		Properties properties = new Properties();
		properties.setProperty(Environment.DRIVER, "org.h2.Driver");
		properties.setProperty(Environment.URL,
				"jdbc:h2:db;LOG=0;CACHE_SIZE=65536;LOCK_MODE=0;UNDO_LOG=0");
		properties.setProperty(Environment.USER, "");
		properties.setProperty(Environment.PASS, "");
		properties.put(Environment.SHOW_SQL, "true");
		properties.put(Environment.FORMAT_SQL, "true");
		properties.put(Environment.FLUSH_BEFORE_COMPLETION, "false");
		properties.put(Environment.TRANSACTION_STRATEGY,
				PersistenceUnitTransactionType.RESOURCE_LOCAL);
		properties.put(Environment.HBM2DDL_AUTO, "create");
		return properties;
	}
        //Le pasamos una lista con las clases que queremos añadir a la entidad de persistencia
        //Las clases tienen que estar definidas con anotaciones
	public static EntityManagerFactory createPersistenceUnit(List<Class> entities) {
		Ejb3Configuration conf = new Ejb3Configuration();
		conf.addProperties(getProperties());
		//añadimos los entities declarados con anotaciones
		for (Class clazz : entities) {
			conf.addAnnotatedClass(clazz);
		}
		return conf.buildEntityManagerFactory();
	}
}

Creamos las entidades mediante anotaciones

@Entity
public class Persona {
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private int id;
	@Column(nullable = false)
	private String nombre;
	@Column(nullable = false)
	private String apellido;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getNombre() {
		return nombre;
	}
	public void setNombre(String nombre) {
		this.nombre = nombre;
	}
	public String getApellido() {
		return apellido;
	}
	public void setApellido(String apellido) {
		this.apellido = apellido;
	}
}
]]>
http://anabuigues.com/2011/02/07/como-configurar-hibernate-sin-ningun-xml/feed/ 3
Mi primera Code Kata http://anabuigues.com/2011/02/01/mi-primera-code-kata/ http://anabuigues.com/2011/02/01/mi-primera-code-kata/#comments Tue, 01 Feb 2011 17:40:00 +0000 Ana Buigues http://anabuigues.com/?p=3344 Ayer hice junto a Héctor Rodes mi primera Code Kata, gracias a la iniciativa de 12meses12katas, donde cada mes se propone una nueva kata. Abierta a todo el mundo y a todos los lenguajes de programación, excelente para poder practicar y aprender.

Y…¿qué es una Code Kata? pues una Code Kata hace referencia a un ejercicio de programación en el cual se resuelve un problema más o menos complejo donde el objetivo es mejorar las cualidades de un programador mediante la práctica y resolución repetitiva de problemas.

Lo que obtenemos de todo esto es obligarnos a encontrar una solución a un problema y además nos permite contrastar nuestra solución con la de otras personas, de esta forma podemos descubrir nuevas formas de resolver el mismo problema, y si la programas en parejas es todavía más divertido.

Para Enero el ejercicio de la Code Kata era el String Calculator, la idea es hacer un programa que sume  los números de una cadena separados por diferentes delimitadores, por ejemplo: “8*5%3″ . Si queréis ver el código de la gente que ha realizado la Code Kata podéis visitar el github del proyecto. El nuestro está en el directorio de animalaes.

Nuestra solución

La hemos realizado en Java, con la ayuda de la clase Scanner de Java para parsear la cadena mediante expresiones regulares. Se admiten sugerencias sobre mejoras o lo que sea.

package com.anabuigues;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.regex.Pattern;
/**
 * Implementación sencilla de la calculadora para la kata de enero.<br />
 * <br />
 * La implementación está realizada usando java 6 estándar apoyándose en la
 * clase Scanner que permite trocear al vuelo un imput a partir de un o n
 * delimitadores. Para ello extrae los delimitadores y construye una expresión
 * reguar válida para el scanner al que se le solicitan los diferentes enteros
 * para ir sumándolos.
 *
 *
 * @author A. Buigues (@animalaes), H. Rodes (@hector_rodes)
 *
 */
public class StringCalculator {
	public int add(String numbers) throws Exception {
		int returnValue;
		if (numbers == null || numbers.isEmpty()) {
			returnValue = 0;
		} else {
			ByteArrayInputStream bais = new ByteArrayInputStream(
					numbers.getBytes());
			Scanner sc = new Scanner(bais);
			String delimiters = null;
			// Delimiters
			if (numbers.startsWith("//")) {
				sc.useDelimiter("//|\n");
				if (sc.hasNext()) {
					delimiters = sc.next();
				}
			}
			delimiters = getScannerFormattedDelimiters(delimiters);
			// Process number lines with numbers to be added
			sc.useDelimiter(delimiters);
			int sum = 0;
			int currentNumber;
			List<Integer> negativeValues = new ArrayList<Integer>();
			while (sc.hasNext()) {
				currentNumber = sc.nextInt();
				if (currentNumber < 0) {
					negativeValues.add(currentNumber);
				} else if (currentNumber <= 1000) {
					sum += currentNumber;
				}
			}
			checkNegativeValues(negativeValues);
			returnValue = sum;
		}
		return returnValue;
	}
	/**
	 * Método auxiliar que comprueba si hay números negativos y forma el mensaje
	 *
	 * @param negativeValues
	 *            Lista de números negativos
	 * @throws Exception
	 *             Si la lista de números negativos contiene algún número
	 */
	private void checkNegativeValues(List<Integer> negativeValues)
			throws Exception {
		if (negativeValues != null && negativeValues.size() > 0) {
			StringBuilder sb = new StringBuilder("negatives not allowed");
			for (int negative : negativeValues) {
				sb.append(" ").append(negative);
			}
			throw new Exception(sb.toString());
		}
	}
	/**
	 * Genera la cadena de delimitadores necesaria para ser usada en el scanner
	 * y así poder procesar los números.<br />
	 * Siempre introduce el \n como un delimitador válido.<br />
	 * En caso de que los delimiters sean vacÌos o nulos usa la , y el \n como
	 * delimitadores por defecto
	 *
	 * @param delimiters
	 *            Los delimitadores introducidos
	 * @return Cadena con los delimitadores en el formato que espera el Scanner
	 */
	private String getScannerFormattedDelimiters(String delimiters) {
		String delimitersExpression;
		if (delimiters == null || delimiters.isEmpty()) {
			delimitersExpression = ",|\n";
		} else {
			String currentDelimiter;
			StringBuilder sb = new StringBuilder();
			Scanner scDelimiters = new Scanner(delimiters).useDelimiter(Pattern
					.quote("]") + "|" + Pattern.quote("["));
			while (scDelimiters.hasNext()) {
				currentDelimiter = scDelimiters.next();
				if (!currentDelimiter.isEmpty()) {
					sb.append(Pattern.quote(currentDelimiter)).append("|");
				}
			}
			sb.append("\n");
			delimitersExpression = sb.toString();
		}
		return delimitersExpression;
	}
}

Y su test

package com.anabuigues;
import junit.framework.Assert;
import org.junit.Test;
/**
 *
 * @author A. Buigues (@animalaes), H. Rodes (@hector_rodes)
 *
 */
public class CalculatorTest {
	private StringCalculator calculator = new StringCalculator();
	@Test
	public void basicCalculator() throws Exception {
		Assert.assertEquals(0, calculator.add(""));
		Assert.assertEquals(1, calculator.add("1"));
		Assert.assertEquals(10, calculator.add("7,3"));
		Assert.assertEquals(10, calculator.add("7\n2,1"));
		Assert.assertEquals(27, calculator.add("5\n5\n8,2,4\n2,1"));
	}
	@Test
	public void simpleDelimiterCalculator() throws Exception {
		Assert.assertEquals(16, calculator.add("//[*]\n5*5*6"));
		Assert.assertEquals(20, calculator.add("//[*]\n5*5*6\n4"));
		Assert.assertEquals(20, calculator.add("//[*][;][,]\n5,5;6\n4"));
		Assert.assertEquals(20,
				calculator.add("//[*][;][pollofrito]\n5pollofrito5;6\n4"));
	}
	@Test
	public void bigNumbersCalculator() throws Exception {
		Assert.assertEquals(1005, calculator.add("//[*]\n5*1000*1001"));
	}
	@Test
	public void negativeNumbersCalculator() throws Exception {
		try {
			Assert.assertEquals(1005, calculator.add("//[*]\n5*-23*45*-34"));
		} catch (Exception e) {
			Assert.assertTrue("-23 must be in error message ", e.getMessage()
					.indexOf("-23") != -1);
			Assert.assertTrue("-34 must be in error message ", e.getMessage()
					.indexOf("-34") != -1);
		}
	}
	@Test
	public void notDefinedDelimiterCalculator() throws Exception {
		try {
			Assert.assertEquals(8, calculator.add("//[*]\n5;3"));
			Assert.fail("Delimiter ; is not allowed but it has been used");
		} catch (Exception e) {
		}
	}
}

Ahora toca ponerse con la de febrero!!

]]> http://anabuigues.com/2011/02/01/mi-primera-code-kata/feed/ 4 Comparator vs Comparable en Java http://anabuigues.com/2010/09/17/comparator-vs-comparable-en-java/ http://anabuigues.com/2010/09/17/comparator-vs-comparable-en-java/#comments Fri, 17 Sep 2010 16:17:12 +0000 Ana Buigues http://anabuigues.com/?p=1145 Diferencia entre Comparable y Comparator

La diferencia principal entre Comparable y Comparator es que Comparable es utilizado para implementar el orden natural, sin necesidad de especificar un Comparator, por ejemplo, los String son comparados alfabéticamente. Usamos Comparator cuando queramos crear otro tipo de orden que no sea el natural.

El contrato que implica implementar Comparable requiere de un parámetro extra, el objeto con el que comparar compareTo(obj1) de forma que la comparación se realizará del objeto en si (this) con el objeto pasado como parámetro. Comparator, sin embargo obliga a implementar el método compare (obj1, obj2) de forma que los dos objetos a comparar son pasados como parámetros y el objeto que implementa el método compare simplemente sirve de apoyo a la comparación.

Sobreescritura de los métodos compareTo y compare

La forma de sobreescribir estos métodos es muy similar al equals. Los dos métodos cumplen las mismas restricciones que podemos objtener del API de Java:

  • Devolvemos un número negativo si el objeto es menor, devolvemos un cero si es igual y un número positivo si el objeto es mayor.
  • Comparamos el objeto con otro objeto del mismo tipo. Si no son del mismo tipo, lanzamos un ClassCastException.

La notación sgn(expresion) es la función matemática signum, la cual devuelve -1,0, o 1  según sea negativo, zero o positivo

  • Debemos asegurar que: sgn(compare(x,y)) == -sgn(compare(x,y)), para todo x, y. Esto implica que compare(x,y)) lanzará una excepción sii compare(x,y) también la lanza.
  • Debemos asegurar que la relación es transitiva: compare(x,y) > 0 && compare(y,z) > 0 implica que compare(x,z) > 0
  • Debemos asegurar que: compare(x,y) == 0 implica que sgn(compare(x,z)) == sgn(compare(y,z)), para todo z.
  • Se recomienda, aunque no es estrictamente neceario, nos puede evitar poblemas al usar colecciones que implementen esta interfaz. (compare(x,y) == 0) == (x.equals(y))
  • El orden natural para una clase “c”  tiene que ser consistente con el equals si y solo sí: compare(e1,e2) == 0 tiene que ser el mismo resultado que para e1.equals(e2) para cada e1 y e2 de la clase “c”.

Uso de Comparable y Comparator

Podemos usarlos en listas y arrays mediante los métodos Collections.sort y Arrays.sort.También como claves en un mapa ordenado TreeMap o como elementos en un set ordenado TreeSet.

En el caso de Comparable los objetos tienen que implementar esta interfaz para que los ordene automáticamente. Para el caso del Comparator debemos especificar el Comparator.

Ejemplo Comparable

//Ejemplo Comparable
public class Person implements Comparable<Person> {
 private String firstName;
 private String lastName;
 public Person(String firstName, String lastName) {
  if (firstName == null || lastName == null)
   throw new NullPointerException();
  this.firstName = firstName;
  this.lastName = lastName;
 }
 @Override
 public String toString() {
  return String.format("%s, %s", firstName, lastName);
 }
 @Override
 public int compareTo(Person p) {
  int lastCmp = firstName.compareTo(p.firstName);
  return (lastCmp != 0 ? lastCmp : lastName.compareTo(p.lastName));
 }
 public static void main(String[] args) {
  Set<Person> p = new TreeSet<Person>();
  p.add(new Person("Ana", "Rodes"));
  p.add(new Person("Hector", "Lopez"));
  p.add(new Person("Ana", "Buigues"));
  p.add(new Person("Carlitos", "Perez"));
  for (Person person : p)
   System.out.println(person);
 }
}

La salida será:
Ana, Buigues
Ana, Rodes
Carlitos, Perez
Hector, Lopez

Ejemplos Comparator

//Ejemplo Comparator
public class LengthComparator implements Comparator<Person> {
 @Override
 public int compare(Person p1, Person p2) {
  if (p1.toString().length() < p2.toString().length())
   return 1;
  else if (p1.toString().length() > p2.toString().length())
   return -1;
  else
   return 0;
 }
 public static void main(String[] args) {
  List<Person> p = new ArrayList<Person>();
  p.add(new Person("Ana", "Rodes"));
  p.add(new Person("Hector", "Lopez"));
  p.add(new Person("Ana", "Buigues"));
  p.add(new Person("Carlitos", "Perez"));
  Collections.sort(p, new LengthComparator());
  for (Person person : p)
   System.out.println(person);
 }
}

La salida será:
Ana, Rodes
Ana, Buigues
Hector, Lopez
Carlitos, Perez

También lo podemos utilizar de forma anónima

Set<Person> persons = new TreeSet<Person>(new Comparator<Person>() {
  @Override
 public int compare(Person p1, Person p2) {
  if (p1.toString().length() < p2.toString().length())
   return 1;
  else if (p1.toString().length() > p2.toString().length())
   return -1;
  else
   return 0;
 }
});
]]>
http://anabuigues.com/2010/09/17/comparator-vs-comparable-en-java/feed/ 23
Cómo sobreescribir los métodos equals y hashCode de Java http://anabuigues.com/2010/07/06/como-sobreescribir-los-metodos-equals-y-hashcode-de-java/ http://anabuigues.com/2010/07/06/como-sobreescribir-los-metodos-equals-y-hashcode-de-java/#comments Tue, 06 Jul 2010 20:18:23 +0000 Ana Buigues http://anabuigues.com/?p=1051 En la clase java.lang.Object (y por lo tanto, por herencia, en todas las demás clases) tenemos métodos que a veces olvidamos y que son importantes:

  • public boolean equals(Object o)
  • public int hashCode()

Estos métodos son especialmente importantes si vamos a guardar nuestros objetos en cualquier tipo de colección: listas, mapas… y más aun si los objetos que vamos a guardar en la colección son serializables.

Estos métodos tienen formas explicitas de cómo hay que implementarlos. Sobreescribir estos métodos puede parecer simple, pero en realidad hay muchas formas de hacerlo incorrectamente lo que nos puede llevar a muchos crebraderos de cabeza. Lo vemos a continuación.

Sobreescribir el método equals

Cuando sobreescribimos el método equals tenemos que tener en cuenta lo que se especifica en el API de Java para Object sobre este método. Debe cumplir las siguientes propiedades:

  • Reflexiva: para cualquier referencia no nula para un valor x, x.equals(x) debe devolver true.
  • Simétrica: para cualquier referencia no nula para valores x e y, x.equals(y) debe devolver true sii y.equals(x) devuelve true.
  • Transitiva: para cualquier referencia no nula para valores x, y,z, si x.equals(y) devuelve true y y.equals(z) devuelve true, entonces x.equals(z) debe devolver true.
  • Consistente: para cualquier referencia no nula para valores x e y, múltiples llamadas al método x.equals(y) deben consistentemente devolver siempre true o consistentemente devolver false. Siempre y cuando no se modifique la información usada en las comparaciones.
  • No nulo: para cualquier referencia no nula para un valor x, x.equals(null) debe devolver false.

Ahora que sabemos lo que tiene que cumplir, vamos a ver que pasos podemos seguir para su implementación:

  • Usamos el operador == para comprobar si el argumento es una referencia al mismo objeto.
  • Usamos el operador instanceof para comprobar si el argumento es un objeto de nuestra clase.
  • Hacemos un cast del argumento al nuestro objeto. Ya sabemos que es una instancia de nuestro objeto, por el paso anterior.
  • Para cada campo significativo de nuestro objeto, comprobamos que se corresponda con el que se pasa como argumento. Primero comprobamos los tipos primitivos y luego los más complejos. Para los tipos Float y Double, usamos los métodos Float.Compare y Double.Compare. Para el tipo String, usamos el equals del string. Para comparar arrays usamos Arrays.equals. Tenemos que tener en cuenta que los campos pueden contener referencias a null. No debemos incluir campos que tengan que ver con el estado de un objeto, como por ejemplo tipos Lock. Tampoco tenemos que incluir campos que sean cálculos de otros campos, es redundante.
  • Cuando terminemos, tenemos que preguntarnos: ¿es reflexivo?, ¿es simétrico?, ¿es transitivo?, ¿es consistente? y ¿no nullo?
//Ejemplo de cómo sobreescribir el método equals()
public class Casa {
 private int num;
 private String direccion;
 private double precio;
 private List<Propietario> prop;
 public Casa(int num, String direccion, double precio,
            List<Propietario> prop) {
  this.num = num;
  this.direccion = direccion;
  this.precio = precio;
  this.prop = prop;
 }
 @Override
 public boolean equals(Object o) {
  if (o == null)
   return false;
  if (o == this)
   return true;
  if (!(o instanceof Casa))
   return false;
  Casa c = (Casa) o;
  if (num != c.num)
   return false;
  if (direccion == null || !direccion.equals(c.direccion))
   return false;
  if (Double.compare(precio, c.precio) != 0)
   return false;
  if (prop != c.prop && (prop == null || !prop.equals(c.prop)))
   return false;
 return true;
 }
}
public class Propietario {
 private String nombre;
 private String apellidos;
 private int num;
 public Propietario(String nombre, String apellidos, int num) {
  this.nombre = nombre;
  this.apellidos = apellidos;
  this.num = num;
 }
 @Override
 public boolean equals(Object o) {
  if (o == null)
   return false;
  if (o == this)
   return true;
  if (!(o instanceof Propietario))
   return false;
  Propietario p = (Propietario) o;
  if ((nombre == null) ? (p.nombre != null) : !nombre.equals(p.nombre))
    return false;
  if ((apellidos == null) ? (p.apellidos != null) :
     !apellidos.equals(p.apellidos))
    return false;
  if (num != p.num)
    return false;
  return true;
 }
}

Sobreescribir el método hashCode

Siempre que sobreescribamos el método equals, también tenemos que sobreescribir también el método hashCode. En el API de java para Object del método hashCode se especifica lo siguiente:

  • Cuando este método es invocado sobre el mismo objeto una o más veces durante una ejecución en una aplicación, el hashCode debe de ser consistente devolviendo siempre el mismo valor, siempre que no se modifique el objeto. Este valor no tiene que ser consistente entre ejecuciones distintas de la aplicación.
  • Si dos objetos son iguales segun el método equals, entonces el hashCode de los dos objetos tiene que ser el mismo.
  • Si dos objetos no son iguales, el hashCode no tiene que ser necesariamente distinto, pero es recomendable que lo sea.

Unos pasos para implementar un buen método hashCode son:

  • Declaramos una variable entera y le asignamos un número, por ejemplo result=17.
  • Para cada campo significativo de nuestro objeto, f:
    • Calculamos en int c el valor de:
      • Tipo boolean: hacemos (f?1 : 0)
      • Tipos byte, char, short, o int: hacemos (int) f
      • Tipo Long: hacemos (int)(f^(f>>>32))
      • Tipo Float: hacemos Float.doubleToIntBits(f)
      • Tipo Double: hacemos Double.doubleToLongBits(f) (int)(f^(f>>>32))
      • Si es una referencia a un objeto, llamamos al hashCode del objeto. Si la referencia es nula, devolvemos un 0.
      • Si es un Array, utilizamos el método Arrays.hashCode.
    • Acumulamos el valor de c en result : result = 31 * result + c
  • Devolvemos el valor de result

Podemos excluir los campos que no comprobemos en el método equals, pero no es recomendable.

//Ejemplo de cómo sobreescribir el método hashCode
@Override
public int hashCode() {
  int result = 17;
  result = 31 * result + num;
  result = 31 * result + (direccion != null ?
           direccion.hashCode() : 0);
  result = 31 * result + (int) (Double.doubleToLongBits(precio)
           ^((Double.doubleToLongBits(precio) >>> 32));
  result = 31 * result + (propietarios != null) ?
           propietarios.hashCode() : 0);
 return result;
 }

Fuente: Libro Effective Java de Joshua Bloch

]]>
http://anabuigues.com/2010/07/06/como-sobreescribir-los-metodos-equals-y-hashcode-de-java/feed/ 14
Definir una Singleton en Java mediante un enum http://anabuigues.com/2010/02/15/definir-una-singleton-en-java-mediante-un-enum/ http://anabuigues.com/2010/02/15/definir-una-singleton-en-java-mediante-un-enum/#comments Mon, 15 Feb 2010 19:10:47 +0000 Ana Buigues http://anabuigues.com/?p=348 El otro día leyendo el libro de Effective Java de Joshua Bloch, me encontré con una nueva forma de declarar el patrón de diseño Singleton.

Una singleton es simplemente una clase que es instanciada exactamente una vez. Antes de Java 1.5 había dos formas de implementar una singleton, ambas se basan en un contructor privado y en proporcionar un miembro público y estático que da acceso a una sola instancia. A partir de Java 1.5 aparece una tercera opción para implementar una singleton, simplemente mediante un tipo enumerado con un único elemento:

public enum Singleton {
    INSTANCE;
    public String nombre(){
           return this.getClass().getName();
    }
}

Este enfoque es funcionamente equivalente a los dos anteriores, excepto porque es más conciso y ofrece una rigurosa garantía contra múltiples instanciaciones, ya que con los métodos anteriores podíamos invocar al constructor privado mediante reflexión y obtener más de una instancia. En el siguiente ejemplo, creamos por reflexión dos enums:

public static void main(String[] args) {
  Constructor<Singleton> ctor = Singleton.class.getDeclaredConstructor(String.class, int.class);
  Method acqMethod = ctor.getClass().getDeclaredMethod("acquireConstructorAccessor");
  acqMethod.setAccessible(true);
  acqMethod.invoke(ctor);
  Field accessorField = ctor.getClass().getDeclaredField("constructorAccessor");
  accessorField.setAccessible(true);
  Object accessor = accessorField.get(ctor);
  Method newInstanceMethod = accessor.getClass().getMethod("newInstance", Object[].class);
  newInstanceMethod.setAccessible(true);
  Singleton singleton1 = (Singleton) newInstanceMethod.invoke(accessor, new Object[]{new Object[]{"hey", 1}});
  Singleton singleton2 = (Singleton) newInstanceMethod.invoke(accessor, new Object[]{new Object[]{"hey", 1}});
  if(singleton1 == singleton2){
  	System.out.println("son iguales los objetos");
  }else{
  	System.out.println("no son iguales los objetos");
  }
  if(singleton1.INSTANCE == singleton2.INSTANCE){
	System.out.println("son iguales las instancias");
  }else{
	System.out.println("no son iguales las instancias");
  }
}

Como resultado de ejecutar el código, obtenemos dos objetos distintos, pero las instancias de su interior son la misma y por tanto mediante reflexión no hemos roto la singleton.

Además dado que los enumerados implementan la interfaz serializable no necesitamos hacer nada adicional para serializar este objeto.

Según Joshua Bloch es la mejor manera de instanciar una singleton.

]]>
http://anabuigues.com/2010/02/15/definir-una-singleton-en-java-mediante-un-enum/feed/ 6