Foro sobre Java SE > Problema con nextLine
Si quitas este codigo:
System.out.printf( "El salario mensual de %s %s es: $%.2fn" , empleado1.obtenerNombre() ,empleado1.obtenerApellido(), empleado1.obtenerSalario() );
Tambien te pasa?
Un saludo
Saludos.
A penas comencé a estudiar Java hace tres semanas, por lo que quizás mi respuesta no es la más convincente.
Creo el problema está en los métodos nextString, nextInt, etz, de la clase Sanner.
Según la documentación Java http://docs.oracle.com/javase/7/docs/api/java/util/Scanner.html#nextDouble%28%29 , If the translation is successful, the scanner advances past the input that matched.
Parece que después de una llamada al método nextString() el scaner se queda apuntando al byte nueva línea y cuando llamas al método nextLine() eso es lo que ve y ejecuta sin esperar más entrada.
Esto es lo que deduzco, pues probando el código que publicaste, si al entrar el salario del primer empleado pones por ejemplo: 45 Miguel todo se ejecuta bien luego salta la pregunta del nombre del segundo empleado como te ha pasado, pero cuando se imprime los datos verás que en el nombre del segundo empleado está miguel.
No sé si es un error de Java o fue pensado para que sea así y no estás utilizando los métodos adecuados, mi conclusión es por deducción probando una y otra vez tu código y leyendo la documentación Java.
No sé donde revisar la implementación de esos métodos si sabes me dices y así los dos aprendemos.
Buenas,
He probado tu codigo y realmente estoy perplejo. He estado depurando y da realmente la sensacion de que el metodo .nexDouble() de Java tiene un bug, ya que no limpia los bytes de entrada completamente una vez recuperado el valor (se deja el final, el salto de linea). Esto provoca que cuando se hace el siguiente readLine entende que se ha pulsado el Enter sin meter nada.
@choces, puedes echarle tu tambien un vistazo para ver si podemos confirmarlo?
P.D: Para solucionarlo en este caso, simplemente hay que usar el readLine haciendo casting:
salario = Double.valueOf(entrada.nextLine());
...
Un saludo
Saludos.
Incluso si se pone tres nextDouble() seguidos cuando se llama al método del primero el programa salta todos los demás.
Por ejemplo
Sacanner MeSaltoTodo = Scanner(System.io);
a = MeSaltoTodo.nextDouble;
b = MeSaltoTodo.nextDouble;
c = MeSaltoTodo.nextDouble;
Si en la llamada del primer método tecleamos antes del return
34 56 12;
Ni el segundo, ni el tercer método esperan la entrada por teclado
y cuando vemos los valores de a b y c
resultan:
a=34;
b=56;
c=12;
Creo que hay algo que no está bien y va encontra del sentido lógico de la programación.
No veo nada raro con el nextDouble ni con nextLine. Sigue un test:
public class Test {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
double numero1 = scanner.nextDouble();
double numero2 = scanner.nextDouble();
String cadena = scanner.nextLine();
System.out.println("numero1: " + numero1);
System.out.println("numero2: " + numero2);
System.out.println("cadena: " + cadena);
}
// Entrada por teclado: 5554 88887 adfghj <Return>
// Salida por consola:
// numero1: 5554.0
// numero2: 88887.0
// cadena: adfghj
}
Cuando se trata de un scanner para varias entradas directas por teclado, creo que lo más aconsejable es que se lea la línea completa como cadena, se haga un split, y se realice un parser sobre el resultado del split, para analizar uno a uno los elementos introducidos.
De esa manera se evitan las "rarezas" de la clase Scanner para entradas de teclado con mezclas de números y cadenas, por ejemplo, y se puede procesar con más seguridad lo que se haya introducido: discriminar números de cadenas, orden de los datos, etc.
Buenas choces,
A tras ejecutar tu ejemplo y tras introducir los dos números, no me pide nunca la cadena, sino que ya me da directamente el resultado. Esta es mi salida:
43
54
numero1: 43.0
numero2: 54.0
cadena:
Ese es precisamente el problema. Si no se puede mezclar nextDouble (o cualquier next*(), que al final llaman al método privado readInput() ) con nextLine por si hay "rarezas", entonces estamos arreglados. Si a mi se me ocurriera meter una "rareza" así en producción, se me caría el pelo ("he hecho esta API, pero no mezcleis unos métodos con otros, por favor").
Desde mi punto de vista esto es un bug o al menos una inconsistencia en el comportamiento de Scanner. Una "rareza", era el arco de iglesia que había que hacer para leer de consola antes de java 1.5 y la aparición de Scanner y Console (bufferedreader de inputreader sobre system.in :- S ).
Un saludo
No entiendo eso de que "no te pide la cadena"...
Yo introduzco esa línea, tal cual, y después de escribir la cadena, pulso el Return.
Los nextDouble y similares buscan elementos en la misma línea hasta que no hay más.
Si, me refiero a que en el programa lo normal es que pidas un dato, lo introduzcas y des al Enter.
Yo hago 43->Enter->52->Enter y termina.
Pero por lo que me has comentado ya entiendo bien porqué lo hace así (hay que hacer "43 52 micadena"->Enter). Me sigue pareciendo un tanto confuso (y de hecho he visto ya muchos foros en los que no se daba respuesta convincente a este problema), pero lo entiendo. Gracias.
Un saludo
Es confuso, y los Javadocs no me parece que expliquen bien cómo funciona.
Y como haya que introducir un número indeterminado de datos, en la misma línea...
Por eso aconsejaba que, para líneas con múltiples datos, se lea la línea completa como cadena, y se analicen posteriormente los elementos con split.
Saludos.
Acerca de la clase Scanner y su funcionamiento "extraño"
http://puntocomnoesunlenguaje.blogspot.com/2012/08/java-scanner.html
Pues si, no habia visto ese articulo. Buen aporte.
Yo de todos modos, antes de hacer el "truco" de usar un readLine() solo para "limpiar el buffer" considero mas claro leer todos los datos con readLine haciendo castings y parseando las cadenas cuando sea necesario.
Gracias y un saludo
Gracias por las respuestas! Entonces pareciera ser un bug, voy a tener en cuenta sus recomendaciones y tambien lo del buffer, hay muchas cosas que no les entendi muy bien porque la verdad soy muy novato en java. Les agradezco enormemente!
Hola! Este error se produce cuando llamás a nextLine() inmediatamente despues de llamar nextDouble();. Como alguien dijo, lo que pasa es que queda guardado el último enter que se hizo on nextDouble() y cuando se ejecuta nextLine() lo ve y se salta la lectura. Podés solucionarlo llamando a un nextLine antes de la carga sin parámetros. Así:
empleado1.establecerSalario( salario );
System.out.printf( "El salario mensual de %s %s es: $%.2fn" , empleado1.obtenerNombre() ,empleado1.obtenerApellido(), empleado1.obtenerSalario() );
System.out.println( "Introduzca nombre para empleado2: " );
entrada.nextLine();//Sólo para eliminar el enter almacenado
nombre = entrada.nextLine();
Espero que te sirva.
Hola gente! Como están?
Soy un recién iniciado en todo lo que es Java, la verdad lo encuentro muy interesante!
Estoy aprendiendo con el libro "Como programar en Java" de Deitel. Muy bueno!
El problema es el siguiente, estoy usando netbeans, creo 2 objetos de una clase "Empleado", a los mismos les asigno nombre, apellido, y salario mensual.
Primero para el empleado1 asigno nombre, apellido, salario... hasta ahi va barbaro, el problema surge cuando salta al empleado2, cuando pide el nombre es como que "salta" la parte donde ingreso por teclado el nombre y automaticamente me muestra para el apellido, ingreso normalmente el apellido y luego el salario correspondiente.
Al momento de mostrar los atributos de cada objeto, en empleado1 se ve barbar, pero en empleado2 me muestra solo el apellido y el salario (obviamente no el nombre porque no lo pude ingresar porque lo saltea)
La verdad que quiero dejar andando esto antes de continuar leyendo y me esta partiendo la cabeza ^^
Saludos y gracias!!
package Clases;
//@author Marcos
public class Empleado
{
//Parámetros:
private String primerNombre;
private String apellidoPaterno;
private double salarioMensual;
//Constructor de la clase:
public Empleado(String nombre, String apellido, double salario)
{
if ( salario > 0 )
salarioMensual = salario;
primerNombre = nombre;
apellidoPaterno = apellido;
}
//Set & Get Nombre:
public void establecerNombre( String nombre )
{
primerNombre = nombre;
}
public String obtenerNombre()
{
return primerNombre;
}
//Set & Get Apellido:
public void establecerApellido( String apellido )
{
apellidoPaterno = apellido;
}
public String obtenerApellido()
{
return apellidoPaterno;
}
//Set & Get Salario:
public void establecerSalario( double salario )
{
salarioMensual = salario;
}
public double obtenerSalario()
{
return salarioMensual;
}
}
package Clases;
//@author Marcos
import java.util.Scanner;
public class PruebaEmpleado
{
public static void main(String[] args)
{
//Crea 2 objetos de Empleado:
Empleado empleado1 = new Empleado( "Juan", "Perez", -3000 );
Empleado empleado2 = new Empleado( "Oscar", "Rodriguez", -1500 );
//Muestra el valor inicial de los objetos:
System.out.printf( "El valor inicial de empleado1 es:n%s %s $%.2fn" , empleado1.obtenerNombre() ,empleado1.obtenerApellido(), empleado1.obtenerSalario() );
System.out.printf( "El valor inicial de empleado2 es:n%s %s $%.2fn" , empleado2.obtenerNombre() ,empleado2.obtenerApellido(), empleado2.obtenerSalario() );
//Crea objeto Scanner:
Scanner entrada = new Scanner(System.in);
String nombre;
String apellido;
double salario;
//Modifica parametros:
System.out.println( "Introduzca nombre para empleado1: " );
nombre = entrada.nextLine();
empleado1.establecerNombre( nombre );
System.out.println( "Introduzca apellido para empleado1: " );
apellido = entrada.nextLine();
empleado1.establecerApellido( apellido );
System.out.println( "Introduzca salario para empleado1: " );
salario = entrada.nextDouble();
empleado1.establecerSalario( salario );
System.out.printf( "El salario mensual de %s %s es: $%.2fn" , empleado1.obtenerNombre() ,empleado1.obtenerApellido(), empleado1.obtenerSalario() );
System.out.println( "Introduzca nombre para empleado2: " );
nombre = entrada.nextLine();
empleado2.establecerNombre( nombre );
System.out.println( "Introduzca apellido para empleado2: " );
apellido = entrada.nextLine();
empleado2.establecerApellido( apellido );
System.out.println( "Introduzca salario para empleado2: " );
salario = entrada.nextDouble();
empleado2.establecerSalario( salario );
System.out.printf( "El salario mensual de %s %s es: $%.2fn" , empleado2.obtenerNombre() ,empleado2.obtenerApellido(), empleado2.obtenerSalario() );
}
}
PD: Si en lugar de nextLine uso next en lugar de nextLine funciona, el problema surgiria si un nombre se compone por dos nombres separados, ademas quiero saber bien por que no funciona el codigo asi como lo escribi. Gracias!!