Odpowiedzi na pytania i zadania - rozdziały 1-6

Pliki .java z rozwiązaniami do zadań znajdziesz na GitHubie w katalogu rozwiazania_do_zadan:

Rozwiązania do zadań na GitHub

Rozdział 1 – Wstęp

Pytania

  1. Czym jest proces kompilacji?

    Jest to proces, w którym program, zwany kompilatorem, zamienia kod źródłowy zrozumiały dla człowieka, na kod zrozumiały dla komputera bądź na kod pośredni, który może zostać zinterpretowany przez inny program.

  2. Jak nazywa się kompilator Java i jak się go używa?

    Kompilator języka Java nazywa się javac. Aby go użyć, w linii poleceń wywołujemy komendę javac, której, jako argument, przekazujemy nazwę pliku z kodem źródłowym, który ma zostać skompilowany, np.:

    javac HelloWorld.java
  3. Co powstaje w wyniku kompilacji kodu Java?

    W wyniku kompilacji kodu Java powstaje tzw. bytecode, który może zostać uruchomiony w Maszynie Wirtualnej Java i w niej zinterpretowany.

  4. Jak uruchomić kod Java?

    Należy z linii komend wywołać program java, któremu przekażemy jako argument nazwę skompilowanej klasy (bez podawania rozszerzenia .class), na przykład:

    java HelloWorld
  5. Czym różni się kompilator języka Java od Maszyny Wirtualnej Java?

    Kompilator ma za zadanie skompilować kod Java do bytecode'u, natomiast Maszyna Wirtualna Java ma ten skompilowany bytecode zinterpretować i wykonać.

  6. Skąd wziąć kompilator Java i Maszynę Wirtualną Java?

    Należy zainstalować JDK – Java Development Kit, który jest zestawem aplikacji wymaganych do tworzenia i uruchamiania aplikacji napisanych w języku Java.

  7. Jak powinien nazywać się plik z poniższym kodem Java?
    public class Zagadka {
      public static void main(String[] args) {
        System.out.println("Cegla wazy kilo i pol cegly – ile wazy cegla?");
      }
    }
    

    Plik z tym kodem źródłowym powinien nazywać się Zagadka.java

  8. Mając w katalogu jeden plik, o nazwie TestowyProgram.java, z kodem źródłowym Java, czy poniższa komenda jest poprawna?
    javac TestowyProgram

    Nie jest to poprawna komenda, ponieważ, korzystając z kompilatora języka Java, powinniśmy jako argument przekazać nazwę pliku wraz z rozszerzeniem.

  9. Mając plik o nazwie TestowyProgram.class ze skompilowanym kodem źródłowym Java, czy poniższa komenda jest poprawna?
    java TestowyProgram.class

    Nie jest to poprawna komenda, ponieważ wywołując Maszynę Wirtualną Java powinniśmy podać argument bez rozszerzenia .class.

  10. Jakie jest specjalne znaczenie metody main?

    Jest to specjalna metoda, ponieważ to od niej rozpoczyna się wykonanie każdego programu napisanego w języku Java.

  11. Jak wypisać na ekran tekst "Witajcie!"?

    Należy skorzystać z instrukcji System.out.println. W nawiasach, zawarty w cudzysłowach, powinien znaleźć się komunikat do wyświetlenia, na przykład:

    System.out.println("Pewien komunikat.");
    
  12. Jaki będzie efekt próby kompilacji każdego z poniższych programów?
    public class PrzykladPierwszy {
      public static void main(String[] args)
        System.out.println("Pierwsza zagadka.");
      }
    }
    

    Kompilacja zakończy się błędem, ponieważ na końcu drugiej linii brakuje nawiasu otwierającego ciało metody main.

    public class PrzykladDrugi {
      public static void main(String[] args) {
        System.out.println("Druga zagadka.")
      }
    }
    

    Kompilacja zakończy się błędem, ponieważ na końcu trzeciej linii brakuje średnika kończącego instrukcję wypisującą na ekran komunikat.

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

    Kompilacja zakończy się sukcesem. Ciała metod mogą być puste.

    public class PrzykladCzwarty {
      public static void main(String[] args) {
        System.out.println('Czwarta zagadka.');
      }
    }
    

    Kompilacja zakończy się błędem, ponieważ komunikat do wyświetlenia jest ujęty w apostrofy, zamiast w cudzysłowy.

    public class PrzykladPiaty {
      public static void main(String[] args) {
        System.out.println(”Piata zagadka.”);
      }
    }
    

    Kompilacja zakończy się błędem, ponieważ komunikat do wyświetlenia jest ujęty w specjalne znaki cudzysłowów, używane w edytorach dokumentów, zamiast w zwykłe cudzysłowy.

  13. Które z poniższych stwierdzeń jest prawdziwe?
    1. System Windows rozumie bytecode.

      Nieprawda, to Maszyna Wirtualna Java rozumie bytecode.

    2. Program javac jest potrzebny do uruchomienia skompilowanego kodu Java.

      Nieprawda, program javac służy do kompilacji kodu Java, a nie do jego uruchamiania.

    3. Stringi (łańcuchy tekstowe) powinny być zawarte w apostrofach.

      Nieprawda, łańcuchy tekstowe powinny być ujęte w cudzysłowy.

    4. Ciało metody zawarte jest pomiędzy nawiasami: ( )

      Nieprawda, ciało metody powinno być zawarte w nawiasach klamrowych { }

    5. Jeżeli kompilator napotka problemy w naszym kodzie, to nie wygeneruje pliku z rozszerzeniem .class z naszym skompilowanym kodem.

      Prawda.

Zadania

Wypisz imię

Napisz program, który wypisze na ekran tekst "Czesc", po którym nastąpi Twoje imię (nie używaj polskich znaków).

Rozwiązanie jest podobne do pierwszego programu, jaki napisaliśmy w języku Java – programu HelloWorld.

Możemy, wzorując się na tamtym przykładzie, utworzyć plik o nazwie np. WypiszImie.java, następnie skopiować kod klasy HelloWorld, zmienić nazwę klasy na WypiszImie, a na końcu zmienić wypisywany na ekran komunikat:

public class WypiszImie {
  public static void main (String[] args) {
    System.out.println("Czesc, Przemek");
  }
}

Brak końcowego znaku }

Napisz program, który wypisze na ekran powitanie "Witajcie!". Następnie:

  1. Skompiluj i uruchom program.
  2. Po sprawdzeniu, że program działa, usuń końcowy znak } z kodu źródłowego.
  3. Ponownie spróbuj skompilować kod źródłowy. Jaki będzie efekt?
  4. Spróbuj uruchomić swój program. Jaki będzie efekt?

Zacznijmy od programu, który jest poprawny:

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

Ten program kompiluje się poprawnie, a w wyniku jego uruchomienia na ekranie zobaczymy komunikat Witam!.

Zmieńmy plik zgodnie z wymaganiami opisanymi w zadaniu – usuwamy ostatni znak }:

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

Próba kompilacji tej wersji programu kończy się następującym błędem:

Powitanie.java:4: error: reached end of file while parsing } ^ 1 error

Kompilator nie jest w stanie skompilować kodu, ponieważ jest on niepoprawny – brakuje klamry kończącej klasę Powitanie.

Pozostało ostatnie pytanie z zadania – jaki będzie efekt próby uruchomienia programu Powitanie?

W wyniku wywołania komendy java Powitanie ponownie zobaczymy na ekranie komunikat Witaj!.

Dlaczego tak się stało? Przecież nasz program się nie kompiluje! Podczas próby kompilacji niedziałającego programu, wskutek błędnego kodu, plik z bytecodem .class naszego programu nie został wygenerowany. Gdy więc wywołaliśmy komendę java Powitanie, wykonana została poprzednia wersja naszego programu zapisana w bytecodzie w pliku Powitanie.class, który utworzony został po kompilacji pierwszej, poprawnej wersji programu Powitanie.

Jeżeli jednak usunąłeś/usunęłaś wcześniej wygenerowany plik .class, to zobaczysz następujący komunikat:

Error: Could not find or load main class Powitanie Caused by: java.lang.ClassNotFoundException: Powitanie

Rozdział 2 – Komentarze i formatowanie kodu

Pytania

  1. Jak zapisujemy każdy rodzaj komentarza w Javie?

    Komentarze jednolinijkowe zaczynamy od dwóch znaków slash //

    Komentarze wielolinijkowe zaczynamy od znaków /* a kończymy znakami */

    Komentarze dokumentacyjne zaczynamy od znaków /** a kończymy znakami */

  2. Które komentarze mogą być zagnieżdżone?

    Komentarze jednolinijkowe mogą być w sobie zagnieżdżone.

    Komentarze wielolinijkowe/dokumentacyjne nie mogą być w sobie zagnieżdżone.

    Komentarze jednolinijkowe mogą być zagnieżdżone w komentarzach wielolinijkowych i na odwrót.

  3. Co można by poprawić w poniższym kodzie?
    public class zadaniezleformatowanie {
      public static void main(String[] args) {
      // System.out.println("Witaj");
      // System.out.println("Swiecie!");
      System.out.println("Witaj Swiecie!");
      }
    }
    

    Nazwa klasy powinna być zapisana CamelCasem. Pierwsza litera nazwy klasy także powinna być wielka.

    Zakomentowany kod powinien zostać usunięty.

    Wcięcie przed instrukcją wypisująca na ekran tekst powinno być o jeden poziom większe.

    Kod poprawkach, mógłby wyglądać następująco:

    public class ZadanieZleFormatowanie {
      public static void main(String[] args) {
        System.out.println("Witaj Swiecie!");
      }
    }
    
  4. Która z poniższych nazw zapisana jest Camel-Casem?
    1. nazwa
    2. MojaNazwa
    3. mojanazwa
    4. Wiadomosc

    Tylko pierwsza – chociaż zawiera ona tylko jedno słowo, to jest zgodna z konwencją. Nazwa MojaNazwa powinna mieć pierwszą małą literę, tak samo jak nazwa Wiadomosc. Nazwa mojanazwa powinna mieć wielką pierwszą literę drugiego słowa.

  5. Która z nazw klas jest poprawna?
    1. HelloWorld
    2. helloworld
    3. helloWorld
    4. Helloworld

    Jest to podchwytliwe pytanie – wszystkie powyższe nazwy klas są poprawne, chociaż tylko jedna z nich – HelloWorld – jest zgodna z konwencją nazewnictwa klas w języku Java (czyli Camel-Case + wielka pierwsza litera pierwszego wyrazu).

Zadania

Dopisz komentarze

Dopisz do naszego pierwszego programu, wypisującego tekst "Witaj Swiecie!", kilka komentarzy różnych typów.

Przykład rozwiązania z komentarzami:

// poczatek klasy
public class HelloWorld {
  public static void main(String[] args) {
    /*
      wypisujemy na ekran wiadomosc
     */
    System.out.println("Witaj Swiecie!");
  }
} // koniec klasy

Brak main

W tym samym programie zakomentuj całą metodę main.

  1. Czy program się skompiluje?
  2. Czy program da się uruchomić, i jeżeli tak, to co zobaczymy na ekranie?

Program z zakomentowaną metodą main:

public class HelloWorldBezMain {
  /*
  public static void main(String[] args) {
    System.out.println("Witaj Swiecie!");
  }
  */
}

Klasa skompiluje się z zakomentowaną metodą main, ale po uruchomieniu, wykonanie programu zakończy się następującym błędem:

Error: Main method not found in class HelloWorldBezMain, please define the main method as: public static void main(String[] args) or a JavaFX application class must extend javafx.application.Application

Wynika to z faktu, że Maszyna Wirtualna Java próbowała wywołać metodę main w naszej klasie, która to metoda jest punktem startowym wykonywania naszych programów. Metoda nie została znaleziona, więc Maszyna Wirtualna Java zgłosiła błąd.

Rozdział 3 – Zmienne

Podstawy zmiennych – zadania

Dodawanie liczb

Napisz program, w którym zdefiniujesz trzy zmienne typu int. Do dwóch pierwszych przypisz dowolne liczby, a do trzeciej – wynik dodawania dwóch pierwszych liczb. Aby dodać do siebie wartości dwóch zmiennych, skorzystaj ze znaku + (plus). Wypisz wynik na ekran.

Przykładowe rozwiązanie do tego zadania:

public class DodawanieLiczb {
  public static void main(String[] args) {
    int x, y, z;

    x = 5;
    y = 10;
    z = x + y;

    System.out.println("Wynik dodawania: " + z);
  }
}

Obwód trójkąta

Napisz program, który skorzysta z czterech zmiennych w celu policzenia obwodu trójkąta. W trzech zmiennych zapisz długość każdego z boków, a do ostatniej zmiennej przypisz wynik – obwód trójkąta. Wypisz wynik na ekran.

Przykładowe rozwiązanie do tego zadania:

public class ObwodTrojkata {
  public static void main(String[] args) {
    int a = 5;
    int b = 6;
    int c = 7;

    int obwodTrojkata = a + b + c;

    System.out.println("Obwod trojkata wynosi: " + obwodTrojkata);
  }
}

Aktualna data

Napisz program, w którym do trzech różnych zmiennych przypiszesz aktualny dzień, miesiąc, i rok. Pamiętaj o odpowiednim nazewnictwie zmiennych. Wypisz na ekran wszystkie wartości.

Przykładowe rozwiązanie do tego zadania:

public class AktualnaData {
  public static void main(String[] args) {
    int dzien = 7;
    int miesiac = 4;
    int rok = 2019;

    System.out.println(
        "Dzisiaj jest " + dzien + "-" + miesiac + "-" + rok
    );
  }
}

Liczba miesięcy w roku

Napisz program, w którym zdefiniujesz stałą, do której przypiszesz liczbę miesięcy w roku. Pamiętaj o odpowiednim nazewnictwie stałej. Wypisz wartość zdefiniowanej stałej na ekran.

Przykładowe rozwiązanie do tego zadania:

public class LiczbaMiesiecyWRoku {
  public static void main(String[] args) {
    final int LICZBA_MIESIECY_W_ROKU = 12;

    System.out.println(
        "Liczba miesiecy w roku to " + LICZBA_MIESIECY_W_ROKU
    );
  }
}

Inicjały

Napisz program, w którym przypiszesz swoje inicjały do dwóch zmiennych typu char – do każdej ze zmiennych po jednym znaku. Wypisz swoje inicjały na ekran – po każdej literze powinna następować kropka, np. P. K.

Przykładowe rozwiązanie do tego zadania:

public class Inicjaly {
  public static void main(String[] args) {
    char p = 'P';
    char k = 'K';

    System.out.println(p + "." + k + ".");
  }
}

Pojedyncze znaki, które przechowuje typ char, ujmujemy w cudzysłowy. Podczas wypisywania na ekran, znaki powinniśmy połączyć z łańcuchami tekstowymi, które, w tym przypadku, są kropkami po każdej literze inicjału.

Pytania

  1. Jak definiujemy zmienne?

    Zmienne definiujemy podając, kolejno, ich typ, nazwę, oraz ewentualną wartość początkową. Możemy zdefiniować więcej, niż jedną zmienną na raz, oddzielając ich nazwy przecinkami:

    int promienKola = 8;
    char a;
    double x, y;
    
  2. Czy zmiennej trzeba nadać wartość początkową w momencie definicji?

    Nie trzeba, ale przed użyciem takiej zmiennej należy jej przypisać wartość. Jeżeli spróbujemy użyć zmiennej zdefiniowanej w metodzie zanim nadamy tej zmiennej wartość, to nasz program w ogóle się nie skompiluje:

    public class UzycieNiezainicjalizowanejZmiennej {
      public static void main(String[] args) {
        int x;
    
        // blad! nie nadalismy zmiennej x jeszcze zadnej wartosci
        System.out.println("Wartosc x wynosi: " + x);
      }
    }
    

    Błąd kompilacji, jaki zobaczymy na ekranie:

    UzycieNiezainicjalizowanejZmiennej.java:6: error: variable x might not have been initialized System.out.println("Wartosc x wynosi: " + x); ^ 1 error
  3. Jakie są zasady nazewnictwa zmiennych (i innych obiektów) w Javie?

    Nazwy w Javie:

    • muszą zaczynać się od litery, podkreślenia _ bądź znaku dolara $
    • mogą zawierać cyfry, ale nie mogą się od cyfr zaczynać,
    • nie mogą być takie same, jak nazwy zastrzeżone w języku Java (np. class, public, void itd.).

  4. Czy wielkie litery są rozróżniane w nazwach w Javie?

    Tak – dla przykładu, nazwy pi oraz PI to dwie różne nazwy w języku Java.

  5. Jakie typy podstawowe są dostępne w Javie? Czym się różnią?

    Java oferuje 8 typów podstawowych: boolean, byte, short, int, long, float, double, oraz char. Różnią się one rodzajem danych, jakie mogą przechowywać, a także ich zakresem.

  6. Jak nazywamy zwykłe wartości zapisane w kodzie, takie jak 3.14, 25, 'a'?

    Wartości te nazywamy literałami.

  7. Do czego służą stałe, jak się je definiuje i nazywa?

    Stałe służą do przechowywania wartości, które nigdy nie powinny się zmienić w trakcie wykonywania programu, np. stała o nazwie PI powinna mieć wartość 3.14.

    Stałe definiujemy podobnie, jak zmienne, z tym, że dodajemy na początek słowo kluczowe final. Stałe nazywamy używając wielkich liter i oddzielając od siebie słowa znakiem podkreślenia:

    final int LICZBA_DNI_W_TYGODNIU = 7;
    
  8. Czy wszystkie operatory mają takie same priorytety?

    Nie, różne operatory mają różne priorytety. Dla przykładu, operator * (mnożenie) ma wyższy priorytet, niż operator + (dodawanie), więc w wyrażeniu 2 + 2 * 2, w pierwszej kolejności wykonane zostanie mnożenie.

  9. Czy można zmieniać priorytet operatorów?

    Można, korzystając z nawiasów. Aby w wyrażeniu 2 + 2 * 2 najpierw wykonać dodawanie, możemy zapisać je z nawiasami w następujący sposób: (2 + 2) * 2.

  10. Jaki wynik daje użycie operatora dzielenia / gdy jego argumenty (operandy) są liczbami całkowitymi?

    Operatora dzielenia / daje w wyniku liczbę całkowitą, zaokrągloną w dół, gdy oba jego argumenty są liczbami całkowitymi. Wartość wyrażenia 10 / 4 będzie wynosiła 2.

  11. Co to jest rzutowanie typów?

    Rzutowanie typów to traktowanie wartości jednego typu jako wartości innego typu. Aby wykonywać rzutowanie, zapisujemy oczekiwany typ w nawiasach przed wartością, np. (double)zmiennaTypuInt spowoduje, że wartość zmiennej zmiennaTypuInt będzie traktowana jako liczba rzeczywista double.

  12. Jak otrzymać w wyniku dzielenia liczbę rzeczywistą?

    Możemy albo zapisać którąś z liczb jako liczba rzeczywista, albo skorzystać z rzutowania – w drugiej linii poniżej korzystamy z rzutowania, a w trzeciej – pierwszy argument operatora / to liczba rzeczywista, więc wynikiem dzielenia także będzie liczba rzeczywista:

    System.out.println(10 / 4); // wypisze 2 – dzielenie calkowite
    System.out.println((double)10 / 4); // wypisze 2.5
    System.out.println(10.0 / 4); // wypisze 2.5
    
  13. Do czego służą operatory +=, -= itp.?

    Są to skrótowe operatory przypisania. Dla przykładu, operator += powoduje dodanie do zmiennej po lewej stronie operatora wartości wyrażenia po jego prawej stronie:

    int a = 5;
    a += 10; // a bedzie mialo wartosc 5 + 10, czyli 15
    
  14. Do czego służą operatory ++ i -- oraz czym się różnią ich post- i pre-fixowe wersje?

    Operatory te służą do zwiększania oraz zmniejszania wartości zmiennej o 1. Post-fixowe wersje tych operatorów różnią się tym od pre-fixowych, że najpierw wracają wartość zmienianej zmiennej, a dopiero potem ją zmieniają. Pre-fixowe wersje najpierw zmieniają wartość zmiennej, a potem zwracają jej wartość:

    int x = 2;
    int y = 2;
    System.out.println(x++); // wypisze 2
    System.out.println(++y); // wypisze 3
    
  15. Jaką wartość będzie miała zmienna x, a jaką zmienna y, w poniższym przykładzie?
    int x = 5++;
    int y = ++5;
    

    Kod w ogóle się nie skompiluje, ponieważ operator ++ oczekuje zmiennej jako argumentu, a nie literału liczbowego.

  16. Do czego służy typ String?

    Typ String służy do przechowywania stringów, czyli łańcuchów tekstowych.

  17. Jak połączyć ze sobą dwa łańcuchy tekstowe?

    Należy skorzystać z operatora + jak w przykładzie poniżej:

    String komunikat = "Witaj " + "Swiecie!";
    
  18. Co zostanie wypisane w wyniku wykonania poniższego programu?
    String powitanie = "Witajcie!";
    powitanie.toUpperCase();
    
    System.out.println(powitanie);
    

    Wypisany zostanie komunikat "Witajcie!". Co prawda użyliśmy na zmiennej powitanie metody, która zamienia znaki w stringu z małych na duże, ale nie przypisaliśmy wyniku działania tej metody do żadnej zmiennej. Metoda toUpperCase nie modyfikuje oryginalnej zmiennej, lecz zwraca nową wartość z małymi literami zamienionymi na wielkie.

  19. Co zostanie wypisane w wyniku działania poniższego programu?
    public class WypiszX {
      public static void main(String[] args) {
        int x;
        System.out.println("x ma wartosc " + x);
      }
    }
    

    Kod w ogóle się nie skompiluje, ponieważ nie zainicjalizowaliśmy zmiennej x żadną wartością. Kompilator jest to w stanie wykryć i zgłosi błąd już na etapie kompilacji.

  20. Jaką wartość będzie miała zmienna liczba?
    int liczba = 2.5 * 20;
    

    Kod w ogóle się nie skompiluje. Wynik działania 2.5 * 20 to liczba rzeczywista (ponieważ jeden z argumentów operatora * to liczba rzeczywista), a zmienna, do której ten wynik próbujemy przypisać, to zmienna typu int. Zmienne typu int mogą przechowywać jedynie liczby całkowite.

  21. Które z poniższych nazw zmiennych są niepoprawne i dlaczego?
    char ZNAK;
    int class;
    double $saldo;
    int liczbaPrzedmiotow#;
    int 60godzin;
    

    Niepoprawna jest nazwa class, ponieważ jest to słowo kluczowe w języku Java i nie może być używane jako nazwa zmiennej. liczbaPrzedmiotow# także jest niepoprawną nazwą, ponieważ zawiera niedozwolony znak #. 60godzin także jest nieprawidłową nazwą, ponieważ nazwy nie mogą zaczynać się od cyfr.

    Nazwa ZNAK jest co prawda poprawna, ale powinna zostać zapisana małymi literami. Tylko stałe zapisujemy wielkimi literami. Nazwa $saldo jest poprawna – nazwy mogą zaczynać się od znaku dolara.

  22. Co zostanie wypisane w wyniku działania poniższego programu?
    public class WypiszXY {
      public static void main(String[] args) {
        int x = 10;
        int y = -5;
        System.out.println("Wspolrzedne X i Y to: " + X + ", " + Y);
      }
    }
    

    Kod w ogóle się nie skompiluje – kompilator zgłosi błąd, że nie wie, czym są X oraz Y. Zmienne, które wcześniej zdefiniowaliśmy, zapisaliśmy małymi literami – w języku Java wielkość znaków ma znaczenia.

  23. Czy poniższy kod skompiluje się poprawnie?
    char z = "Z";
    
    System.out.println(z);
    

    Nie, ponieważ do zmiennej typu char próbujemy przypisać łańcuchów znaków. Chociaż złożony tylko z jednego znaku, nadal jest to łańcuch znaków, a nie pojedynczy znak. Aby kod był poprawny, do zmiennej powinien zostać przypisany znak ujęty w apostrofy 'Z'.

  24. Jaka wartość zostanie wypisana?
    double liczba = (double)15 / 10;
    
    System.out.println(liczba);
    

    Na ekranie zostanie wypisana wartość 1.5. Skorzystaliśmy z rzutowania, by liczba 15 została potraktowana jako liczba rzeczywista, więc jeden z argumentów operatora / jest liczbą rzeczywistą i wynik też będzie liczbą rzeczywistą.

  25. Jaką wartość będzie miała zmienna y?
    public class JakaWartosc {
      public static void main(String[] args) {
        int x = 5;
        int y = x + y;
      }
    }
    

    Kod się nie skompiluje. Do zmiennej y próbujemy przypisać wartość sumy zmiennych x oraz y. Zmienna y nie ma jeszcze przypisanej żadnej wartości, więc nie może być użyta do wyznaczenia swojej własnej wartości.

Zadania

Obwód trójkąta z pobranych danych

Napisz program, który pobierze od użytkownika trzy boki trójkąta, policzy jego obwód i wypisze wynik na ekran.

Aby pobrać od użytkownika liczby całkowite, należy skorzystać z funkcjonalności przedstawionej pod koniec trzeciego rozdziału – dodać do kodu programu instrukcję import oraz metodę getInt.

Przykładowe rozwiązania korzystające z metody getInt:

import java.util.Scanner;

public class ObwodTrojkataPobraneDane {
  public static void main(String[] args) {
    int x, y, z;
    int obwodTrojkata;

    System.out.println("Podaj pierwszy bok trojkata:");
    x = getInt();

    System.out.println("Podaj drugi bok trojkata:");
    y = getInt();

    System.out.println("Podaj trzeci bok trojkata:");
    z = getInt();

    obwodTrojkata = x + y + z;

    System.out.println(
        "Obwod trojkata o tych bokach wynosi " + obwodTrojkata
    );
  }

  public static int getInt() {
    return new Scanner(System.in).nextInt();
  }
}

Przykładowe wykonanie powyższego programu (na biało zaznaczone zostały dane wprowadzone przez użytkownika z klawiatury):

Podaj pierwszy bok trojkata: 7 Podaj drugi bok trojkata: 8 Podaj trzeci bok trojkata: 9 Obwod trojkata o tych bokach wynosi 24

Pobrane słowa w odwrotnej kolejności

Napisz program, który wczyta od użytkownika trzy słowa i wypisze je w odwrotnej kolejności, niż podał je użytkownik, oddzielone przecinkami. Dla przykładu, gdy użytkownik poda:

  1. Ala
  2. ma
  3. kota
To program powinien wypisać kota, ma, Ala

Tym razem musimy skorzystać z metody getString, wprowadzonej pod koniec trzeciego rozdziału, dzięki której będziemy mogli pobierać od użytkownika słowa (musimy także skorzystać z instrukcji import):

import java.util.Scanner;

public class OdwrotnaKolejnoscPobraneSlowa {
  public static void main(String[] args) {
    String slowo1, slowo2, slowo3;

    System.out.println("Podaj pierwsze slowo:");
    slowo1 = getString();

    System.out.println("Podaj drugie slowo:");
    slowo2 = getString();

    System.out.println("Podaj trzecie slowo:");
    slowo3 = getString();

    System.out.println(slowo3 + ", " + slowo2 + ", " + slowo1);
  }

  public static String getString() {
    return new Scanner(System.in).next();
  }
}

Przykładowe wykonanie powyższego programu (na biało zaznaczone są słowa podane przez użytkownika):

Podaj pierwsze slowo: Ala Podaj drugie slowo: ma Podaj trzecie slowo: kota kota, ma, Ala

Liczba znaków w słowie

Napisz program, który wczyta od użytkownika jeden wyraz i wypisz liczbę znaków, z których się składa. Dla przykładu, dla podanego słowa nauka wypisze 5.

Sprawdź w dokumentacji JavaDoc dla typu String jak dowiedzieć się z ile znaków składa się tekst przetrzymywany w zmiennej typu String:

Java Doc: klasa String

Aby dowiedzieć się, z ilu znaków składa się wartość zmiennej typu String, należy skorzystać z metody length udostępnianej przez typ String, która zwraca liczbę znaków w stringu. W poniższym przykładzie korzystamy także z metody getString, poznanej pod koniec trzeciego rozdziału, do pobrania słowa od użytkownika:

import java.util.Scanner;

public class LiczbaZnakowWSlowie {
  public static void main(String[] args) {
    String slowo;

    System.out.println("Podaj slowo:");
    slowo = getString();

    System.out.println("To slowo ma " + slowo.length() + " znakow.");
  }

  public static String getString() {
    return new Scanner(System.in).next();
  }
}

Przykładowe wykonanie powyższego programu (na biało zaznaczono słowo podane przez użytkownika):

Podaj slowo: nauka To slowo ma 5 znakow.

Wynik rzeczywisty

Zmień poniższy kod, by wynik wypisany na ekran nie był liczbą zaokrągloną do całkowitej wartości, lecz zmienną rzeczywistą (z częścią ułamkową):

public class ZadaniaWynikRzeczywisty {
  public static void main(String[] args) {
    int x = 5;
    int y = 2;

    double wynik = x / y;

    System.out.println(wynik);
  }
}

Aby otrzymać w wyniku dzielenia liczbę rzeczywiste w powyższym przypadku, należy zrzutować wartość jednej ze zmiennych na typ double – gdy jeden z argumentów operatora / będzie liczbą rzeczywistą, to i wynik dzielenia będzie liczbą rzeczywistą:

public class ZadaniaWynikRzeczywisty {
  public static void main(String[] args) {
    int x = 5;
    int y = 2;

    double wynik = (double)x / y;

    System.out.println(wynik);
  }
}

Wynik działania powyższego programu:

2.5

Wielkie litery

Napisz program, który pobierze od użytkownika słowo i wypisze je z małymi literami zamienionymi na wielkie. Skorzystaj z metody toUpperCase typu String.

Ponownie korzystamy z metody getString w celu pobrania słowa od użytkownika:

import java.util.Scanner;

public class WielkieLitery {
  public static void main(String[] args) {
    String slowo;

    System.out.println("Podaj slowo:");
    slowo = getString();

    System.out.println(slowo.toUpperCase());
  }

  public static String getString() {
    return new Scanner(System.in).next();
  }
}

Przykładowe wykonanie tego programu (na biało zaznaczono słowo podane przez użytkownika):

Podaj slowo: nauka NAUKA

Pole koła o podanym promieniu

Napisz program, który policzy pole koła o promieniu podanym przez użytkownika i wypisze wynik na ekran. Promień koła powinien być liczbą całkowitą – do jego przechowywania użyj zmiennej typu int.

W tym programie skorzystamy z poznanej metody getInt w celu pobrania od użytkownika promienia koła:

import java.util.Scanner;

public class PoleKolaOPodanymPromieniu {
  public static void main(String[] args) {
    int promienKola;
    double poleKola;

    System.out.println("Podaj promien kola:");
    promienKola = getInt();

    poleKola = 3.14 * promienKola * promienKola;

    System.out.println(
        "Pole kola o tym promieniu wynosi: " + poleKola
    );
  }

  public static int getInt() {
    return new Scanner(System.in).nextInt();
  }
}

Przykładowe wykonanie powyższego programu (na biało zaznaczono promień koła podany przez użytkownika):

Podaj promien kola: 5 Pole kola o tym promieniu wynosi: 78.5

Rozdział 4 – Instrukcje warunkowe

Pytania

  1. Spójrz na poniższe fragmenty kodu i odpowiedz na pytanie, czy są one poprawnie zapisanym kodem źródłowym Java?
    int x = 5;
    
    if x == 5 {
      System.out.println("x = 5");
    }
    

    Kod jest niepoprawny, ponieważ warunek instrukcji if powinien być ujęty w nawiasy (x == 5).

    int z = 5;
    
    if (z == 5) {
      System.out.println("z = 5");
    }
    else System.out.println("z != 5");
    else if (z < 5) {
      System.out.println("z < 5");
    }
    

    Kod jest niepoprawny, ponieważ sekcja else instrukcji warunkowej powinna być zawsze na końcu.

    int a = 5;
    
    if (a) {
      System.out.println("a = 5");
    }
    

    Kod jest niepoprawny, ponieważ wartość wyrażenia używanego w instrukcji warunkowej powinna zawsze mieć jedną z dwóch wartości: true bądź false.

    boolean b = true;
    
    if (b) {
      System.out.println("Warunek spelniony.");
    }
    

    Kod jest poprawny – możemy użyć zmiennej typu boolean jako warunku instrukcji warunkowej.

    int y = 5;
    
    if (y = 5) {
      System.out.println("y = 5");
    }
    

    Kod jest niepoprawny, ponieważ skorzystaliśmy z nieprawidłowego operatora – zamiast użyć operatora relacyjnego ==, który porównuje do siebie swoje argumentu i zwraca wartość true bądź false, użyty został operator przypisania =. Powyższy kod w ogóle się nie skompiluje.

    int c = 5;
    boolean czyDodatnia = c > 0;
    
    if (czyDodatnia) {
      System.out.println("Warunek spelniony.");
    }
    

    Powyższy kod jest poprawny – możemy przypisać do zmiennej typu boolean wynik wyrażenia, które korzysta z operatora relacyjnego.

  2. Jaki będzie wynik uruchomienia poniższego programu, gdy zmienna x będzie równa:
    1. 10
    2. 20
    3. 30
    4. Będzie miała inną wartość.
        
    switch (x) {
      case 10:
        System.out.println("10");
      case 20:
        System.out.println("20");
        break;
      case 30:
        System.out.println("30");
      default:
        System.out.println("Inna wartosc.");
    }
    

    Dla wartości 10 wypisane zostanie:

    10 20

    Dla wartości 20 wypisane zostanie:

    20

    Dla wartości 30 wypisane zostanie:

    30 Inna wartosc.

    Dla innej wartości, na przykład 50, wypisane zostanie:

    Inna wartosc.

    Dla wartości 10 i 30 wypisane zostały po dwie wartości, ponieważ niektóre bloki case instrukcji switch nie korzystają ze słowa kluczowego break. W takim przypadku, gdy wartość zostanie dopasowana, wykonywane zostają instrukcje z bloków case (oraz bloku default) znajdujących się poniżej. Dla wartości 10 wypisywanie komunikatów zakończyło się na 20, ponieważ sekcja case dla wartości 20 zawiera instrukcję break.

  3. Jaki będzie wynik działania poniższego programu?
    double liczba = 50;
    
    switch (liczba) {
      case 0:
        System.out.println("0");
        break;
      case 50:
        System.out.println("50");
        break;
      case 100:
        System.out.println("100");
        break;
      default:
        System.out.println("Inna wartosc.");
    }
    

    Program w ogóle się nie skompiluje, ponieważ w instrukcji switch nie można sprawdzać wartości typu double.

  4. Czy poniższa instrukcja if oraz użycie trój-argumentowego operatora logicznego są sobie równoważne?
    int z = 5;
    int wartoscAbsolutna;
    
    if (z > 0) {
      wartoscAbsolutna = z;
    } else {
      wartoscAbsolutna = -z;
    }
    
    int wartoscAbsolutna2 = z > 0 ? -z : z;
    

    Nie, instrukcja if i użycie trój-argumentowego operatora logicznego w tym przypadku nie są sobie równoważne. Instrukcja if jest poprawnie użyta do wyznaczenia wartości absolutnej zmiennej z, natomiast w przypadku trój-argumentowego operatora logicznego wyrażenia dla prawdy/fałszu warunku są odwrócone. Jeżeli z jest większe od zero, to wartością wyrażenia będzie odwrotność z. Trój-argumentowy operator logiczny zwraca pierwszą wartość, gdy warunek jest prawdziwy: pewienWarunek ? wartoscGdyTrue : wartoscGdyFalse, więc kod powinien być zapisany następująco: z > 0 ? z : -z

  5. Jak porównać ze sobą dwa łańcuchy tekstowe (String)?

    Należy skorzystać z metody equals typu String, np.:

    zmiennaTypuString.equals("Pewien tekst");
    
  6. W jaki sposób można skrócić poniższy kod?
    int x = 5;
    boolean czyDodatnia;
    
    if (x > 0) {
      czyDodatnia = true;
    } else {
      czyDodatnia = false;
    }
    

    Instrukcja if jest nadmiarowa – możemy przypisać wynik sprawdzenia x > 0 bezpośrednio do zmiennej typu boolean:

    int x = 5;
    boolean czyDodatnia = x > 0;
    
  7. Jaka będzie wartość zmiennej pewnaZmienna?
    boolean pewnaZmienna;
    
    pewnaZmienna = !pewnaZmienna;
    

    Powyższy kod w ogóle się nie skompiluje! Próbujemy przypisać do zmiennej pewnaZmienna jej przeciwieństwo – jednakże, zmiennej pewnaZmienna nie została nadana jeszcze żadna wartość – kompilator zaprotestuje.

  8. Czy operatora = można używać do porównywania wartości?

    Nie, operator = to operator przypisania – do porównywania wartości używamy operatora relacyjnego ==.

  9. Jaka będzie wartość poniższego wyrażenia, jeżeli x = -5, y = -10, z = 20 ?
    x > 0 && y > 0 || z > 0
    

    Wartość tego wyrażenia to true. Operator && ma większy priorytet, niż operator ||. Gdyby było na odwrót, to wartością byłaby wartość false.

  10. Jakie wartości mogą mieć zmienne typu boolean?

    Zmienne typu boolean mogą przyjmować jedną z dwóch wartości: true bądź false.

  11. Jeżeli mamy warunek x > 0 && y > 0, to czy wartość wyrażenia y > 0 będzie zawsze obliczana? Jeśli nie, to dlaczego i w jakim przypadku?

    Wartość wyrażenia y > 0 nie musi być obliczona, jeżeli wartością wyrażenia x > 0 będzie false, ponieważ w takim przypadku wartość całego wyrażenia x > 0 && y > 0 nie ma szansy mieć wartości true – operator && zwraca true tylko, gdy oba argumenty mają wartość true. Funkcjonalność, która powoduje, że niektóre wyrażenia nie są obliczane, nazywa się short-circuit evaluation i jest dla nas dostępna automatycznie w Maszynie Wirtualnej Java.

  12. Jaki będzie wynik działania poniższego programu (załóż, że getInt zwraca liczbę pobraną od użytkownika)?
    int x;
    x = getInt();
    
    if (x >= 0) {
      int poleKwadratu = x * x;
    }
    
    System.out.println("Pole kwadratu wynosi: " + poleKwadratu);
    

    Kod się nie skompiluje, ponieważ zmienna poleKwadratu "żyje" jedynie w bloku instrukcji if – tam została zdefiniowana. Po wyjściu z bloku instrukcji if, zmienna poleKwadratu przestaje istnieć.

  13. Dla jakich wartości argumentów operatory && oraz || zwracają true? A dla jakiego argumentu zwraca wartość true operator ! (zaprzeczenie logiczne)?

    Operator && zwraca true tylko w przypadku, gdy oba jego argumenty mają wartość true.

    Operator || zwraca true, gdy chociaż jeden z jego argumentów ma wartość true.

    Operator ! zwraca true w przypadku, gdy jego argument ma wartość false.

  14. Który z operatorów ma wyższy priorytet: || czy && ?

    Operator && ma wyższy priorytet od operatora ||.

  15. Co zostanie wypisane w wyniku uruchomienia poniższego programu (załóż, że getInt zwraca liczbę pobraną od użytkownika)?
    int x;
    x = getInt();
    
    if (x < 0)
      System.out.println("x jest mniejsze od 0");
      x = -x;
    
    System.out.println("Wartosc absolutna pobranej liczby to: " + x);
    

    Program zawsze wypisze wartość przeciwną podaną przez użytkownika, na przykład, dla 5 wypisze -5, a dla -5 wypisze 5. Instrukcja x = -x powinna być objęta blokiem w instrukcji if. W powyższym programie, do instrukcji if przypisana jest tylko jedna instrukcja – wypisująca na ekran komunikat "x jest mniejsze od 0". Poprawiony kod:

    int x;
    x = getInt();
    
    if (x < 0) {
      System.out.println("x jest mniejsze od 0");
      x = -x;
    }
    
    System.out.println("Wartosc absolutna pobranej liczby to: " + x);
    
  16. Co zostanie wypisane i dlaczego?
    String komunikat = "Bedzie padac";
    
    if (komunikat.equals("bedzie padac")) {
      System.out.println("Wez parasol!");
    } else {
      System.out.println("Sloneczna pogoda.");
    }
    

    Wypisane zostanie komunikat "Sloneczna pogoda.", ponieważ wielkość znaków podczas porównywania stringów ma znacznie – "Bedzie padac" i "bedzie padac" to dwa różne łańcuchy tekstowe.

  17. Jaki będzie wynik działania poniższego fragmentu kodu (załóż, że getInt zwraca liczbę pobraną od użytkownika)?
    int x;
    int poleKwadratu;
    
    x = getInt();
    
    if (x > 0) {
      System.out.println("x jest wieksze od zero.");
      poleKwadratu = x * x;
    }
    
    System.out.println("Pole kwadratu wynosi: " + poleKwadratu);
    

    Kod się nie skompiluje, ponieważ zmiennej poleKwadratu wartość nadajemy w instrukcji if – istnieje szansa na takie wykonanie naszego programu, w którym poleKwadratu nie otrzyma wartości. Kompilator zaprotestuje, ponieważ zmienna poleKwadratu może nie mieć nadanej wartości zanim zostanie użyta w ostatniej linii.

Zadania

Czy liczba podzielna przez trzy

Napisz program, który wczyta od użytkownika liczbę i wypisze, czy jest podzielna bez reszty przez 3. Skorzystaj z operatora reszty z dzielenia – jeżeli reszta z dzielenia jest równa 0, to liczba jest podzielna przez 3.

Przykładowe rozwiązanie tego zadania:

import java.util.Scanner;

public class CzyPodzielnaPrzezTrzy {
  public static void main(String[] args) {
    int x;

    System.out.println("Prosze podac liczbe:");
    x = getInt();

    if (x % 3 == 0) {
      System.out.println("Liczba " + x + " jest podzielna przez 3.");
    } else {
      System.out.println("Liczba " + x + " nie jest podzielna przez 3.");
    }
  }

  public static int getInt() {
    return new Scanner(System.in).nextInt();
  }
}

Korzystamy z operatora reszty z dzielenia %. Jeżeli wynik wyrażenia x % 3 wynosi 0, oznacza to, że liczba zawarta w zmiennej x dzieli się przez 3.

Czy można zbudować trójkąt

Napisz program, który wczyta od użytkownika trzy liczby i odpowie na pytanie, czy można z nich zbudować trójkąt (suma każdych dwóch boków powinna być większa od trzeciego boku).

W naszym programie musimy sprawdzić, czy suma każdych dwóch boków jest większa od długości trzeciego boku – przykładowe rozwiązanie:

import java.util.Scanner;

public class CzyMoznaZbudowacTrojkata {
  public static void main(String[] args) {
    int a, b, c;

    System.out.println("Podaj pierwszy bok trojkata:");
    a = getInt();

    System.out.println("Podaj drugi bok trojkata:");
    b = getInt();

    System.out.println("Podaj trzeci bok trojkata:");
    c = getInt();

    if (a + b > c && a + c > b && b + c > a) {
      System.out.println("Z tych bokow mozna zbudowac trojkat.");
    } else {
      System.out.println("Z tych bokow nie mozna zbudowac trojkata.");
    }
  }

  public static int getInt() {
    return new Scanner(System.in).nextInt();
  }
}

W instrukcji if korzystamy z operatora warunkowego && do złączenia trzech wyrażeń, które sprawdzają, czy każde dwa z podanych boków są większe od trzeciego.

Wypisz największą z dwóch liczb

Napisz program, który pobierze od użytkownika dwie liczby i wypisze największą z nich.

Przykładowe rozwiązanie tego zadania:

import java.util.Scanner;

public class WypiszNajwiekszaZDwochLiczb {
  public static void main(String[] args) {
    int x, y ;

    System.out.println("Podaj pierwsza liczbe:");
    x = getInt();

    System.out.println("Podaj druga liczbe:");
    y = getInt();

    if (x > y) {
      System.out.println("Najwieksza liczba to " + x);
    } else {
      System.out.println("Najwieksza liczba to " + y);
    }
  }

  public static int getInt() {
    return new Scanner(System.in).nextInt();
  }
}

Wypisz największą z trzech liczb

Napisz program, który pobierze od użytkownika trzy liczby i wypisze największą z nich.

Przykładowe rozwiązanie tego zadania:

import java.util.Scanner;

public class WypiszNajwiekszaZTrzechLiczb {
  public static void main(String[] args) {
    int a, b, c;

    System.out.println("Podaj pierwsza liczbe:");
    a = getInt();

    System.out.println("Podaj druga liczbe:");
    b = getInt();

    System.out.println("Podaj trzecia liczbe:");
    c = getInt();

    if (a >= b) {
      if (a >= c) {
        System.out.println("Najwieksza liczba to " + a);
      } else {
        System.out.println("Najwieksza liczba to " + c);
      }
    } else {
      if (b >= c) {
        System.out.println("Najwieksza liczba to " + b);
      } else {
        System.out.println("Najwieksza liczba to " + c);
      }
    }
  }

  public static int getInt() {
    return new Scanner(System.in).nextInt();
  }
}

W tym rozwiązaniu skorzystaliśmy z zagnieżdżonej instrukcji if. Najpierw sprawdzamy wartość a względem b, by wyeliminować jedną z nich, dzięki czemu zagnieżdżonych instrukcjach if mamy już do porównania tylko dwie liczby.

Zamień liczbę na nazwę miesiąca

Napisz program, który pobierze od użytkownika numer miesiąca i wypisze jego nazwę, lub komunikat "Nieprawidlowy numer miesiaca", jeżeli podany numer będzie spoza zakresu 1..12. Skorzystaj z instrukcji switch.

W rozwiązaniu musimy pamiętać, by w każdej sekcji case użyć słowa kluczowego break – inaczej nasz program będzie wypisywał wiele nazw miesięcy:

import java.util.Scanner;

public class ZamienLiczbeNaNazweMiesiaca {
  public static void main(String[] args) {
    int numerMiesiaca;

    System.out.println("Podaj numer miesiaca:");
    numerMiesiaca = getInt();

    switch (numerMiesiaca) {
      case 1:
        System.out.println("Styczen");
        break;
      case 2:
        System.out.println("Luty");
        break;
      case 3:
        System.out.println("Marzec");
        break;
      case 4:
        System.out.println("Kwiecien");
        break;
      case 5:
        System.out.println("Maj");
        break;
      case 6:
        System.out.println("Czerwiec");
        break;
      case 7:
        System.out.println("Lipiec");
        break;
      case 8:
        System.out.println("Sierpien");
        break;
      case 9:
        System.out.println("Wrzesien");
        break;
      case 10:
        System.out.println("Pazdziernik");
        break;
      case 11:
        System.out.println("Listopad");
        break;
      case 12:
        System.out.println("Grudzien");
        break;
      default:
        System.out.println("Nieprawidlowy numer miesiaca!");
    }
  }

  public static int getInt() {
    return new Scanner(System.in).nextInt();
  }
}

Sprawdź imię

Napisz program, który pobierze od użytkownika jego imię i odpowie na pytanie, czy jego imię jest takie samo, jak Twoje (załóżmy, że użytkownik podaje swoje imię bez polskich znaków).

Uwaga! Pamiętaj, aby skorzystać z metody equals typu String zamiast porównywać stringi za pomocą operatora == !

Przykładowe rozwiązanie tego zadania – zwróć uwagę, że pobrane imię porównujemy za pomocą metody equals, a nie operatora ==:

import java.util.Scanner;

public class SprawdzImie {
  public static void main(String[] args) {
    String imie;

    System.out.println("Podaj swoje imie:");
    imie = getString();

    if (imie.equals("Przemek")) {
      System.out.println("Mamy takie samie imiona.");
    } else {
      System.out.println("Mamy rozne imiona.");
    }
  }

  public static String getString() {
    return new Scanner(System.in).next();
  }
}

Czy pełnoletni

Napisz program, który pobiera wiek od użytkownika. Zapisz w zmiennej typu boolean informację, czy użytkownik jest pełnoletni, czy nie. Skorzystaj z trój-argumentowego operatora warunkowego. Wypisz wynik zdefiniowanej zmiennej typu boolean na ekran.

Przykładowe rozwiązanie tego zadania:

import java.util.Scanner;

public class CzyPelnoletni {
  public static void main(String[] args) {
    int wiek;
    boolean czyPelnoletni;

    System.out.println("Podaj swoj wiek:");
    wiek = getInt();

    czyPelnoletni = wiek >= 18 ? true : false;

    System.out.println("Czy pelnoletni? " + czyPelnoletni);
  }

  public static int getInt() {
    return new Scanner(System.in).nextInt();
  }
}

Moglibyśmy też po prostu przypisać do zmiennej czyPelnoletni wynik wyrażenia wiek >= 18:

czyPelnoletni = wiek >= 18;

Czy rok przestępny

Napisz program, który pobierze od użytkownika rok i odpowie na pytanie, czy podany rok jest rokiem przestępnym, czy nie.

Wskazówka: rok jest rokiem przestępnym, jeżeli:

  • dzieli się przez 4 i nie dzieli się przez 100
  • lub
  • dzieli się przez 400.

Przykładowe rozwiązanie do tego zadania:

import java.util.Scanner;

public class CzyRokPrzestepny {
  public static void main(String[] args) {
    int rok;

    System.out.println("Podaj rok:");
    rok = getInt();

    if ((rok % 4 == 0 && rok % 100 != 0) || rok % 400 == 0) {
      System.out.println("Ten rok jest przestepny.");
    } else {
      System.out.println("Ten rok nie jest przestepny.");
    }
  }

  public static int getInt() {
    return new Scanner(System.in).nextInt();
  }
}

W tym programie korzystamy ze złożonego warunku w instrukcji if – jeżeli rok dzieli się bez reszty przez 4 i nie dzieli się bez reszty przez 100, to jest to rok przestępny. Jeżeli ten warunek nie jest spełniony, to sprawdzone będzie, czy rok dzieli się bez reszty przez 400 – wtedy także rok jest rokiem przestępnym. W przeciwnym razie, nie jest to rok przestępny.

Rozdział 5 – Pętle

Pytania

  1. Do czego służą pętle?

    Pętle służą do wielokrotnego wykonywania zestawu instrukcji, który nazywamy ciałem pętli. Pętla wykonuje się tak długo, jak jej warunek jest spełniony, tzn. ma wartość true. Język Java ma cztery rodzaje pętli – do tej pory poznaliśmy pętle while, do..while, oraz for. Ponadto, istnieje także pętla for-each.

  2. Czym różnią się od siebie poznane dotąd pętle?

    W przeciwieństwie do pętli do..while, ciała pętli while oraz for mogą nie wykonać się ani razu, jeżeli warunek pętli nie będzie spełniony na początku działania pętli. Pętla for jako jedyna posiada trzy człony – instrukcję inicjalizującą, warunek, oraz inicjalizację kroku. Pozostałe dwie pętle posiadają jedynie warunek pętli.

  3. Jak nazywamy obieg pętli?

    Obieg pętli nazywamy iteracją.

  4. Czy ciało pętli zawsze wykona się chociaż raz?

    Zależy to od rodzaju pętli, którego użyjemy. Ciało pętli do..while zawsze wykona się przynajmniej raz, ponieważ jej warunek sprawdzany jest na końcu iteracji. Z kolei, pętle for i while sprawdzają swój warunek na początku, więc ich ciała mogą się nie wykonać ani razu.

  5. Co się stanie, gdy warunek pętli będzie zawsze spełniony i nie zmieni się w trakcie działania programu?

    Program będzie działał "w nieskończoność". Program taki, jeżeli jest to program konsolowy (uruchamiany z linii poleceń), możemy spróbować zakończyć skrótem Ctrl + c.

  6. Z jakich części składa się pętla for? Czy są one wymagane?

    Pętla for posiada: instrukcję inicjalizującą, warunek, oraz instrukcję kroku, a także ciało pętli. Żadna z nich nie jest wymagana.

  7. Jak sprawdzić znak na danej pozycji w zmiennej typu String? Jak sprawdzić pierwszy znak, a jak ostatni?

    Aby otrzymać znak na danej pozycji w zmiennej typu String, korzystamy z metody charAt, której przekazujemy jako argument indeks znaku, który chcemy pobrać. Indeks pierwszego znaku to 0, a nie 1! Ostatni znak ma indeks o jeden mniejszy, niż liczba znaków w stringu. Możemy się do niego odnieść korzystając z metody length, która zwraca liczbę znaków w stringu: tekst.charAt(tekst.length() - 1);

  8. Jak sprawdzić z ilu znaków składa się zmienna typu String?

    Aby sprawdzić liczbę znaków w stringu, korzystamy z metody length: tekst.length();

  9. Czy poniższe fragmenty kodów źródłowych są sobie równoważne?
    int i = 0;
    while (i < 10) {
      System.out.print(i + " ");
      i++;
    }
    
    for (int i = 0; i < 10; i++) {
      System.out.print(i + " ");
    }
    

    Nie, te fragmentu kodu nie są sobie równoważne, ponieważ, w przypadku pętli for, zmienna i nie jest dostępna po zakończeniu działania pętli. W przypadku pierwszego fragmentu (z pętlą while) moglibyśmy nadal korzystać ze zmiennej i, ponieważ została zdefiniowana jeszcze przed pętlą, a nie wewnątrz niej, jak w przypadku fragmentu z pętlą for.

  10. Do czego służą instrukcje break oraz continue?

    Instrukcja break służy do natychmiastowego przerwania działania pętli, w której została użyta, natomiast instrukcja continue przerywa jedynie aktualną iterację tejże pętli.

  11. Jaki będzie wynik działania poniższego fragmentu kodu?
    for (int i = 0; i < 3; i++) {
      for (int j = 0; j < 10; j++) {
        System.out.println("j = " + j);
    
          if (j == 1) {
            break;
          }
      }
    }
    

    Na ekranie zobaczymy poniższy tekst, ponieważ instrukcja break przerywa jedynie wykonanie wewnętrznej pętli – nie ma ona wpływu na pętlę zewnętrzną:

    j = 0 j = 1 j = 0 j = 1 j = 0 j = 1
  12. Co zostanie wypisane w ramach działania poniższego fragmentu kodu?
    String komunikat = "Witaj";
    
    for (int i = 0; i <= komunikat.length(); i++) {
      System.out.print(komunikat.charAt(i) + " ");
    }
    

    Na ekranie zobaczymy komunikat W i t a j (ze spacjami po każdym znaku), a także komunikat błędu spowodowanym tym, że wyszliśmy poza zakres indeksów zmiennej komunikat – w pętli powinniśmy użyć operatora < zamiast <=, ponieważ indeks ostatniego znaku w stringach to length() - 1, a nie length():

    W i t a j Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 5 at java.lang.String.charAt(String.java:658)
  13. Przepisz kod poniżej tak, by używał pętli while:
    for (int i = 1, j = 1; i * j < 100; i++, j += 2) {
      System.out.print((i * j) + " ");
    }
    

    Powyższa pętla, przepisana na pętlę while, mogłaby wyglądać następująco:

    int i = 1, j = 1;
    
    while (i * j < 100) {
      System.out.print((i * j) + " ");
      i++;
      j += 2;
    }
    

Zadania

While i liczby od 1 do 10

Napisz program z użyciem pętli while, który wypisuje wszystkie liczby od 1 do 10 (włącznie), oddzielone przecinkami, poza liczbą 10, po której nie powinno być przecinka.

W ciele pętli musimy sprawdzić wartość zmiennej i – gdy będzie równa 10, nie będziemy chcieli wypisywać na ekran przecinka:

public class WhileILiczbyOd1Do10 {
  public static void main(String[] args) {
    int i = 1;

    while (i <= 10) {
      if (i != 10) {
        System.out.print(i++ + ", ");
      } else {
        System.out.print(i++);
      }
    }
  }
}

Efekt uruchomienia programu:

1, 2, 3, 4, 5, 6, 7, 8, 9, 10

Policz silnię

Napisz program, który policzy i wypisze silnię liczby, którą poda użytkownik. Silnia to iloczyn kolejnych liczb całkowitych od 1 do danej liczby, np. silnia 5 to 1 * 2 * 3 * 4 * 5, czyli 120. Silnia liczby 0 to 1.

W poniższym przykładowym rozwiązaniu sprawdzamy najpierw pobraną liczbę – silnię powinniśmy liczyć jedynie dla liczb nieujemnych.

Jeżeli liczba jest nieujemna, to w pętli for przemnażamy zmienną wynik przez kolejne liczby całkowite.

Zauważmy, że podanie przez użytkownika liczby 0 spowoduje, że ciało pętli nie wykona się ani razu, ponieważ warunek pętli nie będzie spełniony. Wtedy zmienna wynik będzie miała po prostu swoją wartość nadaną jej podczas inicjalizacji, czyli 1 – a silnia 0 wynosi właśnie 1.

import java.util.Scanner;

public class PoliczSilnie {
  public static void main(String[] args) {
    System.out.print("Podaj liczbe nieujemna: ");
    int liczba = getInt();

    if (liczba < 0) {
      System.out.println("Nastepnym razem podaj liczbe nieujemna.");
    } else {
      int silnia = 1;

      for (int i = 1; i <= liczba; i++) {
        silnia = silnia * i;
      }

      System.out.println("Silnia " + liczba + " to " + silnia);
    }
  }

  public static int getInt() {
    return new Scanner(System.in).nextInt();
  }
}

Trzy przykładowe wykonanie powyższego programu:

Podaj liczbe nieujemna: -2 Nastepnym razem podaj liczbe nieujemna.
Podaj liczbe nieujemna: 0 Silnia 0 to 1
Podaj liczbe nieujemna: 6 Silnia 6 to 720

Palindrom

Napisz program, który odpowie na pytanie, czy podane przez użytkownika słowo jest palindromem. Palindrom to słowo, które jest takie samo czytane od początku i od końca, np. kajak.

Zauważmy, że aby słowo było palindromem, jego pierwsza litera musi być taka sama, jak jego ostatnia litera, oraz jego druga litera musi być taka sama, jak jego przedostatnia litera itd.

Zauważmy także, że w przypadku słów o nieparzystej liczbie znaków, środkowa litera nie ma dla nas znaczenia – nie ma ona pary, więc nie musimy jej sprawdzać.

Aby odpowiedzieć na pytanie, czy słowo jest palindromem, powinniśmy w takim razie porównać kolejne pary liter: pierwszą i ostatnią, drugą i przedostatnią itd. – jeżeli którakolwiek z par znaków nie będzie się zgadzać, to słowo nie będzie palindromem. Możemy w takim razie w pętli przejść przez połowę długości stringu, ponieważ w każdym obiegu będziemy porównywać ze sobą dwa znaki:

import java.util.Scanner;

public class Palindrom {
  public static void main(String[] args) {
    System.out.print("Podaj slowo: ");
    String slowo = getString();

    boolean czyPalindrom = true;
    int dlugoscSlowa = slowo.length();

    for (int i = 0; i < dlugoscSlowa / 2; i++) {
      if (slowo.charAt(i) != slowo.charAt(dlugoscSlowa - 1 - i)) {
        czyPalindrom = false;
        break;
      }
    }

    if (czyPalindrom) {
      System.out.println("Slowo " + slowo + " jest palindromem.");
    } else {
      System.out.println("Slowo " + slowo + " nie jest palindromem.");
    }
  }

  public static String getString() {
    return new Scanner(System.in).next();
  }
}

W programie:

  1. Wczytujemy słowo.
  2. Ustawiamy wartość zmiennej czyPalindrom na true – jeżeli znajdziemy w dalszej części programu parę znaków, które się różnią na swoich pozycjach, to ustawimy tą zmienną na false.
  3. Zapisujemy w pomocniczej zmiennej dlugoscSlowa długość podanego przez użytkownika słowa.
  4. W pętli for przechodzimy przez połowę indeksów znaków pobranego słowa – ta połowa indeksów to indeksy od 0 do dlugoscSlowa / 2 (bez uwzględniania końcowego indeksu dzięki użyciu operatora < w warunku pętli).
  5. W ciele pętli porównujemy kolejne pary znaków – jeżeli sobie nie odpowiadają, to wiemy, iż słowo nie będzie palindromem – ustawiamy zmienną czyPalindrom na false i korzystamy z instrukcji break do przerwania działania pętli – nie ma potrzeby sprawdzać kolejnych par znaków – wiemy już, że słowo nie jest palindromem. Zauważmy, że aby odnieść się do n-tego znaku od końca, używamy formuły dlugoscSlowa - 1 – i. W ten sposób, w pierwszym obiegu pętli odniesiemy się do ostatniego znaku (dlugoscSlowa - 1 - 0 to indeks ostatniego znaku). W drugim obiegu pętli – do przedostatniego (dlugoscSlowa - 1 - 1) itd.
  6. Wypisujemy na ekran informację, czy podane słowo jest palindromem, czy nie.

Kilka przykładowych uruchomień tego programu:

Podaj slowo: kajak Slowo kajak jest palindromem.
Podaj slowo: programowanie Slowo programowanie nie jest palindromem.
Podaj slowo: anna Slowo anna jest palindromem.
Podaj slowo: kotek Slowo kotek nie jest palindromem.

Wypisz największą liczbę z podanych

Napisz program, który z liczb podanych przez użytkownika wypisze największą. Program po pobraniu każdej liczby powinien pytać, czy użytkownik chce podać kolejną liczbę. Po podaniu liczb, program powinien wypisać największą z nich.

Zauważmy, że nie potrzebujemy mieć wszystkich liczb na raz, aby odpowiedzieć na pytanie, która z nich jest największa – wystarczy, że po pobraniu każdej liczby sprawdzimy, czy jest ona większa od poprzednio zapamiętanej, największej liczby:

  • jeżeli tak, to wczytana właśnie liczba staje się największą (dotychczasowo) liczbą,
  • jeżeli nie, to nie robimy nic z wczytaną liczbą.

Musimy w naszym programie wziąć pod uwagę jednak jeden przypadek – pobieranie pierwszej liczby od użytkownika – tej liczby nie mamy jeszcze z czym porównać, więc powinniśmy od razu potraktować ją jako największą (bo nie ma jeszcze żadnych innych kandydatów).

Możemy obejść się z tą sytuacją na wiele sposobów – w poniższym przykładowym rozwiązaniu najpierw pobieramy pierwszą liczbę i od razu przypisujemy ją do zmiennej najwiekszaLiczba, a dopiero w ciele pętli, po pobraniu kolejnej liczby, porównujemy nową liczbę z poprzednio zapisaną.

Skoro jeszcze przed działaniem pętli pobraliśmy liczbę, musimy już na samym początku pętli zapytać użytkownika, czy chce kontynuować – jeżeli nie, przerywamy pętlę instrukcją break:

import java.util.Scanner;

public class WypiszNajwiekszaZPodanychLiczb {
  public static void main(String[] args) {
    System.out.print("Podaj liczbe: ");
    int najwiekszaLiczba = getInt();

    while (true) {
      System.out.print("Czy chcesz zakonczyc program? [t/n] ");
      String czyKoniec = getString();

      if (czyKoniec.equals("t")) {
        break;
      }

      System.out.print("Podaj kolejna liczbe: ");
      int nowaLiczba = getInt();

      if (nowaLiczba > najwiekszaLiczba) {
        najwiekszaLiczba = nowaLiczba;
      }
    }

    System.out.println(
        "Najwieksza liczba z podanych to " + najwiekszaLiczba
    );
  }

  public static int getInt() {
      return new Scanner(System.in).nextInt();
  }

  public static String getString() {
      return new Scanner(System.in).next();
  }
}

Przykładowe wykonania tego programu:

Podaj liczbe: 10 Czy chcesz zakonczyc program? [t/n] n Podaj kolejna liczbe: 15 Czy chcesz zakonczyc program? [t/n] n Podaj kolejna liczbe: 1 Czy chcesz zakonczyc program? [t/n] n Podaj kolejna liczbe: 20 Czy chcesz zakonczyc program? [t/n] t Najwieksza liczba z podanych to 20
Podaj liczbe: 20 Czy chcesz zakonczyc program? [t/n] t Najwieksza liczba z podanych to 20
Podaj liczbe: 100 Czy chcesz zakonczyc program? [t/n] n Podaj kolejna liczbe: -5 Czy chcesz zakonczyc program? [t/n] n Podaj kolejna liczbe: 99 Czy chcesz zakonczyc program? [t/n] t Najwieksza liczba z podanych to 100

Zagnieżdżone pętle

Napisz program z dwoma pętlami (jedna zagnieżdżona w drugiej), każda z pętli powinna iterować od 1 do 10.

  1. Pętla główna powinna pomijać swoje iteracje za pomocą instrukcji continue, gdy jej zmienna jest nieparzysta.
  2. Pętla zagnieżdżona powinna wypisywać wartość swojej zmiennej. Następnie, gdy zmienna pętli zagnieżdżonej jest większa od zmiennej pętli głównej, pętla zagnieżdżona powinna spowodować, że przejdziemy do kolejnej iteracji pętli głównej (w tym przypadku skorzystaj z etykiety i instrukcji continue).

Przykładowe rozwiązanie tego programu – pętla główna ma etykietę glowna_petla, do której odnosimy się podczas użycia instrukcji continue w pętli zagnieżdżonej – dzięki temu, przerywana jest aktualna iteracja nie pętli zagnieżdżonej, lecz pętli głównej:

public class ZagniezdzonePetle {
  public static void main(String[] args) {
    glowna_petla:
    for (int i = 1; i <= 10; i++) {
      // wymaganie 1: petla glowna pomija swoje
      // iteracje, gdy jej zmienna jest nieparzysta
      if (i % 2 == 1) {
        continue;
      }

      for (int j = 1; j <= 10; j++) {
        System.out.print(j + " ");

        // wymganie 2: petla zagniezdzona
        // powoduje zakonczenie iteracji glownej petli
        // gdy wartosc zmiennej petli zagniezdzonej
        // jest wieksza od wartosci petli glownej
        if (j > i) {
          continue glowna_petla;
        }
      }
    }
  }
}

Wynik uruchomienia tego programu:

1 2 3 1 2 3 4 5 1 2 3 4 5 6 7 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 10

Kalkulator

Napisz program, który będzie pobierał od użytkownika liczby i działania do wykonania na nich. Program powinien wypisywać wynik po każdym działaniu. Możliwe działania to:

  • *     mnożenie,
  • /     dzielenie,
  • -     odejmowanie,
  • +     dodawanie.

Jeżeli podane zostanie inne działanie, lub podana zostanie liczba 0 jako dzielnik podczas dzielenia, program powinien wypisać stosowny komunikat i ponownie pobrać od użytkownika dane.

Na początku, program powinien pobrać od użytkownika dwie liczby i działanie do wykonania na nich. Za każdy kolejnym razem, program powinien pobierać od użytkownika już tylko jedną liczbę i działanie, po czym powinien wykonać podane działanie na poprzednim wyniku i podanej liczbie.

Dla przykładu:

  1. Program pobiera najpierw dwie liczby od użytkownika: 10 i 15, oraz działanie: dodawanie.
  2. Program dodaje do siebie liczby i wypisuje wynik 25 na ekran.
  3. Program pyta, czy użytkownik chce wykonać kolejne działanie.
    1. Jeżeli nie, program kończy działanie.
    2. Jeżeli tak, to program pobiera jedną liczbę i działanie, np. 2 i mnożenie. Program mnoży poprzedni wynik działania – czyli 25 * 2 i wypisuje wynik 50 na ekran. Wracamy do punktu 3. i ponownie pytamy o chęć dalszych kalkulacji.

Ten program jest bardziej skomplikowany, niż te, które do tej pory pisaliśmy:

  • musimy wziąć pod uwagę szczególny przypadek w naszym programie – na samym jego początku potrzebujemy dwóch liczb, a nie jednej – po pierwszym obliczeniu działania będziemy zawsze mieli już jedną liczbę – poprzedni wynik,
  • musimy wziąć pod uwagę, że użytkownik może podać drugą liczbę jako 0 podczas dzielenia lub wpisać znak działania, którego nie obsługujemy,
  • chcemy także wypisać na ekran wykonywane działanie, ale tylko w przypadku, gdy obliczenie się udało (tzn. użytkownik nie podał 0 jako mianownik podczas dzielenia bądź nieznanego działania).

Przykładowe rozwiązanie tego zadania jest następujące:

import java.util.Scanner;

public class Kalkulator {
  public static void main(String[] args) {
    // poczatek dzialania programu jest szczegolny,
    // bo potrzebujemy dwoch liczb od uzytkownika,
    // wiec pobierzemy pierwsza z nich od razu i zapiszemy
    // w zmiennej poprzedniWynik
    System.out.print("Podaj liczbe: ");
    int poprzedniWynik = getInt();

    String czyKoniec;
    boolean czyBlednaOperacja;

    do {
      int nowyWynik = 0;
      // ustaw ta zmienna na false - jezeli cos bedzie nie tak,
      // ustawimy wartosc na true (patrz instrukcja switch ponizej)
      czyBlednaOperacja = false;

      System.out.print("Podaj dzialanie (* / - +): ");
      String dzialanie = getString();

      System.out.print("Podaj kolejna liczbe: ");
      int drugaLiczba = getInt();

      // wykonaj dzialanie i zapisz wynik w zmiennej nowyWynik
      switch (dzialanie) {
        case "+":
          nowyWynik = poprzedniWynik + drugaLiczba;
          break;
        case "-":
          nowyWynik = poprzedniWynik - drugaLiczba;
          break;
        case "*":
          nowyWynik = poprzedniWynik * drugaLiczba;
          break;
        case "/":
          if (drugaLiczba == 0) {
            czyBlednaOperacja = true;
            System.out.println("Nie moge podzielic przez 0.");
          } else {
            nowyWynik = poprzedniWynik / drugaLiczba;
          }
          break;
        default:
          czyBlednaOperacja = true;
          System.out.println("Nieprawidlowa operacja.");
      }

      // jezeli podano 0 przy dzieleniu lub nieznany symbol dzialania,
      // nie chcemy nic wypisywac na ekranie
      if (!czyBlednaOperacja) {
        // wypisz wykonywane dzialanie i jego wynik
        System.out.println(
           poprzedniWynik + " " + dzialanie + " " + drugaLiczba + " = " + nowyWynik
        );

        // nowy wynik staje sie poprzednim wynikiem
        poprzedniWynik = nowyWynik;
      }

      System.out.println("Czy chcesz zakonczyc program? [t/n]");
      czyKoniec = getString();
    } while (!czyKoniec.equals("t"));
  }

  public static int getInt() {
      return new Scanner(System.in).nextInt();
  }

  public static String getString() {
      return new Scanner(System.in).next();
  }
}

W programie pobieramy pierwszą liczbę, a następnie, w pętli, pobieramy działanie i kolejną liczbę. W instrukcji switch sprawdzamy podane działanie i wykonujemy je, przypisując wynik do pomocniczej zmiennej nowyWynik, ale tylko wtedy, gdy podane działanie jest poprawne (* / + -) lub nie podano 0 jako dzielnik przy dzieleniu.

Jeżeli jednak działanie jest niepoprawne lub podano 0 podczas dzielenia, ustawiamy wartość pomocniczej zmiennej czyBlednaOperacja na true. Dzięki temu, po instrukcji switch, będziemy wiedzieli, czy powinniśmy wypisywać na ekran wynik działania, czy nie.

Jeżeli nie było problemów, to wypisujemy ładnie sformatowane działanie i jego wynik, a następnie przepisujemy do zmiennej poprzedniWynik wyliczony wynik ze zmiennej nowyWynik, aby w kolejnym obiegu pętli to ta wartość została użyta w kolejnym działaniu.

Przykładowe uruchomienie tego programu:

Podaj liczbe: 10 Podaj dzialanie (* / - +): + Podaj kolejna liczbe: 2 10 + 2 = 12 Czy chcesz zakonczyc program? [t/n] n Podaj dzialanie (* / - +): / Podaj kolejna liczbe: 3 12 / 3 = 4 Czy chcesz zakonczyc program? [t/n] n Podaj dzialanie (* / - +): ! Podaj kolejna liczbe: 10 Nieprawidlowa operacja. Czy chcesz zakonczyc program? [t/n] n Podaj dzialanie (* / - +): - Podaj kolejna liczbe: 3 4 - 3 = 1 Czy chcesz zakonczyc program? [t/n] t

Choinka

Napisz program, który pobierze od użytkownika jedną liczbę całkowitą. Następnie, program powinien wypisać na ekran choinkę ze znaków *, gdzie w ostatniej linii będzie liczba gwiazdek podana przez użytkownika, a w każdej powyższej o dwie gwiazdki mniej, niż w poniższej.

Przykład pierwszy – użytkownik podał liczbę 5, efekt wyświetlony na ekranie:

* *** *****

Przykład drugi – użytkownik podał liczbę 6, efekt na ekranie:

** **** ******

Zauważmy, że niezależnie od liczby gwiazdek, jaką podamy, na szczycie choinki (tzn. w pierwszym rzędzie) zawsze będzie albo jedna gwiazdka, albo dwie. Ich liczba zależy od tego, czy w podstawie choinki (która jest liczbą podaną przez użytkownika) jest parzysta liczba gwiazdek, czy nieparzysta:

  • jeżeli podstawa ma nieparzystą liczbę gwiazdek, to na szczycie zawsze będzie 1 gwiazdka,
  • jeżeli podstawa ma parzystą liczbę gwiazdek, to na szczycie zawsze będą 2 gwiazdki.

Wiemy już zatem, od wyświetlania ilu gwiazdek musimy zacząć.

A ile gwiazdek powinniśmy wyświetlić w każdym kolejnym rzędzie? Zauważmy, że każdy kolejny rząd ma o 2 gwiazdki więcej od poprzedniego – będziemy więc zwiększać liczbę gwiazdek do wyświetlania w każdym kolejnym rzędzie o dwie.

Kiedy mamy przestać wyświetlać gwiazdki? Gdy wyświetlimy tyle, ile miało być w podstawie choinki.

Wiemy już prawie wszystko – pozostaje jeszcze jedna kwestia – wcięcia na początku każdego rzędu, by gwiazdki na każdym poziomie były ładnie wyśrodkowane. Skąd mamy wiedzieć, ile spacji powinniśmy wyświetlić na początku w każdym rzędzie?

Ponownie należy zauważyć pewien wzór – spacje, które należy wyświetlić w każdym rzędzie, można wyliczyć następującym wzorem (zaokrąglając wynik w dół):

(liczbaGwiazdekWPodstawie – liczbaGwiazdekWAktualnymRzedzie) / 2

Dzięki powyższym, możemy napisać rozwiązanie do tego zadania:

import java.util.Scanner;

public class Choinka {
  public static void main(String[] args) {
    System.out.print("Podaj liczbe gwiazdek w podstawie: ");
    int liczbaGwiazdekWPodstawie = getInt();

    // liczba gwiazdek w pierwszym rzedzie, czyli
    // na szczycie choinki, zalezy od tego, czy
    // liczba gwiazdek w podstawie jest parzysta, czy nie
    // w kazdym rzedzie sa o 2 gwiazdki mniej,
    // niz w rzedzie ponizej, wiec na szczycie beda
    // 2 gwiazdki, gdy w podstawie jest parzysta liczba gwiazdek,
    // albo 1 gwiazdka, gdy podstawa ma nieparzysta liczbe gwiazdek
    int liczbaGwiazdekNaSzczycie = liczbaGwiazdekWPodstawie % 2 == 0 ? 2 : 1;

    // petla rozpoczyna dzialania od wypisania tylu gwiazdek, ile jest
    //  na szczycie
    // petla dziala dopoki, liczba gwiazdek do wypisania w rzedzie
    //  nie przekroczy liczby gwiazdek w podstawie
    // na koncu kazdego obiegu, zwiekszamy liczbe gwiazdek o 2,
    //  bo w kazdym kolejnym rzedzie sa o 2 gwiazdki wiecej,
    //  niz w poprzednim
    for (int gwiazdkiWRzedzie = liczbaGwiazdekNaSzczycie;
         gwiazdkiWRzedzie <= liczbaGwiazdekWPodstawie;
         gwiazdkiWRzedzie += 2) {

      // przed gwiazdkami musimy wypisac odpowiednia liczbe spacji,
      // aby gwiazdki byly ladnie wysrodkowane
      // liczba spacji do wypisania rowna jest polowie roznicy gwiazdek
      // w podstawie i w aktualnym rzedzie
      int liczbaSpacji = (liczbaGwiazdekWPodstawie - gwiazdkiWRzedzie) / 2;

      for (int i = 0; i < liczbaSpacji + gwiazdkiWRzedzie; i++) {
        // najpierw wypisujemy odpowiednia liczbe spacji, a potem gwiazdki,
        // wiec sprawdzamy wartosc zmiennej i - na jej podstawie wyznaczamy,
        // czy wypisac spacje, czy juz gwiazdke
        System.out.print(i < liczbaSpacji ? " " : "*");
      }

      // wypisujemy znak nowej linii, by kolejny
      // rzad gwiazdek byl wypisywany w nastepnej linii
      System.out.println();
    }
  }

  public static int getInt() {
    return new Scanner(System.in).nextInt();
  }
}

Program wydaje się długi ze względu na dużą ilość komentarzy. Dwa przykładowe działania powyższego programu:

Podaj liczbe gwiazdek w podstawie: 8 ** **** ****** ********
Podaj liczbe gwiazdek w podstawie: 7 * *** ***** *******

Rozdział 6 – Tablice

Pytania

  1. Do czego służą tablice?

    Tablice służą do przechowywania kolekcji elementów pewnego typu. Elementy te są przechowywane w ustalonej kolejności.

  2. Jak definiuje się tablice?

    Tablice definiuje się tak, jak zwykłe zmienne, z tym, że po nazwie typu należy dodać nawiasy kwadratowe. Dozwolone jest także umieszczenie nawiasów kwadratowych nie po nazwie typu, lecz po nazwie zmiennej:

    int[] tablica;
    int tablica[];
    
  3. Jak utworzyć tablicę?

    Aby utworzyć tablicę, możemy albo skorzystać ze słowa kluczowego new wraz z podaniem rozmiaru tablicy, albo, podczas definiowania tablicy, w nawiasach klamrowych { } wyspecyfikować elementy, z których tablica ma się składać. Można też skorzystać ze sposobu łączącego dwie pierwsze opcje, gdzie korzystamy ze słowa kluczowego new i elementów tablicy w nawiasach klamrowych:

    int[] calkowite = new int[5]; // pierwsza opcja
    double[] rzeczywiste = { 3.14, 5, -20.5 }; // druga opcja
    
    String[] slowa;
    slowa = new String[] { "Ala", "ma", "kota" }; // trzecia opcja
    
  4. Czy rozmiar tablicy można zmienić?

    Nie, rozmiaru raz utworzonej tablicy nie można zmienić.

  5. Jak odnieść się do danego elementu tablicy?

    Aby odnieść się do danego elementu tablicy, korzystamy z nawiasów kwadratowych po nazwie zmiennej tablicowej. W nawiasach kwadratowych należy umieścić indeks elementu, do którego chcemy się odnieść. Indeksy elementów tablic zaczynają się w języku Java od 0, a nie 1. Przykład – wypisujemy drugi element tablicy o nazwie tablica:

    System.out.println(tablica[1]);
    
  6. Jak odnieść się do pierwszego, a jak do ostatniego elementu tablicy?

    Pierwszy element tablicy ma zawsze indeks 0.

    Ostatni element tablicy ma indeks równy liczbę elementów w tablicy pomniejszony o 1 – możemy więc skorzystać z pola length tablicy, aby wyliczyć indeks ostatniego znaku:

    System.out.println(tablica[0]); // pierwszy element
    System.out.println(tablica[tablica.length – 1]); // ostatni element
    
  7. Czy poniższy kod jest poprawny?
    int[] tablica = { 1, 2, 3 };
    
    System.out.println(tablica[3]);
    

    Ten kod jest poprawny pod kątem składniowym – program z powyższym fragmentem kodu skompiluje się bez błędów. Z drugiej jednak strony, ten program zawiera błąd, ponieważ próbujemy odnieść się do czwartego elementu tablicy, która ma tylko trzy elementy – ten program zakończy się błędem wykonania ArrayIndexOutOfBoundsException.

  8. Jak sprawdzić ile elementów znajduje się w tablicy?

    Aby dowiedzieć się, z ilu elementów składa się tablica, należy skorzystać z pola length, które ma każda tablica, np.:

    int[] liczby = { 1, 5, -20 };
    System.out.println(liczby.length); // wypisze 3
    
  9. Jaki będzie wynik działania poniższego programu, gdy wartość zmiennej szukanaLiczba będzie równa:
    1. 0
    2. 500
    public static void main(String[] args) {
      boolean znaleziona = false;
      int[] tablica = { -20, 105, 0, 26, -99, 7, 1026 };
    
      int szukanaLiczba = ?; // pewna wartosc
    
      for (int i = 0; i <= tablica.length; i++) {
        if (tablica[i] == szukanaLiczba) {
          znaleziona = true;
          break; // znalezlismy liczbe - mozemy wiec przerwac petle
        }
      }
    
      if (znaleziona) {
        System.out.println("Liczba " + szukanaLiczba + " zostala znaleziona!");
      } else {
        System.out.println("Liczba " + szukanaLiczba + " nie zostala znaleziona.");
      }
    }
    

    W przypadku, gdy będziemy szukać liczby 0, na ekranie zobaczymy komunikat "Liczba 0 zostala znaleziona!". W przypadku liczby 500, jednakże, na ekranie zobaczymy komunikat:

    Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 7

    Wykonanie programu zakończy się błędem. Dlaczego tak się stało? Wynika to z niepoprawnego warunku pętli. Liczby 500 nie ma w tablicy, więc pętla przechodzi przez wszystkie elementy tablicy, jednak w warunku pętli powinniśmy byli użyć operatra < zamiast <=. Użycie operatora <= spowodowało, że w ostatnim obiegu pętli wyszliśmy poza zakres tablicy o nazwie tablica.

  10. Czy poniższy kod:
    1. Skompiluje się?
    2. Wykona się bez błędów?
    int[][] tablica2d = new int[3][5];
    
    tablica2d[3][1] = 1;
    

    Ten kod skompiluje się bez błędów, jednak jego wykonanie zakończy się błędem ArrayIndexOutOfBoundsException, ponieważ tablica tablica2d ma tylko trzy elementy w "pierwszym wymiarze", a w powyższym fragmencie kodu próbujemy odnieść się do czwartego elementu pierwszego wymiaru tej tablicy (która ma indeks 3).

  11. Czy poniższy kod jest poprawny?
    String powitanie = { "Witaj", "Swiecie" };
    
    for (int i = 0; i < powitanie.length(); i++) {
      System.out.println(powitanie[i] + " ");
    }
    

    Ten kod się nie skompiluje, ponieważ odnosząc się do rozmiaru tablicy, nie powinniśmy dodawać nawiasów ( ) po atrybucie length. Nie jest to jednak jedyny problem – tablica powitanie nie jest poprawnie zdefiniowana – brakuje nawiasów [ ] po typie String.

  12. Jaki będzie wynik działania poniższego fragmentu kodu?
    double[] a = { 3.14, 2.44, 0.1 };
    double[] b = { 3.14, 2.44, 0.1 };
    
    if (a == b) {
      System.out.println("Tablice sa takie same.");
    } else {
      System.out.println("Tablice nie sa takie same.");
    }
    

    Na ekranie zobaczymy komunikat "Tablice nie sa takie same.", ponieważ tablice powinniśmy porównywać poprzez sprawdzenie elementów na tych samych pozycjach, a nie poprzez użycie operatora ==.

  13. Która z poniższych tablic jest zdefiniowana/utworzona niepoprawnie i dlaczego?
    // blad: brakuje [] po int
    int liczby = { 1, 2, 3 };
    
    // blad: stringi zapisujemy w "" a nie ''
    String[] litery = { 'a', 'b', 'c' };
    
    
    // blad: brakuje rozmiaru tablicy w []
    String[] slowa = new String[];
    // blad: inicjalizacja za pomoca { } moze być uzyta jedynie podczas
    // definicji tablicy
    slowa = { "Ala", "ma", "kota" };
    
    // poprawna tablica
    double[] rzeczywiste = new double[] { 3.14, 2.44, 20 };
    
    // blad: niespojny typ double <-> int
    double[] innaTablica = new int[3];
    
    // blad: podczas tworzenia tablicy za pomoca new i podawania
    // elementow w {}, nie należy podawac w nawiasach [] rozmiaru tablicy
    int[] tablica = new int[5] { 1, 10, 100 };
    
    // poprawna tablica
    double[] kolejnaTablica = new double[3];
    // blad: ale już w ten sposob po utworzeniu tablicy nie mozemy
    // jej przypisac wartosci
    kolejnaTablica = { 5, 10, 15 };
    
    // poprawna tablica z tylko jednym elementem
    String[] tab = { "Ala ma kota" };
    
  14. Do czego służy pętla for-each i jak się z niej korzysta?

    Pętla for-each służy do iterowania po tablicach (i innych kolekcjach). Jest to krótka i wygodna alternatywa dla iterowania po elementach tablicy za pomocą pętli for.

  15. Co zostanie wypisane w ramach działania poniższego fragment kodu?
    int[] liczby = { 0, 1, 2, -1 };
    
    for (int i : liczby) {
      System.out.println(liczby[i] + ", ");
    }
    

    Jest to podchwytliwe zadanie – pętla for-each jest w nim niepoprawnie użyta. Zamiast po prostu wypisać wartość zmiennej pętli i, która w każdym obiegu przyjmuje wartość kolejnego elementu tablicy liczby, korzystamy z tej wartości jako indeksu elementów tablicy liczby. W związku z tym, że akurat pierwsze wartości przechowywane w tablicy liczby to 0, 1, 2, to odniesiemy się, kolejno, do pierwszego, drugiego, oraz trzeciego elementu tej tablicy i na ekranie zobaczymy liczby 0, 1, 2. Jednakże, w ostatnim obiegu pętli, zmienna i będzie miała wartość -1 – jest to nieprawidłowy indeks tablicy, co spowoduje wystąpienie błędu ArrayIndexOutOfBoundsException. Finalnie, na ekranie zobaczymy:

    0, 1, 2, Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -1

    Powyższy kod poprawnie powinien zostać zapisany w następujący sposób (wtedy na ekranie zobaczylibyśmy 0, 1, 2, -1):

    int[] liczby = { 0, 1, 2, -1 };
    
    for (int i : liczby) {
      System.out.println(i + ", ");
    }
    
  16. Co wypisze na ekran poniższy fragment kodu?
    int[] liczby = new int[3];
    
    System.out.println(liczby[1]);
    

    Na ekranie zobaczymy liczbę 0. Co prawda nie ustawiliśmy w tablicy liczby żadnych wartości, ale elementy tablic są inicjalizowane domyślnymi wartościami typu, którego wartości tablica może przechowywać. W przypadku typu int jest to 0.

  17. Czy poniższy fragment kodu jest poprawny?
    int[][] dwuwymiarowa =
        { 1, 2, 3 },
        { 4, 5, 6 },
        { 7, 8, 9 };
    

    Nie, ta tablica nie jest zainicjalizowana w poprawny sposób – brakuje jeszcze jednej pary nawiasów klamrowych, ponieważ jest to tablica dwuwymiarowa. Poprawna inicjalizacja:

    int[][] dwuwymiarowa = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
    };
    

Zadania

Co druga wartość tablicy

Napisz program, który wypisze co drugi element zdefiniowanych przez Ciebie tablic. Pierwsza tablica powinna mieć parzystą liczbę elementów, a druga nieparzystą.

Nie ma różnicy, czy wypisujemy co drugi element z tablicy o parzystej czy nieparzystej liczbie elementów. Musimy tylko odpowiednio zapisać instrukcję kroku pętli for – tak, aby zmienna pętli zwiększała się o 2, a nie o 1. Przykładowe rozwiązanie tego zadania:

public class CoDrugaWartoscTablicy {
  public static void main(String[] args) {
    // parzysta liczba elementow
    int[] parzysta = { 1, 10, 15, 0, 100, 20 };

    // nieparzysta liczba elementow
    int[] nieparzysta = { 5, 7, 9 };

    for (int i = 0; i < parzysta.length; i += 2) {
      System.out.print(parzysta[i] + ", ");
    }

    System.out.println(); // nowa linia

    for (int i = 0; i < nieparzysta.length; i += 2) {
      System.out.print(nieparzysta[i] + ", ");
    }
  }
}

Wynik działania programu:

1, 15, 100, 5, 9,

Największa liczba w tablicy

Napisz program, który wypisze największą liczbę z tablicy. Tablicę zainicjalizuj przykładowymi wartościami.

Aby znaleźć największy element, musimy porównać kolejne elementy tablicy do poprzedniego, największego elementu. Jeżeli kolejny sprawdzany element będzie większy od poprzednio znalezionego, największego elementu, to zapiszemy tą aktualną wartość jako największą.

Trzeba tylko rozwiązać jeden problem – jaką wartość powinniśmy użyć jako pierwszą "największą" liczbę, do której na początku będziemy porównywać wartości z tablicy? Czy użyć jakiejś bardzo małej liczby, mając nadzieję, że w tablicy będzie chociaż jedna większa od niej liczba? Nie jest to dobre rozwiązanie – tym bardziej, że rozwiązanie poprawne jest proste do implementacji.

Na początku, jako największą liczbę, wystarczy ustawić pierwszy element tablicy. Następnie, w pętli przejdziemy przez pozostałe elementy tablicy i będziemy je porównywać z zapisaną, największą liczbą – jeżeli aktualnie sprawdzana wartość z tablicy będzie większa, niż poprzednio zapamiętana największa liczba, to zapamiętamy ten aktualny element jako największa liczba itd., aż nie przejdziemy przez całą tablicę:

public class NajwiekszaWartoscWTablicy {
  public static void main(String[] args) {
    int[] liczby = { 1, -20, 100, 40, -15 };

    // najwieksza liczbe inicjalizujemy wartoscia
    // pierwszego elementu tablicy
    int najwiekszaLiczba = liczby[0];

    // przechodzimy przez cala tablice, z pominieciem
    // pierwszego elementu - nie ma sensu porownywac go
    // do samego siebie - dlatego zmienna i zaczyna od 1,
    // czyli od indeksu drugiego elementu w tablicy
    for (int i = 1; i < liczby.length; i++) {
      // jezeli aktualnie sprawdzana wartosc z tablicy
      // jest wieksza, niz poprzednio zapamietana najwieksza
      // liczba, to zapisujemy ten aktualny element jako najwiekszy
      if (liczby[i] > najwiekszaLiczba) {
        najwiekszaLiczba = liczby[i];
      }
    }
    System.out.println("Najwieksza liczba to: " + najwiekszaLiczba);
  }
}

Wynik działania tego programu:

Najwieksza liczba to: 100

Słowa z tablicy wielkimi literami

Napisz program, w którym zdefiniujesz tablicę wartości typu String i zainicjalizujesz ją przykładowymi wartościami. Skorzystaj z pętli for-each, aby wypisać wszystkie wartości tablicy ze wszystkimi literami zamienionymi na wielkie. Skorzystaj z funkcjonalności toUpperCase wartości typu String, którą poznaliśmy już w jednym z poprzednich rozdziałów.

Korzystając z funkcjonalności toUpperCase typu String, możemy wypisać na ekran elementy tablicy ze wszystkimi literami zamienionymi na wielkie. Skorzystamy z pętli for-each, aby przejść przez wszystkie elementy tablicy:

public class SlowaTablicyWielkimiLiterami {
  public static void main(String[] args) {
    String[] slowa = { "Ala", "ma", "kota", "i", "psa" };

    for (String slowo : slowa) {
      System.out.print(slowo.toUpperCase() + " ");
    }
  }
}

Wynik działania:

ALA MA KOTA I PSA

Odwrotności słów w tablicy

Napisz program, który pobierze od użytkownika pięć słów i zapisze je w tablicy. Następnie, program powinien wypisać wszystkie słowa, od ostatniego do pierwszego, z literami zapisanymi od końca do początku. Dla przykładu, dla podanych słów "Ala", "ma", "kota", "i", "psa" program powinien wypisać: "asp", "i", "atok", "am", "alA".

Aby pobrać słowa od użytkownika, skorzystamy z poznanej już metody getString, którą dodamy do naszego programu.

Najpierw wypiszemy na ekran komunikat "Prosze podac 5 slow:". Następnie, w pętli, pobierzemy tyle słów, ile może pomieścić tablica slowa. Zauważmy, że poniższy program napisany jest w taki sposób, że nieważne jest, z ilu elementów składa się tablica slowa – jeżeli zmienilibyśmy jej maksymalny rozmiar na 3 bądź 20 elementów – program nadal działałby poprawnie. Wynika to z faktu użycia atrybutu length tablicy, zamiast zaszycia w kodzie na stałe liczby 5 jako liczby elementów podczas pobierania danych od użytkownika.

Po pobraniu od użytkownika słów, korzystamy z zagnieżdżonej pętli for. W pętli zewnętrznej iterujemy od końca tablicy do jej początku. Zwróćmy uwagę, że w warunku pętli korzystamy z length, a nie length(), gdyż odnosimy się do rozmiaru tablicy.

W pętli wewnętrznej iterujemy od ostatniego do pierwszego znaku w aktualnym słowie – tym razem w warunku pętli korzystamy z length(), ponieważ sprawdzamy liczbę znaków wartości typu String, którą pobieramy z tablicy slowa. Wypisujemy na ekran kolejne znaki za pomocą poznanej już metody charAt, której podajemy jako argument indeks znaku zapisany w zmiennej j. Zmienna j, dzięki odpowiednio zapisanej wewnętrznej pętli for, będzie przybierała indeksy znaków przetwarzanego aktualnie słowa – od ostatniego znaku, do pierwszego.

Przykładowe rozwiązanie:

import java.util.Scanner;

public class OdwrotnosciSlowWTablicy {
  public static void main(String[] args) {
    String[] slowa = new String[5];

    System.out.println("Podaj " + slowa.length + " slow:");

    for (int i = 0; i < slowa.length; i++) {
      System.out.print((i + 1) + " slowo: ");
      slowa[i] = getString();
    }

    for (int i = slowa.length - 1; i >= 0; i--) {
      for (int j = slowa[i].length() - 1; j >= 0; j--) {
        System.out.print(slowa[i].charAt(j));
      }
      System.out.println(); // nowa linia
    }
  }

  public static String getString() {
    return new Scanner(System.in).next();
  }
}

Przykładowy wynik działania powyższego programu:

Podaj 5 slow: 1 slowo: Ala 2 slowo: ma 3 slowo: kota 4 slowo: i 5 slowo: psa asp i atok am alA

Sortowanie liczb

Napisz program, który pobierze od użytkownika osiem liczb, zapisze je w tablicy, a następnie posortuje tą tablicę rosnąco i wypisze wynik sortowania na ekran. Dla przykładu, dla liczb 10, -2, 1, 100, 20, -15, 0, 10, program wypisze -15, -2, 0, 1, 10, 10, 20, 100. Zastanów się, jak można posortować ciąg liczb i spróbuj zaimplementować swoje rozwiązanie. Przetestuj je na różnych zestawach danych. Możesz też skorzystać z jednego z popularnych algorytmów sortowania, np. sortowania przez wstawianie. Opis tego algorytmu znajdziesz w internecie.

Sortowanie jest bardzo często potrzebne w programowaniu. Ma ono bardzo wiele zastosowań, chociażby podczas wyświetlania danych dla użytkownika czy też wyszukiwania elementów (szybciej szuka się w posortowanych danych).

Istnieje wiele sposobów na sortowanie danych, m. in.:

  • sortowanie bąbelkowe,
  • sortowanie przez wstawianie,
  • quicksort,
  • mergesort,
  • heapsort,
  • i inne.

Różne algorytmy sortowania mają różne cechy oraz różne złożoności obliczeniowe i pamięciowe, czyli (w uproszczeniu), jak dużo czasu i pamięci wymagane jest, aby posortować zbiór elementów. Porównując algorytmy oceniamy również, jaki wpływ ma rozmiar danych na czas działania algorytmu.

Poniższe, przykładowe rozwiązanie zadania, korzysta z algorytmu "insertion sort", czyli sortowania przez wstawianie – jest to jeden z prostszych algorytmów sortowania, który nadaje się dla niewielkich zestawów danych, ponieważ dla zbiorów wieloelementowych (tysiące/setki tysięcy/miliony) będzie niewydajny (będzie potrzebował zbyt dużo czasu na wykonanie).

Algorytm działa w następujący sposób: zaczynając od drugiego elementu, sprawdzamy poprzedzające go elementy i przesuwamy te poprzednie elementy w tablicy w prawo o jedno miejsce dopóki aktualnie przetwarzany element jest mniejszy niż poprzednie elementy. W pewnym momencie znajdziemy miejsce w (już przetworzonej) części tablicy, w której aktualna liczba powinna być ustawiona. Kontynuujemy w ten sposób dla wszystkich kolejnych elementów tablicy.

Spójrzmy na przykład działania tego algorytmu – posortowane zostaną liczby: 4, 3, 2, 5:

4 3 2 5
  1. Algorytm zaczyna zawsze działanie od drugiego elementu tablicy – w tym przypadku, jest to liczba 3. Sprawdzamy elementy ją poprzedzające – jest to liczba 4, która jest większa od aktualnej liczby – przeniesiemy więc liczbę 4 na miejsce liczby 3:
    4 3 2 5

    4 4 2 5
  2. Więcej elementów wcześniej nie było, więc wpisujemy 3 na pierwszą pozycję w tablicy:
    4 4 2 5

    3 4 2 5
  3. Przechodzimy do kolejnego, czyli trzeciego elementu – jest to liczba 2. Sprawdzamy poprzedzające ją elementy. Liczba 4 jest większa od 2, więc przesuwamy liczbę 4 o jedno miejsce w prawo:
    3 4 2 5

    3 4 4 5
  4. Sprawdzamy kolejny poprzedni element – jest to liczba 3, która także jest większa od 2. Przesuwamy 3 o jedno miejsce w prawo:
    3 4 4 5

    3 3 4 5
  5. Więcej elementów nie ma, więc wpisujemy dwójkę na pierwszą pozycję:
    3 3 4 5

    2 3 4 5
  6. Przechodzimy do kolejnego elementu tablicy – będzie to już ostatni element, liczba 5. Sprawdzamy poprzedzające ją elementy – pierwszy to liczba 4, która jest mniejsza od liczby 5, więc przerywamy procesowanie liczby 5, gdyż na pewno nie będzie już musiała być ustawiona wcześniej w tablicy (ponieważ przed nią będą na pewno mniejsze elementy).
  7. Przeszliśmy przez wszystkie elementy – tablica jest teraz posortowana.

Przykładowe rozwiązanie:

import java.util.Scanner;

public class SortowaniePobranychLiczb {
  public static void main(String[] args) {
    int[] liczby = new int[8];

    System.out.println("Podaj " + liczby.length + " liczb:");

    // pobieramy liczby
    for (int i = 0; i < liczby.length; i++) {
      System.out.print((i + 1) + " liczba: ");
      liczby[i] = getInt();
    }

    // zaczynamy od drugiego elementu tablicy
    // (ktorego indeks to 1, bo pierwszy element ma indeks 0)
    for (int i = 1; i < liczby.length; i++) {
      // liczba, dla ktorej aktualnie chcemy znalezc miejsce
      int aktualnaLiczba = liczby[i];

      // zaczynamy sprawdzanie elementow poprzednich
      // od elementu o indeksie o 1 mniejszym
      // niz ten, dla ktorego aktualnie szukamy miejsca
      int j = i – 1;

      // dopoki nie wyjdziemy poza zakres tablicy
      // lub poprzedni element jest wiekszy od tego,
      // dla ktorego aktualnie szukamy miejsca...
      while (j >= 0 && liczby[j] > aktualnaLiczba) {
        // ...przesuwamy poprzedni element o jedno miejsce w prawo
        liczby[j + 1] = liczby[j];
        j--;
      }

      // gdy petla sie konczy, j + 1 wyznacza indeks
      // dla wartosci, dla ktorej szukalismy miejsca
      liczby[j + 1] = aktualnaLiczba;
    }

    // na koncu petli jest posortowana
    System.out.println("Posortowane liczby:");

    for (int x : liczby) {
      System.out.print(x + ", ");
    }
  }

  public static int getInt() {
    return new Scanner(System.in).nextInt();
  }
}

Trzy przykładowe uruchomienia tego programu:

Pierwsze uruchomienie

Podaj 8 liczb: 1 liczba: 1 2 liczba: 2 3 liczba: 3 4 liczba: 4 5 liczba: 5 6 liczba: 6 7 liczba: 7 8 liczba: 8 Posortowane liczby: 1, 2, 3, 4, 5, 6, 7, 8,

Drugie uruchomienie

Podaj 8 liczb: 1 liczba: 8 2 liczba: 7 3 liczba: 6 4 liczba: 5 5 liczba: 4 6 liczba: 3 7 liczba: 2 8 liczba: 1 Posortowane liczby: 1, 2, 3, 4, 5, 6, 7, 8,

Trzecie uruchomienie

Podaj 8 liczb: 1 liczba: 10 2 liczba: -5 3 liczba: 100 4 liczba: 0 5 liczba: 250 6 liczba: -1 7 liczba: 0 8 liczba: 500 Posortowane liczby: -5, -1, 0, 0, 10, 100, 250, 500,

Silnia liczb w tablicy

Napisz program, który pobierze od użytkownika pięć liczb, zapisze je w tablicy, a następnie policzy i wypisze silnię każdej z pobranych liczb.

Podobnie, jak w poprzednich dwóch zadaniach, pobierzemy w pętli od użytkownika dane, którymi zasilimy tablicę.

Następnie, korzystając z zagnieżdżonych pętli, przeiterujemy w pętli zewnętrznej przez tablicę pobranych liczb, używając do tego celu pętli for-each. W pętli wewnętrznej, dla aktualnej liczby z tablicy, wyliczymy silnię. Najpierw sprawdzimy jednak, czy liczba jest ujemna – jeżeli tak, to taką liczbę pominiemy – silnię powinniśmy liczyć tylko dla liczb nieujemnych. Po zakończeniu działania pętli wewnętrznej, wypiszemy na ekran policzoną silnię:

import java.util.Scanner;

public class SilniaLiczbWTablicy {
  public static void main(String[] args) {
    int[] liczby = new int[5];

    System.out.println("Podaj " + liczby.length + " liczb:");

    // pobieramy liczby
    for (int i = 0; i < liczby.length; i++) {
      System.out.print((i + 1) + " liczba: ");
      liczby[i] = getInt();
    }

    // iterujemy po pobranych liczbach
    for (int x : liczby) {
      // silnie powinnismy liczyc tylko dla liczb nieujemnych
      if (x < 0) {
        System.out.println(
            "Silnie mozna policzyc tylko dla liczb >= 0. " +
            "Pomijam liczbe " + x
        );
      } else {
        int silnia = 1;

        // liczymy silnie aktualnej liczby
        for (int i = 1; i <= x; i++) {
          silnia *= i;
        }

        System.out.println("Silnia liczby " + x + " wynosi " + silnia);
      }
    }
  }

  public static int getInt() {
    return new Scanner(System.in).nextInt();
  }
}

Przykładowe wykonanie tego programu:

Podaj 5 liczb: 1 liczba: 1 2 liczba: 4 3 liczba: -2 4 liczba: 0 5 liczba: 5 Silnia liczby 1 wynosi 1 Silnia liczby 4 wynosi 24 Silnie mozna policzyc tylko dla liczb >= 0. Pomijam liczbe -2 Silnia liczby 0 wynosi 1 Silnia liczby 5 wynosi 120

Porównaj tablice stringów

Napisz program, w którym zdefiniujesz dwie tablice przechowujące wartości typu String. Zainicjalizuj obie tablice takimi samymi wartościami, w takiej samej kolejności. Napisz kod, który porówna obie tablice i odpowie na pytanie, czy są one takie same.

To zadanie ma na celu sprawdzenie dwóch rzeczy:

  • czy będziemy pamiętali o porównaniu tablic nie za pomocą operatora ==, lecz poprzez porównanie liczby elementów tablic oraz ich wartości parami, na odpowiadających sobie indeksach?
  • czy będziemy pamiętali, że wartości typu String powinniśmy porównywać za pomocą equals, a nie operatora == ?

Przykładowe rozwiązanie tego zadania:

public class PorownajTabliceStringow {
  public static void main(String[] args) {
    String[] pierwsza = { "Ala", "ma", "kota" };
    String[] druga = { "Ala", "ma", "kota" };

    boolean roznicaZnaleziona = false;

    if (pierwsza.length != druga.length) {
      roznicaZnaleziona = true;
    } else {
      for (int i = 0; i < pierwsza.length; i++) {
        if (!pierwsza[i].equals(druga[i])) {
          roznicaZnaleziona = true;
          break;
        }
      }
    }

    if (roznicaZnaleziona) {
      System.out.println("Tablice nie sa takie same.");
    } else {
      System.out.println("Tablice sa takie same");
    }
  }
}

W programie definiujemy dwie przykładowe tablice przechowujące wartości typu String. Tablice te mają takie same elementy, w tej samej kolejności.

Zmienna roznicaZnaleziona będzie służyła za wyznacznik, czy tablice są takie same, czy nie. Na początku inicjalizujemy ją wartością false. Jeżeli znaleźlibyśmy różnicę w tablicach, to ustawilibyśmy jej wartość na true.

Sprawdzamy liczbę elementów obu tablic. Jeżeli się zgadza, to w pętli porównujemy pary elementów z obu tablic – co ważne, stosujemy metodę equals, zamiast porównywać wartości typu String za pomocą operatora ==. Jeżeli znajdziemy różnicę, ustawimy zmienną roznicaZnaleziona na true i zakończymy działanie pętli instrukcją break – wystarczy nam znalezienie jednej różnicy, by wiedzieć, że tablice nie są sobie równe.

Na końcu programu wypisujemy informację, czy tablice są takie same – w tym przypadku zobaczymy komunikat "Tablice sa takie same", ponieważ nadaliśmy im takie same wartości.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Nie musisz podawać swojego imienia, e-mailu, ani strony www, aby opublikować komentarz. Komentarze muszą zostać zatwierdzone, aby były widoczne na stronie.