Buscar
Social
Ofertas laborales ES
« Entrevista a Howard Lewis Ship sobre Tapestry en DevRates | Main | ZK 6.5 Empezando con el Responsive Design »
lunes
nov192012

Top 5 errores que comenten los programadores Java

Este artículo describe lo que el autor considera que son los cinco errores más comunes que los programadores Java cometen. El primero consiste en creer que estamos sobreescribiendo un método cuando realmente no lo estamos haciendo como por ejemplo:

public class MyWindowListener extends WindowAdapter {

        public void WindowDeactivate(WindowEvent e) {
               System.exit(0);
        }
});

Un problema que se soluciona fácilmente en la actualidad empleando @Override. El siguiente es confundir paso por valor y paso por referencia, algo muy común en los programadores novatos que se creen que, por ejemplo, al pasar un objeto a un método y modificarlo dentro, pensando que el objeto que habían pasado inicialmente "no se va a modificar porque el método trabaja con una copia del original".

El tercero error de la lista es dejar vacíos los bloques catch. El siguiente error es el típico de confundir creer que "==" sirve para comparar por igualdad (equals ()), y no por identidad. Aunque en esencia lo que dice el artículo original es cierto, su ejemplo no lo es:

String string1 == “string1”; 
String string2 == “string1”;
boolean iguales = string1 ==string2;

Después de ejecutar este código, al menos en cualquier máquina virtual moderna que yo conozco, iguales vale "true", aunque "no por el motivo adecuado" ;). El último error son lo que él llama errores de capitalización, usar mayúsculas cuando no se deben usar o viceversa. Este, francamente, no lo entiendo. El compilador suele avisar de un error de esta naturaleza, a no ser que estemos hablando de temas como creer que estamos sobreescribiendo un método cuando no lo estamos haciendo.

Yo a esta lista añadiría el creer que los objetos "File" son ficheros que están en el disco duro, y no meramente rutas en un sistema de ficheros, y escribir código del tipo:

File file = new File("path aqui");
// Si el fichero no existe
if(file==null)
{
...
}

O el invocar métodos que devuelven algo sobre objetos inmutables, pensando que el objeto inmutable va a cambiar:

String text0 = "HOLA";
texto.toLowerCase();

¿Cuales son otros errores comunes de programación en Java según vuestra experiencia?

PrintView Printer Friendly Version

EmailEmail Article to Friend

Reader Comments (23)

Uno que veo seguido: por algún motivo es necesario recorrer todos los elementos de un Map, y el programador usa

for(String key: map.keySet()){
System.out.println("key:" + key + " " + map.get(key));
}


en vez de

for(Map.Entry<String, String> entry: map.entrySet()){
System.out.println("key:" + entry.getKey() + " " + entry.getValue());
}

noviembre 19, 2012 | Unregistered CommenterPablo Grisafi

Ja ja ja. Se puede hacer una lista tremenda.

En estos que pongo, algunos son "comunes" y otros no tanto pero las consecuencias son bastantes costosas de arreglar o fastidian bastante.

1. No es un error pero si una mejora.
poner variable.equals("valor") en vez de "valor".equals(variable) y que te llegue que variable vale null.

2. Convertir un for de ascendente a descendente y olvidarte cambiar el ++;
for (int n=0;n<CONS;n++) --> for (int n=CONS-1;n>=0;n++) . ¡Como fastidia cuando te das cuenta tarde!

3. Importar una clase con Ctrl+May+O (Eclipse) y que aparezcan dos iguales (en diferentes paquetes) que pueden ser candidatas y tienen ambas buena pinta. Momentazo "me la juego al...".

4. Escribir un delete from y olvidar poner el where. //Facepalm 2 segundos más tarde.

5. Querer comentar un amplio código (muchas líneas e incluso una clase) con /* */ y justo por el medio había otro /* */ en medio.

6. while(true) insertaEnLog(); //Donde pone true, poner cualquier condición que no acabe nunca. A partir de aquí, si no te dás cuenta, dejas el ordenador sin HD y según como esté la instalación y que sistema operativo tiene puede incluso no poder rearrancar.

......

noviembre 19, 2012 | Unregistered CommenterJorge Rubira

Uno que no es un error ni tampoco es especifico de java, pero lo veo muy habitualmente y me pone de los nervios :P:

if (condicion) {
return true;
} else {
return false;
}

en lugar de poner simplemente: return condicion

Otro muy tipico es el abuso de "static" cuando no se tiene nada claro como va esto de la OO.

Otro que me hizo gracia porque no lo había oído nunca así, el código "baklava" (postre arabe compuesto de muchas capas finas), es el tipico código lleno de capas innecesarias que sólo se pasan mensajes de una a la otra sin contener ningún tipo de logica.

noviembre 20, 2012 | Registered Commenteralfredocasado

hola, Abroham no soy programador java, ni tampoco experto y mi comentario es para notificarte de un error de escritura en el titulo de tu post, deberia ser "errores" en lugar de "erores" como lo escribiste.

un saludo y que tengas buen dia

noviembre 20, 2012 | Unregistered Commenterbasilio

mmm, creo que ya somos dos los de los dedos chuecos...:D escribi mal tu nombre :D

noviembre 20, 2012 | Unregistered Commenterbasilio

corregido, basilio

noviembre 20, 2012 | Registered CommenterAbraham

No entiendo porque al hacer boolean iguales = string1 ==string2; iguales va a ser siempre true. ¿No se debería hacer con el .equals?

noviembre 20, 2012 | Unregistered CommenterAlbert

Sí, Albert. Lo correcto es usar el equals. Pero el ejemplo que se ha puesto como error está mal, porque si lo ejecutas, dependiendo de la implementación de la JVM, te dará true.

¿Cómo? Pues porque una de las optimizaciones que hace el compilador es que, aprovechando que los objetos String son inmutables, si se asigna un mismo literal a variables diferentes, el compilador, internamente, hará que apunten a la misma posición de memoria, donde está almacenada la cadena.

Así que si hacemos

String string1 = "string1";
String string2 = "string1";

en realidad, ambas instancias apuntan a lo mismo, y un string1==string2 devolverá true.

Sin embargo, si tienes, por ejemplo:

String string1 = "string1";
String string2 = new StringBuilder("string").append("1");

la comparación string1==string2 devolverá false.

noviembre 20, 2012 | Registered Commenteradeteran

La respuesta de adeteran es perfecta; a esto se le conoce como internalización, y se aplica a todos los tipos primitivos inmutables (Float, Doble, Integer...) y a Strings. Y este es, por cierto, un motivo por el cual suele ser una mala idea tomar un lock sobre cualquiera de los tipos de datos mencionados en la frase anterior; podrías estar guardando más cosas de las que crees.

noviembre 20, 2012 | Registered CommenterAbraham

Este artículo puede arrojar toda la luz necesaria sobre la cuestión de la igualdad.
http://www.xyzws.com/Javafaq/what-is-string-literal-pool/3

A destacar este párrafo:
"String allocation, like all object allocation, proves costly in both time and memory. The JVM performs some trickery while instantiating string literals to increase performance and decrease memory overhead. To cut down the number of String objects created in the JVM, the String class keeps a pool of strings. Each time your code create a string literal, the JVM checks the string literal pool first. If the string already exists in the pool, a reference to the pooled instance returns. If the string does not exist in the pool, a new String object instantiates, then is placed in the pool. Java can make this optimization since strings are immutable and can be shared without fear of data corruption."

Es un proceso de la VM, no del compilador.

noviembre 20, 2012 | Registered Commenterchoces

Este creo que es mas bien de principiantes. Cascar un método de líneas (o más) y llenarlo de return,

noviembre 21, 2012 | Registered Commenterrobertiano

@choces, el compilador también entra; la máquina virtual sólo hace lo que el compilador le dice, pero el compilador es quien selecciona los Strings que, en base al contenido de nuestro código fuente, van a ir al pool. En el caso de otras clases wraper (Float, Doble, Integer...) si que probablemente sea sólo la máquina virtual ya que hay un conjunto predefinido de ellas que van a su respectivo pool.

noviembre 21, 2012 | Registered CommenterAbraham

@Abraham, el compilador javac no crea los objetos ni asigna las referencias.

En las especificaciones de la VM
http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4
se puede ver que el tratamiento de los Strings es idéntico al de otras clases por el estilo: todas usan el Constant_Pool, y es la VM quien decide qué va al pool y cuándo.

El compilador javac no sabe si el String declarado, u otra clase susceptible de usar el pool, ya existe en el pool, ni si se debe crear o no un objeto nuevo. Se puede desensamblar ese código fuente con el javap, y ver el tipo de bytecode tan extremadamente simple que se genera.

Desde hace bastante tiempo, la VM no es un "simple intérprete" del bytecode. Como ejemplo reciente, los nuevos lambdas del JavaSE 1.8 van a usar el invokedynamic, por lo que el javac no va a "generar" el "código ejecutable" de los lambdas.

noviembre 21, 2012 | Registered Commenterchoces

Desde luego, tiene mucho más sentido que sea la VM quien haga este tipo de optimizaciones, ya que se tiene más información en tiempo de ejecución que en tiempo de compilación. Pero ya me ha picado la curiosidad de qué criterio emplea exactamente, y cómo decide si reutiliza un elemento del pool.

El siguiente código:

public static void main(String[] args) {
String string1 = "string1";
String string2 = new StringBuilder("string").append("1").toString();
System.out.println(string1 == string2);
System.out.println(string1.equals(string2));
}

me escupe por la consola lo siguiente:

false
true

en una 1.6 y en una 1.5 (ambas bajo Windows XP).

Pero la VM debería saber que el resultado del toString() del StringBuilder es un literal que ya existe en el pool.

El siguiente código, en cambio:

public static void main(String[] args) {
String string1 = "string1";
String string2 = "string" + "1";
System.out.println(string1 == string2);
System.out.println(string1.equals(string2));
}

me escupe por la consola lo siguiente:

true
true

¿Alguien se anima a hacer más experimentos? (yo tengo que seguir currando)

noviembre 21, 2012 | Registered Commenteradeteran

Un error muy común que también encuentro es:
String s = "esto "
+ "es "
+ "un "
+ "string "
+ "ultra "
+ "concatenado "
+ "de "
+ "alguien "
+ "que "
+ "no "
+ "conoce "
+ "la "
+ "clase "
+ "StringBuilder()";

O incluso este:

StringBuilder b = new StringBuilder();
b.append("este ");
b.append("es ");
b.append("otro " + "error comun");

Netbeans por lo menos tiene una opción que te muestra estos y otros errores comunes.

noviembre 21, 2012 | Unregistered CommenterLeopard

En una VM 1.7 el resultado también es el mismo. Si fuese diferente...

Esta es la sección relevante del bytecode para el primer caso:

0: ldc #2 // String string1
2: astore_1
3: new #3 // class java/lang/StringBuilder
6: dup
7: ldc #4 // String string
9: invokespecial #5 // Method java/lang/StringBuilder."<
init>":(Ljava/lang/String;)V
12: ldc #6 // String 1
14: invokevirtual #7 // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
17: invokevirtual #8 // Method java/lang/StringBuilder.to
String:()Ljava/lang/String;
20: astore_2

y ésta para el segundo caso:

Code:
0: ldc #2 // String string1
2: astore_1
3: ldc #2 // String string1
5: astore_2

He omitido las demás líneas porque solo hacen referencia a la clase y a la impresión por consola.

La diferencia entra ambas, es que en el primer caso se necesita crear un nuevo objeto y
la VM crea una nueva entrada en el Pool, al margen de que ya exista un objeto con el mismo contenido, pero que tendrá una referencia diferente.
El bytecode del segundo caso no puede ser más simple: se limita a ejecutar una instrucción astore con el valor de cada variable. Aquí es donde la VM debe comprobar si ya existe en el Pool, y proceder según el resultado.

noviembre 21, 2012 | Registered Commenterchoces

@choces Parece que tienes razón. Tengo un recuerdo de que hace tiempo, leyendo cosas sobre internalización de String (hace muchos años, la primera vez que oí hablar del concepto) lo que había leído es que el compilador jugaba un papel en determinar los Strings que iban al pool a la hora de generar el bytecode. No sé si recuerdo mal, o es que han cambiado las cosas.

noviembre 21, 2012 | Registered CommenterAbraham

@Leopard

En ambos ejemplos que has puesto, el compilador realiza directamente la cocatenación, por lo que usar StringBuilder es más lento ;)
Cuando hay que concatenar cadenas explícitas con variables, es cuando StringBuilder es útil si la cocatenación se realiza dentro de un bucle. Aún en este caso, el uso del método concat de la clase String, puede ser más eficiente en algunos casos.

String variable; // se inicializa en algún momento
El compilador convierte esta asignación a StringBuilder en el bytecode:
String cadena = "cadena" + variable;

Por esa razón, a menos que la concatenación se realice en un bucle, tanto da.
El problema con los bucles es que se crea una nueva instancia de StringBuilder en cada iteración, si se usa la concatenación con +

noviembre 21, 2012 | Registered Commenterchoces

@Abraham,

No sé si te refieres a algo como ésto:

public class internExample{
public static void main(String[] args){
String str1 = "Hello Java";
String str2 = new StringBuffer("Hello").append(" Java").toString();
String str3 = str2.intern();
System.out.println("str1 == str2 " + (str1 == str2)); //false
System.out.println("str1 == str3 " + (str1 == str3)); //true
}
}

noviembre 21, 2012 | Registered Commenterchoces

No me refiero algo como eso. Pero esto sí que podría ser resultado del compilador:

String str1 = "Hello Java";
String str2 = "Hello " + "Java";
System.out.println("str1 == str2 " + (str1 == str2)); //true

A lo mejor es la máquina virtual; pero se me antoja más eficiente que sea el compilador.

noviembre 21, 2012 | Registered CommenterAbraham

El compilador javac no "conoce" necesariamente todos los String que se declaran y asignan. Imagina que puede haber cadenas que se declaran en librerías ya compiladas, algo muy frecuente en desarrollos modulares, de las que la compilación actual no sabe nada.
Esa es la motivación principal para el uso del Constant_Pool de la VM. Gracias a ese pool, el compilador no necesita saber si una cadena existe o no: la pasa a la VM, y que ella verifique lo necesario.

noviembre 21, 2012 | Registered Commenterchoces

Muy buen tema mi error mas comun es invocar una lista sin valores

noviembre 22, 2012 | Unregistered CommenterArturo

@choces

No sabia que el compilador cambiaba el codigo:

String str = "cadena" + varTipoStr

a algo del tipo

String str = new StringBuilder("cadena").append(varTipoStr).toString()

Es interesante saberlo, pues estaba usando StringBuilder por eficiencia pero parece bastante mas legible usar String

noviembre 23, 2012 | Registered Commentergolthiryus

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>