Módulo 03: Variables, tipos de datos y operadores

Objetivos del módulo

  • Entender qué es una variable y cómo se guarda en memoria
  • Declarar, inicializar y asignar variables
  • Elegir buenos identificadores siguiendo las convenciones de Java y conocer las palabras reservadas
  • Conocer los ocho tipos primitivos (tamaño, rango y uso) y escribir correctamente sus literales
  • Dar los primeros pasos con String, un tipo de referencia
  • Declarar constantes con final y usar la inferencia de tipos con var
  • Realizar conversiones de tipo: ampliación automática y casting
  • Comprender el desbordamiento y las diferencias entre la aritmética entera y la real
  • Dominar los operadores de Java, su precedencia y la evaluación de expresiones
  • Usar la clase Math
  • Reconocer y corregir los errores frecuentes con variables y tipos

1. ¿Qué es una variable?

En el módulo 02 usaste variables en el pseudocódigo (suma <- 0, Leer edad). Ahora vamos a ver qué son exactamente en Java.

📘 Concepto: Una variable es un espacio de la memoria RAM al que le damos un nombre y en el que podemos guardar un valor de un tipo determinado. El valor puede cambiar durante la ejecución del programa (por eso se llama variable).

La mejor analogía es la de una caja con etiqueta:

  • La etiqueta es el nombre de la variable (edad).
  • El tamaño y la forma de la caja dependen del tipo (int es una caja para números enteros de 4 bytes).
  • Lo que hay dentro es el valor (18).

Variables como cajas en memoria con nombre, tipo y valor, y una variable String que guarda una referencia a un objeto

Figura 1. Cada variable es una caja con nombre y tipo. Los tipos primitivos guardan el valor; un String guarda una referencia a un objeto.

⚠️ Importante: Java es un lenguaje de tipado estático y fuerte: cada variable tiene un tipo fijo que se decide al escribir el programa y no puede cambiar. Si edad es un int, nunca podrás guardar en ella un texto. El compilador lo comprueba y, si te equivocas, no te deja compilar. Esto evita muchísimos errores.


2. Declaración, inicialización y asignación

Operación Sintaxis Ejemplo Qué hace
Declaración tipo nombre; int edad; Reserva la caja e indica su tipo
Asignación nombre = valor; edad = 18; Guarda un valor (sustituye al anterior)
Inicialización tipo nombre = valor; int edad = 18; Declaración + primera asignación a la vez
public class Variables {
    public static void main(String[] args) {
        int edad;              // declaración: reservamos una caja para un int
        edad = 18;             // asignación: guardamos un valor
        System.out.println("Edad: " + edad);

        edad = edad + 1;       // el valor puede cambiar (por eso es "variable")
        System.out.println("Un año después: " + edad);

        double precio = 19.99; // declaración + inicialización en una línea
        int unidades = 3;
        double total = precio * unidades;
        System.out.println("Total: " + total);

        int a = 1, b = 2, c = 3;   // varias variables del mismo tipo (poco recomendable)
        System.out.println(a + b + c);
    }
}

Salida:

Edad: 18
Un año después: 19
Total: 59.97
6

📘 Concepto: El signo = en Java no es una igualdad matemática: es el operador de asignación. edad = edad + 1 se lee “calcula edad + 1 y guarda el resultado en edad”. Primero se evalúa todo lo que hay a la derecha y después se guarda en la variable de la izquierda.

2.1. Una variable debe tener valor antes de usarse

Las variables locales (las que declaras dentro de un método, como main) no tienen valor por defecto. Si intentas usar una sin haberle asignado nada, el compilador protesta:

public class SinInicializar {
    public static void main(String[] args) {
        int total;
        System.out.println(total);
    }
}
SinInicializar.java:4: error: variable total might not have been initialized
        System.out.println(total);
                           ^
1 error

2.2. No se puede declarar dos veces la misma variable

public class Redeclarada {
    public static void main(String[] args) {
        int edad = 18;
        int edad = 19;
    }
}
Redeclarada.java:4: error: variable edad is already defined in method main(String[])
        int edad = 19;
            ^
1 error

Para cambiar el valor, escribe solo edad = 19;: el tipo se escribe una única vez, al declarar. Fíjate también en que los mensajes de error no siempre dicen exactamente qué pasa; a veces el compilador simplemente “no entiende la frase” (sección 3.1).

2.3. Ámbito de una variable

Una variable solo existe dentro del bloque (las llaves { }) donde se declara, desde la línea de su declaración hasta la llave de cierre. Es su ámbito (scope). Por ahora todas tus variables viven dentro de main; en los módulos 05-07 verás variables declaradas dentro de un if, de un bucle o de otro método, que no existen fuera de él.


3. Identificadores: cómo nombrar las cosas

Un identificador es el nombre que das a una variable, constante, método o clase.

3.1. Reglas obligatorias

  1. Puede contener letras, dígitos, el guion bajo _ y el símbolo $.
  2. No puede empezar por un dígito.
  3. No puede contener espacios ni símbolos como -, +, ., !, @, #.
  4. No puede ser una palabra reservada (int, class, if…).
  5. Distingue mayúsculas de minúsculas: edad, Edad y EDAD son tres identificadores distintos.
Identificador ¿Válido? Motivo
edad Sí  
precioTotal Sí  
nota1 Sí Los dígitos se permiten si no van al principio
_contador Sí Pero no es la convención en Java
$precio Sí Pero $ se reserva para código generado automáticamente: evítalo
1nota No Empieza por un dígito
precio total No Contiene un espacio
precio-total No El - es el operador resta
class No Palabra reservada
año Sí Pero evita la ñ y las tildes (módulo 01)

Errores reales del compilador al saltarse las reglas:

        int 2numero = 5;
Identificador.java:3: error: not a statement
        int 2numero = 5;
        ^
Identificador.java:3: error: ';' expected
        int 2numero = 5;
           ^
2 errors
        int class = 3;
Reservada.java:3: error: not a statement
        int class = 3;
        ^
Reservada.java:3: error: ';' expected
        int class = 3;
           ^
Reservada.java:3: error: <identifier> expected
        int class = 3;
                 ^
3 errors

3.2. Convenciones de nombres

Además de las reglas, la comunidad Java sigue unas convenciones que debes respetar siempre:

Elemento Convención Ejemplos
Variables y métodos camelCase: empieza en minúscula y cada palabra siguiente en mayúscula edad, precioConIva, numeroDeAlumnos, calcularMedia()
Clases PascalCase: cada palabra empieza en mayúscula Alumno, CuentaBancaria
Constantes UPPER_SNAKE_CASE: mayúsculas separadas por guion bajo IVA, DIAS_SEMANA, NOTA_MINIMA
Paquetes Minúsculas es.curso.banco

💡 Consejo: Un buen nombre explica qué contiene la variable. Compara double x = a * b * 0.21; con double cuotaIva = precio * unidades * IVA;. Evita abreviaturas crípticas (nmAl), nombres de una letra (salvo contadores de bucles como i, j) y nombres genéricos (dato, valor, aux2). Los nombres pueden ser largos: el IDE los autocompleta.


4. Palabras reservadas

Las palabras reservadas (keywords) tienen un significado especial en Java y no pueden usarse como identificadores. Java 25 tiene estas:

         
abstract assert boolean break byte
case catch char class const
continue default do double else
enum extends final finally float
for goto if implements import
instanceof int interface long native
new package private protected public
return short static strictfp super
switch synchronized this throw throws
transient try void volatile while
_ (guion bajo)        

Algunas curiosidades:

  • const y goto están reservadas pero no se usan en Java.
  • true, false y null no son técnicamente palabras reservadas, sino literales, pero tampoco pueden usarse como nombres.
  • Hay palabras reservadas contextuales, que solo son especiales en ciertos lugares y sí pueden usarse como nombres en otros: var, record, sealed, non-sealed, permits, yield, when, module, exports, requires… (aunque no es buena idea llamar var a una variable).

5. Tipos de datos primitivos

Java tiene ocho tipos primitivos, los “ladrillos” con los que se construye todo lo demás. Se llaman primitivos porque guardan directamente el valor y no son objetos.

Los ocho tipos primitivos de Java representados por su tamaño en bytes

Figura 2. Tamaño en bytes de los tipos primitivos.

Tipo Tamaño Rango de valores Valor por defecto* Ejemplo de literal
byte 1 byte (8 bits) −128 a 127 0 (byte) 100
short 2 bytes (16 bits) −32 768 a 32 767 0 (short) 1000
int 4 bytes (32 bits) −2 147 483 648 a 2 147 483 647 0 42, -7, 1_000_000
long 8 bytes (64 bits) −9,22 × 10¹⁸ a 9,22 × 10¹⁸ 0L 42L, 9_000_000_000L
float 4 bytes (32 bits) ±3,4 × 10³⁸, unas 7 cifras significativas 0.0f 3.14f
double 8 bytes (64 bits) ±1,8 × 10³⁰⁸, unas 15-16 cifras significativas 0.0 3.14, 1.5e3
char 2 bytes (16 bits) Un carácter Unicode (códigos 0 a 65 535) '\u0000' 'A', 'ñ', '\n'
boolean No especificado true o false false true

* El valor por defecto solo se aplica a los atributos de una clase (módulo 10) y a los elementos de un array (módulo 08). Las variables locales no tienen valor por defecto.

¿Cuál uso?

  • Para números enteros, usa int salvo que necesites valores mayores de 2 000 millones (entonces, long). byte y short solo se usan para ahorrar memoria en grandes cantidades de datos o para leer ficheros binarios.
  • Para números con decimales, usa double. float solo se usa en casos muy concretos (gráficos, dispositivos con poca memoria).
  • Para un único carácter, char. Para textos, String (sección 7).
  • Para sí/no, verdadero/falso, boolean.

Puedes consultar los límites exactos con las constantes de las clases envoltorio (Integer, Double…), que verás en el módulo 11:

public class Rangos {
    public static void main(String[] args) {
        System.out.println("byte:   " + Byte.MIN_VALUE + " a " + Byte.MAX_VALUE);
        System.out.println("short:  " + Short.MIN_VALUE + " a " + Short.MAX_VALUE);
        System.out.println("int:    " + Integer.MIN_VALUE + " a " + Integer.MAX_VALUE);
        System.out.println("long:   " + Long.MIN_VALUE + " a " + Long.MAX_VALUE);
        System.out.println("float:  " + Float.MIN_VALUE + " a " + Float.MAX_VALUE);
        System.out.println("double: " + Double.MIN_VALUE + " a " + Double.MAX_VALUE);
        System.out.println("char:   " + (int) Character.MIN_VALUE + " a " + (int) Character.MAX_VALUE);
        System.out.println("Bits de un int: " + Integer.SIZE + ", de un long: " + Long.SIZE);
    }
}
byte:   -128 a 127
short:  -32768 a 32767
int:    -2147483648 a 2147483647
long:   -9223372036854775808 a 9223372036854775807
float:  1.4E-45 a 3.4028235E38
double: 4.9E-324 a 1.7976931348623157E308
char:   0 a 65535
Bits de un int: 32, de un long: 64

(En float y double, MIN_VALUE es el número positivo más pequeño que se puede representar, no el más negativo. 3.4028235E38 significa 3,4028235 × 10³⁸.)

📘 Concepto: ¿Por qué un byte va de −128 a 127? Con 8 bits hay 2⁸ = 256 combinaciones posibles. La mitad se usan para los negativos (−128 a −1) y la otra mitad para el cero y los positivos (0 a 127). Con 32 bits (int) hay 2³² ≈ 4 295 millones de combinaciones: de −2 147 483 648 a 2 147 483 647.


6. Literales

Un literal es un valor escrito directamente en el código: 42, 3.14, 'A', true, "Hola".

6.1. Literales enteros

Forma Ejemplo Valor Comentario
Decimal 255 255 La habitual
Hexadecimal (base 16) 0xFF 255 Prefijo 0x. Habitual en colores (0xFF7043)
Binario (base 2) 0b1111_1111 255 Prefijo 0b (Java 7+)
Octal (base 8) 0377 255 Prefijo 0. ¡Cuidado! 010 vale 8, no 10
Con guiones bajos 1_000_000 1 000 000 Solo para leer mejor (Java 7+)
long 9_000_000_000L 9 000 000 000 Sufijo L (usa la mayúscula: la l minúscula se confunde con un 1)

Un literal entero sin sufijo es siempre int. Si es demasiado grande para un int, el compilador da error aunque lo vayas a guardar en un long:

        long distancia = 9460730472580800;
EnteroGrande.java:3: error: integer number too large
        long distancia = 9460730472580800;
                         ^
1 error

Solución: 9460730472580800L.

6.2. Literales reales

Forma Ejemplo Tipo
Con punto decimal 3.14, 0.5, .5, 2. double
Notación científica 1.5e6 (1,5 × 10⁶), 2.5E-3 (2,5 × 10⁻³) double
Sufijo f 3.14f float
Sufijo d (opcional) 3.14d double

⚠️ Importante: En el código Java el separador decimal es siempre el punto: 3.14, nunca 3,14. La coma separa elementos (int a = 1, b = 2;). Otra cosa es cómo se muestran o leen los números con printf y Scanner en un ordenador configurado en español (módulo 04).

Un literal real sin sufijo es double, así que no puedes guardarlo directamente en un float:

        float precio = 9.99;
FloatSinF.java:3: error: incompatible types: possible lossy conversion from double to float
        float precio = 9.99;
                       ^
1 error

Solución: float precio = 9.99f; (o, mejor, usar double).

6.3. Literales de carácter y Unicode

Un char se escribe entre comillas simples y contiene exactamente un carácter. Internamente, Java guarda el código Unicode del carácter, un número entre 0 y 65 535.

Literal Significado
'A', 'z', '7', 'ñ', ' ' Un carácter (el espacio también es un carácter)
'\n' Salto de línea
'\t' Tabulador
'\'' Comilla simple
'\\' Barra invertida
'ñ' Carácter Unicode por su código hexadecimal (aquí, ñ)

Como un char es en el fondo un número, se puede operar con él:

public class Caracteres {
    public static void main(String[] args) {
        char c = 'A';
        System.out.println(c);             // A
        System.out.println((int) c);       // su código: 65
        System.out.println(c + 1);         // char + int = int: 66
        System.out.println((char) (c + 1)); // B
        char d = 'a';
        d++;                               // los char se pueden incrementar
        System.out.println(d);             // b
        System.out.println('7' - '0');     // truco: dígito a número = 7
    }
}
A
65
66
B
b
7

💡 Consejo: Las letras 'A' a 'Z' tienen códigos consecutivos (65 a 90), igual que 'a' a 'z' (97 a 122) y los dígitos '0' a '9' (48 a 57). Por eso '7' - '0' da 7. Lo usarás mucho en el módulo 09.

6.4. Literales numéricos en otras bases

println muestra siempre los números en decimal, aunque los escribas en otra base: System.out.println(0xFF + " " + 0b1111_1111 + " " + 0377) muestra 255 255 255. El literal es solo una forma de escribir el valor en el código; el valor guardado es el mismo.


7. String: un primer contacto con los tipos de referencia

Para guardar textos se usa String (con S mayúscula). No es un tipo primitivo, sino una clase de la biblioteca estándar: una variable String no guarda el texto, sino una referencia (la dirección) a un objeto que contiene el texto (figura 1). En el módulo 09 lo estudiarás a fondo; de momento, lo esencial:

public class Cadenas {
    public static void main(String[] args) {
        String nombre = "Ana";
        String apellido = "López";
        String completo = nombre + " " + apellido;   // concatenación
        System.out.println(completo);
        System.out.println("Longitud: " + completo.length());
        System.out.println("En mayúsculas: " + completo.toUpperCase());

        int edad = 20;
        System.out.println(nombre + " tiene " + edad + " años");
        System.out.println("1 + 2 = " + 1 + 2);       // ¡cuidado!
        System.out.println("1 + 2 = " + (1 + 2));
        System.out.println(1 + 2 + " es la suma");    // de izquierda a derecha

        String vacia = "";
        System.out.println("Vacía tiene " + vacia.length() + " caracteres");
    }
}
Ana López
Longitud: 9
En mayúsculas: ANA LÓPEZ
Ana tiene 20 años
1 + 2 = 12
1 + 2 = 3
3 es la suma
Vacía tiene 0 caracteres
  • El operador + con un String concatena (une textos). Si uno de los operandos es un número, se convierte a texto.
  • + se evalúa de izquierda a derecha: en "1 + 2 = " + 1 + 2, primero se une el texto con el 1 ("1 + 2 = 1") y luego con el 2. En 1 + 2 + " es la suma", primero se suman los números (3) y luego se concatena.
  • Los objetos String tienen métodos que se llaman con un punto: length(), toUpperCase()…
  Tipos primitivos Tipos de referencia
Ejemplos int, double, char, boolean String, Scanner, arrays, tus clases
Nombre En minúsculas Empiezan por mayúscula (convención de clases)
Qué guarda la variable El valor Una referencia a un objeto
¿Tiene métodos? No Sí: texto.length()
Valor “vacío” No existe null (no apunta a ningún objeto)

⚠️ Importante: Para comparar dos String no uses ==, sino texto1.equals(texto2). El motivo (== compara referencias, no contenidos) lo entenderás en el módulo 09.


8. Constantes con final

Una constante es una variable cuyo valor no puede cambiar una vez asignado. Se declara con la palabra final y, por convención, su nombre va en mayúsculas.

public class Constantes {
    public static void main(String[] args) {
        final double IVA = 0.21;
        final int DIAS_SEMANA = 7;

        double precio = 100;
        double conIva = precio * (1 + IVA);
        System.out.println("Precio con IVA: " + conIva);
        System.out.println("Horas en una semana: " + DIAS_SEMANA * 24);

        final int MAXIMO;      // se puede declarar sin valor...
        MAXIMO = 10;           // ...pero solo se puede asignar una vez
        System.out.println("Máximo: " + MAXIMO);
    }
}
Precio con IVA: 121.0
Horas en una semana: 168
Máximo: 10

Si intentas cambiarla:

        final double IVA = 0.21;
        IVA = 0.10;
FinalReasignada.java:4: error: cannot assign a value to final variable IVA
        IVA = 0.10;
        ^
1 error

¿Por qué usar constantes?

  • Legibilidad: precio * IVA se entiende; precio * 0.21 obliga a adivinar qué es 0.21.
  • Mantenimiento: si el IVA cambia, se modifica en un solo sitio.
  • Seguridad: el compilador impide modificarla por error.

💡 Consejo: Evita los números mágicos (valores sueltos sin explicación en mitad del código). Cualquier número que tenga un significado (el IVA, la nota mínima para aprobar, el número de alumnos por grupo) debería ser una constante con nombre. En el módulo 11 verás las constantes de clase con static final, como Math.PI o Integer.MAX_VALUE.


9. Inferencia de tipos con var

Desde Java 10 puedes declarar variables locales con var y dejar que el compilador deduzca el tipo a partir del valor inicial:

public class Inferencia {
    public static void main(String[] args) {
        var edad = 18;             // int
        var precio = 9.95;         // double
        var inicial = 'J';         // char
        var nombre = "Java";       // String
        var grande = 10L;          // long
        var mitad = 7 / 2;         // int (¡la expresión es int!)

        System.out.println(edad + " " + precio + " " + inicial + " " + nombre + " " + grande + " " + mitad);
    }
}
18 9.95 J Java 10 3

⚠️ Importante: var no convierte Java en un lenguaje de tipado dinámico. La variable sigue teniendo un tipo fijo, solo que no lo escribes tú. Después de var edad = 18;, edad es un int para siempre y no podrás guardar en ella un texto.

Reglas de var:

  • Solo para variables locales (dentro de métodos), no para atributos ni parámetros.
  • Hay que inicializarla en la misma línea, para que el compilador pueda deducir el tipo.
  • No se puede inicializar con null (no hay tipo que deducir).
        var x;
        var y = null;
VarSinValor.java:3: error: cannot infer type for local variable x
        var x;
            ^
  (cannot use 'var' on variable without initializer)
VarSinValor.java:4: error: cannot infer type for local variable y
        var y = null;
            ^
  (variable initializer is 'null')
2 errors

💡 Consejo: Mientras aprendes, escribe los tipos explícitamente (int, double, String): te obliga a pensar qué tipo necesitas y hace el código más claro para quien empieza. var es útil cuando el tipo es largo y evidente, como var teclado = new Scanner(System.in);.


10. Conversiones de tipo

A menudo hay que pasar un valor de un tipo a otro: un int a double para hacer una división con decimales, un double a int para quedarse con la parte entera…

Jerarquía de conversiones entre tipos primitivos: byte, short, int, long, float y double, con char hacia int

Figura 3. Conversiones de ampliación (automáticas) y de estrechamiento (con casting).

10.1. Conversión de ampliación (automática)

Cuando el tipo de destino es más grande (está a la derecha en la figura), Java convierte automáticamente, porque no se pierde información: es como pasar el contenido de una caja pequeña a una grande.

byte → short → int → long → float → double
               char ↗

10.2. Conversión de estrechamiento: el casting

Cuando el destino es más pequeño, puede perderse información, así que Java exige que lo indiques explícitamente con un casting: el tipo de destino entre paréntesis delante del valor.

        double d = 3.99;
        int n = d;
Perdida.java:4: error: incompatible types: possible lossy conversion from double to int
        int n = d;
                ^
1 error

“Posible conversión con pérdida de double a int”. Con casting compila, y tú asumes la responsabilidad:

public class Conversiones {
    public static void main(String[] args) {
        // Ampliación (automática)
        int entero = 42;
        long largo = entero;
        double real = entero;
        System.out.println(largo + " " + real);

        char letra = 'A';
        int codigo = letra;          // char -> int
        System.out.println(codigo);

        // Estrechamiento (casting explícito)
        double d = 3.99;
        int truncado = (int) d;          // se pierden los decimales (no redondea)
        System.out.println((int) 3.99 + " " + (int) -3.99 + " " + truncado);

        int grandote = 200;
        byte pequeno = (byte) grandote;  // 200 no cabe en un byte
        System.out.println(pequeno);

        long muyGrande = 123_456_789_012L;
        int recortado = (int) muyGrande;
        System.out.println(recortado);

        System.out.println((char) 66);   // int -> char: B

        // long -> float: automático, pero con pérdida de precisión
        long exacto = 123_456_789L;
        float aproximado = exacto;
        System.out.println(exacto + " -> " + aproximado + " -> " + (long) aproximado);
    }
}
42 42.0
65
3 -3 3
-56
-1097262572
B
123456789 -> 1.2345679E8 -> 123456792

Analiza cada resultado:

  • (int) 3.99 da 3 y (int) -3.99 da −3: el casting de real a entero trunca (descarta los decimales), no redondea. Para redondear usa Math.round (sección 14).
  • (byte) 200 da −56: 200 no cabe en un byte (máximo 127). Java se queda con los 8 bits de menor peso y el resultado “da la vuelta”: 200 − 256 = −56.
  • (int) 123456789012L da un número sin sentido aparente por el mismo motivo: se pierden los 32 bits superiores.
  • long → float es automático, pero un float solo tiene unas 7 cifras significativas: 123 456 789 se convierte en 123 456 792.

10.3. Promoción en las expresiones

Cuando operas con tipos distintos, Java promociona los operandos al tipo más grande antes de calcular. Y hay una regla que sorprende: las operaciones con byte, short y char se hacen siempre en int.

public class Promocion {
    public static void main(String[] args) {
        byte a = 10;
        byte b = 20;
        int suma = a + b;            // byte + byte se calcula como int
        byte suma2 = (byte) (a + b); // para guardarlo en byte hace falta casting
        a += 5;                      // la asignación compuesta incluye el casting
        System.out.println(suma + " " + suma2 + " " + a);

        int i = 5;
        double r = i * 2.0;          // int * double -> double
        long l = i * 1_000_000_000L; // int * long -> long
        System.out.println(r + " " + l);
    }
}
30 30 15
10.0 5000000000

Si intentas guardar directamente la suma de dos byte en un byte:

        byte a = 10, b = 20;
        byte c = a + b;
SumaBytes.java:4: error: incompatible types: possible lossy conversion from int to byte
        byte c = a + b;
                   ^
1 error
Operandos Tipo del resultado
byte, short, char o int entre sí int
Alguno es long (y ninguno real) long
Alguno es float (y ninguno double) float
Alguno es double double
Alguno es String (con +) String

10.4. Conversiones que no existen

Entre boolean y los tipos numéricos no hay conversión posible (en Java, true no es 1). Tampoco se puede convertir un String en un número con un casting:

        int n = "42";
        boolean b = 1;
TiposIncompatibles.java:3: error: incompatible types: String cannot be converted to int
        int n = "42";
                ^
TiposIncompatibles.java:4: error: incompatible types: int cannot be converted to boolean
        boolean b = 1;
                    ^
2 errors

Para convertir texto en número se usan métodos de las clases envoltorio (los verás a fondo en el módulo 11):

public class Parseo {
    public static void main(String[] args) {
        String texto = "42";
        int numero = Integer.parseInt(texto);       // String -> int
        double real = Double.parseDouble("3.75");   // String -> double
        System.out.println(numero + 1);
        System.out.println(real * 2);

        String deNuevo = String.valueOf(numero);    // int -> String
        String otraForma = "" + real;               // truco: concatenar con ""
        System.out.println(deNuevo + otraForma);

        int mal = Integer.parseInt("cuarenta");     // ¡error en ejecución!
        System.out.println(mal);
    }
}
43
7.5
423.75
Exception in thread "main" java.lang.NumberFormatException: For input string: "cuarenta"
	at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
	at java.base/java.lang.Integer.parseInt(Integer.java:565)
	at java.base/java.lang.Integer.parseInt(Integer.java:662)
	at Parseo.main(Parseo.java:13)

El programa compila, pero al ejecutar parseInt("cuarenta") se produce un error de ejecución: una excepción NumberFormatException. La última línea del mensaje indica dónde: Parseo.java:13. Aprenderás a capturar estas excepciones en el módulo 15.


11. Desbordamiento

¿Qué pasa si un cálculo produce un número que no cabe en su tipo? En Java, los enteros dan la vuelta sin ningún aviso, como un cuentakilómetros que pasa de 99999 a 00000.

Rueda de valores de un byte en la que 127 + 1 da -128

Figura 4. El desbordamiento de enteros: al pasar del máximo se vuelve al mínimo.

public class Desbordamiento {
    public static void main(String[] args) {
        int max = Integer.MAX_VALUE;
        System.out.println("Máximo int:      " + max);
        System.out.println("Máximo + 1:      " + (max + 1));

        byte b = 127;
        b++;
        System.out.println("byte 127 + 1:    " + b);

        int poblacionMundial = 2_000_000_000;
        int doble = poblacionMundial * 2;              // desborda
        long dobleBien = poblacionMundial * 2L;        // se calcula en long
        System.out.println("Mal:  " + doble);
        System.out.println("Bien: " + dobleBien);

        System.out.println(Math.addExact(max, 1));     // lanza una excepción
    }
}
Máximo int:      2147483647
Máximo + 1:      -2147483648
byte 127 + 1:    -128
Mal:  -294967296
Bien: 4000000000
Exception in thread "main" java.lang.ArithmeticException: integer overflow
	at java.base/java.lang.Math.addExact(Math.java:915)
	at Desbordamiento.main(Desbordamiento.java:17)

⚠️ Importante: Fíjate en el caso poblacionMundial * 2: aunque guardaras el resultado en un long, el cálculo se hace en int (porque los dos operandos son int) y ya se ha desbordado antes de guardarse. La solución es que al menos un operando sea long: poblacionMundial * 2L.

Los métodos Math.addExact, Math.subtractExact y Math.multiplyExact hacen la operación y, si el resultado no cabe, lanzan una excepción en lugar de dar un valor erróneo en silencio. Úsalos cuando el desbordamiento sea un riesgo real (cálculos con dinero, contadores muy grandes).


12. Aritmética de enteros frente a reales

12.1. La división entera

Esta es probablemente la trampa número uno de los principiantes en Java:

⚠️ Importante: Si los dos operandos de una división son enteros, el resultado es entero: se descartan los decimales. 7 / 2 vale 3, no 3.5.

public class Division {
    public static void main(String[] args) {
        System.out.println(7 / 2);              // entero / entero = entero
        System.out.println(7.0 / 2);            // si uno es real, resultado real
        System.out.println(7 / 2.0);
        System.out.println((double) 7 / 2);     // casting ANTES de dividir
        System.out.println((double) (7 / 2));   // casting DESPUÉS: ya es tarde
        System.out.println(7 % 2);              // resto
        System.out.println(-7 / 2 + " " + -7 % 2);  // el resto lleva el signo del dividendo
        System.out.println(7.5 % 2);            // % también funciona con reales

        int nota1 = 7, nota2 = 8;
        double mediaMal = (nota1 + nota2) / 2;  // 15 / 2 = 7 (entera) -> 7.0
        double mediaBien = (nota1 + nota2) / 2.0;
        System.out.println(mediaMal + " " + mediaBien);
    }
}
3
3.5
3.5
3.5
3.0
1
-3 -1
1.5
7.0 7.5

El caso de mediaMal es un error lógico clásico: el programa compila y funciona, pero la media de 7 y 8 sale 7.0. Guardar el resultado en un double no sirve de nada, porque la división ya se hizo en enteros. Solución: que al menos un operando sea real (2.0) o hacer casting de un operando antes de dividir.

12.2. El operador módulo %

a % b devuelve el resto de la división entera de a entre b. Es mucho más útil de lo que parece:

Uso Expresión Ejemplo
¿Es par? n % 2 == 0 10 % 2 = 0 → par
¿Es múltiplo de k? n % k == 0 15 % 5 = 0 → sí
Último dígito n % 10 472 % 10 = 2
Quitar el último dígito n / 10 472 / 10 = 47
Ciclos (horas, días de la semana) (hora + 5) % 24 (22 + 5) % 24 = 3
Segundos sobrantes total % 60 3725 % 60 = 5

12.3. División entre cero

public class DivisionCero {
    public static void main(String[] args) {
        System.out.println(1.0 / 0);     // Infinity
        System.out.println(-1.0 / 0);    // -Infinity
        System.out.println(0.0 / 0);     // NaN (Not a Number)
        int cero = 0;
        System.out.println(10 / cero);   // con enteros: excepción
    }
}
Infinity
-Infinity
NaN
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at DivisionCero.main(DivisionCero.java:7)

Con reales, dividir entre cero no es un error: da Infinity o NaN (“no es un número”), valores especiales definidos por el estándar IEEE 754. Con enteros, se produce una excepción ArithmeticException y el programa se detiene.

12.4. La precisión de los números reales

Los tipos float y double guardan los números en binario con un número limitado de bits. Igual que 1/3 no se puede escribir exactamente en decimal (0,3333…), muchos números decimales sencillos, como 0,1, no tienen representación exacta en binario. El resultado son pequeños errores de redondeo:

import java.math.BigDecimal;

public class Precision {
    public static void main(String[] args) {
        System.out.println(0.1 + 0.2);
        System.out.println(0.1 + 0.2 == 0.3);
        System.out.println(1.1 * 3);
        System.out.println(6 * 0.6 + 9 * 0.4);

        // Comparar reales con un margen de tolerancia
        double x = 0.1 + 0.2;
        System.out.println(Math.abs(x - 0.3) < 1e-9);

        // float tiene menos precisión que double
        float f = 0.1f + 0.2f;
        System.out.println(f);
        System.out.println(1.0f / 3 + " vs " + 1.0 / 3);

        // BigDecimal: aritmética decimal exacta (para dinero)
        BigDecimal a = new BigDecimal("0.1");
        BigDecimal b = new BigDecimal("0.2");
        System.out.println(a.add(b));
    }
}
0.30000000000000004
false
3.3000000000000003
7.199999999999999
true
0.3
0.33333334 vs 0.3333333333333333
0.3
  • 0.1 + 0.2 no da exactamente 0.3, así que 0.1 + 0.2 == 0.3 es falso. Ahora entiendes el 7.199999999999999 de la nota final del módulo 02.
  • Nunca compares reales con ==. Comprueba si la diferencia es muy pequeña: Math.abs(a - b) < 1e-9.
  • Con float el resultado aparenta ser exacto (0.3) solo porque, al tener menos precisión, Java muestra menos cifras; compara 0.33333334 con 0.3333333333333333.
  • Para dinero u otros cálculos que exigen exactitud decimal, los programas profesionales usan la clase BigDecimal (creada a partir de un String), que calcula en decimal sin errores de redondeo. Es más lenta y engorrosa; la verás en el Curso de Java Avanzado. Otra alternativa sencilla es trabajar en céntimos con long.
  • Para mostrar los reales de forma amigable, se redondean al imprimirlos: printf("%.2f", x) (módulo 04) o Math.round(x * 100) / 100.0 (sección 14).

13. Operadores

13.1. Operadores aritméticos

Operador Operación Ejemplo Resultado
+ Suma (o concatenación con String) 7 + 2 9
- Resta (o cambio de signo: -x) 7 - 2 5
* Multiplicación 7 * 2 14
/ División (entera si ambos son enteros) 7 / 2 / 7.0 / 2 3 / 3.5
% Resto (módulo) 7 % 2 1

Java no tiene operador de potencia: 2 ^ 3 no es 8 (es un operador bit a bit, sección 13.7). Usa Math.pow(2, 3).

13.2. Asignación y asignación compuesta

Operador Equivale a Ejemplo con x = 10 Resultado
= Asignación x = 5 5
+= x = x + y x += 3 13
-= x = x - y x -= 3 7
*= x = x * y x *= 3 30
/= x = x / y x /= 3 3
%= x = x % y x %= 3 1
int saldo = 100;
saldo += 50;            // 150
saldo -= 30;            // 120
saldo *= 2;             // 240
String mensaje = "Hola";
mensaje += ", mundo";   // "Hola, mundo" (también con String)

13.3. Incremento y decremento

x++ suma 1 a x y x-- le resta 1. Pueden escribirse antes o después de la variable, y la diferencia importa cuando se usan dentro de una expresión:

Forma Nombre Qué hace Valor de la expresión
x++ Postincremento Incrementa x El valor antiguo de x
++x Preincremento Incrementa x El valor nuevo de x
x-- Postdecremento Decrementa x El valor antiguo
--x Predecremento Decrementa x El valor nuevo
public class Incremento {
    public static void main(String[] args) {
        int a = 5;
        a++;                       // a = a + 1
        System.out.println(a);     // 6
        a--;                       // a = a - 1
        System.out.println(a);     // 5

        int x = 5;
        int y = x++;               // postincremento: primero usa, luego incrementa
        System.out.println("x = " + x + ", y = " + y);

        int m = 5;
        int n = ++m;               // preincremento: primero incrementa, luego usa
        System.out.println("m = " + m + ", n = " + n);

        int k = 10;
        System.out.println(k++ + " " + k);   // 10 11
        System.out.println(++k + " " + k);   // 12 12
    }
}
6
5
x = 6, y = 5
m = 6, n = 6
10 11
12 12

💡 Consejo: Usa x++ y x-- solos, como una instrucción (contador++;), o en el for (i++). Meterlos dentro de expresiones más complejas (y = x++ + ++x;) hace el código difícil de leer y es una fuente de errores. Los ejercicios de examen que lo hacen sirven para comprobar que entiendes la diferencia, no para imitarlos.

13.4. Operadores relacionales

Comparan dos valores y devuelven un boolean (true o false):

Operador Significado Ejemplo (a = 7, b = 3) Resultado
== Igual a a == b false
!= Distinto de a != b true
> Mayor que a > b true
< Menor que a < b false
>= Mayor o igual a >= 7 true
<= Menor o igual a <= b false

⚠️ Importante: No confundas = (asignación) con == (comparación). Y no se pueden encadenar comparaciones como en matemáticas: 0 <= nota <= 10 no compila; se escribe nota >= 0 && nota <= 10.

13.5. Operadores lógicos

Combinan valores boolean:

Operador Nombre Resultado true cuando…
&& Y lógico (AND) Ambos operandos son true
\|\| O lógico (OR) Al menos uno es true
! NO lógico (NOT) El operando es false (invierte)
^ O exclusivo (XOR) Exactamente uno es true

Tabla de verdad:

a b a && b a \|\| b !a a ^ b
true true true true false false
true false false true false true
false true false true true true
false false false false true false

Evaluación en cortocircuito: && y || dejan de evaluar en cuanto conocen el resultado. En a && b, si a es false, el resultado ya es false y b no se evalúa. En a || b, si a es true, b no se evalúa. Esto permite escribir comprobaciones seguras:

public class Logicos {
    public static void main(String[] args) {
        int edad = 25;
        boolean carnet = true;

        System.out.println(edad >= 18 && carnet);     // Y: las dos deben ser true
        System.out.println(edad < 18 || carnet);      // O: basta con una
        System.out.println(!carnet);                  // NO: invierte
        System.out.println(edad >= 18 ^ carnet);      // O exclusivo: solo una

        // Comprobar si un valor está en un intervalo
        int nota = 7;
        System.out.println("¿Notable? " + (nota >= 7 && nota < 9));

        // Cortocircuito: si la primera parte decide, la segunda NO se evalúa
        int divisor = 0;
        boolean ok = divisor != 0 && 10 / divisor > 1;   // no divide entre 0
        System.out.println("ok = " + ok);

        int contador = 0;
        boolean r1 = false && (++contador > 0);   // && no evalúa la derecha
        boolean r2 = false & (++contador > 0);    // & sí la evalúa
        System.out.println(r1 + " " + r2 + " contador = " + contador);
    }
}
true
true
false
false
¿Notable? true
ok = false
false false contador = 1

En divisor != 0 && 10 / divisor > 1, como divisor != 0 es falso, la división nunca se ejecuta y el programa no falla. Los operadores & y | aplicados a boolean hacen lo mismo que && y || pero sin cortocircuito (evalúan siempre ambos lados): por eso contador vale 1. En la práctica, usa siempre && y ||.

13.6. El operador ternario (adelanto)

condición ? valorSiTrue : valorSiFalse devuelve uno de dos valores según una condición. Lo verás con detalle junto al if en el módulo 05:

int nota = 6;
String resultado = nota >= 5 ? "Aprobado" : "Suspenso";   // "Aprobado"
int a = 8, b = 12;
int mayor = a > b ? a : b;                                // 12

13.7. Operadores bit a bit (breve)

Trabajan directamente con los bits de los números enteros. Se usan en programación de bajo nivel, criptografía, compresión o para guardar muchas banderas en un solo número. Basta con que sepas que existen:

Operador Nombre Ejemplo (a = 12 = 1100₂, b = 10 = 1010₂) Resultado
& Y bit a bit a & b 1000₂ = 8
\| O bit a bit a \| b 1110₂ = 14
^ O exclusivo bit a bit a ^ b 0110₂ = 6
~ Complemento (invierte todos los bits) ~a −13
<< Desplazamiento a la izquierda (× 2ⁿ) 1 << 4 16
>> Desplazamiento a la derecha con signo (÷ 2ⁿ) -16 >> 2 −4
>>> Desplazamiento a la derecha sin signo -16 >>> 28 15

13.8. Precedencia y asociatividad

Cuando una expresión tiene varios operadores, la precedencia decide cuál se aplica primero (como en matemáticas, la multiplicación antes que la suma) y la asociatividad decide el orden entre operadores del mismo nivel.

Nivel Operadores Descripción Asociatividad
1 (máxima) x++ x-- Postincremento y postdecremento Izquierda → derecha
2 ++x --x +x -x ! ~ (tipo) Unarios y casting Derecha → izquierda
3 * / % Multiplicativos Izquierda → derecha
4 + - Aditivos (y concatenación) Izquierda → derecha
5 << >> >>> Desplazamiento Izquierda → derecha
6 < > <= >= instanceof Relacionales Izquierda → derecha
7 == != Igualdad Izquierda → derecha
8 & Y bit a bit / lógico sin cortocircuito Izquierda → derecha
9 ^ O exclusivo Izquierda → derecha
10 \| O bit a bit / lógico sin cortocircuito Izquierda → derecha
11 && Y lógico Izquierda → derecha
12 \|\| O lógico Izquierda → derecha
13 ? : Ternario Derecha → izquierda
14 (mínima) = += -= *= /= %= … Asignación Derecha → izquierda

💡 Consejo: No hace falta memorizar la tabla entera. Recuerda lo esencial: unarios → * / % → + - → relacionales → == != → && → || → asignación. Y ante la duda, usa paréntesis: (a + b) * c es más claro que confiar en la precedencia, y no tiene ningún coste.

13.9. Evaluación de expresiones paso a paso

Evaluación paso a paso de la expresión 5 + 3 * 2 - 8 / 4 % 3, que da 9

Figura 5. Evaluación de 5 + 3 * 2 - 8 / 4 % 3.

Para evaluar a mano una expresión:

  1. Resuelve primero los paréntesis, de dentro hacia fuera.
  2. Aplica los operadores de mayor precedencia.
  3. Entre operadores del mismo nivel, sigue la asociatividad (casi siempre de izquierda a derecha).
  4. Recuerda los tipos: si todos los operandos son enteros, las divisiones son enteras.
public class Precedencia {
    public static void main(String[] args) {
        System.out.println(5 + 3 * 2 - 8 / 4 % 3);   // 9
        System.out.println((5 + 3) * 2);             // 16
        System.out.println(10 - 4 - 3);              // 3: de izquierda a derecha
        System.out.println(2 + 3 > 4 && 1 + 1 == 2); // true
        int x = 4;
        System.out.println(-x * 2 + x++ * 3);        // -8 + 12 = 4
        System.out.println(x);                       // 5
        int a, b;
        a = b = 3 + 2;                               // la asignación va al final
        System.out.println(a + " " + b);
    }
}
9
16
3
true
4
5
5 5

Analicemos 2 + 3 > 4 && 1 + 1 == 2:

  1. Aritméticos primero: 5 > 4 && 2 == 2.
  2. Relacionales: true && 2 == 2.
  3. Igualdad: true && true.
  4. Lógico: true.

Y -x * 2 + x++ * 3 con x = 4: el -x unario da −4; -4 * 2 = −8; x++ vale 4 (y después x pasa a 5); 4 * 3 = 12; −8 + 12 = 4.


14. La clase Math

La clase Math (del paquete java.lang, que no necesita import) reúne las funciones matemáticas más habituales. Todos sus métodos se usan como Math.metodo(...).

Método / constante Qué hace Ejemplo Resultado
Math.pow(a, b) Potencia aᵇ (devuelve double) Math.pow(2, 10) 1024.0
Math.sqrt(x) Raíz cuadrada Math.sqrt(144) 12.0
Math.cbrt(x) Raíz cúbica Math.cbrt(27) 3.0
Math.abs(x) Valor absoluto Math.abs(-7.5) 7.5
Math.max(a, b) El mayor de dos Math.max(3, 9) 9
Math.min(a, b) El menor de dos Math.min(-2, 4) -2
Math.round(x) Redondeo al entero más cercano (devuelve long) Math.round(2.5) 3
Math.floor(x) Redondeo hacia abajo Math.floor(2.7) 2.0
Math.ceil(x) Redondeo hacia arriba Math.ceil(2.1) 3.0
Math.random() Número aleatorio en [0, 1) Math.random() 0.886...
Math.hypot(a, b) √(a² + b²) Math.hypot(3, 4) 5.0
Math.PI, Math.E Constantes π y e Math.PI 3.141592653589793
public class ClaseMath {
    public static void main(String[] args) {
        System.out.println(Math.pow(2, 10));       // potencia (double)
        System.out.println(Math.sqrt(144));        // raíz cuadrada
        System.out.println(Math.cbrt(27));         // raíz cúbica
        System.out.println(Math.abs(-7.5));        // valor absoluto
        System.out.println(Math.max(3, 9));        // máximo
        System.out.println(Math.min(-2, 4));       // mínimo
        System.out.println(Math.round(2.5));       // redondeo al entero más cercano (long)
        System.out.println(Math.round(-2.5));      // ¡-2! (redondea hacia arriba en el empate)
        System.out.println(Math.floor(2.7));       // hacia abajo
        System.out.println(Math.ceil(2.1));        // hacia arriba
        System.out.println(Math.PI);
        System.out.println(Math.E);
        System.out.println(Math.hypot(3, 4));      // hipotenusa
        System.out.println(Math.round(Math.PI * 100) / 100.0);  // redondear a 2 decimales
    }
}
1024.0
12.0
3.0
7.5
9
-2
3
-2
2.0
3.0
3.141592653589793
2.718281828459045
5.0
3.14

💡 Consejo: Para redondear a 2 decimales un valor que vas a seguir usando en cálculos: Math.round(x * 100) / 100.0. Si solo quieres mostrarlo redondeado, es mejor printf("%.2f", x) (módulo 04). Fíjate en que Math.round devuelve un long: para guardarlo en un int necesitas un casting, (int) Math.round(x).

14.1. Números aleatorios

Math.random() devuelve un double aleatorio mayor o igual que 0 y menor que 1. Para obtener un entero entre min y max (ambos incluidos) se usa esta fórmula:

(int) (Math.random() * (max - min + 1)) + min
public class Aleatorios {
    public static void main(String[] args) {
        double r = Math.random();                   // [0.0, 1.0)
        System.out.println("Aleatorio: " + r);

        int dado = (int) (Math.random() * 6) + 1;   // entero de 1 a 6
        System.out.println("Dado: " + dado);

        int min = 10, max = 20;
        int enRango = (int) (Math.random() * (max - min + 1)) + min;   // de 10 a 20
        System.out.println("Entre 10 y 20: " + enRango);
    }
}

Una ejecución (cada vez saldrán valores distintos):

Aleatorio: 0.8864964649034035
Dado: 1
Entre 10 y 20: 14

¿Por qué funciona la fórmula del dado? Math.random() da un número en [0, 1); multiplicado por 6, en [0, 6); con el casting a int, uno de los enteros 0, 1, 2, 3, 4 o 5; sumando 1, de 1 a 6.


15. Errores frecuentes

Error Ejemplo Mensaje / síntoma Solución
Usar una variable sin inicializar int t; System.out.println(t); variable t might not have been initialized Dale un valor inicial
Declarar dos veces int edad = 1; int edad = 2; variable edad is already defined Quita el tipo en la segunda: edad = 2;
Mayúsculas en el nombre Declaras precioTotal y usas preciototal cannot find symbol Usa exactamente el mismo nombre
Decimal con coma en el código double p = 3,5; Error de sintaxis 3.5
float sin f float p = 9.99; possible lossy conversion from double to float 9.99f o usa double
Entero grande sin L long d = 9460730472580800; integer number too large Añade L
Guardar un real en un entero int n = 3.99; possible lossy conversion from double to int Casting (int) o Math.round
División entera inesperada double media = (a + b) / 2; Resultado sin decimales (error lógico) / 2.0 o casting antes de dividir
Comparar reales con == 0.1 + 0.2 == 0.3 false Math.abs(a - b) < 1e-9
Desbordamiento int x = 2_000_000_000 * 2; Número negativo absurdo Usa long (2L) o Math.multiplyExact
= en lugar de == if (a = 5) incompatible types: int cannot be converted to boolean ==
Potencia con ^ int c = 2 ^ 3; Da 1 (es XOR) Math.pow(2, 3)
Concatenar sin paréntesis "Suma: " + a + b Une los dígitos ("Suma: 23") "Suma: " + (a + b)
Comparar String con == nombre == "Ana" Resultados impredecibles nombre.equals("Ana")
Cambiar una constante IVA = 0.10; cannot assign a value to final variable IVA No es una variable: si debe cambiar, quita final

Uno de los más frecuentes es el de las mayúsculas en un nombre:

        int precioTotal = 10;
        System.out.println(preciototal);
VarMayus.java:4: error: cannot find symbol
        System.out.println(preciototal);
                           ^
  symbol:   variable preciototal
  location: class VarMayus
1 error

16. Ejercicios

Ejercicio 1: Intercambiar dos variables

Declara dos variables a = 5 y b = 9, muestra sus valores, intercambia su contenido y vuelve a mostrarlos. ¿Por qué no basta con escribir a = b; b = a;?

Ver solución
public class Intercambio {
    public static void main(String[] args) {
        int a = 5;
        int b = 9;
        System.out.println("Antes:   a = " + a + ", b = " + b);

        int aux = a;   // guardamos a para no perderlo
        a = b;
        b = aux;
        System.out.println("Después: a = " + a + ", b = " + b);
    }
}
Antes:   a = 5, b = 9
Después: a = 9, b = 5

Con a = b; b = a;, la primera instrucción sobrescribe a con 9 y el 5 original se pierde para siempre; la segunda copia ese 9 en b, y ambas acaban valiendo 9. Es como intercambiar el contenido de dos vasos: necesitas un tercer vaso (la variable auxiliar). Es el patrón de intercambio del módulo 02.

Ejercicio 2: De segundos a horas, minutos y segundos

Lee un número de segundos y muéstralo en horas, minutos y segundos. Por ejemplo, 3725 s son 1 h 2 min 5 s.

Ver solución

La clave es combinar la división entera / (cuántas veces cabe) y el resto % (lo que sobra). Una hora tiene 3600 s y un minuto, 60 s.

import java.util.Scanner;

public class Segundos {
    public static void main(String[] args) {
        Scanner teclado = new Scanner(System.in);
        System.out.print("Segundos: ");
        int total = teclado.nextInt();

        int horas = total / 3600;
        int minutos = total % 3600 / 60;
        int segundos = total % 60;

        System.out.println(total + " s = " + horas + " h " + minutos + " min " + segundos + " s");
    }
}
Segundos: 3725
3725 s = 1 h 2 min 5 s

Paso a paso: 3725 / 3600 = 1 hora. 3725 % 3600 = 125 s que sobran; 125 / 60 = 2 minutos. 3725 % 60 = 5 s. En total % 3600 / 60, % y / tienen la misma precedencia y se evalúan de izquierda a derecha, así que primero se hace el resto y luego la división.

Ejercicio 3: Media de notas sin errores

Calcula la media de tres notas enteras (7, 8 y 6) y después la de cuatro (añadiendo un 9), mostrándola también redondeada a 2 decimales. Evita la trampa de la división entera.

Ver solución
public class MediaNotas {
    public static void main(String[] args) {
        int n1 = 7, n2 = 8, n3 = 6;
        double media = (n1 + n2 + n3) / 3.0;
        System.out.println("Media: " + media);

        int n4 = 9;
        double media4 = (n1 + n2 + n3 + n4) / 4.0;
        System.out.println("Media de cuatro: " + media4);
        System.out.println("Redondeada a 2 decimales: " + Math.round(media4 * 100) / 100.0);
    }
}
Media: 7.0
Media de cuatro: 7.5
Redondeada a 2 decimales: 7.5

Dividiendo entre 3.0 y 4.0 (literales double), la división es real. Si hubiéramos escrito / 4, la media de cuatro saldría 7.0 en lugar de 7.5: 30 / 4 = 7 en división entera. Fíjate también en los paréntesis: sin ellos, n1 + n2 + n3 / 3.0 dividiría solo n3.

Ejercicio 4: Círculo

Con una constante RADIO = 2.5, calcula el área (π·r²) y la longitud de la circunferencia (2·π·r) usando Math.PI y Math.pow. Muestra los resultados completos y redondeados a 2 decimales.

Ver solución
public class Circulo {
    public static void main(String[] args) {
        final double RADIO = 2.5;
        double area = Math.PI * Math.pow(RADIO, 2);
        double longitud = 2 * Math.PI * RADIO;

        System.out.println("Área:     " + area);
        System.out.println("Longitud: " + longitud);
        System.out.println("Área (2 decimales):     " + Math.round(area * 100) / 100.0);
        System.out.println("Longitud (2 decimales): " + Math.round(longitud * 100) / 100.0);
    }
}
Área:     19.634954084936208
Longitud: 15.707963267948966
Área (2 decimales):     19.63
Longitud (2 decimales): 15.71

Math.round(area * 100) convierte 1963.4954… en 1963; al dividir entre 100.0 (no 100, que daría división entera) obtenemos 19.63.

Ejercicio 5: ¿Qué muestra?

Sin ejecutarlo, calcula qué muestra este programa. Haz una tabla con el valor de cada variable después de cada línea.

public class Predice {
    public static void main(String[] args) {
        int a = 3;
        int b = a++ + 2;
        int c = ++a * 2;
        int d = a-- - --b;
        System.out.println(a + " " + b + " " + c + " " + d);
    }
}
Ver solución
Línea Cálculo a b c d
int a = 3;   3      
int b = a++ + 2; a++ vale 3 (después a = 4); 3 + 2 = 5 4 5    
int c = ++a * 2; ++a: a = 5 y vale 5; 5 × 2 = 10 5 5 10  
int d = a-- - --b; a-- vale 5 (después a = 4); --b: b = 4 y vale 4; 5 − 4 = 1 4 4 10 1

Salida real:

4 4 10 1

Ejercicio 6: Cifras de un número

Dado el número de tres cifras 472, obtén sus centenas, decenas y unidades usando / y %, y construye el número al revés (274).

Ver solución
public class Cifras {
    public static void main(String[] args) {
        int numero = 472;
        int centenas = numero / 100;
        int decenas = numero / 10 % 10;
        int unidades = numero % 10;

        System.out.println("Centenas: " + centenas);
        System.out.println("Decenas:  " + decenas);
        System.out.println("Unidades: " + unidades);
        System.out.println("Al revés: " + (unidades * 100 + decenas * 10 + centenas));
    }
}
Centenas: 4
Decenas:  7
Unidades: 2
Al revés: 274

472 / 100 = 4. 472 / 10 = 47 y 47 % 10 = 7. 472 % 10 = 2. Observa los paréntesis en la última línea: sin ellos, el + concatenaría los números como texto.

Ejercicio 7: Distancia entre dos puntos

Calcula la distancia entre los puntos (1, 2) y (4, 6) con la fórmula d = √((x₂ − x₁)² + (y₂ − y₁)²). Hazlo con Math.sqrt y Math.pow, y después con Math.hypot.

Ver solución
public class Distancia {
    public static void main(String[] args) {
        double x1 = 1, y1 = 2;
        double x2 = 4, y2 = 6;
        double distancia = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
        System.out.println("Distancia: " + distancia);
        System.out.println("Con hypot: " + Math.hypot(x2 - x1, y2 - y1));
    }
}
Distancia: 5.0
Con hypot: 5.0

Las diferencias son 3 y 4, así que d = √(9 + 16) = √25 = 5. Fíjate en que double x1 = 1 es correcto: el int 1 se amplía automáticamente a 1.0.

Ejercicio 8 (propuesto): Tabla de tipos

Escribe un programa que declare una variable de cada uno de los ocho tipos primitivos con un valor razonable para un contexto real (por ejemplo, byte edadMascota, long poblacionMundial, char grupo…) y las muestre todas con un texto explicativo. Elige nombres que sigan las convenciones de Java.

Ejercicio 9 (propuesto): Predice los castings

Sin ejecutar, indica el resultado de cada expresión y después compruébalo en JShell: (int) 7.9, (int) -7.9, (byte) 130, (char) 97, (int) 'Z', 7 / 2 * 2, 7 / 2.0 * 2, (double) (7 / 2), Math.round(7.5), Math.round(-7.5), 10 % 3 * 2, 1 + 2 + "3" + 4 + 5.

Ejercicio 10 (propuesto): Precio con IVA y descuento

Con constantes para el IVA (21 %) y un descuento (15 %), calcula el precio final de un producto de 79,90 €: primero se aplica el descuento y después el IVA. Muestra el precio original, el descuento, la base, la cuota de IVA y el total, redondeados a 2 decimales con Math.round.

Ejercicio 11 (propuesto): Desbordamiento a propósito

Calcula el factorial de 13, 20 y 21 con multiplicaciones sucesivas usando primero int y después long. ¿En qué momento deja de ser correcto cada uno? Repite el cálculo con Math.multiplyExact y observa qué ocurre.

Ejercicio 12 (propuesto): Lanzar dos dados

Simula el lanzamiento de dos dados de seis caras con Math.random() y muestra cada resultado y la suma. Ejecuta el programa varias veces. Después modifícalo para simular un dado de 20 caras (de 1 a 20).

Ejercicio 13 (propuesto): Expresiones lógicas

Para int x = 7, int y = 3, boolean b = false, calcula a mano y después comprueba: x > 5 && y > 5, x > 5 || y > 5, !(x == 7), x % 2 == 1 && !b, y != 3 || b, x > y == !b, x - y * 2 > 0 ^ b. Indica el orden en que se evalúa cada operador.


Resumen

Concepto Sintaxis / ejemplo Idea clave
Declaración int edad; Reserva una caja de un tipo
Inicialización int edad = 18; Declaración + valor inicial
Asignación edad = edad + 1; Se evalúa la derecha y se guarda en la izquierda
Identificadores precioTotal, Alumno, IVA camelCase, PascalCase, UPPER_SNAKE_CASE
Enteros byte, short, int, long Usa int; long para valores enormes (L)
Reales float, double Usa double; precisión limitada, no compares con ==
Carácter / lógico char c = 'A'; / boolean ok = true; char es un código Unicode
Literales 0xFF, 0b1010, 1_000, 10L, 2.5f, 1e3, '\n' Formas de escribir valores
String String s = "Hola"; Tipo de referencia; + concatena
Constante final double IVA = 0.21; No puede cambiar
var var n = 10; Tipo deducido (Java 10); sigue siendo fijo
Ampliación double d = 5; Automática, sin pérdida
Casting int n = (int) 3.99; Explícito; trunca y puede perder datos
Texto a número Integer.parseInt("42") NumberFormatException si no es válido
Desbordamiento Integer.MAX_VALUE + 1 Da la vuelta sin aviso
División entera 7 / 2 = 3, 7 / 2.0 = 3.5 Si ambos son enteros, sin decimales
Resto 7 % 2 = 1 Par/impar, dígitos, ciclos
Asignación compuesta x += 5; x = x + 5
Incremento x++ / ++x Post: usa y luego suma; pre: suma y luego usa
Relacionales == != < > <= >= Devuelven boolean
Lógicos && \|\| ! ^ && y \|\| en cortocircuito
Precedencia unarios → * / % → + - → relacionales → && → \|\| → = Ante la duda, paréntesis
Math pow, sqrt, abs, max, min, round, random, PI Funciones matemáticas