Witam Cię w kolejnej już piątej części kursu języka Java. Dowiesz się z niej co to są instrukcje warunkowe i pętle. Czyli poznasz jedne z najważniejszych konstrukcji języka Java. To one są esencją programowania i każdy programista musi je znać, tak samo jak np. kolekcje. Na szczęście nie są one tak trudne, jakby mogło się wydawać.
Instrukcje warunkowe
Instrukcja warunkowa if
Jedną z najczęściej wykorzystywanych instrukcji warunkowych jest instrukcja if
. Instrukcja ta składa się ze słowa kluczowego if
i warunku, który po nim następuje oraz klamer {}
, pomiędzy którymi możesz zawrzeć jakieś polecenia (klamry nie są obowiązkowe, ale są bardzo przydatne).
int i = 1; if (i == 1) { System.out.println("i jest równe 1"); }
Instrukcja ta ma także swoją rozszerzoną formę, gdzie dodatkowo podajemy frazę else
wraz z klamrami {}
:
int i = 1; if (i == 1) { System.out.println("i jest równe 1"); } else { System.out.println("i nie jest równe 1"); }
Możemy także zrobić tak jakby kombinację tych dwóch słów kluczowych i dzięki temu dodać kolejne bloki kodu, poprzedzając je słowami else if
i dodatkowym warunkiem:
int i = 1; if (i == 1) { System.out.println("i jest równe 1"); } else if (i == 2) { System.out.println("i jest równe 2"); } else { System.out.println("i nie jest równe 1"); }
Możemy dodać dowolną ilość bloków else if
, ale tylko po jednym if
oraz else
.
Instrukcje warunkowe – operatory
Jako warunek możesz podstawić każde wyrażenie, które zwraca wartość boolean
(true
lub false
). Mogą to być również metody zwracające boolean
. Do komponowania wyrażeń warunkowych Java udostępnia nam kilka rodzajów operatorów:
- operator porównania
==
, przy pomocy którego możemy porównywać np. wartości prymitywów; oraz jego zaprzeczenie!=
. - operatory relacyjne
<
(mniejszy),>
(większy),<=
(mniejszy lub równy),>=
(większy lub równy),instanceof
(sprawdza czy dany obiekt jest instancją konkretnej klasy). - operatory logiczne
&&
(oznacza AND czyli i) oraz||
(oznacza OR czyli lub).
Szczegółowy opis operatorów w Javie możesz znaleźć tutaj.
Możesz komponować wyrażenia warunkowe w dowolny sposób w zależności od potrzeb np.:
// gdy oba warunki są spełnione, wyrażenie jest prawdziwe x == 1 && y == 1 // gdy przynajmniej jeden warunek jest spełniony, wyrażenie jest prawdziwe x == 1 || y == 1 // gdy oba warunki są spełnione, wyrażenie jest prawdziwe x > 1 && y > 1 // gdy przynajmniej jeden warunek jest spełniony, wyrażenie jest prawdziwe x == 1 || y == 1 || z == 1
Pamiętaj, że kolejność występowanie warunków logicznych (Operator Precedence) ma znaczenie. Jeśli w jednym wyrażeniu użyjesz
&&
i||
, to warunkiem, który najpierw zostanie wykonany będzie warunek&&
(AND). Dopiero później zostanie wykonany warunek||
(OR). Więc gdy stworzymy warunek w taki sposóbx == 1 || x == 2 && y == 1
, to najpierw zostanie wykonana część (AND)x == 2 && y == 1
, a dopiero później pierwszy warunek (OR)x == 1 || (wartość wyrażenie AND)
.Poniżej przykład demonstrujący to zachowanie:
int x = 1; int y = 2; // Wyrażenie: x == 1 || x == 2 && y == 1 // po rozłożeniu na poszczególne warunki // x == 2 && y == 1 // wyrażenie AND zwróci false // x == 1 || false // całe wyrażenie zwróci true System.out.println(x == 1 || x == 2 && y == 1); // wydrukuje trueŻeby sobie z tym poradzić musimy odpowiednio odseparować warunki za pomocą nawiasów:
int x = 1; int y = 2 // Wyrażenie: (x == 1 || x == 2) && y == 1 // po rozłożeniu na poszczególne warunki // Najpierw wykonuje się warunek w nawiasach // (x == 1 || x == 2) // wyrażenie OR zwróci true // true && y == 1 // całe wyrażenie zwróci false System.out.println((x == 1 || x == 2) && y == 1); // wydrukuje false
A w całości może wyglądać to tak:
int x = 1; int y = 1; if (x == 1 && y == 2) { System.out.println("x jest równe 1 i y jest równy 2"); } else if (x == 2) { System.out.println("x jest równe 2"); } else if (x == 1 || y == 1) { System.out.println("x jest równe 1 lub y jest równy 1"); } else { System.out.println("x jest równy " + x + " y jest równy" + y); }
Oczywiście to tylko kawałek przykładowego kodu. Nie jest on bardzo sensowny, ale docelowy kod w wielu aplikacjach wygląda bardzo podobnie.
Instrukcja warunkowa Switch
Kolejną instrukcją warunkową jest instrukcja switch
. Jest ona rzadziej spotykana i właściwie jest bardzo podobna w działaniu do instrukcji if
. Wygląda tak:
int x = 1; switch (x) { case 1: System.out.println("x jest równy 1"); break; case 2: System.out.println("x jest równy 2"); case 3: System.out.println("x jest równy 3"); break; default: System.out.println("x to jakaś inna cyfra"); }
W zależności od wartości x
, pokazywany jest inny komunikat (podobnie możemy napisać bloki if/else
), każdy blok case
jest zakończony instrukcją break
, która kończy wykonywanie kodu w instrukcji switch
(nie jest on jednak konieczny, jeżeli nie umieścimy w case
instrukcji break
, to wywoła się kolejny blok case
, który jest poniżej). Ostatni blok default
jest blokiem domyślnym, jeśli warunki w poszczególnych case'ach
nie są spełnione, wykonuje się blok domyślny (jest on jednak opcjonalny).
Jeśli nie wiesz czy i gdzie stosować instrukcję swich
, to mam dla ciebie dobrą wiadomość. Blok if/else if/else
jest najpowszechniejszą instrukcją warunkową i sprawdza się dosłownie wszędzie, więc śmiało możesz go używać nie zastanawiając się w ogóle nad switchem
.
Pętle
Pętle to instrukcje, które służą do wykonywania zadań iteracyjnych, np. przeglądanie tablic, kolekcji, iteracyjne wypisywanie wartości. Są to kolejne po instrukcjach warunkowych podstawowe elementy języka pomagające w przetwarzaniu danych.
W poprzednich rozdziałach poznałeś już podstawowe pętle: for (iteracyjną) i pętle for each. Pozwól, że teraz omówię je bardziej szczegółowo.
Pętla for
Pętla for to bardzo uniwersalna pętla, ponieważ w wielu językach programowania ma dokładnie taką samą konstrukcję jak w języku Java. Jest ona na tyle uniwersalna, że w zasadzie nie potrzebujesz żadnych innych rodzajów pętli. Jej konstrukcja pozwala przeglądać tablice, kolekcje, jak i przetwarzać różne inne rzeczy.
Podstawowa konstrukcja tej pętli wygląda tak jak poniżej:
for(int i = 0; i < 10; i++) { System.out.println(i); }
Składa się ona ze słowa kluczowego for
oraz nawiasów, w których mamy po kolei: inicjalizację zmiennej (licznika pętli), warunek ograniczający ilość obrotów pętli oraz wyrażenie zwiększające lub zmniejszające licznik. Wszystkie te elementy są opcjonalne, co znaczy, że możesz też spotkać się z takim zapisem:
for(;;) { //pętla nieskończona }
Zajmijmy się teraz po kolei poszczególnymi elementami:
Inicjalizacja zmiennej
Tutaj niewiele może się zmienić ale warto zauważyć, że zmienna pętli może być dowolną nazwą. Zwyczajowo nazywa się ją i
albo j
jeżeli mamy dwie zagnieżdżone pętle. Może to też być np. index
. Co do inicjalizacji tej zmiennej, niekoniecznie musi być ona równa zero. Może być np. równa długości tablicy, którą przeglądamy (w przypadku gdy chcemy przeglądać ją od końca i użyć dekrementacji w warunku i--
):
int[] array = new int[]{1,2,3};
for (int i = array.length - 1; i >= 0; i--) {
System.out.println(array[i]);
} // wyświetli 3 2 1
Warunek pętli
Warunkiem może być każdy dowolny warunek logiczny zgodny z konwencją Javy (może zawierać && i ||, tak jak każdy inny warunek). Może to też być metoda zwracająca wartość boolean
. Nie ma tu specjalnych ograniczeń, jedyne co mi przychodzi do głowy to to, że przy większej ilość składników warunku, pętla zaczyna być nieczytelna.
Wyrażenie zmieniające licznik
Do tej pory przedstawiłem dwie opcje: inkrementację ++
i dekrementację --
licznika. Inkrementacja to skrócony zapis wyrażenia i = i + 1
(dekrementacja podobnie), więc nic nie stoi na przeszkodzie, żeby dowolnie zmodyfikować takie wyrażenie i użyć w pętli np. i = i + 2
int[] array = new int[]{1,2,3}; for (int i = 0; i < array.length; i = i + 2) { System.out.println(array[i]); } //wyświetli 1 3
Pętla foreach
To ulepszona pętla for. Pozwala ona w bardzo prosty sposób przeglądać kolekcje i tablice. I do tego została stworzona. Ponieważ iterowanie po różnych strukturach danych to czynność wykonywana bardzo często przez każdego programistę.
for(String city : citiesList) { System.out.println(city); }
Pętla ta jest uproszczeniem pętli for
i nie ma żadnych dodatkowych opcji. Nie posiada też licznika, więc jeśli potrzebujesz licznika powinieneś użyć zwykłej pętli for
.
Pętla while
Jest to kolejny rodzaj pętli, inny niż poprzednie. Składa się ze słowa kluczowego while
i warunku zawartego w nawiasach. Jeśli chcemy, żeby nasza pętla się obracała i kiedyś zatrzymała musimy użyć licznika podobnie jak w pętli for
:
int i = 0; while (i < 10) { System.out.println(number); i++; }
Ale w takiej formie pętla ta nie ma za bardzo sensu, ponieważ możemy ją zastąpić zwykłą pętlą for (otrzymamy wtedy bardziej zwięzły kod). Jednak możemy tą pętlę stosować, gdy potrzebujemy pętli nieskończonej, wtedy wystarczy, że jako warunek podamy true
:
while (true) { System.out.println("To jest pętla while"); }
Generalnie można przyjąć, że możesz używać pętli while, jeśli nie wiesz jak dużo obrotów pętli będziesz potrzebować np. podczas odczytywania pliku linijka po linijce. Nie wiesz ile linii będzie miał dany plik, dlatego wygodnie jest użyć właśnie pętli while
.
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("file.txt"))); String strLine; while ((strLine = br.readLine()) != null) { System.out.println (strLine); }
Pętla do while
Jest odmianą pętli while, która charakteryzuje się tym, że zostanie wykonana co najmniej raz. Warunek w tej pętli jest sprawdzany po wykonaniu bloku do
.
int i = 0; do{ System.out.println(i); i++; } while(i < 10);
Instrukcje break i continue
Dodatkowo w pętlach możemy użyć instrukcji break
, która zatrzymuje pętle oraz continue
, która sprawia, że wykonanie obrotu pętli zostanie pominięte (jeśli umieścimy tą instrukcję na początku pętli).
Przykład użycia instrukcji break
for(String city : citiesList) { if(city.equals("Warszawa")) { break; } System.out.println(city); }
Pętla zatrzyma się, jeśli na liście znajduje się Warszawa. Natomiast jeśli Warszawa będzie pierwsza na liście, to pętla zakończy się zanim cokolwiek się wyświetli.
Przykład użycia instrukcji continue
for(String city : citiesList) { if(city.equals("Warszawa")) { continue; } System.out.println(city); }
Jeśli na liście znajduje się Warszawa, to dzięki instrukcji continue
nie zostanie ona wypisana.
Podsumowanie
Przedstawione w tej części kursu instrukcje warunkowe pozwolą Ci swobodnie programować i zrealizować prawie każde zadnie programistyczne, które stanie na twojej drodze. Dogłębne zapoznanie się z instrukcjami warunkowymi i pętlami będzie stanowiło podstawę twojego warsztatu programistycznego. Dlatego w wolnych chwilach polecam poeksperymentować z pętlami i z instrukcjami warunkowymi. Spróbuj się z nimi oswoić tak, by ich działanie nie było dla ciebie czymś niejasnym. Raz poznane i dobrze nauczone, będą ci służyć przez lata.
Kurs Java dla początkujących
Spis Treści:
- Wprowadzenie
- Klasy i Obiekty
- Tablice
- Kolekcje
- Instrukcje warunkowe i pętle
- Operacje wejścia i wyjścia
- Dziedziczenie, Polimorfizm, Interfejsy
- Stream’y i lambdy