Intermediate Java concepts

Packages, exceptions, collections, enums and wrapper classes, with interactive examples.

Introduction to Intermediate Concepts

The intermediate concepts in Java are essential tools that let you write more organized, robust and efficient code. Mastering these concepts is crucial for developing professional applications.

Packages

They organize classes into logical namespaces, avoiding name conflicts and making maintenance easier.

Exceptions

They handle errors elegantly, allowing programs to recover from unexpected situations.

Collections

Flexible data structures to efficiently store and manipulate groups of objects.

Enumerations

They define fixed sets of named constants, improving code readability and safety.

Wrapper Classes

They wrap primitive types in objects, allowing their use in contexts that require objects.

Packages (Packages)

Package Structure Explorer

📁 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));
    }
}

📌 Package Naming Conventions:

  • Use reverse domain: com.empresa.proyecto.modulo
  • All letters lowercase
  • Avoid Java reserved words
  • Names should be descriptive and hierarchical
  • Example: org.apache.commons.math3.analysis

Exception Handling

Exception Flow Simulator

Exceptions are events that interrupt the normal execution flow. Java provides a robust mechanism for handling them.

Block try

Code that might throw exceptions


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

Catches and handles specific exceptions


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

Always runs, whether there's an exception or not


} 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;
}

Collections Framework

Java Collections Explorer

The Collections Framework provides a unified architecture for representing and manipulating collections.

List - Lists

Ordered collections that allow duplicates

Manzana
Banana
Orange

// 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);

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

Set - Sets

Collections that don't allow duplicates

Red
Green
Blue

// 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);
// Result: [1, 3, 5] (ordenado)

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

Map - Maps

Store key-value pairs

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

// Features:// • Claves únicas, valores pueden repetirse// • Acceso rápido por clave O(1)// • HashMap no mantiene orden// • TreeMap mantiene orden por clave
Interface Implementation Order Duplicates Access Typical Use
List ArrayList By index Yes O(1) by index Dynamic lists
List LinkedList Por índice O(n) by index Frequent insertions/deletions
Set HashSet No No O(1) average Unordered sets
Set TreeSet Natural order No O(log n) Ordered sets
Map HashMap No Unique keys O(1) average Hash tables
Map TreeMap By key Unique keys O(log n) Ordered maps

Enumerations (Enum)

Enumeration Simulator

Enums define a fixed set of named constants, improving code readability and safety.

Days of the Week
LUNES
MARTES
MIÉRCOLES
JUEVES
VIERNES
SÁBADO
DOMINGO
Select a day to see information
Order States
PENDIENTE
PROCESANDO
ENVIADO
ENTREGADO
CANCELADO
Select a state to see information

// 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);
    }
}

Wrapper Classes

Conversions between Primitive Types and Objects

Wrapper classes wrap primitive types in objects, allowing their use in collections and other contexts that require objects.

Primitive Type Wrapper Class Size Default Value Example
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
Result will appear here
Useful Methods
Result will appear here
Constants
Result will appear here

// 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);

Memory Game: Concepts

Find the Concept Pairs

Flip the cards to find the definitions that match each concept.

Score: 0

Practical Exercises

Library Management System

Create a complete library system using all the concepts learned:

  1. Packages: Organize the code into packages: com.biblioteca.modelo, com.biblioteca.servicio, com.biblioteca.util
  2. Exceptions: Create custom exceptions: LibroNoDisponibleException, UsuarioBloqueadoException
  3. Collections: Use HashMap to look up books by ISBN and TreeSet for sorted users
  4. Enums: Define EstadoLibro (DISPONIBLE, PRESTADO, RESERVADO, REPARACION)
  5. Wrappers: Use wrapper classes for statistical calculations

Order System with Validation

Implement an order system with robust validation:

  1. Exceptions: Create an exception hierarchy for different error types
  2. Collections: Use ArrayList for order items, HashSet for unique codes
  3. Enums: Define EstadoPedido, CategoriaProducto, Prioridad
  4. Wrappers: Implement calculations with BigDecimal for monetary precision
  5. Validation: Use validation methods from wrapper classes

Text Analyzer with Collections

Create a text analyzer that uses collections in advanced ways:

  1. HashMap: Count word frequency (word → count)
  2. TreeMap: Sort words by descending frequency
  3. HashSet: Identify unique words
  4. ArrayList: Store words in order of appearance
  5. Enums: Define analysis types (FRECUENCIA, UNICAS, LONGITUD)
  6. Wrappers: Use Character for character analysis

Game System with All Concepts

Implement a game system that integrates all the intermediate concepts:

  1. Packages: Organized structure: com.juego.entidades, com.juego.mecanicas, com.juego.util
  2. Exceptions: Exceptions for the game: JugadorSinVidaException, ItemNoDisponibleException
  3. Collections: Use different collections: inventory (ArrayList), achievements (HashSet), ranking (TreeMap)
  4. Enums: Define Dificultad, TipoItem, EstadoJuego
  5. Wrappers: Scoring system with precise calculations

Integrative Challenge

Question Bank: How Much Do You Know?

1
2
3
4
5
Question 1: What is the main advantage of using packages in Java?
They make the code run faster
They organize the code and avoid naming conflicts
They allow using reserved words as class names
They eliminate the need to use import

Summary and Best Practices

Key Concepts

  • Packages: Organize code into hierarchical namespaces
  • Exceptions: Handle errors in a controlled way (checked/unchecked)
  • Collections: Unified framework for data structures
  • List: Ordered collections that allow duplicates
  • Set: Collections that don't allow duplicates
  • Map: Store key-value pairs
  • Enums: Fixed sets of named constants
  • Wrappers: Wrap primitive types in objects
  • Autoboxing/Unboxing: Automatic conversions primitive↔wrapper

Best Practices

  • Use package names based on reverse domain
  • Catch specific exceptions instead of general Exception
  • Use try-with-resources for auto-closeable resources
  • Choose the right collection based on your needs
  • Use enums instead of magic numeric constants
  • Prefer valueOf() over constructors for wrappers
  • Use equals() to compare wrappers, not ==
  • Document exceptions thrown by methods
  • Use specific imports instead of import *

Common Errors

  • Not handling checked exceptions (compile error)
  • Catching Exception instead of specific exceptions
  • Using == to compare wrapper objects outside the cache
  • Forgetting to close resources in finally blocks
  • Using ArrayList when frequent insertions/deletions are needed
  • Confusing HashSet with TreeSet (order vs speed)
  • Not overriding equals() and hashCode() in classes used in HashSet/HashMap
  • Using magic numbers instead of enums
  • Not validating data when converting Strings to wrappers

Quick Reference Sheet

Packages:
package com.empresa.proyecto;
import java.util.List;
import static java.lang.Math.PI;
Exceptions:
try {
    // código
} catch (IOException e) {
    // manejo} finally {
    // limpieza}
Collections:
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");

Final Concepts Test

Evaluate your knowledge

Answer the following questions about intermediate Java concepts

Question 1 expression in 10

Which of these is NOT an advantage of using packages in Java?
Avoiding class naming conflicts
Organizing code logically
Improving execution performance
Controlling access to classes