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.

Pregunta Java: wait() fuera de synchronized

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

Pregunta Java · Threads

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

public class Prueba {
    public static void main(String[] args) {
        String mensaje = "Saludos";
        try {
            mensaje.wait();
        } catch (InterruptedException e) {
        }
        System.out.println("mensaje: " + mensaje);
    }
}
  1. La compilación falla.
  2. Se lanza una IllegalMonitorStateException en tiempo de ejecución.
  3. No se imprime nada, espera indefinidamente.
  4. Se imprime "mensaje: Saludos"
Ver la respuesta

Respuesta: 2.

java PruebaException in thread "main" java.lang.IllegalMonitorStateException: current thread is not owner
	at java.base/java.lang.Object.wait0(Native Method)
	at java.base/java.lang.Object.wait(Object.java:389)
	at java.base/java.lang.Object.wait(Object.java:351)
	at Prueba.main(Prueba.java:5)

wait() hace que el hilo espere hasta que otro llame a notify() o notifyAll() sobre el mismo objeto, y solo se puede llamar teniendo su monitor, es decir, dentro de un synchronized (mensaje) {…}. Aquí no hay ningún bloque synchronized, así que compila pero falla al ejecutar.

Es la hermana de otra pregunta en la que sí hay synchronized, pero sobre el objeto equivocado. Hoy rara vez se usan wait y notify directamente: java.util.concurrent tiene herramientas más seguras, como CountDownLatch o las colas bloqueantes.

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: dos hilos y una variable compartida

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

Pregunta Java · Threads

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

public class Prueba extends Thread {
    static String cadena = "hola";
    public static void main(String argv[]) {
        Prueba t = new Prueba();
        t.inicia(cadena);
        System.out.println(cadena);
    }
    public void inicia(String aux) {
        cadena = aux + " mundo ";
        start();
    }
    public void run() {
        for (int i=0; i<4; i++) { cadena = cadena + " " + i; }
    }
}
  1. imprime "hola mundo"
  2. imprime "hola mundo 0 1 2 3"
  3. El resultado es impredecible.
Ver la respuesta

Respuesta: 3.

300 ejecuciones, contadas con uniq -c300 hola mundo

inicia() cambia la cadena a "hola mundo " y arranca un hilo nuevo; el hilo principal sigue y hace el println sin esperar a que el otro termine. Los dos hilos usan la misma variable estática sin ninguna sincronización, así que nada garantiza qué parte del bucle habrá hecho el segundo hilo cuando el primero imprima, ni siquiera que vea sus cambios.

¿Y la salida? En 300 ejecuciones salió siempre hola mundo : arrancar un hilo tarda bastante más que llegar al println. Pero eso es una casualidad del momento, no una garantía, y con otra máquina o con el sistema cargado podría salir otra cosa. Precisamente por eso la respuesta correcta es la 3: que un error de concurrencia no aparezca en las pruebas no significa que no exista. Para esperar al hilo se usa t.join() antes de imprimir.

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: start() con un argumento

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

Pregunta Java · Threads

¿Cuál es el resultado de ejecutar el siguiente código?

public class Prueba extends Thread {
    public void run() {
        System.out.println("método run 1");
    }
    public void run(String s) {
        System.out.println("método run 2");
    }
    public static void main(String[] args) {
        new Prueba().start("hola");
    }
}
  1. imprime "método run 1"
  2. imprime "método run 2"
  3. imprime "hola"
  4. No compila
Ver la respuesta

Respuesta: 4.

javac Prueba.javaPrueba.java:9: error: method start in class Thread cannot be applied to given types;
        new Prueba().start("hola");
                    ^
  required: no arguments
  found:    String
  reason: actual and formal argument lists differ in length
1 error

start() no recibe argumentos, y el compilador lo rechaza. Definir un run(String s) no cambia nada: es una sobrecarga que Thread no conoce. start() arranca un hilo nuevo y la máquina virtual llama en él al run() sin parámetros.

Si se escribe new Prueba().start();, el programa imprime método run 1. Para pasarle datos a un hilo se usa un campo o el constructor, o mejor aún, un Runnable con una lambda.

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.

Lo que no es Java

Texto sobre fondo claro: «Java != javascript».
«Java != javascript»

A pesar del nombre, Java y JavaScript son lenguajes distintos, que solo comparten parte de la sintaxis. JavaScript se llamó así en 1995 por motivos de marketing, aprovechando la fama que Java tenía entonces.

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.

Una pequeña lista de los puertos mas conocidos

Estos son algunos de los puertos que quizá debas conocer o con los que debes familiarizarte cuando estás comenzando a desarrollar, algunos no se usan tanto, y desde luego se usan muchos mas.  Pero vale la pena conocer aquellos que son por decirlo de alguna manera «populares».


Puerto Descripción
1 TCP Port Service Multiplexer (TCPMUX)
5 Remote Job Entry (RJE)
7 ECHO
18 Message Send Protocol (MSP)
20 FTP — Data
21 FTP — Control
22 SSH Remote Login Protocol
23 Telnet
25 Simple Mail Transfer Protocol (SMTP)
29 MSG ICP
37 Time
42 Host Name Server (Nameserv)
43 WhoIs
49 Login Host Protocol (Login)
53 Domain Name System (DNS)
69 Trivial File Transfer Protocol (TFTP)
70 Gopher Services
79 Finger
80 HTTP
103 X.400 Standard
108 SNA Gateway Access Server
109 POP2
110 POP3
115 Simple File Transfer Protocol (SFTP)
118 SQL Services
119 Newsgroup (NNTP)
137 NetBIOS Name Service
139 NetBIOS Datagram Service
143 Interim Mail Access Protocol (IMAP)
150 NetBIOS Session Service
156 SQL Server
161 SNMP
179 Border Gateway Protocol (BGP)
190 Gateway Access Control Protocol (GACP)
194 Internet Relay Chat (IRC)
197 Directory Location Service (DLS)
389 Lightweight Directory Access Protocol (LDAP)
396 Novell Netware over IP
443 HTTPS
444 Simple Network Paging Protocol (SNPP)
445 Microsoft-DS
458 Apple QuickTime
546 DHCP Client
547 DHCP Server
563 SNEWS
569 MSN
1080 Socks

Desde luego algunos de estos puertos no se usan de forma obligatoria, los de http o https pueden cambiar, sin embargo los navegadores apuntan por default a estos puertos y si son cambiados se deben escribir explicitamente junto con la dirección IP del servicio al que se está accediendo.

10 consejos para programadores que empiezan, explicados uno a uno

En 2013 publicamos aquí diez consejos para quien empieza a programar, en una lista de una línea cada uno. Más de diez años después siguen valiendo, pero merecen una explicación: qué quieren decir en la práctica y qué ha cambiado ahora que un asistente de IA puede escribir buena parte del código.

Los diez consejos

1. Recuerda siempre lo que te enseñaron tus maestros. Los fundamentos envejecen mucho más despacio que las herramientas. Estructuras de datos, complejidad de los algoritmos, cómo funciona la memoria, qué es una transacción en una base de datos: todo eso servía hace treinta años y seguirá sirviendo dentro de otros treinta. Un framework de moda dura unos pocos años; saber por qué una búsqueda en una lista es más lenta que en un diccionario vale para siempre.

2. No emplees nuevas tecnologías solo porque son nuevas. Cada herramienta nueva trae su curva de aprendizaje, sus errores aún sin descubrir y el riesgo de que deje de mantenerse. En un proyecto real, elige lo que el equipo conoce y lo que tiene una comunidad grande detrás, salvo que la novedad resuelva un problema que de verdad tienes.

3. Pero sí debes probar nuevas técnicas y tecnologías. No es una contradicción con el anterior: el sitio para probar es un proyecto personal o una prueba aparte, no la aplicación de producción. Aprender un lenguaje con otra forma de pensar, como Rust si vienes de Python o Go si vienes de Java, te hace mejor programador también en tu lenguaje de siempre.

4. Lee mucho. Libros, documentación oficial y, sobre todo, código de otros. Leer el código de un proyecto de código abierto bien mantenido enseña más sobre cómo se organiza un programa grande que cualquier tutorial. Y la documentación oficial suele ser mejor y estar más al día que la primera respuesta que aparece en un buscador.

5. No acumules errores ni malas prácticas. Cada atajo que dejas «para luego» es una deuda que se paga con intereses: un nombre de variable confuso, una función de doscientas líneas, un error que se ignora en lugar de tratarse. Arreglarlo cuando lo ves cuesta minutos; arreglarlo meses después, cuando hay código que depende de ello, cuesta días.

6. Comparte tu conocimiento. Explicar algo a otra persona es la mejor forma de descubrir si lo entiendes. Escribir un artículo, responder una pregunta en un foro, documentar bien tu proyecto o contribuir a uno de código abierto te obliga a ordenar lo que sabes, y de paso te da visibilidad.

7. Ten en cuenta la concurrencia. Casi todos los programas actuales hacen varias cosas a la vez: atienden a varios usuarios, esperan a la red o reparten el trabajo entre los núcleos del procesador. Los errores de concurrencia son de los más difíciles de encontrar, porque aparecen solo a veces. Aprende pronto qué es una condición de carrera y cómo se evitan. Tenemos ejemplos en Python, Go y Rust, y uno muy claro en String, StringBuffer y StringBuilder, donde dos hilos pierden datos al escribir a la vez.

8. Prueba tu código. No basta con que funcione la vez que lo ejecutas a mano. Las pruebas automáticas te avisan cuando un cambio rompe algo que antes funcionaba, y te dan confianza para mejorar el código sin miedo. Empieza por lo sencillo: una prueba por cada función importante y otra por cada error que corrijas, para que no vuelva.

9. Cuídate. Programar es un trabajo mental y se hace mal cansado. Dormir, moverse, descansar la vista y desconectar de vez en cuando no es tiempo perdido: muchos problemas que parecían imposibles a medianoche se resuelven en diez minutos a la mañana siguiente.

10. Diviértete. La programación tiene mucho de oficio, y los oficios se aprenden con horas de práctica. Esas horas son mucho más llevaderas si disfrutas con lo que haces. Busca proyectos que te interesen de verdad, aunque sean pequeños: un juego, una herramienta para tu día a día o un programa que automatice algo aburrido.

¿Y con la IA? Los diez siguen en pie, y algunos pesan más que antes. Si un asistente escribe el código, tú tienes que entenderlo, revisarlo y probarlo: los fundamentos (1), las pruebas (8) y no acumular malas prácticas (5) son lo que te permite hacerlo. Escribimos sobre ello en aprender a programar cuando la IA ya escribe el código: diez consejos para quien empieza.

Por dónde empezar

Si estás dando tus primeros pasos, elige un lenguaje y quédate con él el tiempo suficiente para hacer algo útil. En decodigo.com tienes cursos completos y gratuitos de Python, Java, Go y Rust, y una guía de Git para guardar tu trabajo desde el primer día.