Syntax jazyka Java: Rozdiel medzi revíziami
Riadok 387: | Riadok 387: | ||
=== Vetvenie switch === | === Vetvenie switch === | ||
− | '''Príklad''' - použitie vetvenia switch | + | '''Príklad''' - použitie vetvenia ''switch-case'' |
V príklade ukážeme jednoduchý postup na konverziu reťazca s hexadecimálnymi číslami na numerickú hodnotu. | V príklade ukážeme jednoduchý postup na konverziu reťazca s hexadecimálnymi číslami na numerickú hodnotu. |
Verzia zo dňa a času 23:31, 15. február 2010
V predchádzjúcej kapitole sme sa obznámili s elementárnymi postupmi pri tvorbe aplikácií. Najjednoduchšie môžeme začať môžeme tak, že do textového editora napíšeme (okopírujeme) minimálnu verziu zo súbodu Hello.java z predchádzajúcej kapitoly, triedu teraz už môžeme pomenovať tak, aby vystihovala, čo daný program vykonáva (to je ideálne), súbor uložíme ako VypisNasobilku.java a namiesto System.out.println("Hey, it really works!"); napíšeme skutočný algoritmus podľa naších predstáv:
class VypisNasobilku {
public static void main(String[] args) {
...
}
}
Kľúčové slová jazyka Java
Aby sme však mohli písať samotný algoritmus, musíme si povedať, z čoho jazyk Java pozostáva a aká je jeho syntax. Pri riešení príkladu na papieri, keď zo zadaných hodnôt počítate výsledok, máte premenné, ktorých hodnoty poznáte a vzorec, podľa ktorého plánujete počítať. Program začneme tiež tak, že povieme, s akými premennými budeme pracovať a aké majú hodnoty (pre tie premenné, ktoré ich majú známe). Podobne, ako v iných programovacích jazykoch, aj v Jave je potrebné najskôr premennú deklarovať (povedať: premennú s týmto menom budem používať a bude takéhoto typu) - špecifikovanie typu umožňuje určiť, ako sa s premennou bude narábať pri rôznych operáciách. Už pri deklarovaní budeme potrebovať kľúčové slová jazyka Java, uvedené v tabuľke:
abstract | continue | for | new | switch |
assert | default | goto | package | synchronized |
boolean | do | if | private | this |
break | double | implements | protected | throw |
byte | else | import | public | throws |
case | enum | instanceof | return | transient |
catch | extends | int | short | try |
char | final | interface | static | void |
class | finally | long | strictfp | volatile |
const | float | native | super | while |
Súčasťou jazyka sú špeciálne konštrukcie, ktoré predstavujú cykly, vetvenie, dátové typy, modifikátory, objektové kľúčové slová a práca s výnimkami, čo musíme taktiež zohľadniť pri výbere mien premenných. Preddefinované konštanty true, false a null sa tak isto nesmú použiť ako názvy premenných.
Dátové typy
Už sľúbená deklarácia premennej bude potom vyzerať nasledovne:
class vypis_nasobilku {
public static void main(String[] args) {
int k; // len deklaracia
...
}
}
pričom sme použili kľúčové slovo označujúce dátový typ int. V premennej k môžeme mať uložené celé číslo v rozsahu -2147483648 ... 2147483647.
Numerický rozsah je určený dátovým typom, ktorý určuje aj veľkosť priestoru, ktorý je pre premennú vyhradený v pamäti počítača. Pre primitívne dátové typy potom platí nasledujúce
- pamäťové miesta v pamäti počítača svojim stavom reprezentujú určitú informáciu
- každá premenná v programe má dátový typ - určuje počet vyhradených pamäťových miest pre ňu
- základná jednotka informácie - bit má dva stavy: 0/1
- dátový typ boolean má hodnoty true, false
- minimálne adresovateľné množstvo informácie v pamäti je 8 bitov, aj boolean je (väčšinou) 8 bitov
- štandardne sa používa byte = 8 bitov (2^8 = 256 stavov)
- 8 bitov v pamäti možno interpretovať ako binárne číslo s hodnotou n=0..255
- celé číslo v rozsahu -128..127 (1. bit je znamienko) - v Jave typ byte
- n-tý znak v tabuľke ASCII
- inštrukciu číslo n v strojovom kóde
- farbu číslo n v palete obrázka
- a podobne ...
- v 2 bytoch (216 = 65536 stavov) možno uchovávať číslo v rozsahu 0..65535
- celé číslo v rozsahu -32768..32767 (typ short)
- znak v UNICODE (typ char)
- v 4 bytoch sa uchováva číslo v rozsahu -2147483648..2147483647 (typ int)
- reálne číslo typu float
- v 8 bytoch sa uchováva celé číslo typu long
- reálne číslo typu double
Najdôležitejšie z tejto časti je si zapamatať, že pre celé čísla väčšinou použijeme typ int, reálne čísla double a logické premenné typ boolean. Písmená môžeme uložiť do premennej typu char, osobitný typ na uchovávanie viacerých písmen (slov, reťazcov písmen) je objektový typ String.
Reprezentácia celočíselného dátového typu
Pri niektorých operáciách (zmena typov, aritmetické operácie ...) je vhodné vedieť niečo o tom, ako sú informácie uložené v pamäti. Pre celočíselné dátové typy si to ukážeme na príklade dátového typu byte.
- 1 bit určuje znamienko čísla
- 7 bitov celé číslo 0..127
- vždy nasledujúce číslo získame pripočítaním 1 k predošlému
- pri výpočte 127 + 1 = -128 dôjde k "pretečeniu", jednotka, ktorá sa dostala na miesto znamienka spôsobí skok
- podobný prípad nastane pri výpočte - 1 + 1 = 0, dôjde k "pretečeniu" a jednotka sa dostane mimo bitov" danej premennej, výsledok je nula v každom bite
čísla sú za sebou zoradené takto:
bity | hex | číslo |
0000 0000 | 00 | 0 |
0000 0001 | 01 | 1 |
0000 0010 | 02 | 2 |
0000 0011 | 03 | 3 |
... | ... | ... |
0000 1111 | 0f | 15 |
... | ... | ... |
0111 1111 | 7f | 127 |
1000 0000 | 80 | -128 |
1000 0001 | 81 | -127 |
... | ... | ... |
1111 1111 | ff | -1 |
Reprezentácia reálnych dátových typov
Reálne dátové typy sú v pamäti počítača uložené v semilogaritmickom tvare. Formmát pre základný typ float má tvar
- 1 bit určuje znamienko čísla
- 8 bitov reprezentuje exponent (-128..127)
- ostatné bity reprezentujú tzv. mantisu: desatinné číslo d = Σ bi 2-i
- číslo sa vždy upraví tak, aby malo tvar 1.1001101... x 2exp a zapisujú sa len jednotky za desatinnou bodkou
Zložitejšie dátové typy sú objekty, sú konštruované pomocou primitívnych dátových typov, predstavujú komplikovanejšiu informáciu ako len uchovanie hodnoty. Budeme sa nimi zaoberať neskôr.
Viac o dátových typoch nájdete aj na stránkach firmy Sun ... (v angličtine). Pouužitie dátových typov si môžete vyskúšať na nasledujúcom príklade.
class TestPrem{
public static void main(String[] args) {
byte k; // len deklaracia
int i=0xff; // hexadecimalna sustava, definujeme i=255
// double pi=3.14159265358979;
// Ako si zapamatat hodnotu pi - pocitajte pismena v slovach:
// I want a drink, alcoholic of course after the
// heavy lectures involving quantum mechanics.
double pi = Math.acos(-1);
boolean b = true; // false
float fl = 1.11f; // f je potrebne
char c ='\u0041'; // v unicode
String s = "moze obsahovat viac pismen a slov"; //retazec
//System.out.println("Hodnota premennej k : "+k); // nemoze byt pred
k=(byte) i; // musi byt pretypovane
System.out.println("Hodnota premennej c : " + c);
System.out.println("Hodnota premennej k : " + k);
System.out.println("Hodnota premennej pi: " + pi);
System.out.println("Hodnota premennej s : " + s);
}
}
Môžeme si teraz povedať niečo o použitej funkcii System.out.println. V zátvorkach je uvedený jej parameter - reťazec, ktorý sa vypíše na výstup (štandardne konzola). Túto funkciu budeme používať často, lebo je to najjednoduchší spôsob ako dostať informáciu o tom, ako beží program a aké hodnoty majú prememnné.
Spomínaná funkcia taktiež ilustruje vyžitie objektov, bez ktorých sa v Jave nezaobídeme. Že pracujeme s objektami rozoznáme na základe bodiek, ktoré oddeľujú "úrovne" daného objektu. T.j. operačný systém ako objekt (System) má v sebe objekt výstup (out = output) a ten vie vykonať výpis (println = print line). Podobne Math v sebe obsahuje funkciu arkuskosínus (acos - príklad ilustruje, že pri definícii môžeme využiť funkčnú hodnotu).
Pretypovanie (byte) znamená, že sa na byty premennej i (celé číslo v pamäti zaberajúce 4 byty) počítač pozrie ako by mala iný typ (popzrie sa len na najnižší 1 byte, ostatné ignoruje). Musíme pri tom pamätať na to, že môžeme dostať "prekvapujúce" výsledky.
Často je možné sa pretypovaniu vyhnúť, niekedy to však bez neho nejde. Mnohokrát ho vykoná kompilátor za nás, preto je "skryté": pri použití znamienka + medzi reťazcom a číslom sa najskôr číslo pretypuje na reťazec (vytvorí sa z neho) a oba reťazce sa spoja. Podobne je to aj pri aritmetických operáciách s číslami - napr. ak máme výraz, v ktorom vystupujú len celé čísla, aj delenie bude celočíselne, ak však aspoň jeden z operandov je double, delenie bude reálne.
Na výstupe zistíme, že premenné majú hodnoty A, -1, 3.141592653589793. ( (byte)255 = -1 je vďaka reprezentovaniu premennej typu byte, viď vyššie)
Operátory
Operátory slúžia na vykonanie určitej akcie s premennými: aritmetické operácie, porovnanie, priradenie hodnoty (pozri príklad).
Operátor | Priorita |
postfix | expr++ expr-- |
unárne | ++expr --expr +expr -expr ~ ! |
multiplikatívne | * / % |
additívne | + - |
posúvanie bitov | << >> >>> |
relačné | < > <= >= instanceof |
rovnosť, nerovnosť | == != |
bitové AND | & |
bitový exkluzívny OR (XOR) | ^ |
bitový inkluzívny OR | ! |
logický AND | && |
logický OR | || |
ternárny operátor | ? : |
priradenie | = += -= *= /= %= &= ^= |= <<= >>= >>>= |
Všimnite si zatiaľ predovšetkým, že násobenie má vyššiu prioritu, ako sčítanie a odčítanie, porovnávanie naopak nižšiu:
class priorita {
public static void main(String args[]) {
System.out.println("2*3 + 5 = " + 2*3+5 ); // 65
System.out.println("2*3 + 5 = " + (2*3+5) ); // 11
System.out.println("2*(3 + 5) = " + 2*(3+5) ); // 16
System.out.println("2+3 == 5 = " + (2+3==5) ); // true
//System.out.println("2+3 == 5 = " + 2+3==5 ); // chybne
}
}
V prvom riadku sa najskôr k reťazcu pripojí 6 a potom 5. V druhom riadku sa najskôr čísla sčítajú a výsledok (11) sa pripojí k reťazcu. Sčítanie môžeme uprednostniť pred násobením ako sme zvyknutí použitím zátvoriek. V štvrtom riadku sa najskôr vykoná sčítanie čísel a výsledok sa porovná s pravou stranou, výsledkom je logická hodnota true. Posledný zakomentovaný riadok je vďaka nízkej priorite operátora porovnávania chybný a nedá sa skompilovať.
Príklad - Priorita aritmetických operátorov
class operator {
public static void main(String[] args) {
int i,j,k;
i=5; j=10;
k=i+++j;
System.out.println(i+","+j+","+k);
i=5; j=10;
k=(i++)+j;
System.out.println(i+","+j+","+k);
i=5; j=10;
k=i+(++j);
System.out.println(i+","+j+","+k);
}
}
Výstup bude : 6,10,15 ; 6,10,15 ; 5,11,16. Teda prvý a druhý príkaz sú identické (vyššiu prioritu má postinkrementácia ako sčítanie). V treťom prípade pomocou zátvoriek uprednostníme preinkrementáciu. Príklady možno prepísať bez použitia operátora ++: Prvý a druhý prípad: k = i + j; i = i + 1; Tretí prípad: j = j + 1; k = i + j;
Príkazy, bloky, cykly a vetvenie algoritmov
Program možno chápať ako "návod" podľa ktorého počítač zostaví postupnosť príkazov, ktoré sa budú vykonávať, aby sa zrealizovala nejaká úloha. Jeden príkaz vykoná jednu konkrétnu úlohu (môže to ale byť aj zložený príkaz, ktorý pozostáva z ďalších podpríkazov). Príkaz v Jave je potrebné ukončiť bodkočiarkou (;). Skupina príkazov logicky k sebe patriacich a vykonávaných naraz býva zoskupená do bloku. Ten je uzatvorený v zložených zátvorkách: {blok;}.
Aby nebolo nutné písať príkaz po príkaze za sebou (čo v prípade častých opakovaní operácií a zložitosti algoritmov ani nie je možné), existujú v programe príkazy pre riadenie vykonávania kódu programu.
Cyklus for, while
Cykly riadia opakovanie časti kódu, pri ktorom niektoré parametre sa menia, iné nie, ale robí sa "tá istá vec" Základným typom je výraz for, zložitejšie sú do..while a while
...
for(i=0;i<10;i++) {
System.out.println("Hodnota premennej i: "+i);
}
...
Posledný cyklus možno ekvivalentne zapísať pomocou cyklu while:
...
i=0;
while(i<10) {
System.out.println("Hodnota premennej i: "+i);
i++;
}
...
V obidvoch prípadoch vyššie uvedených príkladoch sa vypíše 10 čísel: 0...9
Príklad - Cyklus for
class MalaNasobilka1 {
public static void main(String[] args) {
int i, j;
for(i=1;i<10;i++) {
for(j=1;j<10;j++) {
System.out.println(i+" * "+j+" = "+(i*j));
}
System.out.println();
}
}
}
Daný príklad vypíše tabuľku malej násobilky. System.out.println(); je použitý na vynechanie riadku (vypíše nič a odriadkuje - po konci každej tabuľky pre jedno číslo).
Príklad - Cyklus do .. while
Príklad ilustruje cyklus s použitím while a podmienkou na konci.
class hladanie {
public static void main(String[] args) {
String s = "Veta na hladanie pismen.";
char hladany_znak = 'l', c;
int i = 0;
do {
c = s.charAt(i);
i++;
} while ((c != hladany_znak) && (i < s.length()));
if (c == hladany_znak){
System.out.println(hladany_znak + " je na mieste " + i);
} else {
System.out.println("Znak sa nenašiel");
}
}
}
Vetvenie if
Vetvenie umožňuje podmienkové vykonávanie časti kódu podla postupu ak (podmienka_1) pokračuj príkaz_1, ak (podmienka_2) pokračuj príkaz_2 atď. Základným typom vetvenia je výraz if-else , zložitejší je switch-case
...
if(i>=3) {
System.out.println("i = "+i+" je vacsie alebo rovne 3");
} else if(i>2) {
System.out.println("i = "+i+" je medzi 2 a 3");
} else {
System.out.println("i = "+i+" je mensie alebo rovne ako 2");
}
...
Príklad - Riešenie kvadratickej rovnice
Budeme potrebovať premenné a, b, c - koeficienty v rovnici ax2 + bx + c = 0. Ďalej si ešte môžeme deklarovať premenné D, x1, x2 (diskriminant, korene) Výpočet je priamočiary, jediné na čo si je potrebné dávať pozor je, či nie je diskriminant záporný. Najjednoduchšia verzia programu môže vyzerať napr. takto:
class kvadr_rovn {
public static void main(String[] args) {
double a = 1, b = 2, c = 1, x1, x2, D;
D = b*b - 4*a*c;
if(D>=0) {
x1 = (-b + Math.sqrt(D))/(2*a);
x2 = (-b - Math.sqrt(D))/(2*a);
System.out.println("Koren x1 : "+x1);
System.out.println("Koren x2 : "+x2);
}
}
}
V prípade záporného diskriminantu sa nevypíše nič. Prečo sa nedá skompilovať tento príklad?
V prípade, že D<0 sa nepriradia hodnoty premenným x1, x2, ale pokúšame sa ich vypísať. Kompilátor takýto kód neskompiluje a sťažuje sa na túto skutočnosť (že by sa vypísali nedefinované čísla - to čo by na danom mieste v pamäti bolo, čo nedáva zmysel a bývalo často zdrojom chýb, preto je to neprípustné).
Ak chceme zahrnúť aj prípad komplexných koreňov, pomôžeme si takto (úplný if):
class kvadr_rovn {
public static void main(String[] args) {
double a = 1, b = 0, c = 1, x1, x2, D;
double re, im;
D = b*b - 4*a*c;
if(D>=0) {
x1 = (-b + Math.sqrt(D))/(2*a);
x2 = (-b - Math.sqrt(D))/(2*a);
System.out.println("Koren x1 : "+x1);
System.out.println("Koren x2 : "+x2);
} else {
re = -b/(2*a);
im = Math.sqrt(-D)/(2*a);
System.out.println("Koren x1 : ("+re+") + ("+im+") * i");
System.out.println("Koren x2 : ("+re+") - ("+im+") * i");
}
}
}
Vetvenie switch
Príklad - použitie vetvenia switch-case
V príklade ukážeme jednoduchý postup na konverziu reťazca s hexadecimálnymi číslami na numerickú hodnotu.
class prikl1 {
public static void main(String[] args) {
String hex = "b9eD";
int k, vysledok = 0;
for(int i = 0; i < hex.length(); i++) {
char znak = hex.charAt(i);
switch(znak) {
case('0'):
case('1'):
case('2'):
case('3'):
case('4'):
case('5'):
case('6'):
case('7'):
case('8'):
case('9'): k = znak - '0'; break;
case('a'):
case('b'):
case('c'):
case('d'):
case('e'):
case('f'): k = znak - 'a' + 10; break;
case('A'):
case('B'):
case('C'):
case('D'):
case('E'):
case('F'): k = znak - 'A' + 10; break;
default: k = 0;
}
vysledok = 16 * vysledok + k;
}
System.out.println("Cislo v retazci je " + vysledok);
}
}
Ako možno vidieť, reťazce (String) nie sú primitívnym dátovým typom, ale sú to relatívne komplikované objekty, preto sa môžeme opýtať na ich dĺžku pomocou hex.length() a na znak na i-tom mieste získame pomocou hex.charAt(i).
Tento príklad využíva vetvenie switch na konvertovanie hexadecimálneho čísla v reťazci na jeho číselnú hodnotu. Java automaticky chápe znak ako číslo - jeho poradie v UNICODE tabuľke. Skupiny znakov '0'..'9','a'..'f' a 'A'..'F' sú v tabuľke za sebou, s pomocou ich poradových čísel im potrebujeme priradiť hodnoty 0..15. V príkaze switch je využité "prepadávanie", takto sa dosahuje, že sa vykoná rovnaká akcia pre viaceré hodnoty c. Pre každú zo skupín vždy odčítame prvý znak od daného znaku (tým v každej skupine máme lokálne číslovanie od 0), a pripočítame požadovanú hodnotu prvého znaku. Príkaz n = 16 * n + k; postupne vytvorí číslo v šestnástkovej sústave. Chybné znaky sú považované za 0.