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!

I co się stało?! Program zapytał o kolejną wartość?!

Jak widać nie! Zaraz po wpisaniu ciągu nastąpiło wypełnienie wartościami (interpretowanymi po kolei) kolejnych zapytań do zmiennych, aż do wyczerpania zawartości z bufora klawiatury. Jak widać jest to niebezpieczne, bowiem nie jest to nasze zamierzone działanie przy pobieraniu wartości zmiennych. Pewnie chcielibyśmy, aby przy każdym pytaniu o wartość program zatrzymywał się, a użytkownik wpisywał właściwą wartość.

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ć nastąpiło nieoczekiwane zakończenie działania programu i na pewno nie powinno być takie działanie programu zamierzone przez programistę.

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ą.