«IIII IIII» día del programador
El Día del Programador se celebra el día 256 del año, el 13 de septiembre, porque 256 es el número de valores que caben en un byte. Y, como dice la imagen, lo normal es pasarlo trabajando.
Piensa la respuesta antes de abrirla. Todas las opciones parecen razonables, y casi siempre hay un detalle que lo cambia todo.
Pregunta Java · Collections
import java.util.HashSet;
import java.util.Set;
public class Test {
static void imprime(Set<? extends String> set) {
for (String elemento : set) {
System.out.format("%s ", elemento);
}
}
public static void main(String[] args) {
Set<String> set = new HashSet<String>();
set.add("A");
set.add("J");
set.add("A");
set.add("X");
imprime(set);
}
}"A J A X""A J X"Respuesta: 3.
java TestA X J
Un HashSet no guarda duplicados, así que la segunda "A" se descarta y quedan tres elementos. Pero no garantiza ningún orden: los coloca según su hashCode, y en nuestra ejecución salió A X J, que no es el orden en que se añadieron ni el alfabético. La documentación ni siquiera garantiza que ese orden se mantenga a lo largo del tiempo.
Si necesitas que salgan en el orden en que se insertaron, usa LinkedHashSet; si los quieres ordenados, TreeSet.
Más preguntas de Java
Pregunta publicada en 2013 y revisada en octubre de 2026. La respuesta se comprobó compilando y ejecutando el código con Java 25 LTS en un contenedor limpio; las salidas están copiadas de esa ejecución.
1–Neutralización inadecuada de elementos especiales usados en un comando SQL (SQL Injection)
La inyección SQL es un método de infiltración de código intruso que se vale de una vulnerabilidad informática presente en una aplicación en el nivel de validación de las entradas para realizar consultas a una base de datos.
El origen de la vulnerabilidad radica en el incorrecto chequeo y/o filtrado de las variables utilizadas en un programa que contiene, o bien genera, código SQL. Es, de hecho, un error de una clase más general de vulnerabilidades que puede ocurrir en cualquier lenguaje de programación o script que esté embebido dentro de otro.
Un ataque de inyección de comandos en el sistema operativo se produce cuando un atacante intenta ejecutar comandos de sistema a través de una aplicación vulnerable. Las aplicaciones se consideran vulnerables al ataque de inyección de comandos si se utilizan las entradas del usuario y se ejecutan directamente.
3.- Copia de buffer sin comprobar el tamaño de la entrada. (Clásico ‘Buffer Overflow’)
Es un error de software que se produce cuando un programa no controla adecuadamente la cantidad de datos que se copian sobre un área de memoria reservada a tal efecto (buffer), de forma que si dicha cantidad es superior a la capacidad preasignada los bytes sobrantes se almacenan en zonas de memoria adyacentes, sobrescribiendo su contenido original.
4.-Neutralización inadecuada de datos entrada durante la generación de Web Pages (‘Cross-site Scripting’)
XSS, del inglés Cross-site scripting es un tipo de inseguridad informática o agujero de seguridad típico en las aplicaciones Web, que permite a una tercera parte inyectar páginas web o contenido ajeno, para ser visto por el usuario usando código JavaScript u otro lenguaje script similar (ej: VBScript), evitando medidas de control como la Política del mismo origen. Este tipo de vulnerabilidad se conoce en español con el nombre de Secuencias de comandos en sitios cruzados.
5.- Falta de Autenticación en Funciones Críticas.
La falta de autenticación es común en los sistemas que por falta de tiempo para su desarrollo o descuido son dejados al final. Los programadores se enfocan en crear los módulos críticos del sistema que son los mínimos necesarios para operar y dejan funciones de autenticación para el final.
6.- Falta de Autorización.
Este problema no es igual al del punto 5, aquí se trata de restringir a los usuarios de un sistema a ciertas funciones dentro de una aplicación. No todos lo usuarios deben tener los mismos privilegios y la autenticación no es suficiente. Por ejemplo, si tienes una función para crear o dar de alta a usuarios nuevos, esta función debe quedar restringida para perfiles con privilegios de administrador.
7.- Uso de credenciales “Hard-code”
Crear una contraseña oculta en el código para poder acceder sin restricciones es muy mala idea, una vez que esta contraseña se sabe la violación de seguridad es permanente.
8.- La falta de cifrado en datos confidenciales.
Siempre que se provee de autenticación a usuarios es necesario almacenar de alguna forma un username y una contraseña. Almacenar la contraseña o cualquier otro dato crítico, sin ningún tipo de cifrado, permite que un atacante que haya podido vulnerar el acceso a una base de datos o cualquier otro medio de almacenamiento que use tu sistema, tome también los datos de acceso de los usuarios y use otras funciones críticas de la aplicación de forma directa.
9.- Carga no restringida de archivos potencialmente peligrosos.
Algunas veces se provee a los usuarios de funciones que permitan cargar imágenes, documentos o cualquier clase de archivo que la solución lo requiera. Se debe tener cuidado en filtrar todos aquellos archivos que puedan ser ejecutados tanto por el sistema operativo como por el contenedor web, o bien simplemente utilizados con fines distintos a los que se ha diseñado la solución.
10.- Depender de Entradas que no son confiables para decisiones críticas.
No se debería por ejemplo, permitir a usuarios ejecutar funciones críticas o la descarga de contenido no apropiado basándose en los datos que proporciona, como su edad o algún otro dato que pueda ser fácilmente falseable. A veces es necesario proveer a las aplicaciones de una serie de candados como números de seguridad, identificaciones oficiales, captchas, etc.
Esta lista circula desde hace más de diez años. La mayoría de los consejos siguen siendo buenos, pero algunos han envejecido y un par de ejemplos originales ni siquiera compilaban. Los repasamos uno a uno con Java 25, ejecutando todo lo que se puede ejecutar.
También te puede interesar
El consejo original era no crear objetos hasta que hagan falta:
class Paises {
private List<String> paises;
List<String> getPaises() {
if (paises == null) paises = new ArrayList<>();
return paises;
}
}Crear una lista vacía cuesta muy poco en una máquina virtual moderna, y este patrón tiene un problema que no se ve: no es seguro con varios hilos. Si dos hilos llaman a la vez a getPaises(), los dos pueden ver null y crear cada uno su lista. Lanzamos ocho hilos contra el mismo objeto, varias veces:
java Perezosa.javados hilos recibieron listas distintas en el intento 1
En tres ejecuciones pasó en los intentos 1, 178 y 1: depende del momento, que es lo peor que puede tener un error. Lo sensato hoy es inicializar en la declaración (private final List<String> paises = new ArrayList<>();) y reservar la inicialización perezosa para objetos realmente caros, como una conexión o un archivo grande, y con el cuidado que requiera.
Un arreglo público lo puede cambiar cualquiera. Pero hacerlo privado y devolverlo con un get no basta: quien lo recibe tiene el mismo arreglo y puede modificarlo igual.
private static final String[] DIAS = {"Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado"};
private static final List<String> LISTA = List.of(DIAS);
static String[] diasArreglo() { return DIAS; }
static String[] diasCopia() { return DIAS.clone(); }
static List<String> dias() { return LISTA; }Calendario.diasArreglo()[0] = "¿?"; IO.println(Calendario.diasCopia()[0]);
salida¿? List.of no deja cambiarla: UnsupportedOperationException
Alguien cambió el primer día desde fuera, y el cambio se ve en la copia que pide después: el original quedó estropeado para todos. Devolver clone() protege, pero copia el arreglo en cada llamada. Hoy lo más limpio es devolver una List.of, que no se puede modificar y no hay que copiar.
"Jueves", "Sabado", "Domingo". Le faltaba el viernes y repetía el domingo.El original proponía un constructor privado con un método valueOf para hacer la clase inmutable. Eso controla cómo se crean los objetos, pero no impide cambiarlos:
public class Empleado {
private String nombre;
private Empleado(String nombre) { this.nombre = nombre; }
public static Empleado valueOf(String nombre) { return new Empleado(nombre); }
public String getNombre() { return nombre; }
public void setNombre(String nombre) { this.nombre = nombre; }
public static void main(String[] args) {
Empleado e = Empleado.valueOf("Ana");
e.setNombre("Luis");
System.out.println(e.getNombre());
}
}java EmpleadoLuis
Para que sea inmutable, los campos tienen que ser final y no puede haber ningún método que los cambie. Desde Java 16 el record hace todo eso de una vez. Si tiene una lista, hay que copiarla, porque el record solo impide cambiar la referencia, no el contenido:
record Empleado(String nombre, List<String> proyectos) {
Empleado {
Objects.requireNonNull(nombre);
proyectos = List.copyOf(proyectos);
}
}java Registro.javaEmpleado[nombre=Ana, proyectos=[web]] no se puede: UnsupportedOperationException true
Añadir "móvil" a la lista original después de crear el empleado no le afecta, y su lista no se puede tocar. El original también decía que los objetos inmutables «golpean el rendimiento». Crear objetos pequeños es barato, y en la práctica se ganan otras cosas: se pueden compartir entre hilos sin sincronizar y usar como claves de un mapa. Lo explicamos en qué es un objeto inmutable.
Sigue siendo buen consejo: una clase puede implementar varias interfaces, pero solo heredar de una clase. Lo que ha cambiado es la objeción del original, que decía que añadir un método a una interfaz rompe todas las clases que la implementan. Desde Java 8 existen los métodos default, que traen su implementación, así que se pueden añadir sin romper nada. Lo vemos en el curso: herencia e interfaces.
Declara cada variable justo donde se usa por primera vez, y a ser posible inicializada en la misma línea. El original añadía «si no es posible, asígnale null», y eso es mal consejo: el compilador de Java ya avisa si usas una variable local sin haberle dado valor, y un null de relleno desactiva ese aviso y lo cambia por un NullPointerException en tiempo de ejecución.
No reinventes lo que ya está probado. El original recomendaba «Google Collections», que hace años pasó a llamarse Guava. Además, buena parte de lo que se usaba de ella ya está en el JDK: List.of, Map.of, Optional, los streams, Objects.requireNonNull. Antes de añadir una dependencia, merece la pena mirar si no lo hace ya el propio Java.
Integer a = 127, b = 127;
Integer c = 128, d = 128;
IO.println("127 == 127 -> " + (a == b));
IO.println("128 == 128 -> " + (c == d));
IO.println("128 equals 128 -> " + c.equals(d));
Boolean bandera = null;
try {
if (bandera == true) IO.println("verdadero");
} catch (NullPointerException e) {
IO.println(e.getMessage());
}
IO.println("Boolean.TRUE.equals(bandera) -> " + Boolean.TRUE.equals(bandera));javac -g Wrappers.java && java Wrappers127 == 127 -> true 128 == 128 -> false 128 equals 128 -> true Cannot invoke "java.lang.Boolean.booleanValue()" because "bandera" is null Boolean.TRUE.equals(bandera) -> false
Esto es más traicionero de lo que contaba el original. Integer guarda una caché de los valores entre -128 y 127, así que == funciona con números pequeños y falla con los grandes: el error no sale en las pruebas con datos de ejemplo y aparece en producción. Con Integer, siempre equals.
El Boolean a null lanza la excepción porque, para compararlo con true, Java tiene que convertirlo a boolean, y null no tiene conversión. Boolean.TRUE.equals(bandera) es la forma segura.
new Integer(10). Ese constructor está obsoleto desde Java 9 y el compilador lo avisa. Se usa Integer.valueOf(10), o directamente Integer x = 10;.Dos cosas. new String("texto") crea un objeto innecesario: escribe directamente "texto". Y no acumules cadenas con += dentro de un bucle, porque el coste crece con el cuadrado del número de vueltas. Lo medimos en concatenar cadenas en un bucle.
List<String> buscarNull(String texto) { return texto.isEmpty() ? null : List.of(texto); }
List<String> buscar(String texto) { return texto.isEmpty() ? List.of() : List.of(texto); }java Vacias.javavacía: 0 null: NullPointerException
Con una lista vacía, el for simplemente no da ninguna vuelta. Con null, el for lanza la excepción, y cada uno de los que llamen al método tiene que acordarse de comprobarlo. Devolver List.of() no cuesta prácticamente nada, así que no hay excusa de rendimiento.
El ejemplo clásico es una clase que guarda una fecha que le pasan:
class Estudiante {
private final Date nacimiento;
Estudiante(Date nacimiento) { this.nacimiento = nacimiento; }
Date getNacimiento() { return nacimiento; }
}
class EstudianteCopia {
private final Date nacimiento;
EstudianteCopia(Date nacimiento) { this.nacimiento = new Date(nacimiento.getTime()); }
Date getNacimiento() { return new Date(nacimiento.getTime()); }
}Date fecha = new Date(100, 4, 17); var e = new Estudiante(fecha); var c = new EstudianteCopia(fecha); fecha.setYear(2019);
salidasin copia: 3919 con copia: 2000 LocalDate: 2000-05-17 / 2019-05-17
La versión sin copia ve el cambio, porque guarda el mismo objeto que tiene quien la llamó. Y el año sale 3919, no 2019: setYear cuenta los años desde 1900. Es uno de los motivos por los que Date está obsoleta casi entera.
La copia del original, new Date(fechaNacimiento), no compila, porque Date no tiene ese constructor:
javac DateMal.javaDateMal.java:5: error: no suitable constructor found for Date(Date)
Date copia = new Date(original);
^
constructor Date.Date(long) is not applicable
(argument mismatch; Date cannot be converted to long)
constructor Date.Date(String) is not applicable
(argument mismatch; Date cannot be converted to String)Lo correcto es new Date(fecha.getTime()), en el constructor y en el get. Aunque la solución de verdad es no usar Date: LocalDate, de java.time, es inmutable, así que no hace falta copiarla. withYear devuelve una fecha nueva y deja la original como estaba (última línea de la salida).
El original solo daba el título. Esto es lo que pasa:
int dividir(int a, int b) {
try {
return a / b;
} finally {
throw new IllegalStateException("fallo al cerrar");
}
}
int conReturn(int a, int b) {
try {
return a / b;
} finally {
return -1;
}
}java Finally.javaexcepción que llega: java.lang.IllegalStateException: fallo al cerrar con return en finally: -1 try-with-resources: java.lang.ArithmeticException: / by zero suprimida: java.lang.IllegalStateException: fallo al cerrar
Las dos divisiones son entre cero, pero esa excepción desaparece. En la primera, la del finally la sustituye y nadie llega a saber que hubo una división entre cero. En la segunda, el return del finally se la traga y el método devuelve -1 como si todo hubiera ido bien.
try con recursos lo resuelve: si al cerrar el recurso también falla algo, la excepción original es la que llega, y la del cierre queda adjunta como «suprimida», sin perderse. Es la forma de cerrar archivos y conexiones desde Java 7.
Tampoco tenía explicación. throw new Exception("algo falló") obliga a quien te llama a capturar Exception, que es tanto como capturarlo todo: también los errores de programación que deberían verse. Lanza la excepción más concreta que exista (IllegalArgumentException si te pasan un dato inválido, IllegalStateException si el objeto no está listo, IOException si falla la entrada o salida) o crea la tuya. Lo vemos en el curso: excepciones y Optional.
Basado en 10 Most Useful Java Best Practice Quotes, de Viral Patel.
Entrada publicada en 2013 y revisada en octubre de 2026. Todo el código se compiló y se ejecutó con Java 25 LTS en un contenedor limpio; las salidas están copiadas de esa ejecución.