#02 – interakcja z użytkownikiem

By 21 sierpnia 2014Kurs Javy
Wpis-Header (26)

Prawie zawsze program w jakiś sposób dokonuje interakcji z użytkownikiem – prosi o wprowadzenie jakichś danych lub wyświetla jakieś informacje. W tej lekcji dowiemy się jak obsługiwać najprostszą interakcję – za pomocą linii komend.

Linia komend (zwana też wierszem poleceń) to tekstowy tryb obsługi komputera. Zanim w komputerach pojawił się tryb graficzny, pozwalający obsługiwać go za pomocą m.in. myszki, podstawowym sposobem obsługi komputera był tryb tekstowy. Do tej pory jest on wykorzystywany w niektórych aplikacjach, także my z niego skorzystamy na początku.

Lekcja

Pierwszy program

Żeby móc zacząć pisać kod i sprawdzać jak będzie działał, musimy powiedzieć Javie że to, co piszemy może bezpośrednio uruchomić. Szczegółowo omówimy to w kolejnych lekcjach, ale na tą chwilę to, co musisz wiedzieć, to to, że wystarczy dodać do naszej klasy metodę o sygnaturze public static void main(String[] args), a w jej ciele zaimplementować to, co chcemy zrobić. Ponieważ omówienie i szczegółowy opis pojawi się na blogu później, zainteresowanych odsyłam do krótkiego opisu z wyjaśnieniami w języku angielskim. A u nas na tę chwilę wersja instant – fragment kodu:

class HelloWorldApp {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

Aby przypomnieć sobie, jak uruchomić nasz program, możesz zajrzeć na stronę o narzędziach i środowisku.

Pojawia się tutaj pierwszy nieznany nam element, przejdźmy zatem do wyjaśnienia, co on robi.

Wypisywanie danych dla użytkownika

Podstawowym sposobem na wypisywanie czegoś w konsoli są dwie metody: System.out.print(..) oraz System.out.println(..) . Jedyną różnicą jest to, że druga z nich wypisuje też znak końca linii – tzn. kursor w konsoli przechodzi do następnego wiersza zaraz po wypisanym tekście.

Aby wyświetlić użytkownikowi informację „Podaj imię” wystarczy zapisać w naszym programie polecenie:

System.out.print("Podaj imię: ");

Spowoduje to wyświetlenie powyższego pytania w oknie konsoli, a znak zachęty pojawi się na lewo od naszego pytania.

Chcąc wyświetlić dodatkowo wartość pewnej zmiennej, możemy po prostu ją ‚dodać’ (dokonać konkatenacji). Należy jednak uważać, ponieważ nie każdy obiekt możemy w ten sposób dodać lub to, co zostanie wyświetlone będzie niezrozumiałe dla użytkownika.

Integer zmienna = 1;
System.out.print("Wartość zmiennej: " + zmienna);

Tutaj uwaga natury technicznej: używanie polskich znaków w konsoli jest problematyczne, ponieważ dochodzi tutaj kwestia kodowania znaków, które jest różne w różnych systemach (dla zainteresowanych – w systemach Windows używamy cp1250). Oczywiście Java daje narzędzia pozwalające ten problem rozwiązać, ale tematyka ta jest poza obszarem tego kursu, a w dalszych etapach wczytywanie danych będziemy realizowali w bardziej kontrolowany sposób. Dlatego kwestię polskich znaków w oknie konsoli w ramach tego kursu zignorujemy.

Jeśli chodzi o wyświetlenie w oknie konsoli, to jest podstawowy sposób i wszystkie inne korzystają z niego ostatecznie. Oczywiście istnieje wiele możliwości formatowania wyjścia, wyświetlania liczb z określoną precyzją, dat w określonym formacie, ale prawda jest taka, że niewiele aplikacji posiada interfejs tekstowy, z którego korzystają użytkownicy. Nam będzie to potrzebne do nauki kilku kolejnych elementów po czym zajmiemy się bardziej współczesnymi sposobami na interakcję z użytkownikiem. Z tego powodu, poprzestaniemy na tej podstawowej wiedzy. Zainteresowanych odsyłam np. do dokumentacji klasy Formatter, która jest jednym z prostszych a jednocześnie potężnych narzędzi służących do formatowania wyjścia w języku Java.

Uproszczony schemat jak wypisujemy informacje dla użytkownika

Uproszczony schemat jak wypisujemy informacje dla użytkownika

Wczytywanie danych od użytkownika

Jeśli chodzi o wczytywanie danych, mamy zdecydowanie więcej możliwości i opcji. Podstawowy sposób to odczyt bajt po bajcie (czyli znak po znaku) i następnie sklejanie z tego informacji, które chcemy wczytać od użytkownika. Jest to rozwiązanie bardzo elastyczne, ale sprawiające równie dużo kłopotów, szczególnie początkującym programistom. Skorzystamy więc z innej klasy, którą oferuje nam Java – Scanner. Klasa ta jest bardzo uniwersalna, pozwala nam na obsługę konsoli, plików, strumieni (czyli np. danych pobieranych z internetu czy streamingu video), wykorzystamy tylko jej podstawowe funkcjonalności. Ale zachęcam do eksperymentów, nie ugryzie (w przeciwieństwie do Tesli na diecie) :)

Przede wszystkim zdefiniujmy w naszej klasie pole o nazwie sc i typie Scanner (a umiemy już to robić):

static Scanner sc = new Scanner(System.in);

To co robi ta linijka, to deklaruje pole o nazwie sc (nazwa oczywiście jest dowolna, ważne żeby używać jej poprawnie w odpowiednich miejscach w kodzie) i przypisuje do niego nowy obiekt typu Scanner, który jako źródło danych wykorzystuje System.in (tzw. standardowe wejście – stąd możemy uzyskać to, co użytkownik wpisze w konsoli).

Następnie dodajemy do tej klasy metodę (to też umiemy już robić), której będziemy używać za każdym razem, kiedy będziemy potrzebowali wczytać dane od użytkownika. Metoda ta wygląda następująco:

public static String getUserInput() {
    return sc.nextLine();
}

Metoda ta zwraca następną linijkę z wejścia (czyli tego co wpisał użytkownik) za każdym razem kiedy zostanie wywołana. Wykorzystuje do tego metodę nextLine z klasy Scanner (jeśli chcemy wcześniej sprawdzić, czy użytkownik wpisał cokolwiek wcześniej i zabezpieczyć się przed sytuacją ‚zawieszenia’ aplikacji, można użyć metody hasNextLine() z tejże klasy – zachęcam do wyszukania przykładu lub poeksperymentowania samodzielnie).

Uwaga: metoda Scanner.nextLine ‚blokuje się’ dopóki użytkownik nie wpisze czegoś i nie zatwierdzi klawiszem ‚Enter’. Oznacza to, że aplikacja będzie czekała aż użytkownik nie wpisze czegoś i nie zatwierdzi tego enterem. Dopóki to się nie stanie, żadna dalsza akcja nie zostanie wykonana.

Jeśli dodaliśmy opisane wyżej elementy, w dowolnym miejscu naszej klasy możemy napisać np:

System.out.print("Podaj imię: ");
String imieWczytaneOdUzytkownika = getUserInput();

To tyle :) I tak, to naprawdę jest tak proste, tutaj nie ma haczyków ;)

Uproszczony diagram obrazujący wczytywanie danych od użytkownika

Uproszczony diagram obrazujący wczytywanie danych od użytkownika

Dwa słowa o tym, czym jest ‚static’

Jak pewnie zauważyłaś, wszystkie elementy, które tworzyliśmy w kodzie były statyczne – tzn poprzedzaliśmy je słówkiem ‚static’. Dzięki temu, możliwe było ich użycie w metodzie main (czyli głównej metodzie programu), bez konieczności tworzenia nowego obiektu aplikacji. To, że metoda lub pole jest statyczne, oznacza, że jest one ‚własnością’ klasy, a nie konkretnej instancji obiektu. Dzięki temu nie ma potrzeby tworzenia nowego obiektu tylko po to, aby wywołać jakąś metodę. Konsekwencją jest oczywiście to, że takie metody nie mają dostępu do pól konkretnych obiektów, a jedynie do innych pól i metod statycznych. W naszym przypadku nie stanowi to jednak problemu i jest drobnym uproszczeniem, które stosujemy w kodzie.

Zadanie

Utwórz klasę interfejs. Klasa ta powinna być aplikacją (tzn. żeby można było ją uruchomić) i powinna pytać o nastepujące elementy:

  • imię kota
  • opiekun

Oraz zapisywać je w utworzonym na początku działania programu obiekcie kotka (jesli nie pamiętasz jak to zrobić, zajrzyj do lekcji #01, gdzie mówimy o setterach).

Rozwiązanie

Przeglądaj kodPobierz ZIP

Rozwiązania do lekcji są dostępne w serwisie GitHub - użyj przycisków po prawej aby pobrać lub przejrzeć kod do tej lekcji. Jeśli masz wątpliwości, jak posługiwać się Git’em, instrukcje i linki znajdziesz w naszym wpisie na temat Git’a.

2

Licencja Creative Commons

Jeśli uważasz powyższą lekcję za przydatną, mamy małą prośbę: polub nasz fanpage. Dzięki temu będziesz zawsze na bieżąco z nowymi treściami na blogu ( i oczywiście, z nowymi częściami kursu Javy). Dzięki!

  •  
  •  
  •  
  •  
  •  

  • public static String getUserInput() {
    return this.sc.nextLine();
    }

    Koniecznie return this.sc.nextLine(); zamiast return sc.nextLine(); ?

    (Nawiasem mówiąc „this” pojawio się 1-2 odcinki temu, ale widzę, że postanowiłeś przemilczeć co mogłoby znaczyć.
    (Celowo?)
    Ja tam już wiem co to jest, ale zastnawiam się czy totalny nowicjusz się nie pogubi…)

  • public static String getUserInput() {
    return this.sc.nextLine();
    }

    A jeszcze: to nie jest compile error jak próbujesz użyć „this” w statycznym kontekście,
    czyli jak w sumie żaden konkretny obiekt, instance danej klasy, jeszcze nie powstał?

    • Faktycznie, wkradł się błąd, już poprawione w kursie. Dziękujemy za spostrzegawczość!

  • To prawidłowe rozwiązanie pierwszej części zadania – w drugiej części (ostatnie zdanie) należy te dane ‚zapisać’ w obiekcie typu Kot.

    • Michal1511

      Czy teraz kod jest poprawny i wykonałem oba zadania?
      Podaje tylko zmodyfikowany koniec kodu z poprzedniego postu.
      public static void main (String[] args){

      kurs kot = new kurs();

      System.out.println(„Podaj imie kota: „);
      kot.setImie(getUserInput());

      System.out.println(„Podaj imie opiekuna: „);
      kot.setImieOpiekuna(getUserInput());
      }
      }

      • Ogólnie tak, ale wydaje mi się, że zrobiłeś literówkę w tej linijce:

        kurs kot = new kurs();

        Powinno być ( o ile trzymamy się nazw klas poruszanych w kursie ):

        Kot kot = new Kot();

        Wklejając kod do komentarzy, możesz też skorzystać np. z http://pastebin.com/ – dzięki temu będzie on czytelniejszy i z kolorowaniem składni, wystarczy do komentarza wkleić link.

        • Michal1511

          Dzięki za odpowiedź!
          No właśnie jeśli chodzi o nazwę klasy, to ja mam akurat kurs :)
          Co do kodu w komentarzu, tak chciałem zrobić ale zapodziałem, gdzieś link do podobnej strony co podałeś, teraz będę używał już tej stronki :)
          Mam jeszcze pytanie, jak zrobić żeby było tak jak u was w rozwiązaniu, że importujecie na początku kod z poprzedniej lekcji i nie musicie go wpisywać w kod z tego zdania? Bo ja mam wszystko razem, i kod zaczyna robić się nieczytelny a chciałbym mieć tak jak wy :)
          Pozdrawiam!

          • Nie do końca rozumiem pytanie – czy pytasz jak podzielić kod na kilka klas? Jeśli używasz np. Eclipse (lub dowolnego innego IDE), to możesz utworzyć nową klasę i napisać w niej linijkę kodu, która korzysta z drugiej klasy – Eclipse podkreśli, że nie wie o którą klasę chodzi i pozwoli na automatyczne ‚naprawienie’ problemu (czyli tzw. import). Zerknij też na kod odpowiedzi do zadań – tam znajdziesz przykład jak to wygląda w kodzie.

  • Robert Michalski

    Szkoda tylko, że nie ma nigdzie wytłumaczone za co odpowiada „this”. Ponadto w zadaniu należy utworzyć obiekt, a następnie zapisywać od użytkownika pola tego obiektu. Ale gdzie jest wytłumaczone jak tworzyć obiekty? Odpowiedź na to pytanie niestety musiałem znaleźć w innym kursie…

    • Faktycznie, nie wszystkie aspekty są w naszym kursie wyjasnione szczegółowo, ale jest to celowe i wybraliśmy tą drogę głównie dlatego, żeby skupić się na wiedzy praktycznej a nie powtarzać teorii dostępnej w każdym innym kursie. Z tego też powodu każda lekcja zawiera odniesienia do innych stron i informacji w sieci, aby można było uzupełnić swoją wiedzę o potrzebne informacje. Chcąc opisać wszystko obawiam się, że nie byłoby to ani ciekawe ani praktyczne – np. sam rozdział o inicjowaniu obiektów w książce Thinking in Java to kilkadziesiąt stron wiedzy ważnej, ale w dużej części zbędnej na początku drogi jako programista.
      Kurs z czasem na pewno będzie ewoluował i jesteśmy bardzo wdzięczni za wszelkie uwagi, będziemy starali się uzupełniać luki w sposób taki, żeby jednocześnie nie przeładowywać kursu teorią :)

      • Robert Michalski

        Uwag/pytań/feedbacku możecie spodziewać się więcej, gdyż mam zamiar bardzo dokładnie przekopać cały kurs :D Pozdrawiam ;)

  • Chris

    Myślę, że powinniście wytłumaczyć sens korzystania ze specyfikatora static

    • Cześć,
      dzięki za zwrócenie uwagi ;) Uzupełniliśmy wpis o minimalne informacje na ten temat, aby nie rzucać za dużej ilości informacji na raz.

  • Mariuszz

    Próbuje pracować w inteliij IDEA 15.0.4, ale żaden z tych kodów nie działa. Panie Jakubie, czy mógłby Pan to sprawdzić w Intelli?

    • Co to znaczy nie działa? Aby uruchomić klasę main musisz kliknąć lewym i wybrać opcje Run, jak na print screenie z linka. Jak możesz zauważyć, na konsoli (którą trochę przykrywa menu) widać, że aplikacja wypisała wszystko, co zaprogramowaliśmy :) http://prnt.sc/aadqvg

  • Proudmoore

    A tu takie coś wyczarowałem:

    public class AdvScanner {
    static Scanner sc = new Scanner(System.in);
    public static void main(String[] args) {
    ObjectMarine marine = new ObjectMarine();
    System.out.println(„Podaj imie: „);
    if (sc.hasNext()) {
    marine.setImie(sc.next());
    }
    System.out.println(„Podaj wiek: „);
    do {
    try {
    int a;
    a = Integer.parseInt(sc.next());
    if (a > 0) {
    marine.setWiek(a);
    break;
    } else {
    System.out.println(„Nieprawidłowy wiek, spróbuj jeszcze raz”);
    }
    } catch (Exception p) {
    System.out.println(„Nieprawidłowy wiek, użyj cyfr”);
    }
    } while (sc.hasNext());
    }

    • To podejście jak najbardziej zadziała, ale zachęcamy do zapoznania się z lekcjami #3 i #4 – tam znajdziesz więcej opcji jak walidować to, co użytkownik wpisze w aplikacji i jak na to reagować :)

  • Daniel

    import java.util.Scanner;
    Dosyć długo się męczyłem nad akapitem „Wczytywanie danych od użytkownika”. Żeby zdefiniować pole sc:
    static Scanner sc = new Scanner(System.in);
    trzeba zaimportować w/w rzecz.
    W sumie Eclipse,NetBeans podpowiadają co można zrobić, żeby rozwiązać problem, ale dużo czasu mi zajęło dojście to tego, że tak można :)

    • Jak najbardziej masz rację – czasem warto posłuchać, co IDE ma nam do powiedzenia, zdarza się, że ma rację ;)

  • Karol

    package myProject;

    public class pole {
    static Scanner box = new Scanner(System.in);

    public static String getUserInput() {
    return box.nextLine();

    Mam takie coś i w 4 linijce wyświetla mi „Scanner cannot be resolved to a type”

    EDIT: A, ok widzę rozwiązanie niżej.

  • Monika Senderecka

    Witam, czy to jest dobra praktyka?
    Kot nowy = new Kot();
    System.out.println(„Podaj imie kota:”);
    nowy.setImie(getUserInput());
    System.out.println(„Podaj swoje imie”);
    nowy.setImieOpiekuna(getUserInput());
    System.out.println(„Wczytane dane:”);
    System.out.println(„Imie kota: ” + nowy.getImie());
    System.out.println(„Imie Opiekuna: ” + nowy.getImieOpiekuna());
    Nie mam problemu z uruchomieniem ale chciałabym wiedzieć czy, nie będzie mi wstyd pokazać taki kod programiście np. na rozmowie kwalifikacyjnej.

    • Zasadniczo, jeśli na rozmowie rekrutacyjnej ktoś zapyta Cię w jaki sposób wypisać coś na konsoli to powinnaś powiedzieć, że korzystając z biblioteki do logowania (więcej znajdziesz w tym wpisie: http://kobietydokodu.pl/praktyczna-java-biblioteki-do-logowania/). System.out.println() nie jest metodą, z której korzysta się w kodzie produkcyjnym (o tym dlaczego nie, poczytasz m.in. tutaj: http://programmers.stackexchange.com/questions/161194/why-is-using-system-out-println-so-bad).
      Natomiast, na samym początku nauki programowania będziemy z niej korzystać, by móc jak najbardziej uprościć techniczne elementy związane z działaniem aplikacji i skupić się na podstawach samego programowania – takich jak właśnie interakcja z użytkownikiem, pętle, warunki itp. Jest to więc pewnego rodzaju uproszczenie, które powala nam skupić się na innych aspektach, a z drugiej strony w jednoznaczny sposób pokazać, że aplikacją jaką piszemy działa.
      Klasa Scanner również ma głownie zastosowanie w aplikacjach konsolowych. O tym jak przekazuje się informacje od użytkownika w aplikacji webowej piszemy w dalszej części kursu.

      Zachęcamy do kontynuowania nauki Javy razem z naszym kursem (uzupełnionym o wpisy z serii #NiezbędnikJuniora), które w całości pomogą w zdobyciu pracy jako programistka.

      • Monika Senderecka

        dziękuje :)

  • Brakuje Ci importu. Jeśli używasz IDE, to przy tej linijce powinieneś mieć oznaczenie, że jest problem, a po jego kliknięciu możliwość naprawy. Jeśli nie, po prostu dopisz go ręcznie ;)