Tecnicas de Java
Explora los conceptos de Java con un enfoque en las buenas prácticas y patrones de diseño.
Datos Enumerados (Enum)
Los enums en Java son tipos especiales que representan un grupo fijo de constantes. Son perfectos para representar estados, categorías o cualquier conjunto predefinido de valores.
Enum con Estados de Proceso y Métodos
1public enum EstadoProceso {
2 PENDIENTE("En espera de ejecución"),
3 EJECUTANDO("Proceso en curso"),
4 COMPLETADO("Finalizado exitosamente"),
5 ERROR("Falló durante la ejecución");
6
7 private final String descripcion;
8
9 EstadoProceso(String descripcion) {
10 this.descripcion = descripcion;
11 }
12
13 public String getDescripcion() {
14 return descripcion;
15 }
16
17 public boolean puedeCancelar() {
18 return this == PENDIENTE || this == EJECUTANDO;
19 }
20
21 public boolean esFinal() {
22 return this == COMPLETADO || this == ERROR;
23 }
24
25 // Uso de values() y valueOf()
26 public static void mostrarEstados() {
27 for (EstadoProceso estado : EstadoProceso.values()) {
28 System.out.println(estado.name() + ": " + estado.getDescripcion());
29 }
30 }
31}
32
33// Uso en aplicación
34public class GestorProcesos {
35 public static void main(String[] args) {
36 EstadoProceso estado = EstadoProceso.PENDIENTE;
37 System.out.println("Estado: " + estado);
38 System.out.println("Descripción: " + estado.getDescripcion());
39 System.out.println("¿Puede cancelar?: " + estado.puedeCancelar());
40
41 // Conversión desde String
42 EstadoProceso estadoDesdeTexto = EstadoProceso.valueOf("COMPLETADO");
43 System.out.println("Estado desde texto: " + estadoDesdeTexto);
44
45 // Mostrar todos los estados
46 EstadoProceso.mostrarEstados();
47 }
48}Enum con Implementación de Interface
1// Interface para operaciones matemáticas
2public interface Operacion {
3 double aplicar(double a, double b);
4}
5
6// Enum que implementa la interface
7public enum OperacionAritmetica implements Operacion {
8 SUMA {
9 public double aplicar(double a, double b) {
10 return a + b;
11 }
12 },
13 RESTA {
14 public double aplicar(double a, double b) {
15 return a - b;
16 }
17 },
18 MULTIPLICACION {
19 public double aplicar(double a, double b) {
20 return a * b;
21 }
22 },
23 DIVISION {
24 public double aplicar(double a, double b) {
25 if (b == 0) throw new ArithmeticException("División por cero");
26 return a / b;
27 }
28 };
29
30 // Método estático para buscar operación
31 public static OperacionAritmetica desdeSimbolo(String simbolo) {
32 return switch(simbolo) {
33 case "+" -> SUMA;
34 case "-" -> RESTA;
35 case "*" -> MULTIPLICACION;
36 case "/" -> DIVISION;
37 default -> throw new IllegalArgumentException("Símbolo no válido: " + simbolo);
38 };
39 }
40}
41
42// Uso en calculadora
43public class Calculadora {
44 public static void main(String[] args) {
45 double x = 10, y = 3;
46
47 for (OperacionAritmetica op : OperacionAritmetica.values()) {
48 try {
49 double resultado = op.aplicar(x, y);
50 System.out.printf("%s: %.2f %s %.2f = %.2f%n",
51 op.name(), x, op.name().charAt(0), y, resultado);
52 } catch (ArithmeticException e) {
53 System.out.println("Error en " + op.name() + ": " + e.getMessage());
54 }
55 }
56
57 // Uso con símbolo
58 OperacionAritmetica operacion = OperacionAritmetica.desdeSimbolo("*");
59 System.out.println("Resultado: " + operacion.aplicar(5, 4));
60 }
61}Formato de Datos
El formateo de datos es esencial para presentar información de manera legible y profesional. Java ofrece múltiples herramientas para formatear strings, números y fechas.
Formato Avanzado con Locale y Especificadores
1import java.text.DecimalFormat;
2import java.text.NumberFormat;
3import java.time.LocalDateTime;
4import java.time.format.DateTimeFormatter;
5import java.util.Locale;
6
7public class FormatoAvanzado {
8 public static void main(String[] args) {
9 // Formato numérico con diferentes locales
10 double cantidad = 1234567.8912;
11 NumberFormat[] formateadores = {
12 NumberFormat.getNumberInstance(Locale.US), // 1,234,567.891
13 NumberFormat.getNumberInstance(Locale.GERMANY), // 1.234.567,891
14 NumberFormat.getNumberInstance(new Locale("es", "NI")) // 1,234,567.891
15 };
16
17 System.out.println("=== FORMATO NUMÉRICO INTERNACIONAL ===");
18 for (NumberFormat nf : formateadores) {
19 nf.setMaximumFractionDigits(3);
20 System.out.println(nf.format(cantidad));
21 }
22
23 // Formato de moneda
24 NumberFormat monedaUS = NumberFormat.getCurrencyInstance(Locale.US);
25 NumberFormat monedaLocal = NumberFormat.getCurrencyInstance(new Locale("es", "NI"));
26 System.out.println("\n=== FORMATO MONEDA ===");
27 System.out.println("USD: " + monedaUS.format(cantidad));
28 System.out.println("Local: " + monedaLocal.format(cantidad));
29
30 // Formato de fecha y hora avanzado
31 LocalDateTime ahora = LocalDateTime.now();
32 DateTimeFormatter[] formatosFecha = {
33 DateTimeFormatter.ISO_LOCAL_DATE_TIME,
34 DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss"),
35 DateTimeFormatter.ofPattern("EEEE, d 'de' MMMM 'de' yyyy", new Locale("es", "NI")),
36 DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")
37 };
38
39 System.out.println("\n=== FORMATOS DE FECHA ===");
40 for (DateTimeFormatter formato : formatosFecha) {
41 System.out.println(ahora.format(formato));
42 }
43
44 // Formato printf avanzado para reportes
45 System.out.println("\n=== REPORTE TABULAR ===");
46 System.out.printf("%-15s %-10s %12s %12s%n", "Producto", "Categoría", "Precio", "Stock");
47 System.out.println("─".repeat(55));
48 System.out.printf("%-15s %-10s %12.2f %12d%n", "Laptop Dell", "Tecnología", 899.99, 15);
49 System.out.printf("%-15s %-10s %12.2f %12d%n", "Mouse Logi", "Accesorios", 25.50, 100);
50 System.out.printf("%-15s %-10s %12.2f %12d%n", "Monitor 24'", "Tecnología", 299.999, 8);
51
52 // Formato científico y porcentaje
53 System.out.println("\n=== FORMATOS ESPECIALES ===");
54 double velocidad = 299792458;
55 double tasa = 0.075;
56 System.out.printf("Velocidad luz: %e m/s%n", velocidad);
57 System.out.printf("Velocidad luz: %.3e m/s%n", velocidad);
58 System.out.printf("Tasa interés: %.2f%%%n", tasa * 100);
59 System.out.printf("Tasa interés: %.1f%%%n", tasa * 100);
60 }
61}DecimalFormat y Parsing de Datos
1import java.text.DecimalFormat;
2import java.text.ParseException;
3import java.text.DecimalFormatSymbols;
4import java.util.Locale;
5
6public class FormatoDecimalAvanzado {
7 public static void main(String[] args) {
8 // Patrones complejos de DecimalFormat
9 double[] numeros = {1234.567, -9876.543, 0.12345, 1234567.89};
10
11 String[] patrones = {
12 "#,##0.00",
13 "¤#,##0.00;(¤#,##0.00)",
14 "0.###E0",
15 "#,##0.00%",
16 "###,##0.###"
17 };
18
19 System.out.println("=== FORMATOS DECIMALES AVANZADOS ===");
20 for (String patron : patrones) {
21 DecimalFormat df = new DecimalFormat(patron);
22 System.out.println("\nPatrón: " + patron);
23 for (double num : numeros) {
24 System.out.printf(" %12.3f -> %s%n", num, df.format(num));
25 }
26 }
27
28 // DecimalFormat con símbolos personalizados
29 DecimalFormatSymbols simbolosPersonalizados = new DecimalFormatSymbols();
30 simbolosPersonalizados.setDecimalSeparator(',');
31 simbolosPersonalizados.setGroupingSeparator('.');
32 simbolosPersonalizados.setMinusSign('–');
33
34 DecimalFormat dfPersonalizado = new DecimalFormat("#,##0.00", simbolosPersonalizados);
35 System.out.println("\n=== FORMATO PERSONALIZADO ===");
36 System.out.println("Número formateado: " + dfPersonalizado.format(-1234567.891));
37
38 // Parsing de strings numéricos
39 System.out.println("\n=== PARSING DE DATOS ===");
40 String[] entradas = {"1.234,56", "–987.654,32", "45%", "1.234E2"};
41
42 DecimalFormat[] parsers = {
43 new DecimalFormat("#,##0.00", simbolosPersonalizados),
44 new DecimalFormat("#,##0.00", DecimalFormatSymbols.getInstance(Locale.US)),
45 new DecimalFormat("0%"),
46 new DecimalFormat("0.###E0")
47 };
48
49 for (String entrada : entradas) {
50 for (int i = 0; i < parsers.length; i++) {
51 try {
52 Number numero = parsers[i].parse(entrada);
53 System.out.printf("Entrada: %-12s -> Parser %d: %f%n",
54 entrada, i + 1, numero.doubleValue());
55 break;
56 } catch (ParseException e) {
57 // Continuar con el siguiente parser
58 }
59 }
60 }
61
62 // Formato para números muy grandes y muy pequeños
63 System.out.println("\n=== NÚMEROS EXTREMOS ===");
64 double[] extremos = {Double.MAX_VALUE, Double.MIN_VALUE, 0.000000123, 123456789012345.67};
65 DecimalFormat dfCientifico = new DecimalFormat("0.#####E0");
66
67 for (double extremo : extremos) {
68 System.out.printf("Original: %e -> Formateado: %s%n",
69 extremo, dfCientifico.format(extremo));
70 }
71 }
72}Números Aleatorios
La generación de números aleatorios es fundamental para simulaciones, juegos, testing y aplicaciones de seguridad. Java proporciona varias formas de generar números aleatorios.
Simulación con Random y Análisis Estadístico
1import java.util.Random;
2import java.util.Arrays;
3
4public class SimulacionEstadistica {
5 public static void main(String[] args) {
6 Random random = new Random(42); // Semilla fija para reproducibilidad
7
8 // Simulación de lanzamiento de dados
9 System.out.println("=== SIMULACIÓN DE DADOS ===");
10 int[] frecuenciaDados = new int[13]; // 2-12 para dos dados
11
12 for (int i = 0; i < 10000; i++) {
13 int dado1 = random.nextInt(6) + 1;
14 int dado2 = random.nextInt(6) + 1;
15 int suma = dado1 + dado2;
16 frecuenciaDados[suma]++;
17 }
18
19 System.out.println("Resultados de 10,000 lanzamientos:");
20 for (int i = 2; i <= 12; i++) {
21 System.out.printf("Suma %2d: %4d veces (%.1f%%)%n",
22 i, frecuenciaDados[i], (frecuenciaDados[i] / 10000.0) * 100);
23 }
24
25 // Generación de números con distribución normal (Gaussiana)
26 System.out.println("\n=== DISTRIBUCIÓN NORMAL ===");
27 double[] muestras = new double[1000];
28 double suma = 0;
29
30 for (int i = 0; i < muestras.length; i++) {
31 muestras[i] = random.nextGaussian() * 15 + 100; // μ=100, σ=15
32 suma += muestras[i];
33 }
34
35 double media = suma / muestras.length;
36 System.out.printf("Media: %.2f%n", media);
37
38 // Simulación de Monte Carlo para π
39 System.out.println("\n=== SIMULACIÓN DE MONTE CARLO (π) ===");
40 int puntosCirculo = 0;
41 int totalPuntos = 1000000;
42
43 for (int i = 0; i < totalPuntos; i++) {
44 double x = random.nextDouble() * 2 - 1; // -1 to 1
45 double y = random.nextDouble() * 2 - 1; // -1 to 1
46 if (x * x + y * y <= 1) {
47 puntosCirculo++;
48 }
49 }
50
51 double piAproximado = 4.0 * puntosCirculo / totalPuntos;
52 System.out.printf("π aproximado: %.6f (error: %.6f)%n",
53 piAproximado, Math.abs(Math.PI - piAproximado));
54
55 // Generación de datos de prueba
56 System.out.println("\n=== DATOS DE PRUEBA ALEATORIOS ===");
57 String[] nombres = {"Ana", "Carlos", "María", "José", "Laura", "Miguel"};
58 String[] departamentos = {"Ventas", "TI", "RH", "Finanzas", "Marketing"};
59
60 for (int i = 0; i < 5; i++) {
61 String nombre = nombres[random.nextInt(nombres.length)];
62 String depto = departamentos[random.nextInt(departamentos.length)];
63 double salario = 1500 + random.nextDouble() * 3500; // 1500-5000
64 int antiguedad = random.nextInt(20) + 1;
65
66 System.out.printf("Empleado %d: %-10s | %-10s | $%8.2f | %2d años%n",
67 i + 1, nombre, depto, salario, antiguedad);
68 }
69 }
70}SecureRandom para Aplicaciones Seguras
1import java.security.SecureRandom;
2import java.util.Base64;
3import java.util.HexFormat;
4
5public class SeguridadCriptografica {
6 private static final String CARACTERES_SEGUROS =
7 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_=+";
8
9 public static void main(String[] args) {
10 SecureRandom secureRandom = new SecureRandom();
11
12 // Generación de tokens de sesión
13 System.out.println("=== TOKENS DE SEGURIDAD ===");
14 for (int i = 0; i < 3; i++) {
15 byte[] token = new byte[32]; // 256 bits
16 secureRandom.nextBytes(token);
17 String tokenBase64 = Base64.getUrlEncoder().withoutPadding().encodeToString(token);
18 System.out.printf("Token %d: %s%n", i + 1, tokenBase64);
19 }
20
21 // Generación de contraseñas seguras
22 System.out.println("\n=== CONTRASEÑAS SEGURAS ===");
23 for (int longitud : new int[]{12, 16, 20}) {
24 String contraseña = generarContraseñaSegura(secureRandom, longitud);
25 System.out.printf("Contraseña %d caracteres: %s%n", longitud, contraseña);
26 }
27
28 // Claves criptográficas
29 System.out.println("\n=== CLAVES CRIPTOGRÁFICAS ===");
30 byte[] claveAES = new byte[32]; // AES-256
31 byte[] claveHMAC = new byte[64]; // HMAC-SHA512
32 byte[] iv = new byte[16]; // Vector de inicialización
33
34 secureRandom.nextBytes(claveAES);
35 secureRandom.nextBytes(claveHMAC);
36 secureRandom.nextBytes(iv);
37
38 HexFormat hex = HexFormat.of();
39 System.out.println("Clave AES-256: " + hex.formatHex(claveAES));
40 System.out.println("Clave HMAC: " + hex.formatHex(claveHMAC).substring(0, 32) + "...");
41 System.out.println("IV: " + hex.formatHex(iv));
42
43 // Nonce para prevención de replay attacks
44 System.out.println("\n=== NONCE ÚNICOS ===");
45 for (int i = 0; i < 5; i++) {
46 byte[] nonce = new byte[8]; // 64 bits
47 secureRandom.nextBytes(nonce);
48 System.out.printf("Nonce %d: %s%n", i + 1, hex.formatHex(nonce));
49 }
50
51 // Simulación de OTP (One-Time Password)
52 System.out.println("\n=== CÓDIGOS OTP ===");
53 for (int i = 0; i < 5; i++) {
54 String otp = generarOTP(secureRandom, 6);
55 System.out.printf("OTP %d: %s%n", i + 1, otp);
56 }
57 }
58
59 private static String generarContraseñaSegura(SecureRandom sr, int longitud) {
60 StringBuilder sb = new StringBuilder(longitud);
61 for (int i = 0; i < longitud; i++) {
62 int index = sr.nextInt(CARACTERES_SEGUROS.length());
63 sb.append(CARACTERES_SEGUROS.charAt(index));
64 }
65 return sb.toString();
66 }
67
68 private static String generarOTP(SecureRandom sr, int digitos) {
69 StringBuilder otp = new StringBuilder(digitos);
70 for (int i = 0; i < digitos; i++) {
71 otp.append(sr.nextInt(10)); // 0-9
72 }
73 return otp.toString();
74 }
75}Recursividad
La recursividad es una técnica donde una función se llama a sí misma para resolver un problema dividiéndolo en subproblemas más pequeños. Es elegante pero requiere cuidado para evitar desbordamientos de pila.
Algoritmos de Ordenamiento Recursivos
1import java.util.Arrays;
2
3public class OrdenamientoRecursivo {
4
5 // QuickSort recursivo
6 public static void quickSort(int[] arr, int low, int high) {
7 if (low < high) {
8 int pi = particion(arr, low, high);
9 quickSort(arr, low, pi - 1); // Ordenar elementos antes del pivote
10 quickSort(arr, pi + 1, high); // Ordenar elementos después del pivote
11 }
12 }
13
14 private static int particion(int[] arr, int low, int high) {
15 int pivot = arr[high];
16 int i = low - 1;
17
18 for (int j = low; j < high; j++) {
19 if (arr[j] <= pivot) {
20 i++;
21 intercambiar(arr, i, j);
22 }
23 }
24 intercambiar(arr, i + 1, high);
25 return i + 1;
26 }
27
28 // MergeSort recursivo
29 public static void mergeSort(int[] arr, int left, int right) {
30 if (left < right) {
31 int mid = left + (right - left) / 2;
32 mergeSort(arr, left, mid); // Ordenar primera mitad
33 mergeSort(arr, mid + 1, right); // Ordenar segunda mitad
34 merge(arr, left, mid, right); // Combinar mitades ordenadas
35 }
36 }
37
38 private static void merge(int[] arr, int left, int mid, int right) {
39 int n1 = mid - left + 1;
40 int n2 = right - mid;
41
42 int[] L = new int[n1];
43 int[] R = new int[n2];
44
45 System.arraycopy(arr, left, L, 0, n1);
46 System.arraycopy(arr, mid + 1, R, 0, n2);
47
48 int i = 0, j = 0, k = left;
49 while (i < n1 && j < n2) {
50 if (L[i] <= R[j]) {
51 arr[k++] = L[i++];
52 } else {
53 arr[k++] = R[j++];
54 }
55 }
56
57 while (i < n1) arr[k++] = L[i++];
58 while (j < n2) arr[k++] = R[j++];
59 }
60
61 // Búsqueda binaria recursiva
62 public static int busquedaBinaria(int[] arr, int objetivo, int left, int right) {
63 if (left > right) return -1; // Caso base: no encontrado
64
65 int mid = left + (right - left) / 2;
66
67 if (arr[mid] == objetivo) {
68 return mid; // Caso base: encontrado
69 } else if (arr[mid] > objetivo) {
70 return busquedaBinaria(arr, objetivo, left, mid - 1); // Buscar en izquierda
71 } else {
72 return busquedaBinaria(arr, objetivo, mid + 1, right); // Buscar en derecha
73 }
74 }
75
76 // Fibonacci con memoización para optimización
77 private static long[] memoFibonacci = new long[100];
78
79 public static long fibonacci(int n) {
80 if (n <= 1) return n; // Caso base
81
82 if (memoFibonacci[n] != 0) {
83 return memoFibonacci[n]; // Retornar valor ya calculado
84 }
85
86 memoFibonacci[n] = fibonacci(n - 1) + fibonacci(n - 2);
87 return memoFibonacci[n];
88 }
89
90 private static void intercambiar(int[] arr, int i, int j) {
91 int temp = arr[i];
92 arr[i] = arr[j];
93 arr[j] = temp;
94 }
95
96 public static void main(String[] args) {
97 int[] datos = {64, 34, 25, 12, 22, 11, 90, 5, 77, 30};
98 int[] copia1 = Arrays.copyOf(datos, datos.length);
99 int[] copia2 = Arrays.copyOf(datos, datos.length);
100
101 System.out.println("Array original: " + Arrays.toString(datos));
102
103 // QuickSort
104 quickSort(copia1, 0, copia1.length - 1);
105 System.out.println("QuickSort: " + Arrays.toString(copia1));
106
107 // MergeSort
108 mergeSort(copia2, 0, copia2.length - 1);
109 System.out.println("MergeSort: " + Arrays.toString(copia2));
110
111 // Búsqueda binaria
112 int objetivo = 25;
113 int indice = busquedaBinaria(copia1, objetivo, 0, copia1.length - 1);
114 System.out.printf("Búsqueda binaria de %d: índice %d%n", objetivo, indice);
115
116 // Fibonacci con memoización
117 System.out.println("\nFibonacci con memoización:");
118 for (int i = 0; i <= 20; i++) {
119 System.out.printf("F(%2d) = %d%n", i, fibonacci(i));
120 }
121 }
122}Problemas Clásicos de Recursividad
1import java.io.File;
2import java.util.ArrayList;
3import java.util.List;
4
5public class ProblemasRecursivos {
6
7 // Torres de Hanoi
8 public static void torresHanoi(int n, char origen, char destino, char auxiliar) {
9 if (n == 1) {
10 System.out.println("Mover disco 1 de " + origen + " a " + destino);
11 return;
12 }
13 torresHanoi(n - 1, origen, auxiliar, destino);
14 System.out.println("Mover disco " + n + " de " + origen + " a " + destino);
15 torresHanoi(n - 1, auxiliar, destino, origen);
16 }
17
18 // Permutaciones de un string
19 public static List<String> generarPermutaciones(String str) {
20 List<String> resultado = new ArrayList<>();
21 generarPermutaciones("", str, resultado);
22 return resultado;
23 }
24
25 private static void generarPermutaciones(String prefijo, String sufijo, List<String> resultado) {
26 if (sufijo.length() == 0) {
27 resultado.add(prefijo); // Caso base: permutación completa
28 return;
29 }
30
31 for (int i = 0; i < sufijo.length(); i++) {
32 String nuevoPrefijo = prefijo + sufijo.charAt(i);
33 String nuevoSufijo = sufijo.substring(0, i) + sufijo.substring(i + 1);
34 generarPermutaciones(nuevoPrefijo, nuevoSufijo, resultado);
35 }
36 }
37
38 // Recorrido recursivo de directorios
39 public static void listarArchivos(File directorio, String nivel) {
40 if (!directorio.exists() || !directorio.isDirectory()) {
41 return;
42 }
43
44 File[] archivos = directorio.listFiles();
45 if (archivos == null) return;
46
47 for (File archivo : archivos) {
48 System.out.println(nivel + archivo.getName());
49 if (archivo.isDirectory()) {
50 listarArchivos(archivo, nivel + " "); // Llamada recursiva para subdirectorios
51 }
52 }
53 }
54
55 // Problema de las N-Reinas
56 public static void resolverNReinas(int n) {
57 int[] tablero = new int[n];
58 System.out.println("\nSoluciones para " + n + " reinas:");
59 colocarReina(tablero, 0, n);
60 }
61
62 private static void colocarReina(int[] tablero, int fila, int n) {
63 if (fila == n) {
64 imprimirTablero(tablero); // Caso base: solución encontrada
65 return;
66 }
67
68 for (int col = 0; col < n; col++) {
69 if (esSeguro(tablero, fila, col)) {
70 tablero[fila] = col;
71 colocarReina(tablero, fila + 1, n); // Llamada recursiva para siguiente fila
72 }
73 }
74 }
75
76 private static boolean esSeguro(int[] tablero, int fila, int col) {
77 for (int i = 0; i < fila; i++) {
78 // Misma columna o misma diagonal
79 if (tablero[i] == col || Math.abs(tablero[i] - col) == Math.abs(i - fila)) {
80 return false;
81 }
82 }
83 return true;
84 }
85
86 private static void imprimirTablero(int[] tablero) {
87 for (int fila : tablero) {
88 for (int col = 0; col < tablero.length; col++) {
89 System.out.print(fila == col ? "Q " : ". ");
90 }
91 System.out.println();
92 }
93 System.out.println();
94 }
95
96 // Recursión indirecta ejemplo
97 public static void funcionA(int n) {
98 if (n <= 0) return;
99 System.out.println("A: " + n);
100 funcionB(n - 1); // Llamada a otra función que llama de vuelta
101 }
102
103 public static void funcionB(int n) {
104 if (n <= 0) return;
105 System.out.println("B: " + n);
106 funcionA(n - 2); // Llamada recursiva indirecta
107 }
108
109 public static void main(String[] args) {
110 System.out.println("=== TORRES DE HANOI ===");
111 torresHanoi(3, 'A', 'C', 'B');
112
113 System.out.println("\n=== PERMUTACIONES ===");
114 List<String> permutaciones = generarPermutaciones("ABC");
115 System.out.println("Permutaciones de ABC: " + permutaciones);
116
117 System.out.println("\n=== RECORRIDO DE DIRECTORIOS ===");
118 listarArchivos(new File("."), "");
119
120 System.out.println("\n=== N-REINAS ===");
121 resolverNReinas(4);
122
123 System.out.println("=== RECURSIÓN INDIRECTA ===");
124 funcionA(5);
125 }
126}