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 CLASES
import java.util.ArrayList;     // Import específico
import java.util.*;            // Import de todo el paquete
import 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 PAQUETES
package 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 EXCEPCIONES
Throwable (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 PERSONALIZADAS
class 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 PERSONALIZADA
public void retirar(double cantidad) throws SaldoInsuficienteException {
    if (cantidad > saldo) {
        throw new SaldoInsuficienteException(saldo, cantidad);
    }
    saldo -= cantidad;
}

Colecciones Framework

Explorador de Colecciones Java

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 redimensionable
List frutas = new ArrayList<>();
frutas.add("Manzana");
frutas.add("Banana");
frutas.add("Naranja");

// LinkedList - Lista doblemente enlazada
List 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 hash
Set colores = new HashSet<>();
colores.add("Rojo");
colores.add("Verde");
colores.add("Azul");
colores.add("Rojo"); // No se agrega (duplicado)

// TreeSet - Ordenado automáticamente
Set 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 hash
Map capitales = new HashMap<>();
capitales.put("España", "Madrid");
capitales.put("Francia", "París");
capitales.put("Italia", "Roma");

// TreeMap - Ordenado por clave
Map 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ÁSICO
public enum DiaSemana {
    LUNES, MARTES, MIERCOLES, JUEVES, VIERNES, SABADO, DOMINGO
}

// ENUM CON ATRIBUTOS Y MÉTODOS
public 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 ABSTRACTOS
public 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 ENUMS
public 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 → wrapper
Integer 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 → primitivo
int 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éricos
String 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 String
String str1 = Integer.toString(456);        // "456"
String str2 = Double.toString(2.718);       // "2.718"
String str3 = Boolean.toString(false);      // "false"

// 3. Comparación de objetos
Integer 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 útiles
System.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 Character
char 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 ADICIONALES
int hex = Integer.parseInt("FF", 16);      // 255 en hexadecimal
int bin = Integer.parseInt("1010", 2);     // 10 en binario
String binStr = Integer.toBinaryString(10); // "1010"
String hexStr = Integer.toHexString(255);   // "ff"

// WRAPPER PARA TIPOS GENÉRICOS
public 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