Trudno pisać programy, które nie zapewnią interakcji z użytkownikiem. Jedną z form takiej interakcji jest obsługa klawiatury. W języku Java jest kilka możliwości związanych z obsługą klawiatury. Praktycznie operują one na strumieniu wejściowym System.in. Z uwagi jednak na fakt specyficznej trudności w bezpośredniej obsłudze tego strumienia, są dedykowane biblioteki zajmujące się tą tematyką. Jedną z nich jest biblioteka klasy Scanner.
Aby użyć tę bibliotekę należy w programie zdefiniować zmienną tej klasy i powiązać ją ze strumieniem wejściowym System.in. Może to wyglądać tak jak na poniższym listingu:
import java.util.Scanner;
public class Klawiatura {
public static void main(String[] args) {
Scanner klawiatura=new Scanner(System.in);
String linia;
do
System.out.println(linia=klawiatura.nextLine());
while (!linia.equals("."));
}
}
Zaprezentowany wyżej program jest tzw. „papugą” wypisującą na ekran dokładnie tą samą treść jaką wprowadzono za pomocą klawiatury. Mało wysublimowany, ale prezentuje sposób odczytu z klawiatury w sposób wystarczający.
Treść odczytana z klawiatury jest typu String, ale nic nie stoi na przeszkodzie aby za pomocą innych metod dokonać odczytu wartości innych typów. I tak istnieją metody:
- nextByte(); – odczytuje i interpretuje dane z klawiatury jako byte.
- nextShort(); – odczytuje i interpretuje dane z klawiatury jako short.
- nextInt(); – odczytuje i interpretuje dane z klawiatury jako int.
- nextLong(); – odczytuje i interpretuje dane z klawiatury jako long.
- nextDouble(); – odczytuje i interpretuje dane z klawiatury jako double.
- nextFloat(); – odczytuje i interpretuje dane z klawiatury jako float.
- nextBoolean(); – odczytuje i interpretuje dane z klawiatury jako boolean.
- nextBigDecimal(); – odczytuje i interpretuje dane z klawiatury jako typ klasy BigDecimal.
- nextBigInteger();- odczytuje i interpretuje dane z klawiatury jako typ klasy BigInteger.
Sama klasa Scanner zawiera jeszcze kilka innych przydatnych metod, m.in. metody związane z testowaniem zawartości bufora klawiatury na jego ewentualną zajętość pod kątem wskazanego typu np. hasNext(), lub np. hasNextInt();, które to zwracają wynik testu w postaci typu boolean.
Używanie jednak klasy Scanner do bezpośredniego odczytu wartości innych niż String niesie za sobą ryzyko nieoczekiwanego zadziałania programu. Dlatego też w takiej sytuacji nie sposób nie wspomnieć o konieczności walidacji wprowadzonych wartości co do ich zgodności ze spodziewanym typem i zakresem. Rozważmy poniższy przykład:
import java.util.Scanner;
public class ScannerPrzyklad {
public static void main(String[] args) {
Scanner klawiatura=new Scanner(System.in);
int x,y;
System.out.println("Dodawanie dwu liczb");
System.out.print("\nPodaj pierwszą liczbę: "); x=klawiatura.nextInt();
System.out.print("\nPodaj drugą liczbę: "); y=klawiatura.nextInt();
System.out.printf("Dodawane wartości %d i %d mają razem wartość %d. \n\n",x,y,x+y);
}
}
Spodziewamy się działania analogicznego, zgodnego z poniższym zapisem z ekranu:

Zapewne podobny ekran zaobserwujemy także i u nas, gdy inteligentnie wpiszemy wartości… co się jednak stanie, gdy rozpoczniemy inaczej:

Co stanie się po wpisaniu dwu wartości w pierwszym zapytaniu i potwierdzeniu klawiszem Enter? Sprawdźcie to!
Jeden problem z wpisywaniem wartości liczbowych do zmiennych nie wyczerpuje błędów w działaniu programu. A co jeśli wartości wpisanej w ciągu wejściowym nie da się zinterpretować jako liczby?! Jak wtedy zachowa się program?! Przetestuj to:

I jak zachował się program?!
Jak widać z powyższych przykładów ryzykownym jest pozwalanie na niedopracowane pobieranie wartości z klawiatury. Grozi to bądź niepewnym działaniem programu, bądź jego nieoczekiwaną awarią i zamknięciem, co nie sprzyja jego przyjazności dla użytkownika.
Walidacja treści pobieranych
Jednym z zalecanych sposobów jest walidacja pobieranych treści. Zanim przystąpimy do próby pobrania wartości ze strumienia danych i przypisania jej do konkretnego typu należy sprawdzić czy w strumieniu znajduje się ciąg mogący zostać zinterpretowanym jako spodziewana wartość. Można to zrobić tak jak zaprezentowano poniżej:
String linia="";
boolean OK;
do {
OK=true;
System.out.print("\nPodaj pierwszą liczbę: ");
if (klawiatura.hasNextInt()) {
linia=klawiatura.nextLine();
x=Integer.parseInt(linia);
}
else { OK=false; klawiatura.nextLine(); }
} while (!OK);
Wydaje się, że wszystko jest już OK, ale… są też inne sposoby.
Obsługa błędów (wyjątki)
Drugim sposobem to obsługa błędów za pomocą metody try/catch, pozwalająca na przezwyciężenie krytycznego wyjątku pojawiającego się w związku z nieprawidłowymi danymi i zmuszeniem użytkownika do wpisania poprawnych wartości.
import java.util.InputMismatchException; // na początku programu należy dodać podlinkowanie biblioteki obsługi tego typu błędu.
....
boolean OK;
do {
try {
OK=true;
System.out.print("\nPodaj drugą liczbę: ");
y=klawiatura.nextInt();
}
catch (InputMismatchException ex) {OK=false;klawiatura.nextLine();}
}
while (!OK);
Ta metoda ma znamiona bycia niezawodną. Jej działanie polega na:
- sprawdzeniu, czy w buforze strumienia są jakieś nieodczytane znaki, a jeśli tak to usunięcie ich metodą .nextLine();
- wejściu do pętli z przynajmniej raz uruchomionym kodem ustawiającym spodziewany wynik operacji w znaczniku OK na true; oraz wychwytującym spodziewaną zmienną typu int ze bufora strumienia.
- w pętli znajduje się klauzura try/catch czekająca na pojawienie się błędu typu: InputMismatchException 1należy dodać bibliotekę obsługi tego błędu:
import java.util.InputMismatchException;. W przypadku pojawienia się tego błędu następuje uruchomienie sekwencji catch{OK=false;klawiatura.nextLine();}, czyli ustawienie flagi OK na false;, co spowoduje ponowne wywołanie pętli, przy sprawdzeniu warunku na jej końcu. Dodatkowo czyścimy wszystkie błędne treści z bufora strumienia poprzez ich odczytanie za pomocą metody .nextLine();. - podanie prawidłowych wartości nie zmieni znacznika OK, i pozwoli wyjść z pętli celem realizacji podstawowych założeń programu.
Można też powiązać pierwszy i drugi sposób tworząc program reagujący na błędy wynikające z pobrania niewłaściwego co do zinterpretowania ciągu z bufora strumienia wejściowego oraz dla wszelkiej pewności obsługujący błędy parsowania w klasie osłonowej Integer. Ten fragment kodu może wyglądać następująco:
do {
try {
OK=true;
System.out.print("\nPodaj trzecią liczbę: ");
linia=klawiatura.nextLine();
x=Integer.parseInt(linia);
}
catch (InputMismatchException | NumberFormatException ex) {OK=false;}
} while (!OK);
Niezależnie, który sposób będzie brany pod uwagę, jest rzeczą absolutnie pewną, iż brak kontroli (walidacji) tudzież obsługi ewentualnie generowanych błędów w klauzurze try/catch są poważnymi błędami (zaniedbaniami) programistycznymi i nigdy nie powinny mieć miejsca.
Tak więc jest niedopuszczalną kwestią, aby używać jedynie metod probierczych, bez zagwarantowania bezpiecznej ewaluacji ich wyników w ramach programu. Od tej chwili brak właściwej obsługi odczytu z klawiatury obniżał będzie ocenę za programy.
Zadania do samodzielnego wykonania:
1. Napisz program, który z klawiatury pobierze zdanie, a następnie wypisze je wspak.
2. Napisz program, który pobrane z klawiatury zdanie wypisze każdy wyraz w osobnej linii;
3. Napisz program zliczający ilość samogłosek i spółgłosek we wprowadzanym z klawiatury zdaniu i wypisujący te wartości.
4. Napisz program, który realizuje funkcje kalkulatora +, -, *, / obliczając te działania na parze argumentów pobranych z klawiatury.
5. Napisz program podobny do powyższego, ale realizujący funkcje matematyczne poprzez wybór z menu.
6. Napisz program, który policzy ilość słów i liter w zdaniu i wypisze je. (spacje i znaki specjalne się nie liczą!)
7. Napisz program, który pobierze ciąg znakowy (zdanie), oraz kod numeryczny i dokona szyfrowania danych w ciągu znakowym (wyświetli zdanie zaszyfrowane). Kontrolną operacją będzie napisanie drugiej części programu, który tym samym kodem rozszyfruje zdanie i przedstawi je na ekranie do sprawdzenia. Zaproponuj metodę szyfrującą.

