Math y clases envoltorio en Java
En esta lección
Java trae de serie muchas herramientas para trabajar con números: la clase Math para potencias, raíces y redondeos; Random para generar números aleatorios; y las clases envoltorio (Integer, Double…), que convierten textos en números y permiten guardar números donde solo caben objetos. En esta lección las verás todas, junto con una trampa clásica al comparar Integer y la forma correcta de manejar dinero.
La clase Math
Math es una clase con métodos estáticos: se usan escribiendo Math. delante, sin crear ningún objeto. No hace falta ningún import, porque está en java.lang.
System.out.println(Math.abs(-7)); // 7
System.out.println(Math.pow(2, 10)); // 1024.0
System.out.println(Math.sqrt(81)); // 9.0
System.out.println(Math.max(4, 9)); // 9
System.out.println(Math.min(4, 9)); // 4
System.out.println(Math.PI); // 3.141592653589793
| Método | Qué hace | Devuelve |
|---|---|---|
Math.abs(x) | Valor absoluto (quita el signo) | Mismo tipo que x |
Math.pow(base, exp) | Potencia | double |
Math.sqrt(x) | Raíz cuadrada | double |
Math.cbrt(x) | Raíz cúbica | double |
Math.max(a, b) / Math.min(a, b) | Mayor / menor de dos | Mismo tipo |
Math.round(x) | Redondeo al entero más cercano | long (con double) |
Math.floor(x) | Redondeo hacia abajo | double |
Math.ceil(x) | Redondeo hacia arriba | double |
Math.random() | Aleatorio entre 0.0 (incluido) y 1.0 (excluido) | double |
Fíjate en que pow y sqrt siempre devuelven double, aunque el resultado sea exacto. Si lo necesitas entero, haz un casting: int kb = (int) Math.pow(2, 10);.
Un ejemplo práctico: la distancia entre dos puntos del plano (teorema de Pitágoras).
double x1 = 1, y1 = 2, x2 = 4, y2 = 6;
double distancia = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
System.out.println(distancia); // 5.0
Redondear: round, floor y ceil
System.out.println(Math.round(4.5)); // 5
System.out.println(Math.round(4.49)); // 4
System.out.println(Math.floor(4.9)); // 4.0
System.out.println(Math.ceil(4.1)); // 5.0
System.out.println(Math.round(-4.5)); // -4
System.out.println(Math.floor(-4.1)); // -5.0
roundva al entero más cercano; en el empate (.5) sube hacia el positivo, por eso-4.5da-4.floor(“suelo”) siempre baja yceil(“techo”) siempre sube, también con negativos.
ceil es muy útil para calcular cuántos “recipientes” necesitas. Si tienes 23 alumnos y en cada mesa caben 4:
int alumnos = 23;
int porMesa = 4;
int mesas = (int) Math.ceil((double) alumnos / porMesa);
System.out.println(mesas); // 6
Para redondear a dos decimales hay un truco con round: multiplicas por 100, redondeas y divides entre 100.0.
double precio = 19.98765;
double redondeado = Math.round(precio * 100) / 100.0;
System.out.println(redondeado); // 19.99
Si solo quieres mostrarlo con dos decimales, no cambies el número: usa printf("%.2f", precio) (lo tienes en String y StringBuilder).
Números aleatorios con Math.random()
Math.random() devuelve un double entre 0.0 y 1.0 (sin llegar a 1). Para convertirlo en un entero de un rango hay una fórmula que conviene entender, no memorizar:
(int) (Math.random() * cantidadDeValores) + minimo
Para simular un dado (del 1 al 6, que son 6 valores posibles):
int dado = (int) (Math.random() * 6) + 1;
Math.random() * 6da un número entre 0.0 y 5.999…(int)corta los decimales: 0, 1, 2, 3, 4 o 5.+ 1lo desplaza: de 1 a 6.
Para un número entre 10 y 20, ambos incluidos, hay 11 valores: (int) (Math.random() * 11) + 10.
La clase Random
java.util.Random es más cómoda y más completa. Su método nextInt(limite) devuelve un entero entre 0 y limite - 1:
import java.util.Random;
public class Loteria {
public static void main(String[] args) {
Random azar = new Random();
int dado = azar.nextInt(6) + 1; // 1 a 6
int porcentaje = azar.nextInt(101); // 0 a 100
int entre = azar.nextInt(10, 21); // 10 a 20 (Java 17+)
boolean moneda = azar.nextBoolean();
double decimal = azar.nextDouble(); // 0.0 a 1.0
System.out.println(dado >= 1 && dado <= 6); // true
}
}
Desde Java 17, nextInt(origen, limite) acepta directamente el rango (el límite sigue siendo excluido).
Si creas el Random con una semilla, new Random(42), genera siempre la misma secuencia. Parece raro, pero es muy útil para hacer pruebas repetibles.
Clases envoltorio
Cada tipo primitivo tiene una clase envoltorio (wrapper) que “envuelve” el valor dentro de un objeto:
| Primitivo | Clase envoltorio |
|---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
¿Para qué sirven? Por tres motivos:
- Colecciones: un
ArrayListsolo guarda objetos, así que no puedes escribirArrayList<int>; se escribeArrayList<Integer>. Lo verás en ArrayList y listas. - Pueden ser
null: unIntegerpuede representar “sin valor”, algo imposible con unint. - Utilidades: métodos y constantes para convertir, comparar y conocer límites.
Autoboxing y unboxing
Java convierte automáticamente entre el primitivo y su envoltorio:
Integer edad = 25; // autoboxing: int -> Integer
int copia = edad; // unboxing: Integer -> int
Integer suma = edad + 5; // unboxing, suma y autoboxing
System.out.println(suma); // 30
Es cómodo, pero tiene un peligro: si el Integer es null, el unboxing lanza NullPointerException.
Integer stock = null;
int unidades = stock; // NullPointerException
Convertir entre texto y números
Es una de las operaciones más habituales: todo lo que llega del teclado, de un fichero o de un formulario es texto.
int edad = Integer.parseInt("25");
double precio = Double.parseDouble("19.99");
long grande = Long.parseLong("8100000000");
boolean activo = Boolean.parseBoolean("true");
System.out.println(edad + 1); // 26
System.out.println(precio * 2); // 39.98
Y al revés, de número a texto:
String a = String.valueOf(42);
String b = Integer.toString(42);
String c = "" + 42; // funciona, pero es menos claro
System.out.println(a + b + c); // 424242
Integer.parseInt también puede leer otras bases: Integer.parseInt("FF", 16) devuelve 255.
NumberFormatException
Si el texto no es un número válido, la conversión lanza NumberFormatException:
int n = Integer.parseInt("12a"); // NumberFormatException: For input string: "12a"
Ojo con casos que parecen inofensivos: Integer.parseInt(" 12") (con espacio) y Integer.parseInt("12.5") (con decimales) también fallan. Limpia antes con strip() y usa el método adecuado para cada tipo.
La forma correcta de convertir datos del usuario es capturar la excepción:
String entrada = "doce";
try {
int unidades = Integer.parseInt(entrada);
System.out.println("Unidades: " + unidades);
} catch (NumberFormatException e) {
System.out.println("'" + entrada + "' no es un número"); // 'doce' no es un número
}
try y catch se explican en detalle en excepciones.
Límites: MAX_VALUE y MIN_VALUE
Cada envoltorio numérico tiene constantes con sus límites. Son útiles, por ejemplo, como valor inicial al buscar un mínimo:
int[] temperaturas = {18, 12, 25, 9, 21};
int minima = Integer.MAX_VALUE; // cualquier valor real será menor
for (int t : temperaturas) {
if (t < minima) {
minima = t;
}
}
System.out.println(minima); // 9
System.out.println(Integer.MAX_VALUE); // 2147483647
System.out.println(Double.MAX_VALUE); // 1.7976931348623157E308
Otros métodos útiles: Integer.compare(a, b), Integer.sum(a, b), Integer.toBinaryString(10) ("1010") y Character.getNumericValue('7') (7).
Cuidado al comparar Integer con ==
Esta trampa aparece en entrevistas y en exámenes. Mira este código:
Integer a = 100;
Integer b = 100;
System.out.println(a == b); // true
Integer c = 1000;
Integer d = 1000;
System.out.println(c == d); // false
System.out.println(c.equals(d)); // true
¿Por qué el mismo código da true con 100 y false con 1000? Integer es un objeto, y == compara si son el mismo objeto, no si valen lo mismo. Para ahorrar memoria, Java guarda en una caché los Integer entre -128 y 127, y reutiliza el mismo objeto cada vez que haces autoboxing de esos valores. Por eso a y b apuntan al mismo objeto. Con 1000, fuera de la caché, se crean dos objetos distintos.
Regla: compara los envoltorios con equals, igual que los String. O pásalos a primitivo antes: c.intValue() == d.intValue().
Dinero: double no es exacto
Los double se guardan en binario, y muchos decimales no tienen representación exacta, igual que 1/3 no la tiene en decimal (0,333…). El resultado:
System.out.println(0.1 + 0.2); // 0.30000000000000004
System.out.println(1.10 - 1.00); // 0.10000000000000009
Para una nota media no importa, pero con dinero esos céntimos fantasma se acumulan. Para importes se usa BigDecimal, del paquete java.math, que trabaja con decimales exactos:
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Factura {
public static void main(String[] args) {
BigDecimal precio = new BigDecimal("19.99");
BigDecimal unidades = new BigDecimal("3");
BigDecimal iva = new BigDecimal("0.21");
BigDecimal base = precio.multiply(unidades);
BigDecimal total = base.add(base.multiply(iva)).setScale(2, RoundingMode.HALF_UP);
System.out.println(base); // 59.97
System.out.println(total); // 72.56
System.out.println(new BigDecimal("0.1").add(new BigDecimal("0.2"))); // 0.3
}
}
- Créalo siempre a partir de un texto (
new BigDecimal("19.99")). Si usasnew BigDecimal(19.99), arrastras la imprecisión deldouble. - No admite
+ni*: se usan los métodosadd,subtract,multiplyydivide. setScale(2, RoundingMode.HALF_UP)redondea a dos decimales como se hace en las facturas.
Consejo: otra opción sencilla es guardar el dinero en céntimos con un
long: 19,99 euros son1999. Las sumas son exactas y solo divides entre 100 al mostrar.
Errores frecuentes
- Olvidar que
powysqrtdevuelvendouble:int r = Math.pow(2, 3);no compila. - Rango de aleatorios mal calculado:
(int) (Math.random() * 6)da de 0 a 5, no de 1 a 6. - Convertir textos sin validar:
parseIntlanzaNumberFormatExceptioncon espacios, decimales o letras. - Comparar
Integercon==: funciona entre -128 y 127 y falla fuera. Usaequals. - Unboxing de
null:int x = integerNulo;lanzaNullPointerException. - Dinero con
double: usaBigDecimalo céntimos en unlong.
Resumen
Mathtiene métodos estáticos:abs,pow,sqrt,max,min,round,floor,ceilyrandom.- Para aleatorios,
Random.nextInt(n)da de 0 a n-1; conMath.random()usa(int) (Math.random() * cantidad) + minimo. - Cada primitivo tiene su clase envoltorio (
Integer,Double…); Java convierte solo con autoboxing y unboxing. Integer.parseInt,Double.parseDoubleyString.valueOfconvierten entre texto y números; si el texto no es válido, saltaNumberFormatException.- Compara envoltorios con
equals, no con==. - Para dinero,
BigDecimalcreado desde texto.
Con los datos, los operadores y las utilidades numéricas controlados, el siguiente paso es organizar el código en bloques reutilizables: métodos.
Pon a prueba lo que has aprendido
¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.