Un programa de consola con menú para añadir, listar y buscar contactos, que comprueba los datos y guarda la agenda en un archivo para la próxima vez.
5 pasos
1 hora y media aproximadamente
105 líneas de código
Lo que vas a construir
Una agenda es la aplicación perfecta para practicar Java «de verdad»: datos con forma propia (un record), una lista que crece, un menú que se repite, búsquedas con streams y un archivo donde guardar. Se ejecuta en la terminal, sin necesidad de proyecto ni de herramientas extra.
Lo que vas a practicar
Datos con record
Listas con ArrayList
Un menú con while, Scanner y switch
Buscar y ordenar con streams
Leer y escribir archivos con Files
Cómo ejecutarlo: Con Java 21 o posterior, guarda el código como Agenda.java y ejecútalo directamente con java Agenda.java (no hace falta compilar antes).
Un record declara en una línea una clase para guardar datos: Java crea solo el constructor, los métodos para leer cada campo (nombre(), telefono()), equals y toString.
Los contactos se guardan en un ArrayList, una lista que crece según se añaden elementos.
Agenda.java
import java.util.ArrayList;
import java.util.List;
public class Agenda {
record Contacto(String nombre, String telefono) {}
public static void main(String[] args) {
List<Contacto> contactos = new ArrayList<>();
contactos.add(new Contacto("Ana López", "600111222"));
contactos.add(new Contacto("Luis Pérez", "611222333"));
for (Contacto c : contactos) {
System.out.println(c.nombre() + " - " + c.telefono());
}
System.out.println(contactos.size() + " contactos");
}
}
Al ejecutarlo
Ana López - 600111222
Luis Pérez - 611222333
2 contactos
2
Paso 2 de 5
Un menú que se repite
El programa muestra un menú en un bucle while hasta que se elige salir. Scanner lee lo que se escribe con nextLine() y el switch con flechas llama al método de cada opción.
La lista y el Scanner pasan a ser campos static de la clase para que todos los métodos puedan usarlos.
Agenda.javaLo nuevo, resaltado
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Agenda {
record Contacto(String nombre, String telefono) {}
static final List<Contacto> contactos = new ArrayList<>();
// Con el juego de caracteres de la consola, para que las tildes se lean bien también en Windows
static final Scanner teclado = new Scanner(System.in,
System.console() != null ? System.console().charset() : Charset.defaultCharset());
public static void main(String[] args) {
boolean seguir = true;
while (seguir) {
System.out.print("\n1) Añadir 2) Listar 3) Salir\nOpción: ");
String opcion = teclado.nextLine().trim();
switch (opcion) {
case "1" -> anadir();
case "2" -> listar();
case "3" -> seguir = false;
default -> System.out.println("Opción no válida");
}
}
System.out.println("¡Hasta luego!");
}
static void anadir() {
System.out.print("Nombre: ");
String nombre = teclado.nextLine().trim();
System.out.print("Teléfono: ");
String telefono = teclado.nextLine().trim();
contactos.add(new Contacto(nombre, telefono));
System.out.println("Guardado.");
}
static void listar() {
if (contactos.isEmpty()) {
System.out.println("La agenda está vacía.");
return;
}
for (Contacto c : contactos) {
System.out.println(c.nombre() + " - " + c.telefono());
}
}
}
Un stream recorre la lista y encadena operaciones: filter se queda con los contactos cuyo nombre contiene el texto buscado (pasando ambos a minúsculas) y toList() junta el resultado.
Al listar, sorted(Comparator.comparing(...)) los ordena por nombre sin distinguir mayúsculas, sin cambiar el orden de la lista original.
Agenda.javaLo nuevo, resaltado
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Scanner;
public class Agenda {
record Contacto(String nombre, String telefono) {}
static final List<Contacto> contactos = new ArrayList<>();
// Con el juego de caracteres de la consola, para que las tildes se lean bien también en Windows
static final Scanner teclado = new Scanner(System.in,
System.console() != null ? System.console().charset() : Charset.defaultCharset());
public static void main(String[] args) {
boolean seguir = true;
while (seguir) {
System.out.print("\n1) Añadir 2) Listar 3) Buscar 4) Salir\nOpción: ");
String opcion = teclado.nextLine().trim();
switch (opcion) {
case "1" -> anadir();
case "2" -> listar();
case "3" -> buscar();
case "4" -> seguir = false;
default -> System.out.println("Opción no válida");
}
}
System.out.println("¡Hasta luego!");
}
static void anadir() {
System.out.print("Nombre: ");
String nombre = teclado.nextLine().trim();
System.out.print("Teléfono: ");
String telefono = teclado.nextLine().trim();
contactos.add(new Contacto(nombre, telefono));
System.out.println("Guardado.");
}
static void listar() {
if (contactos.isEmpty()) {
System.out.println("La agenda está vacía.");
return;
}
contactos.stream()
.sorted(Comparator.comparing(Contacto::nombre, String.CASE_INSENSITIVE_ORDER))
.forEach(c -> System.out.println(c.nombre() + " - " + c.telefono()));
}
static void buscar() {
System.out.print("Buscar: ");
String texto = teclado.nextLine().trim().toLowerCase();
List<Contacto> encontrados = contactos.stream()
.filter(c -> c.nombre().toLowerCase().contains(texto))
.toList();
if (encontrados.isEmpty()) {
System.out.println("No hay ningún contacto con «" + texto + "».");
} else {
encontrados.forEach(c -> System.out.println(c.nombre() + " - " + c.telefono()));
}
}
}
Antes de guardar se comprueba que el nombre no esté vacío ni repetido (con anyMatch y equalsIgnoreCase) y que el teléfono tenga 9 cifras con la expresión regular \d{9} (en un texto de Java la barra se escribe doble: "\\d{9}"). Se quitan los espacios para aceptar «600 111 222».
Cada comprobación que falla avisa y termina el método con return, sin llegar a guardar nada.
Agenda.javaLo nuevo, resaltado
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Scanner;
public class Agenda {
record Contacto(String nombre, String telefono) {}
static final List<Contacto> contactos = new ArrayList<>();
// Con el juego de caracteres de la consola, para que las tildes se lean bien también en Windows
static final Scanner teclado = new Scanner(System.in,
System.console() != null ? System.console().charset() : Charset.defaultCharset());
public static void main(String[] args) {
boolean seguir = true;
while (seguir) {
System.out.print("\n1) Añadir 2) Listar 3) Buscar 4) Salir\nOpción: ");
String opcion = teclado.nextLine().trim();
switch (opcion) {
case "1" -> anadir();
case "2" -> listar();
case "3" -> buscar();
case "4" -> seguir = false;
default -> System.out.println("Opción no válida");
}
}
System.out.println("¡Hasta luego!");
}
static void anadir() {
System.out.print("Nombre: ");
String nombre = teclado.nextLine().trim();
if (nombre.isEmpty()) {
System.out.println("El nombre no puede estar vacío.");
return;
}
boolean repetido = contactos.stream().anyMatch(c -> c.nombre().equalsIgnoreCase(nombre));
if (repetido) {
System.out.println("Ya existe un contacto con ese nombre.");
return;
}
System.out.print("Teléfono: ");
String telefono = teclado.nextLine().replace(" ", "");
if (!telefono.matches("\\d{9}")) {
System.out.println("El teléfono tiene que tener 9 cifras.");
return;
}
contactos.add(new Contacto(nombre, telefono));
System.out.println("Guardado.");
}
static void listar() {
if (contactos.isEmpty()) {
System.out.println("La agenda está vacía.");
return;
}
contactos.stream()
.sorted(Comparator.comparing(Contacto::nombre, String.CASE_INSENSITIVE_ORDER))
.forEach(c -> System.out.println(c.nombre() + " - " + c.telefono()));
}
static void buscar() {
System.out.print("Buscar: ");
String texto = teclado.nextLine().trim().toLowerCase();
List<Contacto> encontrados = contactos.stream()
.filter(c -> c.nombre().toLowerCase().contains(texto))
.toList();
if (encontrados.isEmpty()) {
System.out.println("No hay ningún contacto con «" + texto + "».");
} else {
encontrados.forEach(c -> System.out.println(c.nombre() + " - " + c.telefono()));
}
}
}
Al ejecutarlo
1) Añadir 2) Listar 3) Buscar 4) Salir
Opción: 1
Nombre: Ana López
Teléfono: 600 11
El teléfono tiene que tener 9 cifras.
1) Añadir 2) Listar 3) Buscar 4) Salir
Opción: 1
Nombre: Ana López
Teléfono: 600 111 222
Guardado.
1) Añadir 2) Listar 3) Buscar 4) Salir
Opción: 4
¡Hasta luego!
5
Paso 5 de 5
Guardar la agenda en un archivo
Al empezar, cargar lee contactos.csv con Files.readAllLines y crea un contacto por línea (nombre y teléfono separados por punto y coma). Al salir, guardar escribe la lista con Files.write.
Leer y escribir archivos puede fallar (permisos, disco lleno), por eso Java obliga a tratar IOException con try/catch. Se indica UTF_8 para que las tildes se guarden bien en cualquier sistema.
Agenda.javaLo nuevo, resaltado
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Scanner;
public class Agenda {
record Contacto(String nombre, String telefono) {}
static final List<Contacto> contactos = new ArrayList<>();
// Con el juego de caracteres de la consola, para que las tildes se lean bien también en Windows
static final Scanner teclado = new Scanner(System.in,
System.console() != null ? System.console().charset() : Charset.defaultCharset());
static final Path ARCHIVO = Path.of("contactos.csv");
public static void main(String[] args) {
cargar();
boolean seguir = true;
while (seguir) {
System.out.print("\n1) Añadir 2) Listar 3) Buscar 4) Salir\nOpción: ");
String opcion = teclado.nextLine().trim();
switch (opcion) {
case "1" -> anadir();
case "2" -> listar();
case "3" -> buscar();
case "4" -> seguir = false;
default -> System.out.println("Opción no válida");
}
}
guardar();
System.out.println("¡Hasta luego!");
}
static void cargar() {
if (!Files.exists(ARCHIVO)) return;
try {
for (String linea : Files.readAllLines(ARCHIVO, StandardCharsets.UTF_8)) {
String[] partes = linea.split(";", 2);
if (partes.length == 2) contactos.add(new Contacto(partes[0], partes[1]));
}
System.out.println("Cargados " + contactos.size() + " contactos.");
} catch (IOException e) {
System.out.println("No se ha podido leer la agenda: " + e.getMessage());
}
}
static void guardar() {
List<String> lineas = contactos.stream().map(c -> c.nombre() + ";" + c.telefono()).toList();
try {
Files.write(ARCHIVO, lineas, StandardCharsets.UTF_8);
System.out.println("Agenda guardada en " + ARCHIVO + ".");
} catch (IOException e) {
System.out.println("No se ha podido guardar: " + e.getMessage());
}
}
static void anadir() {
System.out.print("Nombre: ");
String nombre = teclado.nextLine().trim();
if (nombre.isEmpty()) {
System.out.println("El nombre no puede estar vacío.");
return;
}
boolean repetido = contactos.stream().anyMatch(c -> c.nombre().equalsIgnoreCase(nombre));
if (repetido) {
System.out.println("Ya existe un contacto con ese nombre.");
return;
}
System.out.print("Teléfono: ");
String telefono = teclado.nextLine().replace(" ", "");
if (!telefono.matches("\\d{9}")) {
System.out.println("El teléfono tiene que tener 9 cifras.");
return;
}
contactos.add(new Contacto(nombre, telefono));
System.out.println("Guardado.");
}
static void listar() {
if (contactos.isEmpty()) {
System.out.println("La agenda está vacía.");
return;
}
contactos.stream()
.sorted(Comparator.comparing(Contacto::nombre, String.CASE_INSENSITIVE_ORDER))
.forEach(c -> System.out.println(c.nombre() + " - " + c.telefono()));
}
static void buscar() {
System.out.print("Buscar: ");
String texto = teclado.nextLine().trim().toLowerCase();
List<Contacto> encontrados = contactos.stream()
.filter(c -> c.nombre().toLowerCase().contains(texto))
.toList();
if (encontrados.isEmpty()) {
System.out.println("No hay ningún contacto con «" + texto + "».");
} else {
encontrados.forEach(c -> System.out.println(c.nombre() + " - " + c.telefono()));
}
}
}
Con tu permiso usamos Google Analytics, que guarda cookies, para saber cuántas personas visitan la web y qué páginas les resultan útiles. No usamos cookies de publicidad y tus archivos nunca salen de tu navegador. Puedes cambiar de opinión cuando quieras en «Configurar cookies», al pie de la página. Más información.