Twitter: @grzegg
Kategoria: java, Tagi: - - - .

Java [08] – Interakcja z użytkownikiem w terminalu

Niektóre programy działają od początku do końca samodzielnie, ale niektóre wymagają wprowadzania danych przez użytkownika. Zatem warto dowiedzieć się jak program może komunikować się z użytkownikiem, na razie za pomocą terminala.

Ta strona jest częścią materiałów do kursu “Programowanie w Javie z elementami bioinformatyki dla poczatkujących”. Pozostałe materiały znajdziesz tutaj

Skanuj i parsuj

Zacznijmy od rozwiązania zadania z lekcji Java [06] – Zmienne cz. II: typy podstawowe – używanie.

Wzór z którego powinniśmy skorzystać wygląda tak:


gdzie: Ms = Masa substancji [g], Cp – Stężenie procentowe roztworu [%], Mr – Masa roztworu [g] (=objętość roztworu [ml])

Kod programu może wyglądać na przykład tak:

Program działa prawidłowo, jednak nie ma co ukrywać – korzystanie z takiego programu z poziomu kodu zmienianego i kompilowanego za każdym razem, kiedy chcemy obliczyć wynik dla innego zestawu danych, nie jest zbyt wygodne (mówiąc delikatnie). Znacznie lepiej byłoby zmienić program w ten sposób, żeby po uruchomieniu pytał użytkownika o dane a następnie obliczał dla nich wynik.

Dodajmy zatem możliwość bezpośredniego pobierania danych przez terminal. Zacznij od wpisania takiej linii na samej górze metody main:


Scanner skaner = new Scanner(System.in);

Nawet jeśli powyższy kod został poprawnie wpisany, NetBeans sygnalizuje błąd. Po najechaniu kursorem na podkreśloną część komendy otrzymujemy informację:


cannot find symbol
symbol: class Scanner

___
(Alt+Enter shows hints)

Komunikat oznacza, że nie można zlokalizować klasy Scanner, musimy zatem ją dołączyć, inaczej zaimportować. Jeśli naciśniemy Alt+Ctrl, lub klikniemy na ikonie z żarówką po lewej, otrzymamy podpowiedź, co należy zrobić:


Wybieramy pierwszą opcję: Add import for java.util.Scanner. Na górze pliku pojawia się linia:


import java.util.Scanner;

Komenda odpowiada za dołączenie odpowiedniej klasy do programu. Teraz klasa Scanner będzie już widoczna i „rozumiana” przez kompilator i NetBeans. Oczywiście wygenerowaną automatycznie linię kodu można też dopisać ręcznie.

Wróćmy teraz do linii, którą wcześniej dopisaliśmy na początku metody main. Przypomina ona nieco tworzenie i inicjalizację zmiennej. W tym przypadku jednak tworzymy obiekt typu Scanner. Na bliższe wyjaśnienia czym dokładnie jest obiekt i jak się obiekty tworzy, przyjdzie jeszcze czas. Na razie przyjmij, że obiekt to takie coś co może przechowywać pewne informacje i wykonywać określone metody. Skoro utworzyliśmy obiekt typu Scanner, któremu nadaliśmy nazwę skaner (możesz wybrać inną) to czas go użyć. Zmodyfikuj program, tak aby otrzymać następujący kod:

Uruchom program i sprawdź czy działa. Program pyta o poszczególne wartości, które należy wpisać w terminalu. Po wpisaniu wartości zatwierdzamy ją wciskając Enter. Może jednak czekać Cię pewna niespodzianka. Jeśli wpiszesz wartość stężenia np. tak: 2.5 to program przestanie działać a w terminalu pojawi się komunikat:


Podaj stężenie roztworu [%]
2.5
Exception in thread "main" java.util.InputMismatchException
at java.util.Scanner.throwFor(Scanner.java:864)
at java.util.Scanner.next(Scanner.java:1485)
at java.util.Scanner.nextDouble(Scanner.java:2413)
at zmienne.StezeniaInteraktywne.main(StezeniaInteraktywne.java:19)
Java Result: 1

Co się stało? Jeśli Twój komputer ma polskie ustawienia regionalne (a zapewne ma), należy wpisać liczby zmiennoprzecinkowe zgodnie z konwencją używaną w Polsce, a więc używając przecinka zamiast kropki (inaczej niż w kodzie).


Podaj stężenie roztworu [%]
2,5
Podaj masę roztworu [g]
250
Aby otrzymać 250.0 ml roztworu o stężeniu 2.5%
należy rozpuścić 6.25 g substancji.

Przyjrzyjmy się teraz jednej z linii, która została zmodyfikowana:


double stezenieRoztworu = skaner.nextDouble();

Lewa strona wyrażenia jest oczywista. Po prawej stronie używamy utworzony wcześniej obiekt skaner. Pisałem wcześniej, że obiekty potrafią wykonywać metody, to właśnie nazwa wywoływanej metody (nextDouble()) znajduje się po kropce. Wywoływana metoda jest zdefiniowana w klasie, która opisuje obiekty typu Scanner. Tą klasę przed chwilą zaimportowaliśmy do programu. Tak więc, obiekt skaner wykonuje metodę nextDouble(), której zadaniem jest pobranie wpisanej w terminalu (w tym przypadku) liczby i konwersja jej na liczbę typu double. Konwersja jest konieczna, ponieważ tak naprawdę to co użytkownik wpisuje w terminalu jest pobierane jako ciąg znaków.

nextDouble() nie jest oczywiście jedyną metodą udostępnianą przez klasę Scanner, w zależności od potrzeb możemy np. pobierać liczbę i konwertować ją do typu int (nextInt()), long(nextLong()) czy float (tak, będzie to metoda nextFloat()). Jeśli chcemy natomiast po prostu pobrać łańcuch znaków, to przydatna będzie metoda next(). Szybki przegląd dostępnym metod możesz uzyskać w NetBeans wpisując w kodzie skaner. i po wpisaniu kroki wciskając klawisze Ctrl+Spacja. Warto też zajrzeć do oficjalnej dokumentacji Javy 8, w której znajdują się opisy dostępnych klas i metod.

W pewnych sytuacjach możemy nie chcieć od razu konwertować pobranych wartości na określone typy liczbowe (albo inne). Tak może być np wtedy, gdy nie jesteśmy pewni w jakim formacie zostanie liczba wpisana, albo chcemy przeprowadzić jakieś wstępne operacje, które łatwiej przeprowadzić na łańcuchach znaków niż na liczbach. Wtedy można najpierw pobrać wpisaną wartość jako obiekt typu String (przechowujący łańcuch znaków) a dopiero później dokonać ich konwersji. Może to wyglądać np. tak:


// Pobieranie pierwszej liczby z terminala jako łańcucha znaków
String liczba = skaner.next();
// Konwersja łańcucha znaków na double
double stezenieRoztworu = Double.parseDouble(liczba);

Jednak za taką zmianą znów czai się pułapka. Jeśli teraz wpiszesz liczbę używając przecinka (2,5) zamiast kropki (2.5) to znów otrzymasz błąd. Teraz bowiem podczas konwersji nie są brane pod uwagę ustawienia regionalne. Można to zmienić np. tak:


String liczba = skaner.next();
NumberFormat format = NumberFormat.getInstance();
Number liczbaSformatowana= format.parse(liczba);
double stezenieRoztworu = liczbaSformatowana.doubleValue();;

Nie będę teraz omawiał dokładnie jak ten kod działa, ponieważ nieco odbiega to od głównego tematu lekcji.
Jeśli wpiszesz taki kod, okaże się jednak, że NetBeans znów się buntuje. Informacja o błędzie wygląda tak:
„unreported exception ParseException; must be caught or declared to be thrown”
Znów, nie będę teraz wyjaśniał w czym tkwi problem, wrócę do niego przy okazji omawiania obsługi błędów. Na razie możesz zaufać podpowiedzi zaproponowanej przez NetBeans (Alt+Enter):
Add throws clause for java.text.ParseException.

Zauważ, że linia w której jest deklarowana metoda main wygląda teraz tak:


public static void main(String[] args) throws ParseException {

Zadania

  1. Napisz program, który pobierze od użytkownika masę i nazwę rozpuszczalnika oraz trzech substancji rozpuszczonych a następnie wypisze masę roztworu oraz nazwy i stężenie procentowe wszystkich substancji.

  2. Napisz program, który pobiera od użytkownika częstości dwu alleli a następnie oblicza częstości genotypów przewidziane przez prawo Hardy’ego-Weinberga. Potrzebne wzory można znaleźć np. tu albo w Wikipedii.

Ta strona jest częścią materiałów do kursu “Programowanie w Javie z elementami bioinformatyki dla poczatkujących”. Pozostałe materiały znajdziesz tutaj

11 komentarzy Java [08] – Interakcja z użytkownikiem w terminalu

  • Mariusz

    Witam,

    Program napisany, chociaż wydaje mi się, że nie do końca o to chodziło. Za dużo kodu. Nie wiem czy w takiej postaci chciał zobaczyć Pan rozwiązanie.
    Bardzo proszę o komentarz.
    Dziękuję za ten kurs i możliwość rozwoju (i to jeszcze darmowego).

    kod:
    import java.util.Scanner;

    /**
    * Created by ^^ on 2016-03-04.
    */
    public class Substancje {
    public static void main (String args[]){

    Scanner podaj = new Scanner(System.in);

    System.out.println(„Podaj nazwę rozpuszczalnika”);
    String nazwaRozpuszczalnika = podaj.next();

    System.out.println(„Podaj masę rozpuszczalnika w g”);
    double masaRozpuszczalnika = podaj.nextDouble();

    System.out.println(„Podaj nazwę substancji rozpuszczanej nr 1”);
    String substRozpuszoczna1 = podaj.next();

    System.out.println(„Podaj masę substancji rozpuszczanej nr 1 w g”);
    double masaSubstRozpuszczona1 = podaj.nextDouble();

    double masaRoztworu1 = masaRozpuszczalnika + masaSubstRozpuszczona1;
    double stezenie1 = masaSubstRozpuszczona1 / masaRoztworu1;

    System.out.println(„Podaj nazwę substancji rozpuszczanej nr 2”);
    String substRozpuszoczna2 = podaj.next();

    System.out.println(„Podaj masę substancji rozpuszczanej nr 2 w g”);
    double masaSubstRozpuszczona2 = podaj.nextDouble();

    double masaRoztworu2 = masaRozpuszczalnika + masaSubstRozpuszczona2;
    double stezenie2 = masaSubstRozpuszczona2 / masaRoztworu2;

    System.out.println(„Podaj nazwę substancji rozpuszczanej nr 3”);
    String substRozpuszoczna3 = podaj.next();

    System.out.println(„Podaj masę substancji rozpuszczanej nr 3 w g”);
    double masaSubstRozpuszczona3 = podaj.nextDouble();

    double masaRoztworu3 = masaRozpuszczalnika + masaSubstRozpuszczona3;
    double stezenie3 = masaSubstRozpuszczona3 / masaRoztworu3;

    System.out.println(„Substancja rozpuszczona nr 1 to : ” + substRozpuszoczna1 + ” o masie ” + masaRoztworu1 + ” g i stężeniu ” + stezenie1 + ” %”);
    System.out.println(„Substancja rozpuszczona nr 2 to : ” + substRozpuszoczna2 + ” o masie ” + masaRoztworu2 + ” g i stężeniu ” + stezenie2 + ” %”);
    System.out.println(„Substancja rozpuszczona nr 3 to : ” + substRozpuszoczna3 + ” o masie ” + masaRoztworu3 + ” g i stężeniu ” + stezenie3 + ” %”);

    }
    }

  • Croatoan

    Odpowiedź na Zadanie 1:

    /**
    * Created by Croatoan on 25.01.17.
    * IntelliJ IDEA 2016.3.3
    * System Linux Mint 18.1 Cinnamon
    *
    * Napisz program, który pobierze od użytkownika masę i nazwę rozpuszczalnika oraz trzech substancji
    * rozpuszczonych a następnie wypisze masę roztworu oraz nazwy i stężenie procentowe wszystkich substancji
    */
    import java.util.Scanner;

    public class Masa {
    public static void main (String args[]){

    Scanner skanuj = new Scanner(System.in);

    // Pobieranie różnych wartości od użytkownika
    System.out.println(„Podaj masę rozpuszczalnika”);
    double masaRozpuszczalnika = skanuj.nextDouble();

    System.out.println(„Podaj nazwę rozpuszczalnika”);
    String nazwaRozpuszczalnika = skanuj.next();

    System.out.println(„Podaj nazwę pierwszej substancji rozpuszczanej”);
    String nazwaPierwszejSubstancji = skanuj.next();

    System.out.println(„Podaj masę pierwszej substancji rozpuszczanej”);
    double masaPierwszejSubstancji = skanuj.nextDouble();

    double masaRoztworuPierwszejSubstancji = masaRozpuszczalnika + masaPierwszejSubstancji;
    double stezeniePierwszejSubstancji = masaPierwszejSubstancji / masaRoztworuPierwszejSubstancji;

    // Użyłem Ctrl + D w IntelliJ, aby skopiować po zaznaczeniu kolejne dwie substancje
    System.out.println(„Podaj nazwę drugiej substancji rozpuszczanej”);
    String nazwaDrugiejSubstancji = skanuj.next();

    System.out.println(„Podaj masę drugiej substancji rozpuszczanej”);
    double masaDrugiejSubstancji = skanuj.nextDouble();

    double masaRoztworuDrugiejSubstancji = masaRozpuszczalnika + masaDrugiejSubstancji;
    double stezenieDrugiejSubstancji = masaDrugiejSubstancji / masaRoztworuDrugiejSubstancji;

    System.out.println(„Podaj nazwę trzeciej substancji rozpuszczanej”);
    String nazwaTrzeciejSubstancji = skanuj.next();

    System.out.println(„Podaj masę trzeciej substancji rozpuszczanej”);
    double masaTrzeciejSubstancji = skanuj.nextDouble();

    double masaRoztworuTrzeciejSubstancji = masaRozpuszczalnika + masaTrzeciejSubstancji;
    double stezenieTrzeciejSubstancji = masaTrzeciejSubstancji / masaRoztworuTrzeciejSubstancji;

    // Użyłem skrótu sout i Tab pod IntelliJ, aby przyspieszyć wypisanie System.out.println
    System.out.print(„Pierwsza substancja rozpuszczona to : ” + nazwaPierwszejSubstancji);
    System.out.println(” o masie ” + masaRoztworuPierwszejSubstancji + ” i stężeniu ” + stezeniePierwszejSubstancji);

    System.out.print(„Druga substancja rozpuszczona to : ” + nazwaDrugiejSubstancji);
    System.out.println(” o masie ” + masaRoztworuDrugiejSubstancji + ” i stężeniu ” + stezenieDrugiejSubstancji);

    System.out.print(„Trzecia substancja rozpuszczona to : ” + nazwaTrzeciejSubstancji);
    System.out.println(” o masie ” + masaRoztworuTrzeciejSubstancji + ” i stężeniu ” + stezenieTrzeciejSubstancji);

    }
    }

  • tetkris

    O podał pan rozwiązanie zadania, nawet nie wiedziałem nawet że będzie podane o tym gdy je rozwiązałem. Heh ale mam satysfakcje że coś tam uskrobałem sam. Nie jest co prawda to mój pierwszy program, ale jeden z pierwszych w jęzku java.

  • tetkris

    Można jeszcze do programu utworzyć zmienną woda czyli:
    double woda = masaRoztworu – masaSubstancji;

    Wtedy wynik będzie ciekawszy i pomocniejszy bo będzie mówił w jakiej ilości wody należy rozpuścić czyli naprzykład tak:

    Aby otrzymać 700.0 ml roztworu o stężeniu 50.0%
    należy rozpuścić 350.0 g substancji w 350.0ml wody.

  • Karolina

    Dziękuję za kurs. Jest napisany bardzo przejrzyście i są ćwiczenia praktyczne, dzięki którym można dokładnie zrozumieć zasady kodowania w java.
    Mam tylko pytanie.
    Dlaczego stosując kod:
    String liczba = skaner.next();
    NumberFormat format = NumberFormat.getInstance();
    Number liczbaSformatowana= format.parse(liczba);
    double stezenieRoztworu = (double)liczbaSformatowana;
    Kiedy w konsoli wpisuję 3,0 wyskakuje mi błąd:
    Exception in thread „main” java.lang.ClassCastException: java.base/java.lang.Long cannot be cast to java.base/java.lang.Double
    at podstawyJezykaJava.Stezenie3.main
    Tak się dzieje ze wszystkimi liczbami wpisanymi w dormacie x,0 gdzie x to dowolna cyfra. Jeśli wpiszę, np. 3,01 już jest ok.
    Bardzo ładnie proszę o wyjaśnienie :)

    • Grzegorz

      Dzień dobry,
      Przepraszam za późną odpowiedź (urlop) i dziękuję za zwrócenie uwagi.
      Kod należy poprawić na taki:
      double stezenieRoztworu = liczbaSformatowana.doubleValue();
      Teraz powinno działać. Zaraz poprawię w tekście.
      Pozdrawiam serdecznie

  • Lukasz

    Gdzie można zobaczyć rozwiązanie zadnia bo coś nie moge znaleźć.

  • Lukasz

    Gdzie znajdę rozwiązanie zadnia?

  • Slonzok

    Mi się pokazuje taki błąd:
    Exception in thread „main” java.util.NoSuchElementException
    at java.base/java.util.Scanner.throwFor(Scanner.java:937)
    at java.base/java.util.Scanner.next(Scanner.java:1594)
    at java.base/java.util.Scanner.nextDouble(Scanner.java:2564)
    at stezenie.Main.main(Main.java:26)
    double stezenieRoztworu = skaner.nextDouble();

    * What went wrong:
    Execution failed for task ’:run’.
    > Process 'command 'C:\Program Files\Java\jdk-11.0.4\bin\java.exe” finished with non-zero exit value 1

Leave a Reply to Grzegorz Cancel reply