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
finaly usar la inferencia de tipos convar - 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 (
intes una caja para números enteros de 4 bytes). - Lo que hay dentro es el valor (
18).
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
edades unint, 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 + 1se lee “calculaedad + 1y guarda el resultado enedad”. 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
- Puede contener letras, dígitos, el guion bajo
_y el símbolo$. - No puede empezar por un dígito.
- No puede contener espacios ni símbolos como
-,+,.,!,@,#. - No puede ser una palabra reservada (
int,class,if…). - Distingue mayúsculas de minúsculas:
edad,EdadyEDADson 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;condouble cuotaIva = precio * unidades * IVA;. Evita abreviaturas crípticas (nmAl), nombres de una letra (salvo contadores de bucles comoi,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:
constygotoestán reservadas pero no se usan en Java.true,falseynullno 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 llamarvara 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.
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
intsalvo que necesites valores mayores de 2 000 millones (entonces,long).byteyshortsolo se usan para ahorrar memoria en grandes cantidades de datos o para leer ficheros binarios. - Para números con decimales, usa
double.floatsolo 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
byteva 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, nunca3,14. La coma separa elementos (int a = 1, b = 2;). Otra cosa es cómo se muestran o leen los números conprintfyScanneren 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 unStringconcatena (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. En1 + 2 + " es la suma", primero se suman los números (3) y luego se concatena.- Los objetos
Stringtienen 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
Stringno uses==, sinotexto1.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 * IVAse entiende;precio * 0.21obliga 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, comoMath.PIoInteger.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:
varno convierte Java en un lenguaje de tipado dinámico. La variable sigue teniendo un tipo fijo, solo que no lo escribes tú. Después devar edad = 18;,edades unintpara 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.vares útil cuando el tipo es largo y evidente, comovar 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…
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.99da 3 y(int) -3.99da −3: el casting de real a entero trunca (descarta los decimales), no redondea. Para redondear usaMath.round(sección 14).(byte) 200da −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) 123456789012Lda un número sin sentido aparente por el mismo motivo: se pierden los 32 bits superiores.long→floates automático, pero unfloatsolo 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.
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 unlong, el cálculo se hace enint(porque los dos operandos sonint) y ya se ha desbordado antes de guardarse. La solución es que al menos un operando sealong: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 / 2vale 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.2no da exactamente 0.3, así que0.1 + 0.2 == 0.3es falso. Ahora entiendes el7.199999999999999de 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
floatel resultado aparenta ser exacto (0.3) solo porque, al tener menos precisión, Java muestra menos cifras; compara0.33333334con0.3333333333333333. - Para dinero u otros cálculos que exigen exactitud decimal, los programas profesionales usan la clase
BigDecimal(creada a partir de unString), 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 conlong. - Para mostrar los reales de forma amigable, se redondean al imprimirlos:
printf("%.2f", x)(módulo 04) oMath.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++yx--solos, como una instrucción (contador++;), o en elfor(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 <= 10no compila; se escribenota >= 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) * ces más claro que confiar en la precedencia, y no tiene ningún coste.
13.9. Evaluación de expresiones paso a paso
Figura 5. Evaluación de 5 + 3 * 2 - 8 / 4 % 3.
Para evaluar a mano una expresión:
- Resuelve primero los paréntesis, de dentro hacia fuera.
- Aplica los operadores de mayor precedencia.
- Entre operadores del mismo nivel, sigue la asociatividad (casi siempre de izquierda a derecha).
- 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:
- Aritméticos primero:
5 > 4 && 2 == 2. - Relacionales:
true && 2 == 2. - Igualdad:
true && true. - 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 mejorprintf("%.2f", x)(módulo 04). Fíjate en queMath.rounddevuelve unlong: para guardarlo en unintnecesitas 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 |