La clase Scanner de Java tiene una pequeña peculiaridad que puede ser bastante molesta.
Cada vez que lees por teclado un valor numérico, por ejemplo en:
this.edad = sc.nextInt();
Para introducir un valor, tu tecleas el valor y luego pulsas la tecla return/intro para que el valor sea recogido por el programa.
Entonces, en el flujo de datos (stream) ahora mismo se encuentran los caracteres de los valores que has introducido, por ejemplo
23, pero también está el caracter que corresponde a la tecla return, que es
\r. (También conocido como retorno de carro
CR)
Es decir, en el stream tendríamos esto:
23\r
Java, cuando lee números con los métodos adecuados (
nextInt(), nextDouble(), nexftFloat(),....) recoge el valor numérico, pero deja en el flujo de datos el caracter
\r porque no es un número, y por lo tanto lo ignora.
Esto significa que después de que hayas leido la edad, en el stream todavía tenemos esto:
\r
El programa sigue su ejecución, con ese
\r en el stream, aburrido y esperando a que venga alguien que no lo ignore, que sí quiera leerlo y recogerlo como valor.
El método
nextLine(), si que lo recogerá, aunque el caracter
\r no tenga representación visual como el resto de caracteres alfanuméricos (
'a', 'G', 'h', '1', '%',.. etc),
nextLine() lo recogerá y lo sacará del flujo de datos.
Porque de hecho, ese
\r es quien le indica a
nextLine() que el usuario ha terminado de introducir la "linea de datos" y ya puede recoger lo que se haya tecleado.
Y aquí es donde viene el problema.
Si tras leer la edad, haces una lectura de valores "no numéricos" , por ejemplo el nombre de un nuevo contacto, con
nextLine():
System.out.println("Dame el nombre");
this.nombre = sc.nextLine();
Este
nextLine() se va a encontrar con la situación de que en el stream ya hay un
\r, el que había quedado ignorado tras leer la edad.
Y entonces
nextLine() se cree que lo que pasa es que el usuario ha pulsado ya la tecla return, cuando en realidad no ha tenido ocasión de teclear nada todavía, y entonces recoge una "cadena vacía" que termina con el caracter
\r (que ahora sí es recogido).
Y por lo tanto el programa continua pasa a la siguiente inserción de datos:
System.out.println("Dame la direccion");
this.direccion = sc.nextLine();
Este
nextLine() si que se encuentra el stream totalmente vacío, así que ahora si que esperará a que el usuario introduzca datos hasta que realmente pulse la tecla return.
Por esto el usuario percibe que Java se ha "saltado" la inserción de datos para el nombre del contacto. Pero en realidad no se la ha saltado, lo que ha ocurrido es que un
\r olvidado en el flujo de datos le ha hecho creer que el usuario ha pulsado la tecla intro.
Este error es muy puñetero y un auténtico dolor de cabeza cuando nos iniciamos en la programación en Java. Porque es tan tonto y absurdo, que no somos capaces de entender cuál es el problema.
Afortunadamente, es sencillisimo de solucionar.
Basta con que hacer un
nextline() "extra" cuando hayamos terminado de introducir datos numéricos.
Este
nextLine() no hace falta que guarde el dato recogido en una variable, simplemente llamamos al método y punto.
this.edad = sc.nextInt();
sc.nextLine();
Así, tras leer la edad y guardarla en la variable
edad, quedará un
\r en el flujo de datos.
Pero el siguiente
nextLine() lo recogerá y como no está asignado a ninguna variable, simplemente el
\r desaparece y ya tenemos el stream limpito para no tener problemas en la próxima lectura de datos.
Pruébalo, pon un
nextLine() extra tras cada lectura de datos numéricos.
Por ejemplo también en el menú:
System.out.println("9-. Salir");
System.out.println("opcion -->");
op = sc.nextInt();
sc.nextLine();//Para limpiar el flujo de datos
switch (op) {
case 1:
Seguro que con esto evitas este problema.
Otra forma de evitarlo es utilizar únicamente el método nextLine() para recoger datos y luego, si quieres valores numéricos, parsearlo, por ejemplo.
En lugar de esto:
this.edad = sc.nextInt();
Hacemos esto:
String datoEdad= sc.nextLine();
this.edad = Integer.parseInt(datoEdad);
Así tienes un valor numérico y evitas dejar
\r ignorados en el flujo de datos.
Son dos formas distintas de esquivar este problema, no hay una mejor que otra. Yo suelo usar la primera forma, la de meter nextLine() extra tras leer números.
Comentanos si esto te ha solucionado el problema, de lo contrario, buscaremos otro motivo.
Un saludo