Rozdział 3 - Zmienne - Operatory w programowaniu

W poprzednich programach użyliśmy * (gwiazdki) w celu wyliczenia pola i obwodu koła, plusa + do konkatenacji (łączenia) łańcuchów znaków, oraz znaku równości = w celu przypisania zmiennym wartości – są to przykłady operatorów.

Operatory wykonują pewną operację na ich argumentach, które nazywamy operanadami, i zwracają pewną wartość. Dla przykładu, operator * (mnożenia):

  • oczekuje dwóch argumentów (operandów),
  • wykonuje operację na argumentach – mnożenie,
  • zwraca wartość, która jest wynikiem mnożenia argumentów.

Spójrzmy na przykład:

Nazwa pliku: PrzykladOperatoraMnozenia.java
public class PrzykladOperatoraMnozenia {
  public static void main(String[] args) {
    final int SEKUNDY_W_MINUCIE = 60;
    final int MINUTY_W_GODZINIE = 60;

    final int SEKUNDY_W_GODZINIE = SEKUNDY_W_MINUCIE * MINUTY_W_GODZINIE;

    System.out.println("Liczba sekund w godzinie: " + SEKUNDY_W_GODZINIE);
  }
}

W powyższym przykładzie, operator mnożenia * otrzymał dwa argumenty: stałą SEKUNDY_W_MINUCIE oraz MINUTY_W_GODZINIE. Mnożenie zostało wykonane, a jego wynik przypisany do stałej SEKUNDY_W_GODZINIE.

Istnieją różne rodzaje operatorów. Podzielić je możemy ze względu na:

  • rodzaj wykonywanej operacji – wyróżniamy m. in. operatory:
    • arytmetyczne,
    • logiczne,
    • bitowe i inne.
  • liczbę przyjmowanych argumentów – java posiada operatory:
    • jednoargumentowe,
    • dwuargumentowe,
    • jeden operator trójargumentowy.

Każdy język programowania posiada wiele różnych operatorów, które mają różne priorytety – tak jak w matematyce: mnożenie wykonujemy przed dodawaniem, o ile nie użyliśmy nawiasów do zmiany kolejności wykonania działań.

W pierwszej kolejności zaznajomimy się z operatorami arytmetycznymi.

Operatory arytmetyczne

Java posiada następujące operatory arytmetyczne:

  • + dodawanie (służy także do łączenia ciągów znaków i innych wartości)
  • - odejmowanie
  • * mnożenie
  • / dzielenie
  • % modulo – reszta z dzielenia

Operatory te działają na dwóch argumentach i dlatego nazywamy je dwuargumentowymi, bądź binarnymi.

Operatory: mnożenia, dzielenia, oraz reszty z dzielenia, mają taki sam priorytet, który jest wyższy, niż priorytet operatorów dodawania i odejmowania – tak jak w matematyce. Możemy jednak skorzystać z nawiasów, aby zmienić priorytet wykonywania działań.

Operatory dodawania, odejmowania i mnożenia

Spójrzmy na kilka przykładów operatorów dodawania, odejmowania i mnożenia:

Nazwa pliku: OperatoryArytmetycznePodstawy.java
public class OperatoryArytmetycznePodstawy {
  public static void main(String[] args) {
    System.out.println(2 + 2);
    System.out.println(100 - 5);
    System.out.println(10 * 5);

    System.out.println(10 * 0 + 1);
    System.out.println(10 * (0 + 1));
  }
}

W wyniku działania tego programu, na ekranie zobaczymy:

4 95 50 1 10

Dwa ostatnie wyniki różnią się od siebie, ponieważ zmieniliśmy kolejność wykonywania zadań za pomocą nawiasów – w poniższej linii:

System.out.println(10 * 0 + 1);

najpierw mnożmy 10 razy 0, a potem dodajemy 1, a w kolejnej linii:

System.out.println(10 * (0 + 1));

najpierw dodajemy 1 do 0, a potem mnożymy przez 10.

Operatory dzielenia i reszty z dzielenia

Spójrzmy na przykład użycia operatorów dzielenia i reszty z dzielenia:

Nazwa pliku: OperatoryArytmetyczneDzielenie.java
public class OperatoryArytmetyczneDzielenie {
  public static void main(String[] args) {
    System.out.println(10 / 5);
    System.out.println(10 / 4);
    System.out.println(10 % 3);
  }
}

W wyniku działania tego programu, na ekranie zobaczymy:

2 2 1

W trzeciej linii widzimy wartość 1, ponieważ reszta z dzielenia całkowitego 10 przez 3 wynosi 1.

Dlaczego jednak 10 podzielone przez 4 dało 2, a nie 2.5? Otóż, operator dzielenia / zwraca liczbę całkowitą (zaokrągloną w dół), jeżeli oba jego argumenty są typu całkowitego. Jak zatem wykonać dzielenie, by wynikiem była liczba rzeczywista?

  • Możemy albo zapisać jeden z argumentów (o ile jest to literał liczbowy) w taki sposób, aby był on traktowany jako liczba rzeczywista,
  • lub
  • możemy kazać traktować Javie daną liczbę (literał bądź zmienną) jako liczba rzeczywista wykorzystując tzw. rzutowanie.

Rzutowanie

Rzutowanie to traktowanie wartości pewnego typu jako wartości innego typu. Rzutowanie jest często stosowane w programowaniu, szczególnie w językach obiektowych, takich jak Java – więcej o rzutowaniu powiemy sobie, gdy zaczniemy naukę klas w Javie.

Gdy co najmniej jeden z operandów będzie liczbą rzeczywistą, całe wyrażenie będzie liczbą rzeczywistą – spójrzmy na przykład poniżej:

Nazwa pliku: OperatoryArytmetyczneRzutowanie.java
public class OperatoryArytmetyczneRzutowanie {
  public static void main(String[] args) {
    // dodanie wartosci po przecinku powoduje,
    // ze liczba 10.0 jest traktowana jako rzeczywista
    // w takim przypadku, operatora dzielenia zwroci liczbe rzeczywista
    System.out.println(10.0 / 4);

    // rzutowanie literalu liczby calkowitej na rzeczywista
    // wynik jak powyzej - liczba rzeczywista
    System.out.println((double)10 / 4);

    int liczbaCalkowita = 10;
    int innaLiczbaCalkowita = 4;
    // rzutowanie zmiennej calkowitej na rzeczywista
    // ponownie wynikiem bedzie liczba rzeczywista
    System.out.println(liczbaCalkowita / (double)innaLiczbaCalkowita);
  }
}

W pierwszym przykładzie, liczba 10.0 jest traktowana jako liczba rzeczywista, ponieważ ma wartość po przecinku. Co najmniej jeden z argumentów operatora dzielenia jest liczbą rzeczywistą, więc wynikiem będzie także liczba rzeczywista, czyli 2.5.

W drugim i trzecim przypadku używamy niepoznanej jeszcze składni – w nawiasach wpisany jest typ double przed wartością 10 (drugi przykład) i przed zmienną innaLiczbaCalkowita (trzeci przykład). Ma to na celu wskazanie kompilatorowi, iż, zarówno, liczba 10, jak i zmienna innaLiczbaCalkowita, mają być traktowane jako wartości typu double (liczba rzeczywista). W ten sposób, na ekranie zobaczymy:

2.5 2.5 2.5

Zmiana priorytetów operatorów za pomocą nawiasów

Spójrzmy na jeszcze jeden przykład – wykorzystujemy nawiasy, by zmienić kolejność wykonywania działań

Nazwa pliku: OperatoryArytmetyczneNawiasy.java
public class OperatoryArytmetyczneNawiasy {
  public static void main(String[] args) {
    System.out.println(2 + 2 * 2);
    System.out.println((2 + 2) * 2);
    System.out.println(2 * 2 / 2 * 2);
    System.out.println(2 * 2 / (2 * 2));
  }
}

Powyższy kod spowoduje wypisanie na ekran następujących wartości:

6 8 4 1

Zwróćmy uwagę, iż w przypadku, gdy operatory mają taki sam priorytet, to operacje są wykonywane od lewej do prawej.

Plus jako operator konkatenacji

Operatora + możemy używać nie tylko do dodawania liczb, ale także do łączenia łańcuchów znaków z innymi wartościami. Już kilka razy w ten sposób formowaliśmy komunikaty do wypisania za pomocą instrukcji System.out.println:

Nazwa pliku: KonkatenacjaStringow.java
public class KonkatenacjaStringow {
  public static void main(String[] args) {
    int x = 25;
    int y = -10;

    System.out.println("Wspolrzednia x ma wartosc " + x +
        ", a wspolrzedna y ma wartosc " + y);
    System.out.println("Druga" + " " + "linia");
  }
}

W wyniku działania tego programu, na ekranie zobaczymy:

Wspolrzednia x ma wartosc 25, a wspolrzedna y ma wartosc -10 Druga linia

Jak widać, możemy łączyć ze sobą zarówno stringi i wartości innego rodzaju – w tym przypadku, zmienne przechowujące liczby typu całkowitego, jak i łańcuchy tekstowe z innymi łańcuchami tekstowymi.

Operatory przypisania

Innym operatorem dwuargumentowym, o którym już wspominaliśmy, jest operator przypisania = . Ma on za zadanie przypisać zmiennej po jego lewej stronie wartość wyrażenia po jego prawej stronie:

Nazwa pliku: OperatorPrzypisania.java
public class OperatorPrzypisania {
  public static void main(String[] args) {
    int x = 5;
    int y = x * 10;

    double z = (double)y / (x + 3);

    System.out.println(z); // wyswietli 6.25
  }
}

Wynik:

6.25

Wyrażenie jest ważnym pojęciem w programowaniu – jest to dowolna wartość liczbowa, znakowa, logiczna itp., bądź złożone zestawienie wartości i operatorów. Wynikiem wyrażenia jest zawsze finalna wyliczona wartość. Przykładem wyrażeń w powyższym programie są:

x * 10

(double)y / (x + 3)

Gdy przypisujemy zmiennej wartość, możemy użyć tej samej zmiennej do wyliczenia nowej wartości:

Nazwa pliku: OperatorPrzypisaniaTaSamaZmienna.java
public class OperatorPrzypisaniaTaSamaZmienna {
  public static void main(String[] args) {
    int a = 5;
    int b = 10;
    int c = 15;

    c = a + b + c;
  }
}

W powyższym przykładzie, przypisujemy zmiennej c wartość, która jest sumą trzech zmiennych – jedną z nich jest właśnie zmienna c. Istnieje jednak wyjątek – nie możemy użyć tej samej zmiennej do nadania jej wartości, jeżeli ta zmienna nie została jeszcze zainicjalizowana – próba kompilacji poniższego programu zakończy się błędem:

Nazwa pliku: OperatorPrzypisaniaBrakInicjalizacji.java
public class OperatorPrzypisaniaBrakInicjalizacji {
  public static void main(String[] args) {
    int x = 5;

    // blad - zmienna y nie ma jeszcze wartosci
    // wiec nie moze byc czescia wyrazenia
    int y = y * x;
  }
}

Kompilator zgłosi następujący błąd:

OperatorPrzypisaniaBrakInicjalizacji.java:7: error: variable y might not have been initialized int y = y * x; ^ 1 error

Należy tutaj jeszcze zwrócić uwagę, że po lewej stronie operatora przypisania zawsze musi być zmienna – w przeciwnym razie, kompilator zgłosi błąd – nie ma sensu, dla przykładu, przypisywać wartości 10 do wartości 5:

Nazwa pliku: OperatorPrzypisaniaBezZmiennej.java
public class OperatorPrzypisaniaBezZmiennej {
  public static void main(String[] args) {
    // blad! ponizsza linia nie ma sensu
    // do liczby 5 nie mozemy przypisac liczby 10
    5 = 10;
  }
}

Próba kompilacji powyższego programu kończy się następującym błędem:

OperatorPrzypisaniaBezZmiennej.java:5: error: unexpected type 5 = 10; ^ required: variable found: value 1 error

Pomocnicze operatory przypisania

Istnieją także pomocnicze operatory, które można stosować jako "skróty":

  • +=
  • -=
  • *=
  • /=
  • %=

Pierwszy operator dodaje do zmiennej po jego lewej stronie wartość wyliczoną po jego prawej stronie, czyli zapis:

zmienna += 1 + 2;

jest równoznaczny z zapisem:

zmienna = zmienna + (1 + 2);

Analogicznie z każdym z pozostałych operatorów. Przykład:

Nazwa pliku: OperatorPrzypisaniaPomocnicze.java
public class OperatorPrzypisaniaPomocnicze {
  public static void main(String[] args) {
    int a = 10;

    a += 100; // a = 10 + 100, wiec a bedzie rowne 110
    a -= 10; // a = a – 10, wiec a bedzie rowne 100
    a *= 5; // a = a * 5, wiec a bedzie rowne 500
    a /= 25 * 10; // a = a / (25 * 10), wiec a bedzie rowne 2
    a %= 2; // a = a % 2, wiec a bedzie rowne 0

    System.out.println(a); // wypisze 0
  }
}

Wynik działania:

0

Tak samo, jak w przypadku operatora przypisania =, po lewej stronie każdego z powyższych operatorów musi znajdować się zmienna.

Operatory jednoargumentowe

Istnieją także operatory jednoargumentowe:

  • + oznacza, że liczba jest dodatnia (nieużywany – liczby są domyślnie traktowane jako dodatnie),
  • - zamienia wartość wyrażenia na liczbę przeciwną,
  • ++ (prefix) – pre-inkrementacja – zwiększanie wartości zmiennej o 1,
  • ++ (postfix) – post-inkrementacja – zwiększanie wartości zmiennej o 1,
  • -- (prefix) – pre-dekrementacja – zmniejszanie wartości zmiennej o 1,
  • -- (postfix) – post-dekrementacja – zmniejszanie wartości zmiennej o 1.

Operatory inkrementacji i dekrementacji to skrótowe zapisy zwiększania bądź zmniejszania wartości zmiennej o 1. Przykład użycia opeartorów jednoargumentowych:

Nazwa pliku: OperatoryJednoargumentowe.java
public class OperatoryJednoargumentowe {
  public static void main(String[] args) {
    int x = 0;
    int y = 0;
    int z = 100;

    x++;
    ++y;
    z = -z; // zmieniamy wartosc na przeciwna

    System.out.println("x ma wartosc " + x);
    System.out.println("y ma wartosc " + y);
    System.out.println("z ma wartosc " + z);
  }
}

Wynikiem działania tego programu jest:

x ma wartosc 1 y ma wartosc 1 z ma wartosc -100

Operator ++ spowodował zmianę wartości zmiennych x oraz y – obie z nich zwiększył o jeden. Dlaczego istnieją dwa operatory, jeden prefixowy, a drugi postfixowy?

Wersje prefixowe najpierw zmieniają wartość zmiennej, a potem zwracają tę wartość, podczas gdy wersje postfixowe najpierw zwracają wartość zmiennej, a dopiero potem zmieniają zmienną.

Spójrzmy na poniższy przykład:

Nazwa pliku: OperatoryInkrementacji.java
public class OperatoryInkrementacji {
  public static void main(String[] args) {
    int x = 5;
    int y = 5;
    int a = x++;
    int b = ++y;

    System.out.println(x);
    System.out.println(a);

    System.out.println(y);
    System.out.println(b);
  }
}

Wynik działania tego programu:

6 5 6 6

Operator postfixowy ++ najpierw zwrócił wartość zmiennej x (która wynosiła 5) i to ta wartość została wpisana do zmiennej a, a dopiero później zwiększył wartość przechowywaną w zmiennej x o 1 – stąd w pierwszej i drugiej linii wartości to, odpowiednio, 6 oraz 5.

W przypadku zmiennej y użyliśmy operatora prefixowego – najpierw zmieniona została wartość y, a później, ta już zmieniona wartość zmiennej y, została przypisana do zmiennej b – dlatego dwie ostatnie linie na ekranie mają wartości, odpowiednio, 6 oraz 6.

Niektóre operatory operują wyłącznie na zmiennych – zapis ++5, zakończyłby się błędem kompilacji, ponieważ operatory ++ i -- wymagają, by ich argumentem była zmienna – podobnie, jak w przypadku operatorów przypisania.

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.