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 operandami, 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 operatoró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.

Komentarze (10):

  1. Cześć. Nie czaję tego poniżej:

    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

    Czy to jest tak, że zdefiniowana i zainicjowana w pierwszej linii zmienna a, w kolejnych liniach zmienia wartość biorąc ją z linii poprzedzającej, a nie z pierwszej? Ale skoro tak, to dlaczego w linii 6 wartość a będzie równa 0, a nie 1 (w linii piątej wynik jest 2 więc 2%2 powinno być chyba 1?)?

    1. Cześć, Pablito miał podobne pytania w komentarzach, zajrzyj do moich odpowiedzi. Ogólnie program wykonuje się linia po linii i jeżeli w jednym miejscu zmienimy wartość pewnej zmiennej, to w kolejnej linii, w której będziemy korzystać z tej zmiennej, będzie ona miała tę nową wartość. W przypadku tego programu w każdej linii zmieniamy wartość zmiennej a - pomocnicze operatory przypisania modyfikują zmienną, na której działają.

      W linii a %= 2; wynikiem jest zero, bo % to operator wyliczający resztę z dzielenia. Jeżeli a ma wartość 2, to reszta z dzielenia 2 przez 2 wynosi 0.

  2. Przemek ja Cię jeszcze pomęczę 😉
    Jestem na samym początku nauki więc przepraszam za tak zapewne trywialne pytania.

    To co odpisałeś mi w komentarzu powyżej to rozumiem (tak mi się zdaję).

    Potrzebuję wytłumaczenia tego jak program to liczy, skoro mamy zapis, który rozszerzyłem sobie w Inteliji Idea:
    public class test {
    public static void main(String[] args) {
    int x = 5;
    int y = 5;
    int a = x++;
    int b = ++y;
    int c = ++x;
    int d = ++x;
    int e = ++x;

    System.out.println(x);
    System.out.println(a);
    System.out.println(y);
    System.out.println(b);
    System.out.println(c);
    System.out.println(d);
    System.out.println(e);
    }
    }

    Otrzymujemy wtedy wyniki:
    9
    5
    6
    6
    7
    8
    9

    Dlaczego
    System.out.println(x); = 9, a nie 5, skoro jest to pierwsze wyliczenie i pod x podstawiamy 5,
    System.out.println(a); = 5, a nie 6 skoro ,,a'' jest z postfixem, gdzie ja to rozumiem jako: ,,int a = x++; - czyli ,,a'' równe jest x, a ten x zwiekszamy o +1 bo ma postfix ,,++''
    System.out.println(y); = 6 a nie 5, skoro dla ,,y''mamy pierwsze wskazanie y=5,
    System.out.println(b); = 6 to rozumiem skąd bierze się ta wartość - rozumuje to tak, że ,,b = ++y;'' czyli dla mnie b=6, ponieważ pod ,,b'' podstawiamy zwiększoną od razu wartość o +1 czyli ,,5+1=6''
    System.out.println(c); = 7 a nie 6, dla mnie powinno być to 6 ponieważ dla ,,c'' podstawiamy zwiększoną o +1 wartość, ufam Ci i programowi, że liczycie to dobrze i staram się to zrozumieć, że otrzymujemy 7 ponieważ ten ,,x'' był już zwiększany w lini: ,,int a = x++;'' co każe mi myśleć, że program wykonuje obliczenia w miejscu, gdzie mam wpisane ,,System.out.println... i sumuje te wartości, ale wtedy z tym ,,x'' w pierwszym wyrażeniu mi się teoria nie skleja :/
    System.out.println(d); = 8, w tym i kolejnym wierszu rozumuję to jak w wierszy powyższym.
    System.out.println(e); = 9.

    Nie jestem nauczony ludzi tak męczyć więc jest mi głupio, ale czytam w książkach internecie i nie mogę tego dobrze zrozumieć a jestem typem, który jak coś robi to robi to dobrze.

    Z góry dzięki za pomoc 😉

    1. Wyjaśnienia dla każdego z wyszczególnionych przypadków:

      System.out.println(x);

      Tutaj nie ma wyliczania wartości - jest po prostu używana aktualna wartość x. Ta wartość została wcześniej kilka razy przez Ciebie zmieniona używając ++x i x++.

      ---

      System.out.println(a);

      Na odwrót - postfixowy operator (stojący za zmienną) najpierw zwraca wartość, a dopiero potem zwiększa zmienną o 1.

      ---

      System.out.println(y);

      W momencie definicji nadałeś zmiennej y wartość 5, ale potem zwiększyłeś jej wartość o 1 w linii:

       int b = ++y;

      ---

      System.out.println(b);

      Tak 🙂

      ---

      System.out.println(c);
      System.out.println(d);
      System.out.println(e);

      Za każdym razem, gdy używasz operatora ++ (nieważne, czy pre- czy postfixowego), aktualizujesz wartość zmiennej. Jeżeli miałbyś zmienną o nazwie liczba o wartości 0, użyłbyś liczba++, to ta zmienna będzie miała teraz wartość 1. Jeżeli znowu napiszesz liczba++, to teraz liczba będzie przechowywała wartość 2 (bo wcześniej miała wartość 1 i zwiększyliśmy ją znowu o jeden).

      1. Bardzo dziękuję Ci za wyjaśnienie i pomoc.
        Jest to trochę dla mnie pokręcone bo starałem się zrozumieć to, że za każdym razem gdy mamy jakąś zmienną to zaciągamy jej wartość tam gdzie ją określiliśmy na początku (myślenie błędne) teraz rozumiem, że zaciągamy tą wartość z poprzedniej lini kodu i tego będę się trzymał.

        Jeszcze raz dzięki za wyjaśnienie,
        życzę miłego dnia 🙂

  3. 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);
    }
    }

    Ja biorę to na logikę i według mnie powinno to wyjść:
    5
    6
    5
    6
    Wierzę, że napisane jest dobrze przez Ciebie,
    ale nie mogę tego zrozumieć czemu program tak to wylicza :/

    1. Cześć. Wcześniej w tekście napisałem, że te operatory zmieniają wartości zmiennych. Jeżeli napiszesz x++, to wartość x zwiększy się o 1. Jeżeli x ma wartość 5, to po napisaniu x++ wartość zmiennej x zmieni się na 6. Różnica między operatorem prefixowym i postfixowym ++ jest jednak taka, że wartość wyrażenia jako całości w przypadku operatora prefixowego jest obliczana po zmianie wartości zmiennej, a w przypadku operatora postfixowego - przed zmianą wartości zmiennej. Dlatego:

      int x = 5;
      int a = x++;

      powoduje (w tej kolejności):

      1. Zdefiniowanie zmiennej x o wartości 5.
      2. Zdefiniowanie zmiennej a, której zostanie przypisana aktualna wartość zmiennej x, czyli 5.
      3. Zmiana wartości zmiennej x z 5 na 6 za pomocą operatora postfixowego ++.

      Natomiast w przypadku kodu:

      int y = 5;
      int b = ++y;

      wydarzy się:

      1. Zdefiniowanie zmiennej y o wartości 5.
      2. Zdefiniowanie zmiennej b - zanim zostanie jej nadana wartość, operator prefixowy ++ zwiększy wartość zmiennej y z 5 na 6 i dopiero to zaktualizowana wartość, czyli liczba 6, zostanie przypisana jako wartość dla zmiennej b.
      1. Bardzo Ci dziękuję za odpowiedź.
        Szczerze mówiąc nie liczyłem na odpowiedź, a na pewno na tak szybką.
        Dzięki !

        Przy okazji dzięki za ten kurs, robię go od niedawna, bo dla mnie są tutaj podstawowe rzeczy lepiej wytłumaczone niż na Udemy.

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.