Ahora que ya sabemos manejar bien todo el tema de compilados, ejecución, paquetes y hacer jars, vamos con un pequeño programa para ver cómo se lee desde teclado. Java es especialmente críptico en esto.
Al igual que Java nos ofrece System.out para escribir en pantalla, tenemos System.in para leer de ella. System.in es un objeto de una clase de java que se llama InputStream.
Para java, un InputStream es cualquier cosa de la que
se leen bytes. Puede ser el teclado, un fichero, un socket, o cualquier
otro dispositivo de entrada. Esto, por un lado es una ventaja. Si todas
esas cosas son InputStream, podemos hacer código
que lea de ellas sin saber qué estamos leyendo.
Por otro lado, es una pega. Como un InputStream es para
leer bytes, sólo tiene métodos para leer bytes. Nosotros queremos
leer palabras o números del teclado, no bytes. Si escribimos en el
teclado una A mayúscula y la leemos con System.in,
obtendremos un entero de valor 65, que es el valor del byte correspondiente
a la A.
Para java, una clase Reader es una clase que lee caracteres.
Esto se parece más a lo que queremos. Un Reader
tiene métodos para leer caracteres. Con esta clase ya podriamos trabajar.
La pena es que seguimos teniendo System.in, que es un InputStream
y no un Reader.
¿Cómo convertimos el System.in en Reader?.
Hay una clase en java, la InputStreamReader, que nos hace
esta conversión. Para obtener un Reader, únicamente
tenemos que instanciar un InputStreamReader pasándole
en el constructor un InputStream. El código es el
siguiente
Estamos declarando una variable "isr" de tipo InputStreamReader. Creamos un objeto de esta clase haciendo new InputStreamReader(...). Entre paréntesis le pasamos el InputStream que queremos convertir a Reader, en este caso, el System.in
Ya tenemos el Reader. ¿Cómo funciona exactamente?
Con la clase InputStreamReader podríamos apañarnos.
La pega es que nos da los caracteres sueltos. Si estamos leyendo de teclado,
el que usa el programa puede escribir 10 caracteres o 20 o 13. Si usamos
InputStreamReader, como lee caracteres sueltos, Tenemos
que decirle cuántos queremos (que no lo sabemos), o bien ir pidiendo
de uno en uno hasta que no haya más.
Esto es un poco rollo y si sólo tuvieramos la clase InputStreamReader
sería un trozo de código que tendriamos que repetir por muchos
lados. Para el cado concreto de leer de teclado, sería ideal si hubiese
una clase en java que nos lea de golpe todo lo que ha escrito el usuario
de nuestro programa y nos lo diera de un golpe.
Como la gente de Java son muy listos, esa clase existe en Java. Se llama
BufferedReader. El mecanismo para obtener un BufferedReader
a partir de otro Reader cualquiera (por ejemplo el InputStreamReader),
es similar al que usamos antes. Lo instanciamos pasándole en el construtor
el Reader. El código es
El funcionamiento de esta clase es igual que el InputStreamReader. Cuando le pedmos una línea completa de caracteres (un String), ella se lo pide al Reader que tenga dentro, los convierte en String y nos lo devuelve.
Para pedirle un String, se usa el método readLine(). Este método lee todos los caracteres tecleados (recibidos si fuera otro dispositivo de entrada) hasta que encuentra la pulsación de la tecla <INTRO>, <RETURN> o como quieras llamarla.
Esto lee del teclado un String completo y lo guarda en una variable "texto".
Si queremos leer un número del teclado, el usuario escribe por ejemplo 123, con la clase BufferedReader obtendremos un String que contiene "123", es decir, tres caracteres. Eso no se parece en nada a un número 123.
Para convertir el String en un número entero (sin decimales), podemos usar otra clase de Java. La clase Integer vale para muchas cosas, pero entre otras es capaz de convertir un String a int. Siempre que sea posible. Por ejemplo "abc" no se puede convertir a número de ninguna manera.
La clase Integer es muy estricta. Para convertir el String a int necesita que el String sea exactamente un int. Cualquier caracter o letra que tenga el String y no valga, hará que la conversión falle. Por ejemplo, "123a" da fallo por culpa de la "a" del final. Del mismo modo "12 3" da fallo por el espacio entre el "12" y el "3". Bueno, esto en realidad creo que era así de estricto en versiones algo anteriores de java. Las actuales simplemente ignoran lo que va detrás del número.
La conversión se realiza así
Esto intenta convertir texto en un int y si no hay problemas, guarda el resultado en una variable int llamada valor.
Cuando en java puede fallar algo, por ejemplo, la conversión de la cadena en int, suele avisarnos. Esto lo hace "lanzando excepciones". Una excepción es algo que lanza Java para avisarnos que ha habido un problema.
En nuestro código podemos "capturar" esas excepciones y hacer algo para tratar de arreglaro. Por ejemplo, si le pedimos al usuario que meta un número, el usuario escribe "abc", lo leemos e intentamos convertirlo a int, nos salta una excepción. Si capturamos esa excepción, en el código que hagamos para tratarla, podemos avisar al usuario que se ha equivocado un poco al teclear, que lo intente de nuevo y volver a pedirle el número.
Para capturar una excepción, tenemos que hacer el código así
Lo habitual, al menos mientras estamos haciendo nuestro programa, es sacar por pantalla el error, para tener una pista de qué ha fallado. Eso se puede consiguir de las dos formas que he puesto arriba (y otras muchas)
Por un lado, de la forma que ya conocemos, con System.out.println(e); Esto nos saca una línea de texto con el error correspondiente en inglés del bueno.
Por otro lado, llamando al método printStackTrace() de la excepción que se ha provocado. Esta llamada escribe la misma línea de texto de error, pero además nos dice exactamente en qué línea de código se produce el error. Esta información es muy útil si nuestro programa todavía está a medias y no acaba de hacer lo que queremos.
Como ejemplo, vamos a ver un programa completo que pide dos números al usuario, los suma y saca el resultado. El ejemplo es este
Cuando vimos el tema de paquetes, vimos que las clases se pueden agrupar en paquetes. Todas las clases que hemos visto aquí de java relativas a Streams y Readers, están en el paquete java.io. En la primera linea de código, con el import, estamos avisando a java que vamos a usar clases de ese paquete. Es necesario poner esta linea, decirle a java en qué paquetes están las clases, porque en otros paquetes puede haber clases con el mismo nombre, para distinguir unas de otras.
Para verlo funcionando, edita un fichero nuevo que se llame suma.java, copia todo este texto y sálvalo.
Luego lo compilas y lo puedes ejecutar
Si metemos la pata a posta, tendremos esto
Si sabemos un poco de inglés, vemos que obtenemos una excepción "NumberFormatException" para la cadena "asd", es decir, que no puede obtener el número a partir de "asd". En la última línea, vemos que el error se produce en la línea 14 del fichero suma.java, en el método main(). Es decir, si contamos líneas en el código, en la línea
Las otras tres líneas son en las clases de java que se ha producido el error. Normalmente no nos aportan demasiada información.