Conceptos intermedios de Java

Paquetes, excepciones, colecciones, enumeraciones y clases envoltorio, con ejemplos interactivos.

Introducción a Conceptos Intermedios

Los conceptos intermedios de Java son herramientas esenciales que permiten escribir código más organizado, robusto y eficiente. Dominar estos conceptos es crucial para desarrollar aplicaciones profesionales.

Paquetes

Organizan clases en namespaces lógicos, evitando conflictos de nombres y facilitando el mantenimiento.

Excepciones

Manejan errores de manera elegante, permitiendo que los programas se recuperen de situaciones inesperadas.

Colecciones

Estructuras de datos flexibles para almacenar y manipular grupos de objetos de manera eficiente.

Enumeraciones

Definen conjuntos fijos de constantes con nombres, mejorando la legibilidad y seguridad del código.

Clases Wrapper

Envuelven tipos primitivos en objetos, permitiendo su uso en contextos que requieren objetos.

Paquetes (Packages)

Explorador de Estructura de Paquetes

📁 com
└─ 📁 miapp
 └─ 📁 model
  └─ 📄 Usuario.java
  └─ 📄 Producto.java
 └─ 📁 service
  └─ 📄 UsuarioService.java
  └─ 📄 ProductoService.java
 └─ 📁 util
  └─ 📄 Validador.java
  └─ 📄 Calculadora.java
 └─ 📁 main
  └─ 📄 MainApp.java
package com.miapp.model; import java.util.Date; import java.io.Serializable; /** * Clase que representa un usuario en el sistema. * Paquete: com.miapp.model */ public class Usuario implements Serializable { private String nombre; private String email; private Date fechaRegistro; public Usuario(String nombre, String email) { this.nombre = nombre; this.email = email; this.fechaRegistro = new Date(); } // Getters y setters public String getNombre() { return nombre; } public void setNombre(String nombre) { this.nombre = nombre; } // Método toString() @Override public String toString() { return "Usuario{" + "nombre='" + nombre + '\'' + ", email='" + email + '\'' + '}'; } }

// ESTRUCTURA DE DIRECTORIOS (Sistema de archivos)src/
├── com/
│   └── miapp/
│       ├── model/
│       │   ├── Usuario.java
│       │   └── Producto.java
│       ├── service/
│       │   ├── UsuarioService.java
│       │   └── ProductoService.java
│       ├── util/
│       │   ├── Validador.java
│       │   └── Calculadora.java
│       └── main/
│           └── MainApp.java

// DECLARACIÓN DE PAQUETE (en cada archivo .java)package com.miapp.model;  // En Usuario.java
// IMPORTACIÓN DE CLASESimport java.util.ArrayList;     // Import específicoimport java.util.*;            // Import de todo el paqueteimport com.miapp.model.Usuario; // Import de nuestro propio paquete
// IMPORT ESTÁTICO (para métodos/constantes estáticas)import static java.lang.Math.PI;
import static java.lang.Math.pow;

// CLASE PRINCIPAL QUE USA LOS PAQUETESpackage com.miapp.main;

import com.miapp.model.Usuario;
import com.miapp.model.Producto;
import com.miapp.service.UsuarioService;
import static java.lang.System.out;

public class MainApp {
    public static void main(String[] args) {
        // Usar clases importadas        Usuario usuario = new Usuario("Juan", "juan@email.com");
        UsuarioService service = new UsuarioService();
        
        // Usar import estático        out.println("PI = " + PI);
        out.println("2^3 = " + pow(2, 3));
    }
}

📌 Convenciones de Nombres para Paquetes:

  • Usar dominio inverso: com.empresa.proyecto.modulo
  • Todas las letras en minúscula
  • Evitar palabras reservadas de Java
  • Los nombres deben ser descriptivos y jerárquicos
  • Ejemplo: org.apache.commons.math3.analysis

Manejo de Excepciones

Simulador de Flujo de Excepciones

Las excepciones son eventos que interrumpen el flujo normal de ejecución. Java proporciona un mecanismo robusto para manejarlas.

Bloque try

Código que puede lanzar excepciones


try {
    // Código que puede fallar    int resultado = dividir(10, 0);
    System.out.println("Resultado: " + resultado);
}
Bloque catch

Captura y maneja excepciones específicas


} catch (ArithmeticException e) {
    // Maneja división por cero    System.out.println("Error: " + e.getMessage());
    System.out.println("No se puede dividir por cero");
}
Bloque finally

Se ejecuta siempre, haya o no excepción


} finally {
    // Código de limpieza    System.out.println("Operación finalizada");
    cerrarRecursos();
}
// La salida aparecerá aquí // Haz clic en los botones para ver diferentes escenarios

// JERARQUÍA DE EXCEPCIONESThrowable (clase base)
├── Error (errores graves, no se deben capturar)
│   ├── OutOfMemoryError
│   ├── StackOverflowError
│   └── ...
└── Exception (excepciones recuperables)
    ├── RuntimeException (unchecked - no requiere declaración)
    │   ├── NullPointerException
    │   ├── ArrayIndexOutOfBoundsException
    │   ├── ArithmeticException
    │   ├── IllegalArgumentException
    │   └── ...
    └── IOException (checked - requiere manejo)
        ├── FileNotFoundException
        ├── EOFException
        └── ...

// EJEMPLOS COMPLETOS
// 1. EXCEPCIÓN CHECKED (obligatorio manejar)public void leerArchivo(String ruta) {
    try {
        FileReader reader = new FileReader(ruta);
        // Leer archivo...    } catch (FileNotFoundException e) {
        System.err.println("Archivo no encontrado: " + e.getMessage());
    } catch (IOException e) {
        System.err.println("Error de E/S: " + e.getMessage());
    }
}

// 2. EXCEPCIÓN UNCHECKED (opcional manejar)public int dividir(int a, int b) {
    if (b == 0) {
        throw new ArithmeticException("Divisor no puede ser cero");
    }
    return a / b;
}

// 3. MULTI-CATCH (Java 7+)
try {
    // Código que puede lanzar múltiples excepciones} catch (NullPointerException | IllegalArgumentException e) {
    System.err.println("Error de argumento: " + e.getMessage());
}

// 4. TRY-WITH-RESOURCES (Java 7+)
try (FileReader reader = new FileReader("archivo.txt");
     BufferedReader br = new BufferedReader(reader)) {
    String linea;
    while ((linea = br.readLine()) != null) {
        System.out.println(linea);
    }
} catch (IOException e) {
    System.err.println("Error al leer archivo: " + e.getMessage());
}

// 5. EXCEPCIONES PERSONALIZADASclass SaldoInsuficienteException extends Exception {
    private double saldo;
    private double montoRequerido;
    
    public SaldoInsuficienteException(double saldo, double montoRequerido) {
        super("Saldo insuficiente: $" + saldo + ", se requiere: $" + montoRequerido);
        this.saldo = saldo;
        this.montoRequerido = montoRequerido;
    }
    
    public double getSaldo() { return saldo; }
    public double getMontoRequerido() { return montoRequerido; }
}

// USO DE EXCEPCIÓN PERSONALIZADApublic void retirar(double cantidad) throws SaldoInsuficienteException {
    if (cantidad > saldo) {
        throw new SaldoInsuficienteException(saldo, cantidad);
    }
    saldo -= cantidad;
}

Colecciones Framework

Java Explorador de Colecciones

El Framework de Colecciones proporciona una arquitectura unificada para representar y manipular colecciones.

List - Listas

Colecciones ordenadas que permiten duplicados

Manzana
Banana
Naranja

// ArrayList - Array redimensionableList frutas = new ArrayList<>();
frutas.add("Manzana");
frutas.add("Banana");
frutas.add("Naranja");

// LinkedList - Lista doblemente enlazadaList numeros = new LinkedList<>();
numeros.add(1);
numeros.add(2);
numeros.add(3);

// Características:// • Mantienen orden de inserción// • Permiten acceso por índice// • Permiten elementos duplicados// • Tiempo de acceso: ArrayList O(1), LinkedList O(n)

Set - Conjuntos

Colecciones que no permiten duplicados

Rojo
Verde
Azul

// HashSet - Basado en tabla hashSet colores = new HashSet<>();
colores.add("Rojo");
colores.add("Verde");
colores.add("Azul");
colores.add("Rojo"); // No se agrega (duplicado)
// TreeSet - Ordenado automáticamenteSet numeros = new TreeSet<>();
numeros.add(5);
numeros.add(1);
numeros.add(3);
// Resultado: [1, 3, 5] (ordenado)

// Características:// • No permiten duplicados// • HashSet no mantiene orden// • TreeSet mantiene orden natural// • Operaciones O(1) promedio (HashSet)

Map - Mapas

Almacenan pares clave-valor

nombre "Juan"
edad 25
ciudad "Madrid"

// HashMap - Basado en tabla hashMap capitales = new HashMap<>();
capitales.put("España", "Madrid");
capitales.put("Francia", "París");
capitales.put("Italia", "Roma");

// TreeMap - Ordenado por claveMap edades = new TreeMap<>();
edades.put("Ana", 25);
edades.put("Carlos", 30);
edades.put("Beatriz", 28);
// Ordenado: Ana, Beatriz, Carlos

// Características:// • Claves únicas, valores pueden repetirse// • Acceso rápido por clave O(1)// • HashMap no mantiene orden// • TreeMap mantiene orden por clave
Interfaz Implementación Orden Duplicados Acceso Uso Típico
List ArrayList Por índice O(1) por índice Listas dinámicas
List LinkedList Por índice O(n) por índice Inserciones/eliminaciones frecuentes
Set HashSet No No O(1) promedio Conjuntos sin orden
Set TreeSet Orden natural No O(log n) Conjuntos ordenados
Map HashMap No Claves únicas O(1) promedio Tablas hash
Map TreeMap Por clave Claves únicas O(log n) Mapas ordenados

Enumeraciones (Enum)

Simulador de Enumeraciones

Los enums definen un conjunto fijo de constantes con nombres, mejorando la legibilidad y seguridad del código.

Días de la Semana
LUNES
MARTES
MIÉRCOLES
JUEVES
VIERNES
SÁBADO
DOMINGO
Selecciona un día para ver información
Estados de Pedido
PENDIENTE
PROCESANDO
ENVIADO
ENTREGADO
CANCELADO
Selecciona un estado para ver información

// ENUM BÁSICOpublic enum DiaSemana {
    LUNES, MARTES, MIERCOLES, JUEVES, VIERNES, SABADO, DOMINGO
}

// ENUM CON ATRIBUTOS Y MÉTODOSpublic enum EstadoPedido {
    PENDIENTE("En espera de procesamiento", 1),
    PROCESANDO("En preparación", 2),
    ENVIADO("En camino al cliente", 3),
    ENTREGADO("Entregado satisfactoriamente", 4),
    CANCELADO("Pedido cancelado", 0);
    
    private final String descripcion;
    private final int prioridad;
    
    EstadoPedido(String descripcion, int prioridad) {
        this.descripcion = descripcion;
        this.prioridad = prioridad;
    }
    
    public String getDescripcion() { return descripcion; }
    public int getPrioridad() { return prioridad; }
    
    public boolean esFinal() {
        return this == ENTREGADO || this == CANCELADO;
    }
}

// ENUM CON MÉTODOS ABSTRACTOSpublic enum OperacionMatematica {
    SUMA("+") {
        public double aplicar(double x, double y) { return x + y; }
    },
    RESTA("-") {
        public double aplicar(double x, double y) { return x - y; }
    },
    MULTIPLICACION("*") {
        public double aplicar(double x, double y) { return x * y; }
    },
    DIVISION("/") {
        public double aplicar(double x, double y) { 
            if (y == 0) throw new ArithmeticException("División por cero");
            return x / y; 
        }
    };
    
    private final String simbolo;
    
    OperacionMatematica(String simbolo) {
        this.simbolo = simbolo;
    }
    
    public String getSimbolo() { return simbolo; }
    
    // Método abstracto que cada constante debe implementar    public abstract double aplicar(double x, double y);
}

// USO DE ENUMSpublic class EjemploEnums {
    public static void main(String[] args) {
        // Valores básicos        DiaSemana hoy = DiaSemana.LUNES;
        System.out.println("Hoy es: " + hoy);
        
        // Iterar sobre todos los valores        for (DiaSemana dia : DiaSemana.values()) {
            System.out.println(dia);
        }
        
        // Enums avanzados        EstadoPedido estado = EstadoPedido.PROCESANDO;
        System.out.println("Estado: " + estado);
        System.out.println("Descripción: " + estado.getDescripcion());
        System.out.println("Es estado final: " + estado.esFinal());
        
        // Switch con enum        switch (estado) {
            case PENDIENTE:
                System.out.println("El pedido está pendiente");
                break;
            case PROCESANDO:
                System.out.println("El pedido se está procesando");
                break;
            case ENTREGADO:
                System.out.println("¡Pedido entregado!");
                break;
        }
        
        // Enum con métodos        OperacionMatematica op = OperacionMatematica.SUMA;
        double resultado = op.aplicar(10, 5);
        System.out.println("10 " + op.getSimbolo() + " 5 = " + resultado);
        
        // Comparación de enums        if (estado == EstadoPedido.PROCESANDO) {
            System.out.println("El pedido está en proceso");
        }
        
        // Convertir String a enum        String texto = "ENTREGADO";
        EstadoPedido estadoDesdeTexto = EstadoPedido.valueOf(texto);
        System.out.println("Estado desde texto: " + estadoDesdeTexto);
    }
}

Clases Wrapper

Conversiones entre Tipos Primitivos y Objetos

Las clases wrapper envuelven tipos primitivos en objetos, permitiendo su uso en colecciones y otros contextos que requieren objetos.

Tipo Primitivo Clase Wrapper Tamaño Valor por Defecto Ejemplo
byte Byte 8 bits 0 Byte.valueOf((byte)10)
short Short 16 bits 0 Short.valueOf((short)100)
int Integer 32 bits 0 Integer.valueOf(1000)
long Long 64 bits 0L Long.valueOf(100000L)
float Float 32 bits 0.0f Float.valueOf(3.14f)
double Double 64 bits 0.0d Double.valueOf(3.14159)
char Character 16 bits '\u0000' Character.valueOf('A')
boolean Boolean 1 bit false Boolean.valueOf(true)
Autoboxing/Unboxing
Resultado aparecerá aquí
Métodos Utiles
Resultado aparecerá aquí
Constantes
Resultado aparecerá aquí

// AUTOBOXING Y UNBOXING (Java 5+)
// Autoboxing: conversión automática primitivo → wrapperInteger numeroObj = 42;        // Equivale a: Integer.valueOf(42)
Double precioObj = 19.99;      // Equivale a: Double.valueOf(19.99)
Boolean esVerdadero = true;    // Equivale a: Boolean.valueOf(true)

// Unboxing: conversión automática wrapper → primitivoint numero = numeroObj;        // Equivale a: numeroObj.intValue()
double precio = precioObj;     // Equivale a: precioObj.doubleValue()
boolean valor = esVerdadero;   // Equivale a: esVerdadero.booleanValue()

// MÉTODOS ÚTILES DE CLASES WRAPPER
// 1. Conversión de String a tipos numéricosString textoNumero = "123";
int valorInt = Integer.parseInt(textoNumero);      // 123
double valorDouble = Double.parseDouble("3.14");   // 3.14
boolean valorBool = Boolean.parseBoolean("true");  // true

// 2. Conversión a StringString str1 = Integer.toString(456);        // "456"
String str2 = Double.toString(2.718);       // "2.718"
String str3 = Boolean.toString(false);      // "false"

// 3. Comparación de objetosInteger a = 100;
Integer b = 100;
Integer c = 200;
Integer d = 200;

System.out.println(a == b);     // true (cache de -128 a 127)
System.out.println(c == d);     // false (fuera del cache)
System.out.println(c.equals(d)); // true (comparación por valor)

// 4. Constantes útilesSystem.out.println("Máximo int: " + Integer.MAX_VALUE);     // 2147483647
System.out.println("Mínimo int: " + Integer.MIN_VALUE);     // -2147483648
System.out.println("Bytes en int: " + Integer.BYTES);       // 4
System.out.println("Bits en int: " + Integer.SIZE);         // 32

// 5. Métodos de Characterchar letra = 'A';
System.out.println(Character.isLetter(letra));      // true
System.out.println(Character.isDigit(letra));       // false
System.out.println(Character.isUpperCase(letra));   // true
System.out.println(Character.toLowerCase(letra));   // 'a'

// USO EN COLECCIONES (necesitan objetos)List numeros = new ArrayList<>();
numeros.add(1);    // Autoboxing: int → Integer
numeros.add(2);
numeros.add(3);

int suma = 0;
for (Integer num : numeros) {
    suma += num;   // Unboxing: Integer → int
}
System.out.println("Suma: " + suma);

// COMPARACIÓN SEGURA (evitar NullPointerException)Integer posibleNull = obtenerNumero();
int valorSeguro = (posibleNull != null) ? posibleNull : 0;

// MÉTODOS DE CONVERSIÓN ADICIONALESint hex = Integer.parseInt("FF", 16);      // 255 en hexadecimalint bin = Integer.parseInt("1010", 2);     // 10 en binarioString binStr = Integer.toBinaryString(10); // "1010"
String hexStr = Integer.toHexString(255);   // "ff"

// WRAPPER PARA TIPOS GENÉRICOSpublic class Caja {
    private T valor;
    
    public Caja(T valor) {
        this.valor = valor;
    }
    
    public double obtenerValorDouble() {
        return valor.doubleValue();  // Método de Number    }
}

Caja cajaEntero = new Caja<>(100);
Caja cajaDouble = new Caja<>(3.14);

Juego de Memoria: Conceptos

Encuentra las Parejas de Conceptos

Voltea las cartas para encontrar las definiciones que corresponden a cada concepto.

Puntaje: 0

Ejercicios Prácticos

Sistema de Gestión de Biblioteca

Crea un sistema completo para una biblioteca usando todos los conceptos aprendidos:

  1. Paquetes: Organiza el código en paquetes: com.biblioteca.modelo, com.biblioteca.servicio, com.biblioteca.util
  2. Excepciones: Crea excepciones personalizadas: LibroNoDisponibleException, UsuarioBloqueadoException
  3. Colecciones: Usa HashMap para buscar libros por ISBN y TreeSet para usuarios ordenados
  4. Enums: Define EstadoLibro (DISPONIBLE, PRESTADO, RESERVADO, REPARACION)
  5. Wrappers: Usa clases wrapper para cálculos estadísticos

Sistema de Pedidos con Validación

Implementa un sistema de pedidos con validación robusta:

  1. Excepciones: Crea jerarquía de excepciones para diferentes tipos de errores
  2. Colecciones: Usa ArrayList para items del pedido, HashSet para códigos únicos
  3. Enums: Define EstadoPedido, CategoriaProducto, Prioridad
  4. Wrappers: Implementa cálculos con BigDecimal para precisión monetaria
  5. Validación: Usa métodos de validación de clases wrapper

Analizador de Texto con Colecciones

Crea un analizador de texto que use colecciones de manera avanzada:

  1. HashMap: Contar frecuencia de palabras (palabra → cantidad)
  2. TreeMap: Ordenar palabras por frecuencia descendente
  3. HashSet: Identificar palabras únicas
  4. ArrayList: Almacenar palabras en orden de aparición
  5. Enums: Definir tipos de análisis (FRECUENCIA, UNICAS, LONGITUD)
  6. Wrappers: Usar Character para análisis de caracteres

Sistema de Juego con Todos los Conceptos

Implementa un sistema de juego que integre todos los conceptos intermedios:

  1. Paquetes: Estructura organizada: com.juego.entidades, com.juego.mecanicas, com.juego.util
  2. Excepciones: Excepciones para el juego: JugadorSinVidaException, ItemNoDisponibleException
  3. Colecciones: Usar diferentes colecciones: inventario (ArrayList), logros (HashSet), ranking (TreeMap)
  4. Enums: Definir Dificultad, TipoItem, EstadoJuego
  5. Wrappers: Sistema de puntuación con cálculos precisos

Desafío Integrador

Banco de Preguntas: ¿Cuánto Sabes?

1
2
3
4
5
Pregunta 1: ¿Cuál es la ventaja principal de usar paquetes en Java?
Hacen que el código se ejecute más rápido
Organizan el código y evitan conflictos de nombres
Permiten usar palabras reservadas como nombres de clase
Eliminan la necesidad de usar import

Resumen y Buenas Prácticas

Conceptos Clave

  • Paquetes: Organizan código en namespaces jerárquicos
  • Excepciones: Manejan errores de forma controlada (checked/unchecked)
  • Colecciones: Framework unificado para estructuras de datos
  • List: Colecciones ordenadas que permiten duplicados
  • Set: Colecciones que no permiten duplicados
  • Map: Almacenan pares clave-valor
  • Enums: Conjuntos fijos de constantes con nombres
  • Wrappers: Envuelven tipos primitivos en objetos
  • Autoboxing/Unboxing: Conversiones automáticas primitivo↔wrapper

Buenas Prácticas

  • Usar nombres de paquete basados en dominio inverso
  • Capturar excepciones específicas en lugar de Exception general
  • Usar try-with-resources para recursos auto-cerrables
  • Elegir la colección adecuada según las necesidades
  • Usar enums en lugar de constantes numéricas mágicas
  • Preferir valueOf() sobre constructores para wrappers
  • Usar equals() para comparar wrappers, no ==
  • Documentar excepciones lanzadas por métodos
  • Usar imports específicos en lugar de import *

Errores Comunes

  • No manejar excepciones checked (error de compilación)
  • Capturar Exception en lugar de excepciones específicas
  • Usar == para comparar objetos wrapper fuera del cache
  • Olvidar cerrar recursos en bloques finally
  • Usar ArrayList cuando se necesitan inserciones/eliminaciones frecuentes
  • Confundir HashSet con TreeSet (orden vs velocidad)
  • No sobrescribir equals() y hashCode() en clases usadas en HashSet/HashMap
  • Usar números mágicos en lugar de enums
  • No validar datos al convertir Strings a wrappers

Hoja de Referencia Rápida

Paquetes:
package com.empresa.proyecto;
import java.util.List;
import static java.lang.Math.PI;
Excepciones:
try {
    // código
} catch (IOException e) {
    // manejo} finally {
    // limpieza}
Colecciones:
List lista = new ArrayList<>();
Set conjunto = new HashSet<>();
Map mapa = new HashMap<>();
Enums:
enum Color { ROJO, VERDE, AZUL }
Color c = Color.ROJO;
Wrappers:
Integer i = 42;        // autoboxing
int j = i;            // unboxing
int k = Integer.parseInt("123");

Test Final de Conceptos

Evalúa tu conocimiento

Responde las siguientes preguntas sobre conceptos intermedios de Java

Pregunta 1 de 10

¿Cuál de estas NO es una ventaja de usar paquetes en Java?
Evitar conflictos de nombres de clases
Organizar el código de manera lógica
Mejorar el rendimiento de ejecución
Controlar el acceso a clases