Image
21.6.2016 0 Comments

Java pod lupou I. /3.časť

Späť na úvod >> Späť na programovanie >> Späť na seriál

Typy, výrazy, operátory

V predošlej časti sme sa dozvedeli o tom, že program v Jave pozostáva z kolekcie tried, na základe ktorých počas behu programu vznikajú objekty. Tie medzi sebou navzájom komunikujú pomocou správ. Konkrétny spôsob realizácie správ je v prípade Javy klasické volanie funkcií, ktoré v tomto prípade nazývame metódami. Každá metóda, ako aj každá premenná je súčasťou nejakej triedy.

Druhá polovica predchádzajúcej časti opisovala program z  lexikálneho hľadiska – komentáre, oddeľovače a predovšetkým klasifikáciu lexikálnych jednotiek, stavebných jednotiek programu, akoby korálikov, ktoré jeden po druhom navlieka programátor na šnúru, tvoriac tak použiteľný program.

V tretej časti seriálu o Jave sa čitateľ dozvie o údajových typoch, ktoré v Jave možno používať, a o spôsobe ich použitia vo forme výrazov. Takisto tu nájde opis najjednoduchších operátorov, realizujúcich základné matematické operácie, ako sčítanie, násobenie a pod.

Premenné

Skôr než začneme hovoriť o typoch jazyka Java, bolo by nanajvýš vhodné uviesť niekoľko praktických poznámok o premenných. Interpretácia pojmu premenná je v programovacích jazykoch máličko odlišná od klasickej matematickej predstavy. Premenná v programe predstavuje miesto na uchovanie údajov (ang. storage location). Každej premennej možno priradiť niekoľko atribútov: predovšetkým je to jej názov, ktorý ju jednak odlišuje od iných premenných, jednak poskytuje programátorovi jednoznačný spôsob, ako sa na ňu kdekoľvek v programe odvolať. Medzi ďalšie atribúty, o ktorých bude podrobnejšie reč v budúcich častiach, patrí napríklad doba trvania, rozsah platnosti a podobne.

Aby čitateľ mohol sledovať príklady uvedené v nasledujúcich odsekoch, treba aspoň v stručnosti ukázať, akým spôsobom do programu zapíšeme, že chceme pracovať s takou a takou premennou, takého a takého typu. Základná schéma je veľmi jednoduchá: deklarácia premennej je tvorená trojicou typ – názov – inicializátor. Ak teda budeme chcieť deklarovať napríklad premennú val typu int (hneď si o tomto type povieme viac) a súčasne jej priradiť počiatočnú hodnotu 123, vložíme do programu riadok:

int val = 123;

Bodkočiarka za deklaráciou je povinná. Znalosť tejto schémy nám postačí do doby, keď sa k deklaráciám premenných (a nielen tých) vrátime.

Údajové typy v Jave

Nech si zoberieme akýkoľvek aspoň trochu zmysluplný počítačový program, vždy dospejeme k zisteniu, že základom jeho činnosti je práca s údajmi. Ako sa dá predpokladať, vo všeobecnosti to budú rôznorodé údaje: číselné či textové, štruktúrované i neštruktúrované. Pojem údajový typ v programovacích jazykoch túto rôznorodosť kvalifikuje – o rôznych údajoch hovoríme, že sú rôzneho typu. Každý údajový typ má svoje špecifické vlastnosti, predovšetkým je nevyhnutné poznať množinu hodnôt, ktoré obsiahne, a súbor operácií, ktoré nad týmito hodnotami možno vykonávať.

Hneď na úvod treba podotknúť, že Java je jazyk s prísnou typovou kontrolou. Každá premenná a každý výraz má svoj typ a tento typ je vždy možné stanoviť už počas prekladu. Takýto prístup pomáha minimalizovať počet chýb, ktoré sa prejavia až počas používania programu. Množstvo chýb, plynúcich z nekorektnej práce s údajmi (ako sa to, žiaľ, stáva v C++), dokáže zachytiť a ohlásiť už kompilátor.

Údajové typy v Jave možno rozdeliť do dvoch kategórií: primitívne typyreferenčné typy. Špeciálnym typom zostáva nulový typ, ktorý nemá nijaké meno, nie je ho možné použiť na deklaráciu premenných a jedinou platnou hodnotou tohto typu je nulový odkaz, reprezentovaný literálom null. Ako sme sa dozvedeli minulý mesiac, hodnotu null obsahuje premenná referenčného (objektového) typu, ktorá neukazuje na nijaký existujúci objekt. Nulový typ nie je totožný s typom void z jazyka C++. Java obsahuje kľúčové slovo void, no to sa používa bez výnimky na deklaráciu neexistujúcej návratovej hodnoty metód.

Primitívne typy

Prvou skupinou údajových typov v Jave sú primitívne typy. Medzi ne patria všetky číselné typy a typ boolean. Číselné typy možno rozdeliť na celočíselné a s pohyblivou čiarkou. Celočíselných typov je päť: byte, short, int, long char, typy s pohyblivou čiarkou sú dva: float double.

Typ byte má šírku 8 bitov, najvyšší bit je znamienkový. Rozsah hodnôt, ktoré tento typ obsiahne, je –128 až 127. Jeho ekvivalentom v C++ je typ unsigned char. Príklad deklarácie premennej typu byte:

byte b = 0x3F;

Ďalší typ short je v Jave široký vždy 16 bitov (opäť so znamienkom). Hodnoty tohto typu patria do intervalu –32768 až 32767. Podobný typ short int v C++ je oproti tomu implementačne závislý, i keď na väčšine implementácií má takisto 16 bitov. Príklad deklarácie:

short k = –1500;

Typ int má šírku 32 bitov, čo mu umožňuje uchovávať hodnoty v rozsahu od –231 po 231–1. Najvyšší bit aj v tomto prípade slúži na zobrazenie znamienka. Rovnako pomenovaný typ v C++ má podľa tradície šírku totožnú so šírkou slova procesora, čo je dnes zatiaľ ešte 32 bitov (to, samozrejme, neplatí v archaickom DOS-e, tam sa musíme uspokojiť so 16 bitmi). Príklad premennej typu int:

int n = 12345678;

Najširším číselným typom v Jave je typ long – celých 64 bitov. Aj tento typ umožňuje ukladanie záporných hodnôt a jeho rozsah je logicky –263 až 263–1. V C++ nájdeme typ s rovnakým názvom, len s menším, 32-bitovým rozsahom. Príklad deklarácie premennej typu long:

long l = –256L;

Čitateľ si isto spomenie, ako sme v predošlej časti seriálu okrem iného hovorili o celočíselných literáloch. Tie sa v porovnaní s vymenovanými celočíselnými typmi obmedzujú len na typy int long. Na osvieženie pamäti – celočíselnému literálu môžeme typ long explicitne priradiť pomocou prípony l či L. Literály typu byte alebo short v Jave nenájdeme; ak treba, vždy môžeme použiť literál typu int, len musíme dodržať povolený rozsah hodnôt. Nasledujúca deklarácia je teda chybou:

byte b = 500;

Piatym celočíselným typom, stojacim trochu mimo predchádzajúcich štyroch, je typ char. Ako naznačuje jeho názov, použijeme ho na uloženie znakových údajov. Z prvej časti si pamätáme, že Java podporuje štandard Unicode, šírka typu char je z tohto dôvodu 16 bitov. Na rozdiel od štyroch znamienkových typov je typ char bez znamienka a jeho rozsah je teda 0 až 65535 alebo presnejšie '\u0000' '\uFFFF'. Premenné typu char je, samozrejme, možné inicializovať celočíselným i znakovým literálom. Oba nasledujúce príklady sú preto ekvivalentné, i keď z praktických dôvodov sa preferuje ten prvý:

char c = 'Z';

char c = 90;

Celočíselné typy sú vhodné len na reprezentáciu celočíselných hodnôt. Naproti tomu typy s pohyblivou čiarkou použijeme v prípade, že potrebujeme pracovať s číselnými údajmi mimo oboru celých čísel. Je pomerne bežnou chybou domnievať sa, že pomocou typov s pohyblivou čiarkou zobrazujeme reálne čísla. Vzhľadom na konečnú presnosť zobrazenia ide, pochopiteľne, len o  racionálne čísla.

Java poskytuje dva typy s pohyblivou čiarkou: floatdouble. Oba typy vyhovujú štandardu IEEE 754 – prvý poskytuje jednoduchú presnosť a šírku 32 bitov, druhý dvojnásobnú presnosť a šírku 64 bitov. V súlade so zmieneným štandardom umožňujú oba typy reprezentovať aj určité špeciálne hodnoty: kladnú a zápornú nulu, kladné a záporné nekonečno a zvláštnu hodnotu NaN (z anglického Not a Number), ktorá sa používa na reprezentáciu určitých neplatných operácií, ako napríklad delenie nuly nulou. V programe sa môžeme na túto hodnotu odvolávať pomocou konštantných premenných Float.NaNDouble.NaN.

Rozsahy hodnôt typov floatdouble sa rozprestierajú na obe strany od začiatku číselnej osi. Každá hodnota s pohyblivou čiarkou je vnútorne reprezentovaná dvojicou mantisa – exponent (výsledná hodnota je daná výrazom mantisa × 2exponent). V type float je pre mantisu vyhradených 24 bitov a pre exponent 8 bitov, takže celkový rozsah je rádovo v rozmedzí –2127 až –2–126 a 2–126 až 2127. Pre ilustráciu, číslo 2127 je rádovo približne rovné 1038. Typ double poskytuje pre mantisu 53 bitov a pre exponent 11 bitov, čo dáva rozsah v rozpätí –21023 až –2–1022 a 2–1022 až 21023. Číslo 21023 je pri dekadickom základe približne rovné 10308.

Binárna reprezentácia čísel s pohyblivou čiarkou počíta s tzv. normalizovanou mantisou. Normalizáciu môžeme ilustrovať na nasledujúcom príklade: ak by sa na ukladanie hodnôt používala desiatková sústava, číslo 123 × 104 by bolo treba pred uložením normalizovať na tvar 1,23 × 106. Java okrem normalizovaných čísel vyžaduje v súlade so štandardom IEEE 754 podporu aj pre tzv. denormalizované hodnoty. Týmto názvom označujeme čísla také malé, že ich už normalizovať nemožno. V dekadickom ponímaní by išlo napríklad o hodnotu 0,00123 × 10–308. Zmena mantisy z 0,00123 na 1,23 by si totiž v tomto prípade vyžadovala zníženie exponentu na hodnotu –311, čo, samozrejme, nejde.

Príklad deklarácie premennej typu float i typu double:

float f = 1.234F;

double d = –3.894E–21;

Pozorný čitateľ si spomenie, že literály typu float explicitne určuje prípona f, resp. F.

Posledný primitívny typ boolean slúži na reprezentáciu logických hodnôt. Najjednoduchší zo všetkých typov, pozostáva len z jediného bitu. Rozsah typu boolean tvoria len dve hodnoty, reprezentované literálmi truefalse. Na rozdiel od jazyka C++ typ boolean v Jave nemožno len tak jednoducho pretypovať na celočíselný typ. O tom si však ešte povieme. Príklad deklarácie premennej typu boolean:

boolean flag = false;

Špecifickou vlastnosťou primitívnych typov v Jave je skutočnosť, že každá premenná či výraz primitívneho typu predstavuje samostatnú hodnotu. Tento na pohľad možno triviálny fakt možno demonštrovať nasledujúcim jednoduchým príkladom:

public class Test
{
    public static void main(String[] args)
    {
        int a = 1;
        int b = a;
        a = 2;
        System.out.println("a = " + a);
        System.out.println("b = " + b);
    }
}

Po preložení a spustení tohto krátkeho programu by ste mali na obrazovke dostať nasledujúci výstup:

a = 2;

b = 1;

Je teda očividné, že hoci sme premennú b inicializovali pomocou premennej a (zápisom int b = a), obe premenné sú inak samostatné a navzájom nezávislé, takže neskoršia zmena jednej z nich neovplyvní druhú. Toto je fundamentálna vlastnosť primitívnych typov; o chvíľu uvidíme, že pri referenčných typoch je situácia odlišná.

Referenčné typy

Druhú skupinu typov v Jave tvoria referenčné typy. Sem patria triedy, rozhrania a polia. Všetky tri druhy referenčných typov považujeme za objektové typy (v zmysle OOP). O triedach sme si základné informácie povedali v predchádzajúcich častiach, širší pohľad na ne príde na rad v blízkej budúcnosti. Rozhrania sú zvláštnym konceptom, používaným najčastejšie ako náhrada za neexistujúcu jazykovú podporu pre viacnásobnú dedičnosť. V určitom zmysle možno javovské rozhrania chápať ako vzdialeného príbuzného abstraktným triedam v C++. Toto prirovnanie však nie je celkom presné, pretože ako uvidíme, triedy možno explicitne označiť ako abstraktné aj v Jave. O rozhraniach bude reč neskôr.

Zostáva teda tretí referenčný typ, a to pole. Na rozdiel od C++, kde je prakticky celá kontrola korektnej práce s prvkami poľa ponechaná na programátorovi, je v Jave typ pole značne sofistikovaný a výrazne bezpečnejší. Každé pole je vytvárané dynamicky, pri prístupe k jednotlivým jeho prvkom program (presnejšie run-time prostredie) kontroluje, či nie je zadaný index mimo hraníc, a práca s viacrozmernými poľami je oveľa prehľadnejšia a flexibilnejšia.

Poliam bude venovaná samostatná časť seriálu, preto nasleduje len krátka ukážka spôsobu deklarácie poľa a práce s jeho prvkami:

int[] a = { 1, 2, 3, 4, 5 };
a[2] = 333;
System.out.println("a[4] = " + a[4]);

Výstup tohto miniprogramu bude nasledujúci:

a[4] = 5

V uvedenom fragmente deklarujeme pole celých čísel s názvom a, obsahujúce päť prvkov. Indexovanie jednotlivých prvkov sa realizuje pomocou hranatých zátvoriek, prvky číslujeme od nuly. Na príklade vidieť, že v princípe sa deklarácia poľa spojená s inicializáciou nelíši od toho, čo sme videli doteraz. Rozdiel nastane v prípade, že sa rozhodneme inicializačnú časť deklarácie vynechať. Ale o tom neskôr. Za pozornosť ešte stojí, že typ „pole celých čísel typu int“ označujeme veľmi intuitívne ako int[]. Je však možné použiť aj deklaráciu v štýle C++:

int a[] = { 1, 2, 3, 4, 5 };

Pozornému čitateľovi isto neunikla skutočnosť, že dosiaľ sme nehovorili o type, ktorý by reprezentoval textové reťazce. V jazyku C++ je reťazec znakov realizovaný ako pole znakov, ukončené nulovou hodnotou. Tento spôsob je síce pomerne efektívny, ale vzhľadom na nijako neošetrovanú prácu s poľami aj značne náchylný na veľmi ťažko odhaliteľné chyby. A to už vôbec nehovoríme o praktickej nutnosti používať pri práci s reťazcami (a poľami všeobecne) ukazovatele, ktoré sa pri len trochu zložitejších údajových štruktúrach rýchlo stanú nočnou morou menej skúseného programátora.

Java prináša zničenému vývojárovi vytúženú úľavu v podobe realizácie textových reťazcov ako objektov, konkrétne inštancií triedy String. Táto trieda okrem faktu, že oslobodzuje programátora od starostí o detaily skutočnej implementácie, poskytuje aj širokú škálu metód na prácu s reťazcami. Trieda String má určitým spôsobom výnimočné postavenie, pretože sa od ostatných tried líši v drobných detailoch použitia. Konkrétne každý výskyt reťazcového literálu v programe automaticky predstavuje inštanciu triedy String a navyše je na spájanie reťazcov (a nielen reťazcov medzi sebou, ale aj reťazca a ľubovoľnej hodnoty; pozri uvedené príklady) možné použiť operátor +.

Tu je príklad práce s premennou typu String:

String s = "5 * 13 = ";
int x;
x = 5 * 13;
System.out.println(s + x);

Program po spustení vypočíta súčin hodnôt 5 a 13 a vypíše výsledok:

5 * 13 = 65

To, čo odlišuje referenčné typy od primitívnych, je fakt, že každá premenná alebo výraz referenčného typu obsahuje v skutočnosti odkaz na objekt uložený kdesi inde v pamäti. Opäť si môžeme vypomôcť analógiou s C++ – referenčné typy sú v zásade totožné s referenciami jazyka C++, s tým rozdielom, že v C++ možno deklarovať aj referencie na typy, ktoré v Jave považujeme za primitívne.

Zhrňme teda rozdiely: v premennej primitívneho typu je uložená priamo požadovaná hodnota. Ak máme napríklad premennú typu long, bude táto premenná v pamäti uložená ako postupnosť 64 bitov, obsahujúcich binárnu reprezentáciu jej obsahu. Naproti tomu v premennej referenčného typu je uložená len adresa miesta v pamäti, kde nájdeme objekt, ktorý táto premenná „obsahuje“. Inými slovami, nezávisle od svojho typu bude každá referenčná premenná v pamäti zaberať rovnaké miesto, so šírkou závisiacou od šírky pamäťovej adresy na danej implementácii Java VM. Vzhľadom na to, že akýkoľvek prístup k referenčnej premennej automaticky pracuje s odkazovaným objektom, nie je možné a z bezpečnostného hľadiska ani žiaduce uloženú adresu zistiť.

Praktickým dôsledkom odlišnosti medzi primitívnymi a referenčnými typmi je skutočnosť, že sa dve rôzne referenčné premenné môžu odkazovať na rovnaký objekt v pamäti. Ukážme si to na príklade:

public class Test
{
    public static void main(String[] args)
    {
        int[] a = { 1, 2, 3 };
        int[] b = a;
        a[1] = 222;
        System.out.println("a[1] = " + a[1]);
        System.out.println("b[1] = " + b[1]);
    }
}

Ak skúsite preložiť a spustiť tento program, dostanete nasledujúci výstup:

a[1] = 222;

b[1] = 222;

Je očividné, že premenné aj b odkazujú na to isté pole – pri zmene prvku a[1] súčasne došlo k zmene prvku b[1].

Výrazy a operátory

Spomínali sme už, že prakticky každý program nejakým spôsobom pracuje s údajmi. Každý krok spracovania údajov treba nejakým spôsobom zapísať. Ak napríklad počítame korene kvadratickej rovnice, musíme do programu vložiť jednak postup výpočtu diskriminantu, jednak výpočet samotných koreňov a pri skutočne univerzálnom programe nezabudneme zistiť, či rovnica náhodou nemá komplexné korene. Všetky tieto matematické (a iné) operácie zapisujeme prakticky v každom programovacom jazyku pomocou výrazov.

Každý výraz sa skladá z operátorov a ich operandov. Operátor je symbol, ktorý určuje typ operácie vykonávanej s príslušnými údajmi, ako napríklad + pre sčítanie. Údaje, ktoré do operácie vstupujú, sa nazývajú operandy daného operátora. Operátory môžu mať jeden, dva či tri operandy. Podľa toho ich delíme na unárne, binárne a ternárne. Operátor + je napríklad binárny operátor, pretože ním sčítavame dve čísla.

Výskyt akéhokoľvek výrazu v programe vedie k jeho vyhodnoteniu. Pod vyhodnotením výrazu sa rozumie postupná aplikácia jednotlivých operátorov na ich operandy, až kým sa výraz nezredukuje na jedinú hodnotu, ktorá bude hodnotou celého výrazu. Tak napríklad vo výraze 2 + 3 * 4 sa aplikuje operátor * na operandy 3 a 4, čím dostaneme hodnotu 12, a v druhom kroku sa operátor + aplikuje na operandy 2 a 12. Hodnotou výrazu 2 + 3 * 4 bude teda 14.

Poradie, v ktorom sa operátory aplikujú, je dané ich prioritou. Tabuľku priorít jednotlivých operátorov si uvedieme po ich postupnom prebratí. Explicitne môžeme hierarchiu priorít zmeniť pomocou zátvoriek, podobne ako v matematike. Operandy každého operátora sa v Jave vyhodnocujú vždy zľava doprava. Tento princíp možno ilustrovať nasledujúcim príkladom:

int i = 3;
int j = (i = 2) * i;
System.out.println(j);

Po preložení a spustení program vypíše:

4

V príklade chceme premennej j priradiť  hodnotu výrazu (i = 2) * i. Vidíme, že máme vo výraze operátor násobenia * s dvoma operandmi. Vzhľadom na uvedené pravidlo sa najprv vyhodnotí operand (i = 2), ktorý priradí premennej i hodnotu 2. Výslednou hodnotou tohto operandu bude priraďovaná hodnota, teda 2. Až potom sa začne vyhodnocovať druhý operand i, ktorého hodnota bude triviálne hodnotou premennej i. Ale tej sme predsa pred chvíľou priradili hodnotu 2! Preto výslednou hodnotou druhého operandu bude 2 a výsledok celého výrazu bude rovný číslu 4.

Aditívne operátory

Prvými dvoma operátormi, o ktorých si povieme, sú tzv. aditívne operátory + a . Oba operátory sú binárne a slúžia na realizáciu tých najjednoduchších matematických operácií – sčítania a odčítania. Majú rovnakú prioritu a asociujú sa zľava doprava. Asociatívnosť operátorov vyjadruje postupnosť vyhodnocovania operátorov s rovnakou prioritou. Napríklad výraz 2 + 3 + 4 sa vzhľadom na asociatívnosť zľava doprava operátora + vyhodnotí tak, akoby sme ho zapísali (2 + 3) + 4. Keby sa operátor + asocioval opačne, sprava doľava, výraz by sa vyhodnocoval ako 2 + (3 + 4). Zátvorky, pochopiteľne, explicitne určujú prioritu.

Pri sčítaní a odčítaní celých čísel môže dôjsť k pretečeniu či podtečeniu povoleného rozsahu. Tento stav sa nijako nesignalizuje a je len na programátorovi, aby s ním v prípade potreby počítal. Ako príklad možno uviesť takýto kód:

int a = 1000000000;
int b = 2000000000;
System.out.println(a + b);

Výstupom programu bude:

–1294967296

Skutočný výsledok sčítania hodnôt premenných a a b je mimo rozsahu typu int. Výsledok, ktorý dostaneme ako výstup programu, vznikne ignorovaním pretečených bitov. Podobná situácia nastáva pri odčítaní.

Pre sčítanie čísel s pohyblivou čiarkou platia určité pravidlá (podľa štandardu IEEE 754):

  • Ak je niektorým operandom hodnota NaN, výsledok bude takisto NaN.
  • Súčtom dvoch nekonečien s rovnakým znamienkom je nekonečno s týmto znamienkom.
  • Súčtom dvoch nekonečien s opačným znamienkom je NaN.
  • Súčtom nekonečna a konečnej hodnoty je to isté nekonečno.
  • Súčtom dvoch núl s rovnakým znamienkom je nula s týmto znamienkom.
  • Súčtom dvoch núl s opačným znamienkom je kladná nula.
  • Súčtom nuly a konečnej hodnoty je tá istá konečná hodnota.
  • Súčtom dvoch konečných hodnôt s rovnakou absolútnou hodnotou a opačným znamienkom je kladná nula.

Tieto pravidlá sa súčasne vzťahujú na odčítanie, pretože pre číselné typy bez výnimky platí, že výraz a – b dáva rovnaký výsledok ako a + (–b).

Oba operandy operátorov + aj musia byť primitívne číselné typy, s jedinou výnimkou, o ktorej sme už čiastočne hovorili. Ak je aspoň jedným z operandov operátora + typ String, prevedie sa prípadne druhý operand takisto na typ String a výsledkom bude spojenie oboch reťazcov.

Multiplikatívne operátory

Do tejto triedy patria operátory *, / a %, slúžiace po rade na výpočet súčinu, podielu a zvyšku po delení dvoch čísel. Všetky tri operátory majú rovnakú prioritu a asociujú sa zľava doprava. Operandy týchto operátorov musia byť povinne číselného primitívneho typu.

Pre násobenie čísel s pohyblivou čiarkou platia tieto pravidlá:

  • Ak je niektorým operandom operátora * hodnota NaN, výsledok bude takisto NaN.
  • Ak výsledkom násobenia nie je hodnota NaN, znamienko výsledku bude podobne ako v matematike kladné, ak majú oba operandy rovnaké znamienko, a záporné, ak majú znamienko opačné.
  • Súčinom nekonečna a nuly je NaN.
  • Súčinom nekonečna a konečnej hodnoty je nekonečno.

Pri výpočte súčinu môže dôjsť k pretečeniu či podtečeniu; pretečené bity sa opäť ignorujú.

Operátor delenia sa správa trochu rozdielne v závislosti od typu svojich operandov. Výsledkom delenia dvoch celých čísel je podobne ako v C++ opäť celé číslo, ktoré vznikne zo skutočného podielu odstránením desatinnej časti. Ukážka:

int a = 13;
int b = 3;
System.out.println(a / b);

Tento úsek programu vypíše:

4

Ďalej, ak sa pokúsime deliť akékoľvek celé číslo nulou, dôjde k chybe, tzv. výnimke. (Výnimky v Jave slúžia na signalizáciu chybových stavov. Povieme si o nich viac v budúcnosti.) Ak však budeme deliť nulou číslo s pohyblivou čiarkou, k chybe nedôjde. To je ostatne jedným z dôsledkov nasledujúcich pravidiel:

  • Ak je niektorým operandom operátora / hodnota NaN, výsledok bude takisto NaN.
  • Ak výsledkom delenia nie je hodnota NaN, znamienko výsledku bude kladné, ak majú oba operandy rovnaké znamienko, a záporné, ak majú znamienko opačné.
  • Delenie nekonečna nekonečnom dáva výsledok NaN.
  • Delenie nekonečna konečnou hodnotou dáva opäť nekonečno.
  • Delenie konečnej hodnoty nekonečnom dáva nulu.
  • Výsledkom delenia dvoch núl je NaN, výsledkom delenia nuly a konečnej hodnoty je opäť nula.
  • Konečne delenie konečnej hodnoty nulou dáva ako výsledok nekonečno.

Pretečenie či podtečenie sa opätovne ignoruje.

Tretí operátor % slúži na výpočet zvyšku po delení dvoch čísel. Na rozdiel od C++ jeho operandmi môžu byť aj čísla s pohyblivou čiarkou. Výsledkom aplikácie tohto operátora je hodnota, ktorá vyhovuje rovnosti (a / b) * b + (a % b) = a. Znamienko zvyšku je zhodné so znamienkom delenca (t. j. ľavého operandu).

Pri snahe o výpočet zvyšku po delení celého čísla nulou nastane výnimka, pri výpočte zvyšku po delení čísel s pohyblivou čiarkou platia tieto pravidlá:

  • Ak je niektorým operandom operátora % hodnota NaN, výsledok bude takisto NaN.
  • Ak výsledkom výpočtu zvyšku nie je hodnota NaN, znamienko výsledku je zhodné so znamienkom delenca.
  • Ak je delencom nekonečno alebo ak je deliteľom nula (alebo v oboch prípadoch), výsledok bude NaN.
  • Ak je delencom konečná hodnota a deliteľom nekonečno, výsledok je rovný delencu.
  • Ak je delencom nula a deliteľom konečná hodnota, výsledok bude nulový.

Pri výpočte zvyšku k pretečeniu či podtečeniu nemôže dôjsť. Príklad všetkých kombinácií kladného a záporného operandu:

System.out.println( 13 %  3);
System.out.println( 13 % –3);
System.out.println(–13 %  3);
System.out.println(–13 % –3);

Výstupom takéhoto úseku programu budú čísla:

1
1
–1
–1

Unárne plus a mínus

Ďalšie dva operátory sú veľmi triviálne: ide o unárne plus (+) a unárne mínus (). Oba operátory sa asociujú sprava doľava. Ich jediný operand musí mať číselný primitívny typ. Unárne plus vo svojej podstate nerobí nič okrem toho, že podobne ako iné aritmetické operátory automaticky rozširuje typ svojho operandu. Táto konverzia je však prirodzená a ešte si o nej budeme hovoriť. Praktické použitie unárneho + sa obmedzuje azda len na explicitné zdôraznenie kladnosti nejakej konštanty a pod.

Unárne mínus v zásade mení znamienko svojho operandu. To platí tak pre celočíselné typy, ako aj pre typy s pohyblivou čiarkou. Špeciálne hodnoty, ako nula či nekonečno, nevykazujú v tomto prípade nijaké odlišnosti. Jediným rozdielom je aplikácia unárneho – na hodnotu NaN, vtedy je výsledkom opäť NaN.

Operátory ++ a ––

Posledné dva operátory, o ktorých bude v tejto časti reč, slúžia na inkrementáciu (++) a dekrementáciu (––) premenných. Inkrementovať premennú znamená zvýšiť jej hodnotu o 1, dekrementácia predstavuje, naopak, zníženie hodnoty o 1. Oba operátory sú unárne a existujú v dvoch variantoch: prefixovom (operátor je pred operandom) a postfixovom (operátor je za operandom). Ich jediným operandom musí povinne byť premenná číselného typu.

Výsledný efekt aplikácie prefixového a postfixového variantu operátora ++, resp. –– je rovnaký: inkrementácia, resp. dekrementácia. Oba varianty však sa líšia výslednou hodnotou výrazu obsahujúceho príslušný operátor. Pri použití postfixového variantu je výsledkom výrazu hodnota premennej pred inkrementáciou/dekrementáciou, pri použití prefixového variantu je výsledkom výrazu hodnota premennej po inkrementácii/dekrementácii. Ukážme si to na príklade:

int a = 3;
int b = 3;
System.out.println(a++);
System.out.println(++b);
System.out.println(a);
System.out.println(b);

Uvedený fragment kódu po spustení vypíše na obrazovku čísla:

3
4
4
4

Ako vidieť, výsledná hodnota premenných a aj b bude po inkrementácii 4. Výraz a++ však vráti hodnotu a pred inkrementáciou, čo je 3, výraz ++b vráti hodnotu b po inkrementácii a to je už spomínaných 4. Rovnaký príklad si možno vyskúšať pre operátor dekrementácie ––.

Operátory ++ a –– v Jave vykazujú jeden podstatný rozdiel oproti C++: v C++ vracia prefixový variant oboch operátorov l-hodnotu, predstavujúcu inkrementovanú/dekrementovanú premennú, ktorej je tak možné priradiť inú hodnotu. V Jave je výsledkom aplikácie operátorov vždy hodnota, nikdy nie premenná, preto výraz ++a = 3 je v Jave chybný.

Prefixové varianty oboch operátorov sa asociujú sprava doľava, to znamená, že výraz –++a sa vyhodnotí ako –(++a). Pri postfixových variantoch o asociácii nemá zmysel hovoriť, dôležité je vedieť, že tieto operátory majú pomerne veľkú prioritu. Ale k tomu sa ešte dostaneme.

Dokončenie nabudúce

Operátorov je v Jave, samozrejme, podstatne viac. V budúcom pokračovaní seriálu rozprávanie o nich dokončíme. Dovtedy dovidenia.

 

 

 


Nechajte si posielať prehľad najdôležitejších správ emailom

Mohlo by Vás zaujímať

Ako na to

Tipy a triky: Ako na snímku obrazovky na akomkoľvek počítači s Windows?

02.12.2016 00:13

Ak snímky obrazovky robíte často apotrebujete napríklad funkcie na posun stránok alebo snímanie zobrazenia pri vyššom rozlíšení displeja, zrejme používate nejakú špecializovanú aplikáciu. Väčšina použ ...

Ako na to 1

Tipy a triky: Ako aplikácii prednastaviť spúšťanie s administrátorskými právami?

30.11.2016 00:10

Väčšina aspoň trochu skúsenejších používateľov vie, že aj keď máte na operačnom systéme Windows vytvorený administrátorský účet, aplikácie pre bezpečnosť nefungujú vždy splnými administrátorskými práv ...

Ako na to 2

Tipy a triky: Ako vypnúť uzamykaciu obrazovku vo Windows 10?

29.11.2016 00:10

Rozčuľuje vás, že pred každým prihlásením doúčtu vášho počítača musíte prejsť uzamykacou obrazovkou? Windows 10 na tejto obrazovke ukazuje čas,dátum anejakú zaujímavú fotografiu zrôznych kútov sveta. ...

Žiadne komentáre

Vyhľadávanie

Kyocera - prve-zariadenia-formatu-a4-s-vykonom-a3

Najnovšie videá