Poprzednio poznaliśmy podstawową instrukcję warunkową, czyli if…else
. Jest ona na tyle uniwersalna, że w zasadzie można używać tylko jej w sytuacjach, w których należy wykorzystać instrukcję warunkową. Ale Java udostępnia także inne możliwości, które warto wykorzystywać choćby ze względu na przejrzystość kodu. Przy okazji wyjaśnię jak porównać jak porównać dwa łańcuchy znaków.
Ta strona jest częścią materiałów do kursu “Programowanie w Javie z elementami bioinformatyki dla poczatkujących”. Pozostałe materiały znajdziesz tutaj
Jeżeli…, a jeżeli nie ale… (if..else if..else)
Program z poprzedniej lekcji działa, ale jeśli będziemy mieli do czynienia z coraz bardziej zagnieżdżającymi się blokami kodu, jego przejrzystość mimo zastosowania wcięć będzie maleć. Jeśli mamy do czynienia z taką sytuacją, że należy sprawdzić kolejno wiele warunków i w przypadku natrafienia rezultat true
jednego z nich nie sprawdzamy już następnych, warto użyć rozszerzonej wersji if..else
czyli if..else if..else
.
if (warunek1) {
// Kod wykonywany jeśli warunek1 zwraca wartość true
}
else if (warunek2) {
// Kod wykonywany jeśli warunek2 zwraca wartość true
}
else {
// Kod wykonywany jeśli żaden z powyższych warunków
// nie zwróci wartości true
}
Jak widać między wyrażeniami if
oraz else
pojawiło się wyrażenie if else
, po którym także należy umieścić warunek. Jego prawdziwość jest sprawdzana wtedy, gdy poprzedni warunek nie zwraca wartości true
. Jeśli żadne z wyrażeń if
oraz if else
nie zwróci wartości true
to wykonywane są polecenia znajdujące się w bloku kodu po wyrażeniu else
. Wyrażenie else
oczywiście można pominąć. Co ważne, w zasadzie nie ma ograniczeń co do liczby wyrażeń else if
co pozwala sprawdzać wiele warunków po kolei.
Wykorzystując taką konstrukcję można zmodyfikować program na przykład tak:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// Sprawdzenie, czy liczba jest parzysta if (liczba%2 == 0) { // polecenie wykonuje się, jeżeli liczba jest parzysta System.out.println("Liczba "+liczba+" jest parzysta"); } // czy liczba jest podzielna przez 3? else if (liczba%3 == 0){ // polecenie wykonuje się, jeżeli liczba jest podzielna przez 3 System.out.println("Liczba "+liczba+" jest nieparzysta i podzielna przez 3"); } else { // polecenie wykonuje się, jeżeli powyższe nie sa prawdziwe System.out.println("Liczba "+liczba+" jest nieparzysta i nie jest podzielna przez 3"); } |
Ćwiczenie:
Spróbuj zmodyfikować kod w ten sposób, żeby program sprawdzał także podzielność przez 5, 7 i 11.
A oto inny przykład, poprawione zadanie z poprzedniej lekcji. Prawda, że kod jest bardziej czytelny?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
import java.util.Scanner; public class RamionaChromosomow { public static void main(String[] args) { Scanner skaner = new Scanner(System.in); System.out.println("Podaj długośc pierwszego chromosomu [μm]: "); double ramie1 = skaner.nextDouble(); System.out.println("Podaj długośc drugiego chromosomu [μm]: "); double ramie2 = skaner.nextDouble(); if (ramie1 < ramie2) { System.out.println("Ramię dłuższe: "+ ramie2 +"μm" ); System.out.println("Ramię krótsze: "+ ramie1 +"μm" ); System.out.println("Stosunek ramion: " + ramie1 / ramie2); } else if (ramie1 > ramie2) { System.out.println("Ramię dłuższe: "+ ramie1 +"μm" ); System.out.println("Ramię krótsze: "+ ramie2 +"μm" ); System.out.println("Stosunek ramion: " + ramie2 / ramie1); } else { System.out.println("Oba ramiona są równe: " + ramie1); System.out.println("Stosunek ramion: " + 1.0); } } } |
Włącz to (switch)
Ostatnią z omawianych instrukcji wyboru jest switch
, pozwalająca na łatwe uruchamianie fragmentów kodu w zależności od wartości wyrażenia. Ogólny schemat stosowania instrukcji można przedstawić tak:
switch (wyrażenie) {
case wartość1:
//kod który się wykonuje, jeżeli wyrażenie zwraca wartość wartość1
break;
case wartość2:
//kod który się wykonuje, jeżeli wyrażenie zwraca wartość wartość2
break;
…
default:
//kod który się wykonuje domyślnie, jeśli żadne z powyższych nie pasuje
}
Warto zwrócić uwagę, że:
- Struktura
switch
jest nieco nietypowa dla Javy. Bloki kodu wykonujące po poszczególnych wyrażeniachcase
są poprzedzone dwukropkiem i nie są objęte parą nawiasów klamrowych. Parą nawiasów klamrowych jest natomiast objęty cały blok kodu objęty wyrażeniemswitch
- Na końcu każdego z nich znajduje się komenda
break
. Powoduje ona przeskok wykonywanego programu na koniec kodu objętegoswitch
. Co się dzieje, jeślibreak
nie ma wyjaśnię później. - Kod umieszczany po wyrażeniu
default
wykonuje się, gdy żadne z wyrażeń znajdujące się po poprzedzających wyrażeniachcase
nie jest prawdziwe. - Znajdujące się przy
switch
wyrażenie powinno zwracać wartość typubyte
,char
,short
,int
, lub łańcuch znaków (String
). Może to być nazwa zmiennej zwracającej wartość jednego z wymienionych typów.
Poniższy kod pokazuje działanie instrukcji switch
na przykładzie prostego programu tłumaczącego oceny wyrażone w liczbach na oceny słowne (w skali akademickiej):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
System.out.println("Podaj ocenę w formie liczbowej"); int ocena = skaner.nextInt(); switch (ocena) { case 2: System.out.println("ndst"); break; case 3: System.out.println("dst"); break; case 4: System.out.println("db"); break; case 5: System.out.println("bdb"); break; default: System.out.println("Nie rozumiem"); } |
Przykład jest chyba na tyle czytelny, że nie wymaga komentarza.
Spróbujmy teraz sprawdzić co się stanie jeśli usuniemy instrukcję break
na przykład przy case 3
. Teraz po uruchomieniu wpisz ocenę 3
:
Podaj ocenę w formie liczby całkowitej
3
Dostateczny
Dobry
Wynik jest nieco zaskakujący. Zauważ, że wykonała się nie tylko instrukcja znajdująca się przy case 3
ale także ta, która powinna się wykonać dla wartości oceny równej 4
. Wynika z tego, że usunięcie break
powoduje przejście wykonywania kodu do bloku przyporządkowanemu kolejnej instrukcji case
niezależnie od tego czy sprawdzana wartość wyrażenia pasuje czy nie. Jeśli usuniemy wszystkie instrukcje break
, wykonają się wszystkie bloki kodu objęte instrukcją switch
, łącznie z tym który znajduje się po default
.
W nowszych wersjach Javy w instrukcji switch
można również używać łańcuchów znaków (String
). Odwróćmy zatem sposób działania ostatniego programu, teraz ocena wpisana w formie słownej, będzie tłumaczona na cyfrę:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
System.out.println("Podaj ocenę w formie słownej (ndst/dst/db/bdb)"); String ocenaSlowna = skaner.next(); switch (ocenaSlowna) { case "ndst": System.out.println(2); break; case "dst": System.out.println(3); break; case "db": System.out.println(4); break; case "bdb": System.out.println(5); break; default: System.out.println("Nie rozumiem"); } |
Przykładowa sesja może wyglądać tak:
Podaj ocenę w formie słownej (ndst/dst/db/bdb)
dst
3
Porównania łańcuchów znaków
Ostatni przykład pokazał, że można wykorzystywać także łańcuchy znaków w instrukcji warunkowej typu switch
, która porównuje określony String
z innymi łańcuchami umieszczonymi przy instrukcjach case
i na tej podstawie była dokonywana decyzja dotycząca uruchomienia lub nie poszczególnych bloków kodu.
W instrukcjach typu if..else
mieliśmy natomiast do czynienia z warunkami, które zwracały true
lub false
. Dotychczas w takich testach stosowaliśmy liczby sprawdzając na przykład, czy są sobie równe, czy jedna jest większa od drugiej itp. Jak jednak sprawdzić czy jeden ciąg znaków odpowiada innemu? Zapewne przychodzi Ci do głowy coś w tym stylu:
1 2 3 4 5 6 7 8 |
String rodzaj1 = "Poa"; String rodzaj2 = "Poa"; if (rodzaj1 == rodzaj2) System.out.println("Ten sam rodzaj"); else System.out.println("Różne rodzaje"); |
Po uruchomieniu uzyskujemy Ten sam rodzaj
, może się wydawać, że ten sposób działa. Ale spróbujmy nieco zmodyfikować kod:
1 2 3 4 5 6 7 8 9 10 11 |
Scanner skaner = new Scanner(System.in); System.out.println("Podaj pierwszy rodzaj"); String rodzaj1 = skaner.next(); System.out.println("Podaj drugi rodzaj"); String rodzaj2 = skaner.next(); if (rodzaj1 == rodzaj2) System.out.println("Ten sam rodzaj"); else System.out.println("Różne rodzaje"); |
Teraz nasza interakcja z programem może wyglądać następująco:
Podaj pierwszy rodzaj
Poa
Podaj drugi rodzaj
Poa
Różne rodzaje
Jak widać to nie takie proste. Porównywanie łańcuchów znaków za pomocą operatora ==
, nie wchodząc w szczegóły, udaje się tylko czasami nie należy więc go stosować. Jak zatem porównać dwa łańcuchy znaków?
Wcześniej wspomniałem, że String
należy do typów obiektowych a także, że obiekt może przechowywać dane i wykonywać pewne operacje.
Zmodyfikuj program w ten sposób:
…
if (rodzaj1.equals(rodzaj2))
…
Teraz działanie jest poprawne:
Podaj pierwszy rodzaj
Poa
Podaj drugi rodzaj
Poa
Ten sam rodzaj
Jak to działa? Obiekty typu String
nie tylko przechowują łańcuchy znaków ale mogą wykonywać wiele użytecznych operacji na nich, miedzy innymi porównywać do innych łańcuchów znaków. Możliwości z tym związane omówię szerzej w osobnej lekcji, teraz skupimy się właśnie na prostym porównywaniu łańcuchów.
Jedną z dostępnych metod służących porównywaniu znaków jest właśnie użyta przez nas metoda equals()
. Zauważ, że nazwę obiektu i metodę, którą na nim wywołujemy łączy kropka. Metoda equals()
przyjmuje jako argument inny String. Jeśli oba łańcuchy znaków są takie same, to zwracana jest wartość true
, jeśli nie to otrzymujemy false
. Metoda equals()
ma jednak tą właściwość, że zwraca uwagę na wielkość znaków, zatem „Poa” i „poa” to dla niej różne łańcuchy.
Podaj pierwszy rodzaj
Poa
Podaj drugi rodzaj
poa
Różne rodzaje
Jeśli wielkość liter nie jest dla nas istotna to należy użyć innej metody: rodzaj1.equalsIgnoreCase()
. Jeśli więc zmienimy stosowane porównanie na takie:
if (rodzaj1.equalsIgnoreCase(rodzaj2))
To wynik może być następujący:
Podaj pierwszy rodzaj
Poa
Podaj drugi rodzaj
poa
Ten sam rodzaj
W tabelce poniżej pokazałem przykłady wyników porównania łańcuchów znaków za pomocą dwóch poznanych metod.
metoda/przykład | wynik |
---|---|
String tekst = “kot”; | |
tekst.equals(“kot”) | true |
tekst.equals(“pies”) | false |
tekst.equals(“KOT”) | false |
tekst.equalsIgnoreCase(“kot”) | true |
tekst.equalsIgnoreCase(“pies”) | false |
tekst.equalsIgnoreCase(“KOT”) | true |
Albo, albo, czyli operator trójargumentowy (? :)
Na koniec warto wspomnieć o operatorze warunkowym, który ma taką postać:
wyrażenie ? wartośćP : wartośćF
W zależności od tego, czy wyrażenie jest prawdziwe czy nie, zostaje zwrócona wartośćP lub wartośćF.
Na przykład można go użyć w ten sposób:
System.out.println( liczba%2 == 0 ? "Parzysta" : "Nieparzysta");
I jeszcze nieco dłuższy przykład:
1 2 3 4 5 6 7 |
int sumaPunktów = 10; System.out.println("Suma punktów = "+sumaPunktów); String rezultat = "wygrana"; int punkty = rezultat.equals("wygrana") ? 1 : -1; sumaPunktów = sumaPunktów + punkty; System.out.println("Obecna suma punktów = "+sumaPunktów); |
Główną zaletą operatora warunkowego jest to, że jest krótki i przejrzysty. Wady są takie, że pozwala na wybór tylko dwu możliwości (nie ma odpowiednika else if
) a także jego zastosowanie jest w zasadzie ograniczone do zwracania wartości, nie pozwala na bezpośrednie wywołanie bloku kodu.
Operator negacji
Zestaw poznanych w operatorów logicznych należy jeszcze uzupełnić o operator negacji: !
. Znak wykrzyknika stosowaliśmy przy sprawdzaniu czy jakieś wartości nie są równe, służył do tego operator !=
. Wykrzyknik ma tu znaczenie zaprzeczenia i generalnie zaprzecza temu co zwraca argument, który poprzedza. Na przykład jeśli mamy wyrażenie 3 < 5
to zwraca ono wartość true
, natomiast wyrażenie !(3 < 5)
zwróci wartość przeciwną, czyli false
. Operator !
można też zastosować w przypadku łańcuchów znaków, co także widać w poniższym przykładzie.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
int a = 2; int b = 5; System.out.println("a = " + a + " b = " + b); System.out.println("a < b " + (a < b)); System.out.println("a == b " + (a==b)); System.out.println("a != b " + (a!=b)); System.out.println(); // pusta linia System.out.println("!(a < b) " + !(a < b)); System.out.println("(a == b) " + !(a==b)); System.out.println("!(a != b) " + !(a!=b)); System.out.println(); // pusta linia String slowo = "kot"; System.out.println(" kot? "+ slowo.equals("kot")); System.out.println("! kot? "+ !slowo.equals("kot")); |
Zadania:
-
Napisz program, który pobierze od użytkownika informację na temat chromosomów płciowych człowieka (XY lub XX) i na tej podstawie określi płeć (męska lub żeńska)
-
Rozszerz program w ten sposób, żeby uwzględniał organizmy podane w poniższej tabeli :
organizm | samica | samiec |
---|---|---|
ssak | XX | XY |
ryba | XY | XX |
ptak | XY | XX |
motyl | XY | XX |
szczaw | XX | XYY |
Uwaga: tabelka jest oczywiście uproszczona, np. nie wszystkie ssaki i motyle mają prezentowany sposób determinacji płci.
Rozwiązanie w następnej lekcji.
- W starszych wersjach Javy (do 6) wyrażenie było ograniczone to typów
byte
,short
,int
orazchar
.
Ta strona jest częścią materiałów do kursu “Programowanie w Javie z elementami bioinformatyki dla poczatkujących”. Pozostałe materiały znajdziesz tutaj
Poniżej kod dla zadania 2go. Nie wiedziałem jak to zrobić w przypadku XYY, ale będę wiedział przeglądając kolejną lekcję.
public class XXXY {
public static void main(String args[]) {
System.out.println(„Podaj chromosomy płciowe człowieka (XX lub XY)”);
Scanner podaj = new Scanner(System.in);
String plec = podaj.next();
if (plec.equalsIgnoreCase(„XX”))
{
System.out.println(„ssak: samica, ryba: samiec, ptak: samiec, motyl: samiec, szczaw: samica”);
}
else if (plec.equalsIgnoreCase(„XY”))
{
System.out.println(„ssak: samiec, ryba: samica, ptak: samica, motyl: samica, szczaw: samiec”);
}
}
}