Variables en Java — práctica con los errores que todo el mundo comete

Hay un momento en el primer laboratorio de Java que le pasa a casi todo el mundo. Llevas un rato escribiendo código, llegas a una división, ejecutas el programa y el resultado es… 3. No 3,5. No 3,33. Tres. Entero. Y el número que esperabas estaba ahí, perfecto en papel, pero Java decidió truncarlo sin avisar.

Eso es la división entera. Y es solo uno de los tres sustos que te esperan cuando empiezas a mezclar tipos en Java. En esta práctica los vamos a provocar a propósito para que cuando aparezcan en el laboratorio ya los conozcas de antes.

Abre VS Code y crea una carpeta practica_variables. Un fichero .java para cada programa.

Variables en Java práctica — Programa 1: Calculadora con tipos mezclados

¿Por qué empezar con una calculadora si ya hicimos una en la práctica de sintaxis? Porque esta es diferente — aquí el objetivo no es el resultado, sino entender qué pasa cuando mezclas int y double sin pensar.

import java.util.Scanner;

public class CalculadoraTipos {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.println("=== CALCULADORA CON TIPOS ===");
        System.out.println();

        System.out.print("Primer número (entero): ");
        int a = sc.nextInt();

        System.out.print("Segundo número (entero): ");
        int b = sc.nextInt();

        System.out.println();
        System.out.println("--- División: el gran engaño ---");

        // División entera — lo que hace Java por defecto
        int divEntera = a / b;
        System.out.printf("%d / %d = %d  ← división ENTERA%n", a, b, divEntera);

        // División real — necesitas al menos un double
        double divReal1 = (double) a / b;
        System.out.printf("%d / %d = %.4f  ← cast antes de dividir%n",
                          a, b, divReal1);

        double divReal2 = a / (double) b;
        System.out.printf("%d / %d = %.4f  ← cast en el divisor%n",
                          a, b, divReal2);

        double divReal3 = (double)(a / b);
        System.out.printf("%d / %d = %.4f  ← cast DESPUÉS (¡trampa!)%n",
                          a, b, divReal3);

        System.out.println();
        System.out.println("--- Desbordamiento de int ---");

        int limite = Integer.MAX_VALUE;
        System.out.printf("Máximo int:    %,d%n", limite);
        System.out.printf("Máximo int + 1: %,d  ← overflow%n", limite + 1);

        long limiteGrande = Integer.MAX_VALUE + 1L;
        System.out.printf("Con long:      %,d  ← correcto%n", limiteGrande);

        System.out.println();
        System.out.println("--- Widening automático ---");
        int entero = 42;
        long grande = entero;        // automático
        double decimal = entero;     // automático — 42.0
        System.out.printf("int:    %d%n", entero);
        System.out.printf("long:   %d  ← widening automático%n", grande);
        System.out.printf("double: %.1f  ← widening automático%n", decimal);

        sc.close();
    }
}

Ejecuta con a = 7 y b = 2 y mira la cuarta línea de resultados:

7 / 2 = 3      ← división ENTERA
7 / 2 = 3,5000 ← cast antes de dividir
7 / 2 = 3,5000 ← cast en el divisor
7 / 2 = 3,0000 ← cast DESPUÉS (¡trampa!)

La última línea es la trampa que más gente no ve venir. (double)(a / b) convierte el resultado a double, pero la división ya ocurrió como entera — el daño está hecho. El cast tiene que ir antes de la operación, no después.

Y si alguna vez ves un número negativo enorme donde esperabas un número positivo grande — es overflow. Integer.MAX_VALUE + 1 da -2147483648 porque el entero da la vuelta. La solución es usar long.

Variables en Java práctica — Programa 2: Conversor de temperatura con casting

Las temperaturas son perfectas para practicar casting porque las fórmulas mezclan enteros y decimales constantemente. Además, aquí aparece una restricción física real: no puedes bajar del cero absoluto.

import java.util.Scanner;

public class ConversorTemperatura {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.println("=== CONVERSOR DE TEMPERATURA ===");
        System.out.println();

        System.out.println("¿De qué unidad parto?");
        System.out.println("1. Celsius (°C)");
        System.out.println("2. Fahrenheit (°F)");
        System.out.println("3. Kelvin (K)");
        System.out.print("Opción: ");
        int opcion = sc.nextInt();

        System.out.print("Temperatura: ");
        double temp = sc.nextDouble();

        // Convertir primero a Celsius
        double celsius;
        String unidadOrigen;

        switch (opcion) {
            case 1:
                celsius = temp;
                unidadOrigen = "°C";
                break;
            case 2:
                celsius = (temp - 32) * 5.0 / 9.0;
                unidadOrigen = "°F";
                break;
            case 3:
                celsius = temp - 273.15;
                unidadOrigen = "K";
                break;
            default:
                System.out.println("Opción no válida");
                sc.close();
                return;
        }

        // Cero absoluto: -273.15°C
        if (celsius < -273.15) {
            System.out.println("Temperatura por debajo del cero absoluto — imposible");
            sc.close();
            return;
        }

        // Convertir a las otras dos
        double fahrenheit = celsius * 9.0 / 5.0 + 32;
        double kelvin     = celsius + 273.15;

        // Versión entera — truncada, no redondeada
        int celsiusInt     = (int) celsius;
        int fahrenheitInt  = (int) fahrenheit;
        int kelvinInt      = (int) kelvin;

        System.out.println();
        System.out.printf("%.2f %s equivale a:%n", temp, unidadOrigen);
        System.out.println();
        System.out.printf("  Celsius:    %8.2f °C%n", celsius);
        System.out.printf("  Fahrenheit: %8.2f °F%n", fahrenheit);
        System.out.printf("  Kelvin:     %8.2f K%n",  kelvin);
        System.out.println();
        System.out.println("--- En entero (casting a int — trunca, no redondea) ---");
        System.out.printf("  Celsius:    %d °C%n", celsiusInt);
        System.out.printf("  Fahrenheit: %d °F%n", fahrenheitInt);
        System.out.printf("  Kelvin:     %d K%n",  kelvinInt);

        // Datos curiosos
        System.out.println();
        System.out.println("--- Temperaturas de referencia ---");
        System.out.printf("  Cero absoluto: -273.15 °C = %.2f °F = 0 K%n",
                          -273.15 * 9.0 / 5.0 + 32);
        System.out.printf("  Agua congela:    0.00 °C = 32.00 °F = 273.15 K%n");
        System.out.printf("  Agua hierve:   100.00 °C = 212.00 °F = 373.15 K%n");
        System.out.printf("  Cuerpo humano:  37.00 °C = %.2f °F = %.2f K%n",
                          37.0 * 9.0 / 5.0 + 32, 37.0 + 273.15);

        sc.close();
    }
}

Pruébalo con 100 Celsius — deberías obtener exactamente 212 Fahrenheit y 373,15 Kelvin. Si los números no cuadran, comprueba que las fórmulas usan 5.0 y 9.0 y no 5 y 9. ¿Por qué? Porque 5/9 en Java es 0 — división entera otra vez. El punto decimal lo cambia todo.

Variables en Java práctica — Programa 3: Ficha de producto con String

Aquí entra el tercer susto: == vs .equals(). Este programa trabaja con Strings de verdad — los coge del usuario, los compara, los manipula — y en algún momento vas a ver por qué comparar Strings con == es una de esas cosas que parece que funciona… hasta que no.

import java.util.Scanner;

public class FichaProducto {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.println("=== FICHA DE PRODUCTO ===");
        System.out.println();

        System.out.print("Nombre del producto: ");
        String nombre = sc.nextLine();

        System.out.print("Categoría (electronica/ropa/hogar): ");
        String categoria = sc.nextLine();

        System.out.print("Precio (€): ");
        double precio = sc.nextDouble();

        System.out.print("Stock disponible: ");
        int stock = sc.nextInt();
        sc.nextLine();

        System.out.print("Código de producto (ej. PROD-001): ");
        String codigo = sc.nextLine();

        // Cálculos con tipos
        double precioConIVA  = precio * 1.21;
        double precioOferta  = precio * 0.85;    // 15% descuento
        boolean disponible   = stock > 0;
        boolean bajoStock    = stock > 0 && stock < 5;

        // Operaciones con String
        String nombreMayus   = nombre.toUpperCase();
        String codigoLimpio  = codigo.trim().toUpperCase();
        int    longNombre    = nombre.length();
        char   primeraLetra  = nombre.charAt(0);
        String categoriaFmt  = categoria.trim().toLowerCase();

        // Clasificación por categoría
        String icono;
        if (categoriaFmt.equals("electronica")) {
            icono = "💻";
        } else if (categoriaFmt.equals("ropa")) {
            icono = "👕";
        } else if (categoriaFmt.equals("hogar")) {
            icono = "🏠";
        } else {
            icono = "📦";
        }

        // Mostrar ficha
        System.out.println();
        System.out.println("╔══════════════════════════════╗");
        System.out.printf( "║  %s %-24s║%n", icono, nombreMayus);
        System.out.println("╠══════════════════════════════╣");
        System.out.printf( "║  Código:    %-17s║%n", codigoLimpio);
        System.out.printf( "║  Categoría: %-17s║%n", categoriaFmt);
        System.out.printf( "║  Precio:    %8.2f €         ║%n", precio);
        System.out.printf( "║  Con IVA:   %8.2f €         ║%n", precioConIVA);
        System.out.printf( "║  Oferta:    %8.2f €         ║%n", precioOferta);
        System.out.printf( "║  Stock:     %-17d║%n", stock);
        System.out.println("╠══════════════════════════════╣");
        System.out.printf( "║  Disponible:  %-15s║%n", disponible ? "SÍ ✓" : "NO ✗");
        System.out.printf( "║  Bajo stock:  %-15s║%n", bajoStock ? "AVISO ⚠" : "---");
        System.out.println("╚══════════════════════════════╝");

        System.out.println();
        System.out.println("--- Análisis del nombre ---");
        System.out.printf("Original:      %s%n", nombre);
        System.out.printf("Mayúsculas:    %s%n", nombreMayus);
        System.out.printf("Longitud:      %d caracteres%n", longNombre);
        System.out.printf("Primera letra: %c%n", primeraLetra);

        // La trampa del ==
        System.out.println();
        System.out.println("--- La trampa del == con Strings ---");
        String cat1 = "electronica";
        String cat2 = categoria.trim().toLowerCase();
        System.out.printf("cat1 == cat2:      %b  ← puede fallar%n", cat1 == cat2);
        System.out.printf("cat1.equals(cat2): %b  ← siempre correcto%n",
                          cat1.equals(cat2));

        sc.close();
    }
}

Pruébalo con «Portátil Gaming», «electronica», 899.99, 3 y «prod-001». Fíjate en la última sección — según cómo Java gestione la memoria internamente, == puede devolver true o false para el mismo contenido. No es predecible. .equals() siempre funciona. No hay debate.

Visualízalo con Python Tutor

Selecciona Java en el desplegable y pega en pythontutor.com:

public class TiposDemo {
    public static void main(String[] args) {
        // División entera vs real
        int a = 7, b = 2;
        int   divEntera = a / b;
        double divReal  = (double) a / b;

        System.out.println("Entera: " + divEntera);
        System.out.println("Real:   " + divReal);

        // Casting
        double precio = 9.99;
        int precioEntero = (int) precio;
        System.out.println("Precio:        " + precio);
        System.out.println("Precio (int):  " + precioEntero);

        // String y equals
        String s1 = "hola";
        String s2 = new String("hola");
        System.out.println("== :      " + (s1 == s2));
        System.out.println("equals(): " + s1.equals(s2));
    }
}

Ejecuta paso a paso. Cuando llegues a (double) a / b observa cómo a se convierte temporalmente a 7.0 antes de dividir — el valor original no cambia, el cast solo afecta a esa operación. Cuando llegues a (int) precio verás que 9.99 se convierte en 9 — truncado, no redondeado. Y cuando llegues al == con los Strings, Python Tutor te muestra las flechas de memoria: s1 y s2 apuntan a objetos distintos aunque tengan el mismo texto, de ahí que == sea false.

Resumen y siguiente paso

Tres programas, tres sustos resueltos. La división entera ocurre cuando ambos operandos son int — pon el cast antes de la operación. El casting a int trunca, no redondea — si necesitas redondear usa Math.round(). Y los Strings se comparan siempre con .equals(), nunca con ==.

En el siguiente artículo tienes ejercicios para resolver por tu cuenta.

Siguiente paso: ejerciciosVariables en Java — ejercicios para dominar tipos, casting y Strings

Sigue aprendiendo

Un comentario

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *