Pregunta Java: binarySearch con null

Piensa la respuesta antes de abrirla. Todas las opciones parecen razonables, y casi siempre hay un detalle que lo cambia todo.

Pregunta Java · Arrays

¿Cuál es el resultado de compilar y ejecutar el siguiente código? ¿Y por qué?

import java.util.Arrays;

public class Prueba {
  public static void main(String args[]){
    String[] arr = {"texto1", "texto3" , "texto2"};
    Arrays.sort(arr);
    System.out.print(" " + Arrays.binarySearch(arr, "texto2"));
    System.out.print(" " + Arrays.binarySearch(arr, "texto3"));
    System.out.print(" " + Arrays.binarySearch(arr, null));
  }
}
  1. Imprime ' 2 1 -1'
  2. Imprime ' 1 2' y lanza una java.lang.NullPointerException.
  3. Imprime ' 2 1 0'
  4. No compila porque sort no funciona con arreglos de Strings.
Ver la respuesta

Respuesta: 2.

java Prueba1 2Exception in thread "main" java.lang.NullPointerException: Cannot read field "value" because "anotherString" is null
	at java.base/java.lang.String.compareTo(String.java:2105)
	at java.base/java.lang.String.compareTo(String.java:144)
	at java.base/java.util.Arrays.binarySearch0(Arrays.java:2231)
	at java.base/java.util.Arrays.binarySearch(Arrays.java:2171)
	at Prueba.main(Prueba.java:9)

Después de sort el arreglo queda {"texto1", "texto2", "texto3"}, así que "texto2" está en la posición 1 y "texto3" en la 2. Para buscar, binarySearch compara con compareTo, y comparar una cadena con null lanza la excepción. Las dos primeras búsquedas ya se habían impreso, sin salto de línea porque se usa print, y la excepción aparece pegada a ellas.

La imagen original no llevaba import java.util.Arrays;, y sin esa línea el código no compila.

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.

Pregunta Java: leer más allá del final de un archivo

Piensa la respuesta antes de abrirla. Todas las opciones parecen razonables, y casi siempre hay un detalle que lo cambia todo.

Pregunta Java · IO

Suponiendo que tienes los permisos de lectura y escritura, ¿cuál es el resultado de compilar y ejecutar el siguiente código? ¿Y por qué?

import java.io.*;

public class Prueba {
  public static void main(String[] args)
          throws FileNotFoundException, IOException {
    OutputStream out = new FileOutputStream("/archivo.txt");
    out.write(123);
    out.close();
    InputStream input = new FileInputStream("/archivo.txt");
    System.out.println(input.read() + " " + input.read());
    input.close();
  }
}
  1. imprime "123 0"
  2. imprime "123 -1"
  3. imprime "123 null"
  4. se lanza una excepción en tiempo de ejecución.
Ver la respuesta

Respuesta: 2.

java Prueba123 -1

write(123) escribe un solo byte, con el valor 123. El primer read() lo devuelve, y el segundo ya no tiene nada que leer: read() devuelve -1 al llegar al final del archivo. No lanza una excepción, y tampoco puede devolver null, porque devuelve un int.

Por eso read() devuelve un int y no un byte: necesita un valor más, -1, que no se pueda confundir con ningún byte (0 a 255).

La imagen original no llevaba la línea import java.io.*;, y sin ella el código no compila. Lo de «suponiendo que tienes permisos» importa: escribir en /archivo.txt, en la raíz, solo funciona como administrador. Con un usuario normal da FileNotFoundException: /archivo.txt (Permission denied), y por eso esta ejecución se hizo como root.

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.

Comparar cadenas en Java: == frente a equals

¿Qué devuelven las seis expresiones de la imagen? Todo se resume en una idea: == compara si dos referencias apuntan al mismo objeto, y equals compara el contenido. Lo que lía es que, con las cadenas, a veces son el mismo objeto sin que lo parezca.

Las respuestas, ejecutadas

Este programa evalúa las seis expresiones de la imagen y unas cuantas más que ayudan a entenderlas:

import java.util.Objects;

void main() {
    String variable = null;

    IO.println("\"cadena\" == \"cadena\"                    -> " + ("cadena" == "cadena"));
    IO.println("\"cadena\" == \"cad\" + \"ena\"               -> " + ("cadena" == "cad" + "ena"));

    String cad = "cad";
    final String cadFinal = "cad";
    IO.println("\"cadena\" == cad + \"ena\"                 -> " + ("cadena" == cad + "ena"));
    IO.println("\"cadena\" == cadFinal + \"ena\"            -> " + ("cadena" == cadFinal + "ena"));

    IO.println("\"cadena\" == \"-cadena\".substring(1)      -> " + ("cadena" == "-cadena".substring(1)));
    IO.println("new String(\"cadena\") == \"cadena\"        -> " + (new String("cadena") == "cadena"));
    IO.println("new String(\"cadena\").equals(\"cadena\")   -> " + new String("cadena").equals("cadena"));
    IO.println("\"-cadena\".substring(1).intern() == \"cadena\" -> " + ("-cadena".substring(1).intern() == "cadena"));

    IO.println("\"cadena\".equals(variable)               -> " + "cadena".equals(variable));
    IO.println("Objects.equals(variable, \"cadena\")      -> " + Objects.equals(variable, "cadena"));
    IO.println("\"Cadena\".equalsIgnoreCase(\"CADENA\")     -> " + "Cadena".equalsIgnoreCase("CADENA"));
    IO.println("\"abeja\".compareTo(\"abeto\")              -> " + "abeja".compareTo("abeto"));

    try {
        IO.println(variable.equals("cadena"));
    } catch (NullPointerException e) {
        IO.println("variable.equals(\"cadena\") -> " + e);
    }
}
javac -g Cadenas.java
java Cadenas
salida"cadena" == "cadena"                    -> true
"cadena" == "cad" + "ena"               -> true
"cadena" == cad + "ena"                 -> false
"cadena" == cadFinal + "ena"            -> true
"cadena" == "-cadena".substring(1)      -> false
new String("cadena") == "cadena"        -> false
new String("cadena").equals("cadena")   -> true
"-cadena".substring(1).intern() == "cadena" -> true
"cadena".equals(variable)               -> false
Objects.equals(variable, "cadena")      -> false
"Cadena".equalsIgnoreCase("CADENA")     -> true
"abeja".compareTo("abeto")              -> -10
variable.equals("cadena") -> java.lang.NullPointerException: Cannot invoke "String.equals(Object)" because "variable" is null
La opción -g guarda los nombres de las variables locales en el .class, y por eso el último mensaje dice "variable". Si lo ejecutas directamente con java Cadenas.java sale "<local1>" en su lugar: el lanzador compila sin esa información.

Por qué cada una da lo que da

ExpresiónResultadoPor qué
"cadena" == "cadena"trueLos literales iguales se guardan una sola vez, en el pool de cadenas. Los dos son el mismo objeto.
"cadena" == "cad" + "ena"trueEs una expresión constante: el compilador une las dos partes y en el .class ya solo está "cadena".
"cadena" == "-cadena".substring(1)falsesubstring crea un objeto nuevo mientras el programa se ejecuta. Mismo texto, otro objeto.
new String("cadena").equals("cadena")trueequals compara los caracteres, que son los mismos.
new String("cadena") == "cadena"falsenew siempre crea un objeto nuevo, distinto del literal.
new String("cadena") == new String("cadena")falseDos new, dos objetos.

Lo interesante está en las dos líneas que añadimos: si una de las partes es una variable normal, la concatenación ocurre al ejecutar y da un objeto nuevo (false). Si la variable es final y se inicializa con un literal, cuenta como constante y el compilador vuelve a unirlas antes de tiempo (true). Es decir, el resultado de == depende de detalles que no se ven al leer el código. Basta un cambio inocente, como quitar un final, para que una comparación que funcionaba deje de hacerlo.

Pasa lo mismo con cualquier texto que llegue de fuera: lo que se lee de un archivo, de la consola o de una petición web son objetos nuevos, aunque contengan exactamente lo mismo que un literal del programa.

La regla: equals, siempre

Para saber si dos cadenas tienen el mismo texto, equals. == solo sirve para preguntar si son el mismo objeto, y eso casi nunca es lo que quieres saber.

intern() devuelve la copia del pool, y por eso "-cadena".substring(1).intern() == "cadena" da true. Es una curiosidad que conviene conocer, pero no una forma de comparar: obliga a acordarse de llamarlo en todas partes.

Cuando la variable puede ser null

Si la variable es null, variable.equals("cadena") lanza un NullPointerException, como se ve en la última línea de la salida. Hay dos formas de evitarlo:

"cadena".equals(variable)

Poner el literal delante: un literal nunca es null, y equals(null) simplemente devuelve false.

Objects.equals(variable, "cadena")

Objects.equals sirve cuando las dos pueden ser null: devuelve true si ambas lo son y nunca lanza la excepción.

Otras comparaciones

Para…Se usaEjemplo
Mismo textoa.equals(b)true / false
Mismo texto, con posibles nullObjects.equals(a, b)true / false
Sin importar mayúsculasa.equalsIgnoreCase(b)"Cadena" y "CADENA" → true
Ordenara.compareTo(b)negativo, cero o positivo: "abeja" va antes que "abeto" → -10

El -10 de compareTo no es una distancia con significado: es la diferencia entre la primera pareja de caracteres que no coinciden (j y t). Lo único que importa es el signo.

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.

No importa

Texto sobre fondo claro: «¡No me importa si funciona en tu máquina! ¡No vamos a enviar tu máquina!», cita atribuida a Vidiu Platon.
¡No importa si funciona en tu máquina! ¡No vamos a enviar tu máquina!

«En mi máquina funciona» es una de las excusas más famosas de la programación. Contenedores como Docker nacieron, en buena parte, para que el entorno del desarrollador y el del servidor sean el mismo y esta frase deje de tener sentido.

Los gurús y el trabajo en equipo

Sin duda, siempre existen personas brillantes que se distinguen por su buen sentido común, curiosidad y capacidad para resolver problemas difíciles. Pero siempre he sospechado que la imagen del gurú está sobrevalorada.

Algunas malas experiencias me dicen que esto podría ser así.  En cierta ocasión, durante el desarrollo de un proyecto mas o menos grande y en una demostración de capacidades, el líder del equipo, que se sabía muy bueno en el desarrollo de software (el gurú), comenzó a utilizar una serie de tecnologías y frameworks de moda para impresionar al cliente y al administrador de proyecto.  Como resultado, los que tenían menos experiencia comenzaron a sufrir con la curva de aprendizaje y se enfocaron mas a conocer las nuevas tecnologías que al modelo de negocio. El desarrollo se terminó con una aplicación robusta tecnológicamente, pero poco amigable para el cliente y por supuesto en mas tiempo del que se esperaba. Un pequeño desastre.

En otras ocasiones, me he topado con el típico personaje que es capaz de resolver problemas difíciles, pero que cuando tienes que dar mantenimiento a su código o simplemente necesitas usarlo, este te parece tan desordenado que te dan ganas de hacerlo de nuevo.

Otro de los riesgos de tener a una persona que tiene todo el dominio sobre cierta tecnología, herramienta o el conocimiento mas crítico de un proyecto, es que puede simplemente irse, con el todo su conocimiento.

Me agradan mas los equipos de trabajo donde los problemas difíciles o algunas decisiones tecnológicas se resuelven entre varios.

Los gurús se equivocan como cualquier otro.

Además, cuando te autoproclamas gurú, puedes quedarte solo a la hora de resolver problemas difíciles.  Creo que no hay necesidad de que esto sea así, porque el criterio para resolver un problema crítico se reduce al de una sola persona y no creo que esto sea muy conveniente.

Cuando alguien te hace una pregunta complicada sobre algún problema o error, es probable que ella misma tenga mas capacidad para responderla, ya que conoce mejor el contexto del problema.  Por lo que mas que tratar de dar una solución es mejor dar un punto de vista o pistas sobre como resolver el problema.

El protagonismo es algo que nos puede seducir a todos, pero yo me lo pensaría mucho antes de adoptar una postura de este tipo. Sobre todo cuando de lo que se trata es hacer mas agradable y profesional tu trabajo.  Desde luego no estoy diciendo que todos los gurús son malos, de hecho también he conocido a algunos muy buenos. Pero son los menos.

Un hábito que recomiendo a todos es que cuando encuentras la solución a un problema difícil, la comentes a tus demás compañeros. Esto permite tener una base de conocimiento colectiva y evita tener que investigar demasiado sobre un problema que ya se ha sido resuelto con anterioridad.

Tómate con calma eso de ser gurú, puedes serlo sin hacer alarde.  🙂

Suele pasar…

Texto sobre fondo gris azulado: «Suele pasar. Llevas horas buscando un error en tu código y cuando comienzas a explicarlo a alguien más para que te ayude… ¡lo encuentras! :)».

«Suele pasar»

Tiene nombre: depuración con patito de goma. Al explicar el código en voz alta, línea por línea, te obligas a revisar lo que das por supuesto, y ahí suele estar el error. Funciona incluso explicándoselo a un patito de goma.

Diseño

Texto sobre fondo oscuro: «Sin requerimientos o sin diseño, la programación es el arte de agregar errores a un archivo de texto vacío», cita de Louis Srygley.

«Sin requerimientos o sin diseño la programación es el arte de agregar errores a un archivo de texto vacío»

Empezar a programar sin saber bien qué hay que construir es la forma más rápida de escribir código que habrá que tirar. Dedicar un rato a entender el problema ahorra días de correcciones.

Algunos de los Errores Más Críticos en el Desarrollo de Software

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.



2.-Neutralización inadecuada de elementos especiales usados en un comando OS (‘OS Command Injection’)

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.