Programovanie (2) v Jave
1-INF-166, letný semester 2023/24
Letný semester, prednáška č. 1: Rozdiel medzi revíziami
(66 medziľahlých úprav od 2 ďalších používateľov nie je zobrazených) | |||
Riadok 1: | Riadok 1: | ||
− | == | + | == Úvodné informácie == |
+ | |||
+ | === Kontakt na vyučujúcich predmetu === | ||
+ | * Rovnaká stránka predmetu ako v zimnom semestri: https://compbio.fmph.uniba.sk/vyuka/prog/. | ||
+ | * Mailové adresy a čísla kancelárií vyučujúcich možno nájsť na [[Programovanie (2) v Jave|hlavnej stránke]] tohto predmetu. | ||
+ | * '''Hromadná mailová adresa zo zimného semestra v letnom semestri nefunguje'''. | ||
+ | * Konzultácie po predchádzajúcej dohode mailom. | ||
+ | |||
+ | === Testovač === | ||
+ | |||
+ | * Na cvičeniach sa bude používať rovnaký [https://prog.dcs.fmph.uniba.sk/ testovač] ako v zimnom semestri. | ||
+ | * Heslo na testovači: rovnaké ako v minulom semestri resp. minulý rok; u prípadných nových používateľov nastavené na <tt>prog2</tt>. | ||
+ | * V prípade problémov s prihlasovaním dajte vedieť. | ||
+ | |||
+ | === Priebeh semestra === | ||
+ | |||
+ | * Predmet sa bude riadiť [[Letný semester, pravidlá|pravidlami pre letný semester]]. | ||
+ | * Okrem testov a domácich úloh budú na testovači zverejňované aj nebodované cvičenia, ktorých riešenie je silne odporúčané. | ||
+ | |||
+ | === Softvér potrebný na tomto predmete === | ||
+ | |||
+ | * Java SE 21 (prípadne ľubovoľná verzia od 11 vyššie). ''Na testovači beží Java 11''. Rozdiel medzi verziami 21 a 11 nie je veľký, ale programy využívajúce niektoré nepreberané nové črty jazyka Java nemusia na testovači skompilovať. | ||
+ | * Platforma JavaFX pre vývoj aplikácií s grafickým používateľským rozhraním. | ||
+ | * Vývojové prostredie (IDE) pre Javu, odporúčame IntelliJ IDEA Community Edition. | ||
+ | * [[Letný semester, softvér|Informácie o inštalácii a základnom použití uvedeného softvéru]]. | ||
+ | * Vhodnou príležitosťou na vyriešenie prípadných problémov môžu byť prvé cvičenia. | ||
+ | |||
+ | === Zdroje informácií o jazyku Java === | ||
+ | |||
+ | * [https://docs.oracle.com/en/java/javase/21/docs/api/index.html Dokumentácia k Java API]. | ||
+ | * [https://docs.oracle.com/javase/tutorial/ Tutoriály k jazyku Java]. | ||
== Základné črty jazyka Java a programovania v ňom == | == Základné črty jazyka Java a programovania v ňom == | ||
Riadok 7: | Riadok 37: | ||
* Základné syntaktické konštrukcie jazyka Java sú v mnohom veľmi podobné jazykom C a C++. Už tento týždeň by sme mali zvládnuť prepísať do Javy väčšiu časť programov z minulého semestra; budúci týždeň by to už mali byť úplne všetky programy. | * Základné syntaktické konštrukcie jazyka Java sú v mnohom veľmi podobné jazykom C a C++. Už tento týždeň by sme mali zvládnuť prepísať do Javy väčšiu časť programov z minulého semestra; budúci týždeň by to už mali byť úplne všetky programy. | ||
* Jazyk Java je silne (aj keď nie čisto) ''objektovo orientovaný''. Použite ''tried'' – ktoré sú popri ''objektoch'' základným konceptom OOP – je nutné na napísanie aj toho najjednoduchšieho programu. | * Jazyk Java je silne (aj keď nie čisto) ''objektovo orientovaný''. Použite ''tried'' – ktoré sú popri ''objektoch'' základným konceptom OOP – je nutné na napísanie aj toho najjednoduchšieho programu. | ||
− | * Na druhej strane je triedu možné použiť aj ako obyčajný „obal” pre niekoľko ''metód'' (t.j. funkcií) podobného typu ako v minulom semestri. Takýmto spôsobom budeme s Javou pracovať na dnešnej prednáške. S ozajstným objektovo orientovaným programovaním začneme až budúci týždeň. | + | * Na druhej strane je triedu možné použiť aj ako obyčajný „obal” pre niekoľko ''metód'' (t. j. funkcií) podobného typu ako v minulom semestri. Takýmto spôsobom budeme s Javou pracovať na dnešnej prednáške. S ozajstným objektovo orientovaným programovaním začneme až budúci týždeň. |
− | * Programy v jazyku Java sa obvykle nekompilujú do strojového kódu, ale do tzv. javovského ''bytekódu''. Po skompilovaní programu teda nedostávame bežný spustiteľný súbor, ale súbor, ktorý možno spustiť na javovskom virtuálnom stroji (angl. ''Java Virtual Machine''; skr. JVM). Vykonávanie takýchto programov je síce o niečo pomalšie, zato sú však prenositeľné medzi rôznymi operačnými | + | * Programy v jazyku Java sa obvykle nekompilujú do strojového kódu, ale do tzv. javovského ''bytekódu''. Po skompilovaní programu teda nedostávame bežný spustiteľný súbor, ale súbor, ktorý možno spustiť na javovskom virtuálnom stroji (angl. ''Java Virtual Machine''; skr. JVM). Vykonávanie takýchto programov je síce o niečo pomalšie, zato sú však prenositeľné medzi rôznymi operačnými systémami a architektúrami. |
− | Celkovo ide o jazyk omnoho vyššej úrovne, než jazyk C: v oveľa väčšej miere sa tu abstrahuje od počítačovej architektúry. Java napríklad neumožňuje priamy prístup k pamäťi počítača a o uvoľňovanie alokovanej pamäte sa stará JVM automaticky prostrednictvom mechanizmu tzv. ''garbage collection''. Hoci teda jazyk nie je príliš vhodný na nízkoúrovňové programovanie, tvorba „bežných” používateľských programov je tu podstatne pohodlnejšia, než napríklad v jazyku C. Okrem toho Java disponuje veľkou knižnicou štandardných tried (''Java Class Library''; skr. JCL), v ktorej je okrem iného implementované aj množštvo algoritmov a dátových štruktúr. Orientáciu v možnostiach ponúkaných touto knižnicou značne uľahčuje [https://docs.oracle.com/en/java/javase/ | + | Celkovo ide o jazyk omnoho vyššej úrovne, než jazyk C: v oveľa väčšej miere sa tu abstrahuje od počítačovej architektúry. Java napríklad neumožňuje priamy prístup k pamäťi počítača a o uvoľňovanie alokovanej pamäte sa stará JVM automaticky prostrednictvom mechanizmu tzv. ''garbage collection''. Hoci teda jazyk nie je príliš vhodný na nízkoúrovňové programovanie, tvorba „bežných” používateľských programov je tu podstatne pohodlnejšia, než napríklad v jazyku C. Okrem toho Java disponuje veľkou knižnicou štandardných tried (''Java Class Library''; skr. JCL), v ktorej je okrem iného implementované aj množštvo algoritmov a dátových štruktúr. Orientáciu v možnostiach ponúkaných touto knižnicou značne uľahčuje [https://docs.oracle.com/en/java/javase/21/docs/api/index.html dokumentácia k nej]. |
Rozdiel v úrovni abstrakcie medzi jazykmi C a Java sa premieta aj do typického programátorského štýlu. Od efektívnosti samotnej implementácie sa dôraz obvykle posúva k aspektom softvérového inžinierstva, ako sú napríklad čitateľnosť, rozšíriteľnosť a „spravovateľnosť” kódu. S niektorými elementárnymi princípmi softvérového inžinierstva sa zoznámime aj na tomto predmete. | Rozdiel v úrovni abstrakcie medzi jazykmi C a Java sa premieta aj do typického programátorského štýlu. Od efektívnosti samotnej implementácie sa dôraz obvykle posúva k aspektom softvérového inžinierstva, ako sú napríklad čitateľnosť, rozšíriteľnosť a „spravovateľnosť” kódu. S niektorými elementárnymi princípmi softvérového inžinierstva sa zoznámime aj na tomto predmete. | ||
Riadok 30: | Riadok 60: | ||
Rozoberme postupne jednotlivé časti uvedeného programu: | Rozoberme postupne jednotlivé časti uvedeného programu: | ||
− | * | + | * Metóda (funkcia) <tt>main</tt>, ktorá sa podobne ako v C/C++ začne vykonávať bezprostredne po spustení programu, je „obalená” v triede, ktorú sme nazvali <tt>Hello</tt>. V jazyku Java musí byť všetok kód súčasťou nejakej triedy (význam tried pre OOP zatiaľ ponechajme bokom). |
− | * Hlavička | + | * Hlavička metódy <tt>main</tt> musí byť vždy tvaru ako vyššie. Modifikátory <tt>public</tt> a <tt>static</tt> si vysvetlíme neskôr; nasleduje návratový typ <tt>void</tt>, názov metódy <tt>main</tt> a argument tejto metódy, ktorým sú argumenty programu z príkazového riadku (v podobe poľa reťazcov). |
* Samotný výpis na konzolu realizuje metóda <tt>System.out.println</tt>. | * Samotný výpis na konzolu realizuje metóda <tt>System.out.println</tt>. | ||
Riadok 54: | Riadok 84: | ||
Za účelom sprehľadnenia zdrojového kódu a často pomerne rozsiahlych knižníc sa pri pomenúvaní identifikátorov v Jave používajú (okrem iných aj) nasledujúce konvencie: | Za účelom sprehľadnenia zdrojového kódu a často pomerne rozsiahlych knižníc sa pri pomenúvaní identifikátorov v Jave používajú (okrem iných aj) nasledujúce konvencie: | ||
* Názvy tried by mali vždy začínať veľkým písmenom (pri viacslovných názvoch začína veľkým písmenom aj každé ďalšie slovo). | * Názvy tried by mali vždy začínať veľkým písmenom (pri viacslovných názvoch začína veľkým písmenom aj každé ďalšie slovo). | ||
− | * Názvy premenných | + | * Názvy premenných a metód by naopak mali vždy začínať malým písmenom (pri viacslovných názvoch začínajú ďalšie slová veľkým písmenom). |
Nedodržiavanie týchto konvencií budeme na tomto predmete považovať za chybu. O ďalších konvenciách používaných v tomto smere sa možno dočítať [https://www.oracle.com/java/technologies/javase/codeconventions-namingconventions.html tu]. | Nedodržiavanie týchto konvencií budeme na tomto predmete považovať za chybu. O ďalších konvenciách používaných v tomto smere sa možno dočítať [https://www.oracle.com/java/technologies/javase/codeconventions-namingconventions.html tu]. | ||
Riadok 103: | Riadok 133: | ||
a[i] = scanner.nextInt(); | a[i] = scanner.nextInt(); | ||
} | } | ||
− | |||
System.out.println("Sucet: " + sum(a)); | System.out.println("Sucet: " + sum(a)); | ||
Riadok 120: | Riadok 149: | ||
=== Primitívne typy === | === Primitívne typy === | ||
− | Jazyk Java podporuje celkovo osem [https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html primitívnych dátových typov]: | + | Jazyk Java podporuje celkovo osem [https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html primitívnych dátových typov] (<tt>void</tt> sa za primitívny typ nepovažuje): |
* <tt>int</tt>: 32-bitové celé čísla so znamienkom, v rozsahu od ''-2<sup>31</sup>'' po ''2<sup>31</sup> - 1''; ďalšie celočíselné typy sú <tt>byte</tt>, <tt>short</tt> a <tt>long</tt> (na rozdiel od C/C++ už teda <tt>long</tt> a <tt>short</tt> nie sú modifikátory, ale priamo celočíselné typy). | * <tt>int</tt>: 32-bitové celé čísla so znamienkom, v rozsahu od ''-2<sup>31</sup>'' po ''2<sup>31</sup> - 1''; ďalšie celočíselné typy sú <tt>byte</tt>, <tt>short</tt> a <tt>long</tt> (na rozdiel od C/C++ už teda <tt>long</tt> a <tt>short</tt> nie sú modifikátory, ale priamo celočíselné typy). | ||
* <tt>double</tt>: 64-bitové desatinné čísla s pohyblivou desatinnou čiarkou; ďalší typ desatinných čísel je 32-bitový <tt>float</tt>. | * <tt>double</tt>: 64-bitové desatinné čísla s pohyblivou desatinnou čiarkou; ďalší typ desatinných čísel je 32-bitový <tt>float</tt>. | ||
Riadok 143: | Riadok 172: | ||
System.out.println(c); | System.out.println(c); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | + | Možno ale napríklad priamo písať <tt>char c = 65</tt>. | |
=== Operátory === | === Operátory === | ||
Riadok 200: | Riadok 229: | ||
int[] a = {0, 1, 2, 3, 4, 5}; | int[] a = {0, 1, 2, 3, 4, 5}; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | Týmto príkazom sa do premennej <tt>a</tt> priradí referencia na šesťprvkové pole obsahujúce postupne hodnoty <tt>0, 1, 2, 3, 4, 5</tt>. Takéto priradenie možno realizovať ''iba | + | Týmto príkazom sa do premennej <tt>a</tt> priradí referencia na šesťprvkové pole obsahujúce postupne hodnoty <tt>0, 1, 2, 3, 4, 5</tt>. Takéto priradenie možno realizovať ''iba v rámci príkazu, v ktorom je príslušná premenná aj deklarovaná''. Neskôr možno do premennej <tt>a</tt> priradiť takto „vymenované” pole nasledovne. |
<syntaxhighlight lang="java"> | <syntaxhighlight lang="java"> | ||
int[] a; | int[] a; | ||
Riadok 216: | Riadok 245: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | V prípade pokusu o prístup k prvku poľa mimo jeho rozsah Java za behu programu vyhodí výnimku <tt>java.lang.ArrayIndexOutOfBoundsException</tt>. Mechanizmom výnimiek a ich spracovaním sa na tomto predmete budeme zaoberať neskôr; v tomto momente je podstatná skutočnosť, že v Jave nie je možné pomocou prístupu k danému poľu prepísať pamäť mimo jeho | + | V prípade pokusu o prístup k prvku poľa mimo jeho rozsah Java za behu programu vyhodí výnimku <tt>java.lang.ArrayIndexOutOfBoundsException</tt>. Mechanizmom výnimiek a ich spracovaním sa na tomto predmete budeme zaoberať neskôr; v tomto momente je podstatná skutočnosť, že v Jave nie je možné pomocou prístupu k danému poľu prepísať pamäť mimo jeho rozsahu – čo je jedna z najnepríjemnejších chýb v C/C++. |
=== Hodnoty vs. referencie v jazyku Java === | === Hodnoty vs. referencie v jazyku Java === | ||
− | Napriek tomu, že v jazyku Java neexistuje mechanizmus smerníkov | + | Napriek tomu, že v jazyku Java neexistuje mechanizmus smerníkov, ani žiadna obdoba smerníkovej aritmetiky, všetky premenné okrem premenných primitívnych typov obsahujú referencie predstavujúce adresy v pamäti. Presnejšie: |
* Premenné primitívnych typov obsahujú ''hodnoty'' týchto primitívnych typov. | * Premenné primitívnych typov obsahujú ''hodnoty'' týchto primitívnych typov. | ||
* Premenné všetkých zvyšných typov obsahujú ''referencie'' na pole alebo na objekt (aj polia sú v skutočnosti veľmi špeciálne objekty, ale to teraz ponechajme bokom). | * Premenné všetkých zvyšných typov obsahujú ''referencie'' na pole alebo na objekt (aj polia sú v skutočnosti veľmi špeciálne objekty, ale to teraz ponechajme bokom). | ||
Riadok 236: | Riadok 265: | ||
System.out.println(a == b); // Vypise false | System.out.println(a == b); // Vypise false | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | Premenná obsahujúca referenciu môže nadobúdať špeciálnu hodnotu <tt>null</tt>; v takom prípade referencia neukazuje na | + | Premenná obsahujúca referenciu môže nadobúdať špeciálnu hodnotu <tt>null</tt>; v takom prípade referencia neukazuje na žiaden kus pamäte. |
=== Cyklus <tt>for each</tt> === | === Cyklus <tt>for each</tt> === | ||
Riadok 266: | Riadok 295: | ||
Popri jednorozmerných poliach možno v Jave pracovať aj s dvojrozmernými poľami, ktoré sa správajú podobne ako polia smerníkov resp. smerníky na smerníky v C/C++. V javovskej terminológii môžeme povedať, že dvojrozmerné pole je poľom polí. Obdĺžnikové pole vytvoríme napríklad nasledovne. | Popri jednorozmerných poliach možno v Jave pracovať aj s dvojrozmernými poľami, ktoré sa správajú podobne ako polia smerníkov resp. smerníky na smerníky v C/C++. V javovskej terminológii môžeme povedať, že dvojrozmerné pole je poľom polí. Obdĺžnikové pole vytvoríme napríklad nasledovne. | ||
− | <syntaxhighlight lang="java">int[][] a = new int[3][4]; // Vytvori pole s troma riadkami a | + | <syntaxhighlight lang="java">int[][] a = new int[3][4]; // Vytvori pole s troma riadkami a styrmi stĺpcami |
</syntaxhighlight> | </syntaxhighlight> | ||
Takéto pole si možno predstaviť ako trojprvkové pole jednorozmerných polí dĺžky 4. Napríklad <tt>a[0]</tt> je teda jednorozmerné pole zodpovedajúce nultému riadku dvojrozmerného poľa <tt>a</tt>. S dvojrozmerným poľom potom pracujeme prirodzeným spôsobom. | Takéto pole si možno predstaviť ako trojprvkové pole jednorozmerných polí dĺžky 4. Napríklad <tt>a[0]</tt> je teda jednorozmerné pole zodpovedajúce nultému riadku dvojrozmerného poľa <tt>a</tt>. S dvojrozmerným poľom potom pracujeme prirodzeným spôsobom. | ||
Riadok 279: | Riadok 308: | ||
[[Súbor:JPamat1.png]] | [[Súbor:JPamat1.png]] | ||
− | Alternatívne možno najprv | + | Alternatívne možno najprv pamäť alokovať iba pre pole jednotlivých riadkov a následne pre každý z riadkov zvlášť. Takto môžeme vytvoriť aj iné ako obdĺžnikové polia, napríklad „trojuholník” z nasledujúceho príkladu. |
<syntaxhighlight lang="java">int[][] a = new int[3][]; // V tomto momente su a[0], a[1] aj a[2] rovne null (vdaka automatickej inicializacii prvkov pola) | <syntaxhighlight lang="java">int[][] a = new int[3][]; // V tomto momente su a[0], a[1] aj a[2] rovne null (vdaka automatickej inicializacii prvkov pola) | ||
for (int i = 0; i <= a.length - 1; i++) { | for (int i = 0; i <= a.length - 1; i++) { | ||
Riadok 294: | Riadok 323: | ||
[[Súbor:JPamat2.png]] | [[Súbor:JPamat2.png]] | ||
− | Z tejto predstavy o reprezentácii dvojrozmerných polí by malo byť zrejmé, že napríklad | + | Z tejto predstavy o reprezentácii dvojrozmerných polí by malo byť zrejmé, že napríklad príkaz <tt>int[][] a = new int[][10];</tt> nie je korektný. |
− | Rovnako ako s dvojrozmernými poľami možno v Jave pracovať aj s poľami o ľubovoľnom konečnom počte rozmerov. Pri | + | Rovnako ako s dvojrozmernými poľami možno v Jave pracovať aj s poľami o ľubovoľnom konečnom počte rozmerov. Pri alokovaní takýchto polí platí, že je potrebné určiť ''prvých niekoľko'' rozmerov (kde „niekoľko” v tomto prípade znamená „aspoň jeden”). |
<syntaxhighlight lang="java">int[][][] a = new int[3][4][5]; // OK | <syntaxhighlight lang="java">int[][][] a = new int[3][4][5]; // OK | ||
int[][][] b = new int[3][4][]; // OK | int[][][] b = new int[3][4][]; // OK | ||
Riadok 308: | Riadok 337: | ||
Obdobou funkcií, ako ich poznáme z minulého semestra, sú v jazyku Java ''statické metódy'' triedy. | Obdobou funkcií, ako ich poznáme z minulého semestra, sú v jazyku Java ''statické metódy'' triedy. | ||
− | * Definujú sa vždy vo vnútri nejakej triedy s použitím podobnej syntaxe ako v C/C++. Pred návratový typ je potrebné napísať kľúčové slovo <tt>static</tt>, vďaka ktorému pôjde o ''statickú'' metódu triedy | + | * Definujú sa vždy vo vnútri nejakej triedy s použitím podobnej syntaxe ako v C/C++. Pred návratový typ je potrebné napísať kľúčové slovo <tt>static</tt>, vďaka ktorému pôjde o ''statickú'' metódu triedy – čiže zjednodušene povedané „obyčajnú funkciu” – a nie o metódu inštancie triedy, čo je koncept objektovo orientovaného programovania, s ktorým sa zoznámime na budúcej prednáške. |
* Pred klúčové slovo <tt>static</tt> ešte možno pridať ''modifikátor prístupu'' ako napr. <tt>public</tt> alebo <tt>private</tt> hovoriaci o viditeľnosti metódy z iných tried a balíkov. Modifikátormi prístupu sa budeme detailnejšie zaoberať neskôr. | * Pred klúčové slovo <tt>static</tt> ešte možno pridať ''modifikátor prístupu'' ako napr. <tt>public</tt> alebo <tt>private</tt> hovoriaci o viditeľnosti metódy z iných tried a balíkov. Modifikátormi prístupu sa budeme detailnejšie zaoberať neskôr. | ||
* Statické metódy voláme rovnako ako funkcie v C/C++ (pri statickej metóde <tt>metoda</tt> z inej triedy <tt>Trieda</tt> pri jej volaní píšeme <tt>Trieda.metoda</tt>). Kľúčové slovo <tt>return</tt> sa tiež správa podobne ako v C/C++, avšak program obsahujúci metódu s návratovým typom rôznym od <tt>void</tt> a chýbajúcim príkazom <tt>return</tt> nie je možné skompilovať. Rovnako ako v C/C++ funguje aj rekurzia. | * Statické metódy voláme rovnako ako funkcie v C/C++ (pri statickej metóde <tt>metoda</tt> z inej triedy <tt>Trieda</tt> pri jej volaní píšeme <tt>Trieda.metoda</tt>). Kľúčové slovo <tt>return</tt> sa tiež správa podobne ako v C/C++, avšak program obsahujúci metódu s návratovým typom rôznym od <tt>void</tt> a chýbajúcim príkazom <tt>return</tt> nie je možné skompilovať. Rovnako ako v C/C++ funguje aj rekurzia. | ||
Riadok 326: | Riadok 355: | ||
System.out.println(faktorial(18)); | System.out.println(faktorial(18)); | ||
} | } | ||
− | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Riadok 332: | Riadok 360: | ||
=== Metóda <tt>main</tt> === | === Metóda <tt>main</tt> === | ||
− | * Špeciálnou statickou metódou je metóda <tt>main</tt>, ktorá sa vykoná bezprostredne po spustení programu. Tá musí mať návratový typ <tt>void</tt>, modifikátor prístupu <tt>public</tt> a jediný argument typu <tt>String[]</tt> reprezentujúci pole argumentov programu z príkazového riadku | + | * Špeciálnou statickou metódou je metóda <tt>main</tt>, ktorá sa vykoná bezprostredne po spustení programu (presnejšie danej triedy). Tá musí mať návratový typ <tt>void</tt>, modifikátor prístupu <tt>public</tt> a jediný argument typu <tt>String[]</tt> reprezentujúci pole argumentov programu z príkazového riadku. Prípadné metódy <tt>main</tt> s inou hlavičkou sa považujú za „obyčajné” metódy a po spustení programu sa nevykonávajú. |
<syntaxhighlight lang="java">public class Trieda { | <syntaxhighlight lang="java">public class Trieda { | ||
Riadok 343: | Riadok 371: | ||
System.out.println("Pocet argumentov: " + args.length); | System.out.println("Pocet argumentov: " + args.length); | ||
} | } | ||
− | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Riadok 350: | Riadok 377: | ||
* V IDE ako napr. IntelliJ je zvyčajne potrebné vybrať hlavnú triedu projektu, čo je trieda, ktorú sa prostredie bude snažiť spúšťať po spustení celého projektu. Táto trieda tak musí mať definovanú metódu <tt>main</tt>. | * V IDE ako napr. IntelliJ je zvyčajne potrebné vybrať hlavnú triedu projektu, čo je trieda, ktorú sa prostredie bude snažiť spúšťať po spustení celého projektu. Táto trieda tak musí mať definovanú metódu <tt>main</tt>. | ||
− | === Predávanie argumentov | + | === Predávanie argumentov metód === |
Argumenty metód sa v jazyku Java ''vždy predávajú hodnotou''. | Argumenty metód sa v jazyku Java ''vždy predávajú hodnotou''. | ||
* Pre argumenty sa tedy vždy vytvoria nové lokálne premenné, do ktorých sa skopírujú hodnoty argumentov, s ktorými bola metóda volaná. | * Pre argumenty sa tedy vždy vytvoria nové lokálne premenné, do ktorých sa skopírujú hodnoty argumentov, s ktorými bola metóda volaná. | ||
− | * ''Nie je teda možné napísať metódu, ktorá pozmení premenné z volajúcej metódy''. Ak sú ale argumentmi metódy referencie (polia alebo objekty), je možné pozmeniť hodnoty, na ktoré táto referencia ukazuje (t.j. napríklad zmeniť obsah poľa alebo premenné objektu). | + | * ''Nie je teda možné napísať metódu, ktorá pozmení premenné z volajúcej metódy''. Ak sú ale argumentmi metódy referencie (polia alebo objekty), je možné pozmeniť hodnoty, na ktoré táto referencia ukazuje (t. j. napríklad zmeniť obsah poľa alebo premenné objektu). |
''Príklad'': | ''Príklad'': | ||
Riadok 372: | Riadok 399: | ||
System.out.println(a[0]); // Vypise 7 | System.out.println(a[0]); // Vypise 7 | ||
} | } | ||
− | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | * Špeciálne napríklad v Jave nie je možné napísať | + | * Špeciálne napríklad v Jave nie je možné napísať metódu <tt>swap</tt>, ktorá vymení hodnoty dvoch premenných primitívnych typov. |
== Práca s reťazcami == | == Práca s reťazcami == | ||
− | Zvyšok tejto prednášky sčasti presahuje jej rámec tým, že začneme pracovať s niektorými špeciálnymi objektmi bez toho, aby sme si vysvetlili mechanizmus objektov vo všeobecnosti. Už pomerne elementárne úkony, ako práca s reťazcami alebo so vstupom a výstupom, sa totiž v Jave realizujú s využitím objektov. | + | Zvyšok tejto prednášky sčasti presahuje jej rámec tým, že začneme pracovať s niektorými špeciálnymi objektmi bez toho, aby sme si vysvetlili mechanizmus objektov vo všeobecnosti. Už pomerne elementárne úkony, ako práca s reťazcami alebo so vstupom a výstupom, sa totiž v Jave realizujú s využitím objektov. Nasledujúce pasáže sú motivované praktickou potrebou zvládnutia týchto úkonov; hlbšie pochopenie tohto materiálu nadobudneme budúci týždeň. |
=== Trieda <tt>String</tt> === | === Trieda <tt>String</tt> === | ||
− | Reťazce znakov sú v Jave objektmi triedy <tt>String</tt>. Premenné typu <tt>String</tt> teda majú charakter referencií na samotný objekt. Objekty triedy <tt>String</tt> sú | + | Reťazce znakov sú v Jave objektmi triedy [https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/String.html <tt>String</tt>]. Premenné typu <tt>String</tt> teda majú charakter referencií na samotný objekt. Objekty triedy <tt>String</tt> sú nemodifikovateľné reťazce – po vytvorení ich už teda nemožno meniť. Premennú typu <tt>String</tt> ale samozrejme môžeme meniť tak, že do nej priradíme referenciu na iný objekt typu <tt>String</tt>. |
* Text ohraničený úvodzovkami sa považuje za reťazec typu <tt>String</tt>. | * Text ohraničený úvodzovkami sa považuje za reťazec typu <tt>String</tt>. | ||
Riadok 397: | Riadok 423: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | Keďže sú premenné typu <tt>String</tt> referenciami na objekt, operátor <tt>=</tt> tiež kopíruje iba referencie a operátor <tt>==</tt> neporovnáva hodnoty reťazcov (t.j. samotné texty), ale adresy v pamäti. Vzhľadom na konštantnosť reťazcov je tu častejším zdrojom chýb nesprávne použitie operátora <tt>==</tt>. | + | Keďže sú premenné typu <tt>String</tt> referenciami na objekt, operátor <tt>=</tt> tiež kopíruje iba referencie a operátor <tt>==</tt> neporovnáva hodnoty reťazcov (t. j. samotné texty), ale adresy v pamäti. Vzhľadom na konštantnosť reťazcov je tu častejším zdrojom chýb nesprávne použitie operátora <tt>==</tt>. |
− | Porovnanie reťazcov správne realizuje metóda [https://docs.oracle.com/en/java/javase/ | + | Porovnanie reťazcov správne realizuje metóda [https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/String.html#equals(java.lang.Object) <tt>equals</tt>]. |
<syntaxhighlight lang="java">String str1 = "nejaky text"; | <syntaxhighlight lang="java">String str1 = "nejaky text"; | ||
Riadok 421: | Riadok 447: | ||
if (str4.equals(str1)) { | if (str4.equals(str1)) { | ||
System.out.println("str4.equals(str1)"); | System.out.println("str4.equals(str1)"); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | V Jave možno <tt>switch</tt> aplikovať aj na reťazce. | ||
+ | |||
+ | <syntaxhighlight lang="java">String s; | ||
+ | int n; | ||
+ | |||
+ | // ... | ||
+ | |||
+ | switch (s) { | ||
+ | case "ano": | ||
+ | n = 1; | ||
+ | break; | ||
+ | case "nie": | ||
+ | n = 0; | ||
+ | break; | ||
+ | default: | ||
+ | n = -1; | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Z ďalších metód triedy <tt>String</tt>, ktoré možno použiť na manipuláciu s reťazcami, spomeňme aspoň tieto: | Z ďalších metód triedy <tt>String</tt>, ktoré možno použiť na manipuláciu s reťazcami, spomeňme aspoň tieto: | ||
− | * Metóda [https://docs.oracle.com/en/java/javase/ | + | * Metóda [https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/String.html#length() <tt>length</tt>] vráti dĺžku daného reťazca. ''Pozor'': keďže ide o metódu (bez argumentov), na rozdiel od polí pre reťazec <tt>s</tt> píšeme <tt>s.length()</tt> so zátvorkami na konci. |
− | * Metóda [https://docs.oracle.com/en/java/javase/ | + | * Metóda [https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/String.html#charAt(int) <tt>charAt</tt>] s jedným celočíselným argumentom vráti znak na danej pozícii. |
<syntaxhighlight lang="java">String s = "retazec"; | <syntaxhighlight lang="java">String s = "retazec"; | ||
Riadok 433: | Riadok 478: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | * Množstvo ďalších metód na prácu s reťazcami možno nájsť v [https://docs.oracle.com/en/java/javase/ | + | * Množstvo ďalších metód na prácu s reťazcami možno nájsť v [https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/String.html dokumentácii k triede <tt>String</tt>]. |
=== Trieda <tt>StringBuilder</tt> === | === Trieda <tt>StringBuilder</tt> === | ||
− | V prípade | + | V prípade nutnosti daný reťazec často modifikovať je použitie triedy <tt>String</tt> pomerne pomalé, pretože pri každej modifikácii reťazca je potrebné vytvoriť nový objekt. Rýchlejšou alternatívou je použitie triedy [https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/StringBuilder.html <tt>StringBuilder</tt>] reprezentujúcej ''modifikovateľný'' reťazec, ktorý je tiež konvertovateľný na <tt>String</tt>. |
− | Napríklad reťazec <tt>abc...z</tt> obsahujúci všetky písmená malej anglickej abecedy tak môžeme vytvoriť dvoma rôznymi spôsobmi: buď pomocou pomalého vytvárania 27 rôznych <tt>String</tt> | + | Napríklad reťazec <tt>abc...z</tt> obsahujúci všetky písmená malej anglickej abecedy tak môžeme vytvoriť dvoma rôznymi spôsobmi: buď pomocou pomalého vytvárania 27 rôznych objektov triedy <tt>String</tt> |
<syntaxhighlight lang="java"> | <syntaxhighlight lang="java"> | ||
String abeceda = ""; | String abeceda = ""; | ||
Riadok 466: | Riadok 511: | ||
=== Výstupné prúdy: trieda <tt>PrintStream</tt> === | === Výstupné prúdy: trieda <tt>PrintStream</tt> === | ||
− | Textový výstup možno v Jave najjednoduchšie produkovať pomocou triedy [https://docs.oracle.com/en/java/javase/ | + | Textový výstup možno v Jave najjednoduchšie produkovať pomocou triedy [https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/io/PrintStream.html <tt>PrintStream</tt>], ktorú je nutné importovať z balíka <tt>java.io</tt>; prípadne je možné importovať aj kompletný balík <tt>java.io</tt>. |
<syntaxhighlight lang="java"> | <syntaxhighlight lang="java"> | ||
import java.io.*; // alebo: import java.io.PrintStream; | import java.io.*; // alebo: import java.io.PrintStream; | ||
Riadok 475: | Riadok 520: | ||
// ... | // ... | ||
} | } | ||
− | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Riadok 484: | Riadok 528: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
* V prípade, že súbor <tt>vystup.txt</tt> existuje, premaže sa týmto volaním jeho obsah; v prípade potreby zapisovať na koniec existujúceho súboru možno použiť <tt>PrintStream out = new PrintStream(new FileOutputStream("vystup.txt", true))</tt>, kde o pridávaní na koniec súboru hovorí booleovský parameter <tt>true</tt> (viac v dokumentácii). | * V prípade, že súbor <tt>vystup.txt</tt> existuje, premaže sa týmto volaním jeho obsah; v prípade potreby zapisovať na koniec existujúceho súboru možno použiť <tt>PrintStream out = new PrintStream(new FileOutputStream("vystup.txt", true))</tt>, kde o pridávaní na koniec súboru hovorí booleovský parameter <tt>true</tt> (viac v dokumentácii). | ||
− | * Adresár, od ktorého sa počíta takáto relatívna adresa súboru, závisí od prostredia. Pri spúšťaní programu z príkazového riadku sa súbor <tt>vystup.txt</tt> vytvorí v adresári, z ktorého bol spustený interpreter <tt>java</tt> | + | * Adresár, od ktorého sa počíta takáto relatívna adresa súboru, závisí od prostredia. Pri spúšťaní programu z príkazového riadku sa súbor <tt>vystup.txt</tt> vytvorí v adresári, z ktorého bol spustený interpreter <tt>java</tt>. Pri práci s IntelliJ ide pri východzích nastaveniach o koreňový adresár projektu (obsahujúci podadresáre ako <tt>src</tt> a <tt>out</tt>). |
− | * Vytvorenie inštancie triedy <tt>PrintStream</tt> môže spôsobiť výnimku typu <tt>IOException</tt>, ktorú je nutné ošetriť. Spracovaním výnimiek sa na tomto predmete budeme zaoberať až neskôr – zatiaľ teda zvolíme najjednoduchšie riešenie, pri ktorom iba do hlavičky metódy <tt>main</tt> pridáme upozornenie, že v nej môže vzniknúť neošetrená výnimka typu <tt>IOException</tt>. | + | * Vytvorenie inštancie triedy <tt>PrintStream</tt> môže spôsobiť výnimku typu <tt>IOException</tt>, ktorú je nutné ošetriť. Spracovaním výnimiek sa na tomto predmete budeme zaoberať až neskôr – zatiaľ teda zvolíme najjednoduchšie riešenie, pri ktorom iba do hlavičky volajúcej metódy (v príklade nižšie ide o metódu <tt>main</tt>) pridáme upozornenie, že v nej môže vzniknúť neošetrená výnimka typu <tt>IOException</tt> (v prípade, že sme neimportovali celý balík <tt>java.io</tt>, je z neho potrebné importovať triedu <tt>java.io.IOException</tt>). |
<syntaxhighlight lang="java"> | <syntaxhighlight lang="java"> | ||
public static void main(String[] args) throws IOException { | public static void main(String[] args) throws IOException { | ||
PrintStream out = new PrintStream("vystup.txt"); | PrintStream out = new PrintStream("vystup.txt"); | ||
+ | // ... | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Riadok 494: | Riadok 539: | ||
* <tt>print</tt>: zapíše do súboru svoj argument (prakticky ľubovoľného typu). | * <tt>print</tt>: zapíše do súboru svoj argument (prakticky ľubovoľného typu). | ||
* <tt>println</tt>: to isté, len s novým riadkom na konci (pri volaní bez argumentov vypíše iba znak pre nový riadok). | * <tt>println</tt>: to isté, len s novým riadkom na konci (pri volaní bez argumentov vypíše iba znak pre nový riadok). | ||
− | * <tt>format</tt>: zapíše text podľa formátovacieho reťazca podobného ako v C (viac [https://docs.oracle.com/javase/tutorial/essential/io/formatting.html tu]). | + | * <tt>format</tt> alebo <tt>printf</tt>: zapíše text podľa formátovacieho reťazca podobného ako v C (viac [https://docs.oracle.com/javase/tutorial/essential/io/formatting.html tu]). |
* <tt>close</tt>: metóda bez argumentu, ktorá zavrie otvorený výstupný prúd a je potrebné ju zavolať akonáhle so súborom prestaneme pracovať. | * <tt>close</tt>: metóda bez argumentu, ktorá zavrie otvorený výstupný prúd a je potrebné ju zavolať akonáhle so súborom prestaneme pracovať. | ||
Riadok 509: | Riadok 554: | ||
out.close(); | out.close(); | ||
} | } | ||
− | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | Špeciálnym výstupným prúdom typu <tt>PrintStream</tt> je aj štandardný výstupný prúd [https://docs.oracle.com/en/java/javase/ | + | Špeciálnym výstupným prúdom typu <tt>PrintStream</tt> je aj štandardný výstupný prúd [https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/System.html#out <tt>System.out</tt>]. Pri práci s ním tak možno používať rovnaké metódy ako vyššie. Nie je pritom potrebné importovať triedu <tt>PrintStream</tt> (pretože pracujeme iba s jej inštanciou <tt>System.out</tt> a trieda <tt>System</tt> sa importuje automaticky), ani špecifikovať <tt>throws IOException</tt> (pretože nevoláme konštruktor triedy <tt>PrintStream</tt>, ktorý túto výnimku môže spôsobiť). Obvykle tiež nie je vhodné výstupný prúd <tt>System.out</tt> zatvárať metódou <tt>close</tt>, keďže sa tým do budúcnosti používanie štandardného výstupu znemožní. |
<syntaxhighlight lang="java">public class Trieda { | <syntaxhighlight lang="java">public class Trieda { | ||
Riadok 522: | Riadok 566: | ||
System.out.println("Nejaky iny text"); | System.out.println("Nejaky iny text"); | ||
} | } | ||
− | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Riadok 528: | Riadok 571: | ||
=== Vstupné prúdy === | === Vstupné prúdy === | ||
− | Základná trieda pre vstupné prúdy je v Jave [https://docs.oracle.com/en/java/javase/ | + | Základná trieda pre vstupné prúdy je v Jave [https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/io/InputStream.html <tt>InputStream</tt>]. Tohto typu je aj štandardný vstupný prúd [https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/System.html#in <tt>System.in</tt>] pre čítanie z konzoly. Vstupné prúdy pre čítanie zo súboru sú reprezentované triedou [https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/io/FileInputStream.html <tt>FileInputStream</tt>]; pre účely tejto prednášky možno <tt>FileInputStream</tt> považovať za špeciálny prípad <tt>InputStream</tt>. Obe tieto triedy sú definované v balíku <tt>java.io</tt>. |
− | Používanie týchto vstupných prúdov však nie je veľmi pohodlné, pretože umožňujú iba čítanie po bytoch. Preto sa zvyknú používať nadstavbové triedy, ktoré tieto jednoduché vstupné prúdy „obalia” a programátorovi poskytnú aj pokročilejšie | + | Používanie týchto vstupných prúdov však nie je veľmi pohodlné, pretože umožňujú iba čítanie po bytoch. Preto sa zvyknú používať nadstavbové triedy, ktoré tieto jednoduché vstupné prúdy „obalia” a programátorovi poskytnú aj pokročilejšie metódy na prácu so vstupom. V nasledujúcom preskúmame dve z takýchto nadstavbových tried: [https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Scanner.html <tt>Scanner</tt>] a [https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/io/BufferedReader.html <tt>BufferedReader</tt>]. |
=== Trieda <tt>Scanner</tt> === | === Trieda <tt>Scanner</tt> === | ||
− | Trieda [https://docs.oracle.com/en/java/javase/ | + | Trieda [https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Scanner.html <tt>Scanner</tt>] – definovaná v balíku <tt>java.util</tt>, z ktorého je nutné túto triedu importovať – umožňuje rozkladať vstupný prúd na reťazce oddelené bielymi znakmi, pričom kompatibilné reťazce dokáže konvertovať aj na číselné typy. Alternatívne možno <tt>Scanner</tt> použiť aj na čítanie vstupu po riadkoch. |
+ | |||
+ | <tt>Scanner</tt> možno vytvoriť na základe vstupného prúdu typu [https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/io/InputStream.html <tt>InputStream</tt>], ktorým môže byť napríklad štandardný vstupný prúd <tt>System.in</tt> pre čítanie z konzoly, alebo inštancia triedy [https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/io/FileInputStream.html <tt>FileInputStream</tt>] pre čítanie z textového súboru; <tt>Scanner</tt> čítajúci zo vstupného súboru možno vytvoriť aj priamo na základe inštancie triedy [https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/io/File.html <tt>File</tt>] reprezentujúcej cestu k súboru. Triedy <tt>FileInputStream</tt> aj <tt>File</tt> sú definované v balíku <tt>java.io</tt>, z ktorých ich je potrebné importovať. Pri obidvoch spôsoboch vytvárania inštancie triedy <tt>Scanner</tt> ''pre textový súbor'' môže vzniknúť výnimka typu <tt>IOException</tt>, ktorú je potrebné ošetriť (napríklad cez <tt>throws IOException</tt> v hlavičke volajúcej metódy). V prípade čítania z textového súboru je tiež potrebné na konci <tt>Scanner</tt> zavrieť jeho metódou [https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Scanner.html#close() <tt>close</tt>]. | ||
+ | |||
+ | <syntaxhighlight lang="java">import java.util.*; // Kvoli triede Scanner | ||
+ | |||
+ | public class Trieda { | ||
+ | |||
+ | public static void main(String[] args) { | ||
+ | Scanner scanner = new Scanner(System.in); | ||
+ | // ... | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | <syntaxhighlight lang="java">import java.io.*; // Kvoli triede FileInputStream | ||
+ | import java.util.*; // Kvoli triede Scanner | ||
− | < | + | public class Trieda { |
+ | |||
+ | public static void main(String[] args) throws IOException { | ||
+ | Scanner scanner = new Scanner(new FileInputStream("vstup.txt")); | ||
+ | // ... | ||
+ | scanner.close(); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | <syntaxhighlight lang="java">import java.io.*; // Kvoli triede File | ||
+ | import java.util.*; // Kvoli triede Scanner | ||
+ | |||
+ | public class Trieda { | ||
+ | |||
+ | public static void main(String[] args) throws IOException { | ||
+ | Scanner scanner = new Scanner(new File("vstup.txt")); | ||
+ | // ... | ||
+ | scanner.close(); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | Inak je použitie triedy <tt>Scanner</tt> prakticky totožné, nech už čítame z konzoly alebo zo súboru. Kompletný zoznam metód poskytovaných touto triedou možno nájsť [https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Scanner.html v jej dokumentácii]. Tu spomeňme aspoň nasledujúce metódy: | ||
+ | * <tt>next</tt>: vráti nasledujúci reťazec oddelený od zvyšku vstupu bielymi znakmi; ak žiaden neexistuje, vyhodí výnimku (pri čítaní z konzoly sa to môže stať len pri manuálnom zadaní znaku konca súboru; v IntelliJ sa koniec súboru zadáva ako Ctrl+D bez ohľadu na operačný systém, t. j. aj pod Windowsom, avšak časť vstupu „nepotvrdená” klávesou <tt>Enter</tt> sa v takom prípade odignoruje). | ||
+ | * <tt>hasNext</tt>: zistí, či na vstupe zostáva neprečítaný nebiely reťazec, ktorý by mohla vrátiť metóda <tt>next</tt> (pri čítaní z konzoly sa samozrejme vždy čaká na ďalší vstup zadaný používateľom). | ||
+ | * <tt>nextInt</tt>: prečíta nasledujúci reťazec oddelený od zvyšku vstupu bielymi znakmi, pričom ale predpokladá, že ide o celé číslo a na výstup vráti tento reťazec už skonvertovaný na číslo; ak nasledujúci reťazec nebielych znakov neexistuje, alebo nie je korektnou reprezentáciou celého čísla, vyhodí výnimku. | ||
+ | * <tt>hasNextInt</tt>: zistí, či na vstupe nasleduje reťazec interpretovateľný ako celé číslo, ktorý by mohla prečítať metóda <tt>nextInt</tt>. | ||
+ | * Podobne napríklad <tt>nextLong</tt>, <tt>nextDouble</tt>, <tt>hasNextLong</tt>, <tt>hasNextDouble</tt>, atď. | ||
+ | * <tt>nextLine</tt>: prečíta riadok až po jeho koniec a výsledný reťazec (bez prípadného <tt>\n</tt> na konci) vráti na výstupe. | ||
+ | * <tt>hasNextLine</tt>: zistí, či na vstupe nasleduje ďalší riadok. | ||
+ | |||
+ | Na spracovanie jedného vstupného prúdu (napr. jeden prechod cez súbor alebo spracovanie štandardného vstupu) by sa nikdy nemala používať viac ako jedna inštancia triedy <tt>Scanner</tt>. | ||
+ | |||
+ | ''Príklad 1'': nasledujúci program prečíta z konzoly prirodzené číslo <tt>n</tt> a za ním <tt>n</tt> reťazcov oddelených bielymi znakmi, ktoré uloží do pola <tt>a</tt>. Následne na konzolu vypíše tieto reťazce, každý na osobitnom riadku, v opačnom poradí ako na vstupe. | ||
+ | <syntaxhighlight lang="java">import java.util.*; | ||
+ | |||
+ | public class Trieda { | ||
+ | |||
+ | public static void main(String[] args) { | ||
+ | Scanner scanner = new Scanner(System.in); | ||
+ | int n = scanner.nextInt(); | ||
+ | String[] a = new String[n]; | ||
+ | for (int i = 0; i <= n - 1; i++) { | ||
+ | a[i] = scanner.next(); | ||
+ | } | ||
+ | for (int i = n - 1; i >= 0; i--) { | ||
+ | System.out.println(a[i]); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ''Príklad 2'': nasledujúci program číta textový súbor <tt>vstup.txt</tt> obsahujúci niekoľko čísel oddelených bielymi znakmi. Po prečítaní celého vstupného súboru vypíše súčet týchto čísel na konzolu. | ||
+ | |||
+ | <syntaxhighlight lang="java">import java.io.*; | ||
+ | import java.util.*; | ||
+ | |||
+ | public class Trieda { | ||
+ | |||
+ | public static void main(String[] args) throws IOException { | ||
+ | int sucet = 0; | ||
+ | Scanner scanner = new Scanner(new File("vstup.txt")); | ||
+ | while (scanner.hasNextInt()) { | ||
+ | sucet += scanner.nextInt(); | ||
+ | } | ||
+ | scanner.close(); | ||
+ | System.out.println(sucet); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
=== Trieda <tt>BufferedReader</tt> === | === Trieda <tt>BufferedReader</tt> === | ||
+ | |||
+ | Trieda [https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/io/BufferedReader.html <tt>BufferedReader</tt>] – definovaná v balíku <tt>java.io</tt> – umožňuje čítať vstup po znakoch a po riadkoch. Jej metódy spravidla vyhadzujú výnimky typu <tt>IOException</tt>, ktoré je nutné ošetriť (aspoň cez <tt>throws IOException</tt> v hlavičke volajúcej metódy). | ||
+ | |||
+ | * Vytvorenie inštancie triedy <tt>BufferedReader</tt> pre čítanie z konzoly: | ||
+ | <syntaxhighlight lang="java">BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); | ||
+ | // ... | ||
+ | </syntaxhighlight> | ||
+ | * Vytvorenie inštancie triedy <tt>BufferedReader</tt> pre čítanie zo súboru: | ||
+ | <syntaxhighlight lang="java">BufferedReader in = new BufferedReader(new FileReader("vstup.txt")); | ||
+ | // ... | ||
+ | in.close(); | ||
+ | </syntaxhighlight> | ||
+ | Trieda <tt>BufferedReader</tt> pritom poskytuje dve kľúčové metódy: | ||
+ | * <tt>read</tt>: prečíta jeden znak zo vstupu a vráti ho na výstupe (v prípade konca súboru vráti -1); jej návratový typ je <tt>int</tt>. | ||
+ | * <tt>readLine</tt>: prečíta jeden riadok zo vstupu; jej návratový typ je <tt>String</tt> (reťazec bez prípadného <tt>\n</tt> na konci riadku). | ||
+ | |||
+ | ''Príklad 1'': nasledujúci program prečíta z konzoly jeden riadok a následne na konzolu vypíše ten istý riadok, pričom ale malé písmená abecedy prevedie na veľké. | ||
+ | |||
+ | <syntaxhighlight lang="java">import java.io.*; | ||
+ | |||
+ | public class Trieda { | ||
+ | |||
+ | public static void main(String[] args) throws IOException { | ||
+ | BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); | ||
+ | String s = in.readLine(); | ||
+ | System.out.println(s.toUpperCase()); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ''Príklad 2'': nasledujúci program prečíta textový súbor <tt>vstup.txt</tt> po znakoch a jeho obsah vypíše na konzolu. | ||
+ | |||
+ | <syntaxhighlight lang="java">import java.io.*; | ||
+ | |||
+ | public class Trieda { | ||
+ | |||
+ | public static void main(String[] args) throws IOException { | ||
+ | BufferedReader in = new BufferedReader(new FileReader("vstup.txt")); | ||
+ | int c; | ||
+ | while ((c = in.read()) != -1) { | ||
+ | System.out.print((char) c); | ||
+ | } | ||
+ | in.close(); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
== Ďalšie užitočné štandardné triedy == | == Ďalšie užitočné štandardné triedy == | ||
− | + | * Trieda [https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Math.html <tt>Math</tt>] obsahuje množštvo statických metód realizujúcich matematické funkcie a operácie, napríklad: | |
+ | <syntaxhighlight lang="java">public class Trieda { | ||
+ | |||
+ | public static void main(String[] args) { | ||
+ | System.out.println(Math.pow(2, 0.5)); | ||
+ | System.out.println(Math.cos(Math.PI)); | ||
+ | System.out.println(Math.log(Math.E)); | ||
+ | System.out.println(Math.max(1, 2)); | ||
+ | System.out.println(Math.abs(-1)); | ||
+ | System.out.println(Math.toDegrees(Math.acos(Math.sqrt(3) / 2))); | ||
+ | // .. | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | * Trieda [https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Random.html Random] reprezentuje generátor náhodných čísel. Je definovaná v balíku <tt>java.util</tt>. Napríklad nasledujúci program simuluje 10 hodov vyváženou hracou kockou. | ||
+ | <syntaxhighlight lang="java">import java.util.*; | ||
− | + | public class Trieda { | |
− | === Trieda <tt> | + | public static void main(String[] args) { |
+ | Random random = new Random(); | ||
+ | for (int i = 1; i <= 10; i++) { | ||
+ | System.out.println(random.nextInt(6) + 1); // vygeneruj nahodne prirodzene cislo MENSIE ako 6 a zvys ho o 1 | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | * Trieda [https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Arrays.html Arrays] z balíka <tt>java.util</tt> obsahuje statické metódy na prácu s poľami; napr. <tt>equals</tt> na porovnávanie polí (a nie iba referencií), <tt>copyOf</tt> na kopírovanie polí (a nie iba referencií), <tt>sort</tt> na utriedenie poľa, <tt>binarySearch</tt> na binárne vyhľadávanie, atď. |
Aktuálna revízia z 17:17, 13. február 2024
Obsah
- 1 Úvodné informácie
- 2 Základné črty jazyka Java a programovania v ňom
- 3 Prvý program v jazyku Java
- 4 Konvencie pomenúvania identifikátorov
- 5 Príklad o niečo rozsiahlejšieho programu
- 6 Základné konštrukcie jazyka Java
- 7 Statické metódy
- 8 Práca s reťazcami
- 9 Vstup a výstup
- 10 Ďalšie užitočné štandardné triedy
Úvodné informácie
Kontakt na vyučujúcich predmetu
- Rovnaká stránka predmetu ako v zimnom semestri: https://compbio.fmph.uniba.sk/vyuka/prog/.
- Mailové adresy a čísla kancelárií vyučujúcich možno nájsť na hlavnej stránke tohto predmetu.
- Hromadná mailová adresa zo zimného semestra v letnom semestri nefunguje.
- Konzultácie po predchádzajúcej dohode mailom.
Testovač
- Na cvičeniach sa bude používať rovnaký testovač ako v zimnom semestri.
- Heslo na testovači: rovnaké ako v minulom semestri resp. minulý rok; u prípadných nových používateľov nastavené na prog2.
- V prípade problémov s prihlasovaním dajte vedieť.
Priebeh semestra
- Predmet sa bude riadiť pravidlami pre letný semester.
- Okrem testov a domácich úloh budú na testovači zverejňované aj nebodované cvičenia, ktorých riešenie je silne odporúčané.
Softvér potrebný na tomto predmete
- Java SE 21 (prípadne ľubovoľná verzia od 11 vyššie). Na testovači beží Java 11. Rozdiel medzi verziami 21 a 11 nie je veľký, ale programy využívajúce niektoré nepreberané nové črty jazyka Java nemusia na testovači skompilovať.
- Platforma JavaFX pre vývoj aplikácií s grafickým používateľským rozhraním.
- Vývojové prostredie (IDE) pre Javu, odporúčame IntelliJ IDEA Community Edition.
- Informácie o inštalácii a základnom použití uvedeného softvéru.
- Vhodnou príležitosťou na vyriešenie prípadných problémov môžu byť prvé cvičenia.
Zdroje informácií o jazyku Java
Základné črty jazyka Java a programovania v ňom
Prvoradým cieľom tohto predmetu je zvládnutie základov objektovo orientovaného programovania (OOP) prostredníctvom programovacieho jazyka Java. Na úvod je užitočné vedieť o tomto jazyku nasledujúce:
- Základné syntaktické konštrukcie jazyka Java sú v mnohom veľmi podobné jazykom C a C++. Už tento týždeň by sme mali zvládnuť prepísať do Javy väčšiu časť programov z minulého semestra; budúci týždeň by to už mali byť úplne všetky programy.
- Jazyk Java je silne (aj keď nie čisto) objektovo orientovaný. Použite tried – ktoré sú popri objektoch základným konceptom OOP – je nutné na napísanie aj toho najjednoduchšieho programu.
- Na druhej strane je triedu možné použiť aj ako obyčajný „obal” pre niekoľko metód (t. j. funkcií) podobného typu ako v minulom semestri. Takýmto spôsobom budeme s Javou pracovať na dnešnej prednáške. S ozajstným objektovo orientovaným programovaním začneme až budúci týždeň.
- Programy v jazyku Java sa obvykle nekompilujú do strojového kódu, ale do tzv. javovského bytekódu. Po skompilovaní programu teda nedostávame bežný spustiteľný súbor, ale súbor, ktorý možno spustiť na javovskom virtuálnom stroji (angl. Java Virtual Machine; skr. JVM). Vykonávanie takýchto programov je síce o niečo pomalšie, zato sú však prenositeľné medzi rôznymi operačnými systémami a architektúrami.
Celkovo ide o jazyk omnoho vyššej úrovne, než jazyk C: v oveľa väčšej miere sa tu abstrahuje od počítačovej architektúry. Java napríklad neumožňuje priamy prístup k pamäťi počítača a o uvoľňovanie alokovanej pamäte sa stará JVM automaticky prostrednictvom mechanizmu tzv. garbage collection. Hoci teda jazyk nie je príliš vhodný na nízkoúrovňové programovanie, tvorba „bežných” používateľských programov je tu podstatne pohodlnejšia, než napríklad v jazyku C. Okrem toho Java disponuje veľkou knižnicou štandardných tried (Java Class Library; skr. JCL), v ktorej je okrem iného implementované aj množštvo algoritmov a dátových štruktúr. Orientáciu v možnostiach ponúkaných touto knižnicou značne uľahčuje dokumentácia k nej.
Rozdiel v úrovni abstrakcie medzi jazykmi C a Java sa premieta aj do typického programátorského štýlu. Od efektívnosti samotnej implementácie sa dôraz obvykle posúva k aspektom softvérového inžinierstva, ako sú napríklad čitateľnosť, rozšíriteľnosť a „spravovateľnosť” kódu. S niektorými elementárnymi princípmi softvérového inžinierstva sa zoznámime aj na tomto predmete.
Prvý program v jazyku Java
Tradične začneme programom, ktorý na konzolu vypíše text Hello, World!.
public class Hello {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
- Názov súboru sa musí zhodovať s názvom triedy, ku ktorému sa pridá prípona .java – v našom prípade teda musí byť zdrojový kód uložený v súbore Hello.java.
- Program je potrebné skompilovať javovským kompilátorom a následne spustiť na javovskom virtuálnom stroji – návod možno nájsť na osobitnej stránke. Po skompilovaní programu získame súbor java.class spustiteľný na JVM.
Rozoberme postupne jednotlivé časti uvedeného programu:
- Metóda (funkcia) main, ktorá sa podobne ako v C/C++ začne vykonávať bezprostredne po spustení programu, je „obalená” v triede, ktorú sme nazvali Hello. V jazyku Java musí byť všetok kód súčasťou nejakej triedy (význam tried pre OOP zatiaľ ponechajme bokom).
- Hlavička metódy main musí byť vždy tvaru ako vyššie. Modifikátory public a static si vysvetlíme neskôr; nasleduje návratový typ void, názov metódy main a argument tejto metódy, ktorým sú argumenty programu z príkazového riadku (v podobe poľa reťazcov).
- Samotný výpis na konzolu realizuje metóda System.out.println.
Keďže jeden väčší program typicky pozostáva z množstva rôznych tried (v rôznych súboroch), je možné triedy ďalej umiestniť do balíkov; program tak často pozostáva z niekoľkých balíkov navzájom súvisiacich tried. Umiestnenie triedy do balíka možno realizovať príkazom na začiatku zdrojového súboru.
package somepackage;
public class Hello {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
- V našom prípade sme triedu Hello umiestnili do balíka somepackage. Pri práci s IDE ako napr. IntelliJ je často potrebné pridať balík aj do projektu; rozdiel je tiež pri kompilovaní a spúšťaní triedy z príkazového riadku. Viac sa možno dočítať tu.
- Pokiaľ deklarácia balíka chýba, považuje sa trieda za súčasť nepomenovaného balíka (angl. unnamed package alebo default package). Používanie nepomenovaného balíka vo všeobecnosti nie je dobrá prax, ale je zmysluplné pri menších programoch pre vlastnú potrebu (alebo pre potreby tohto predmetu).
- Za dobrú prax sa naopak považuje pomenúvanie balíkov tak, aby boli celosvetovo jednoznačne identifikovateľné – za týmto účelom preto boli vybracované špeciálne konvencie, ktoré ale na tomto predmete nebudeme dodržiavať.
Konvencie pomenúvania identifikátorov
Za účelom sprehľadnenia zdrojového kódu a často pomerne rozsiahlych knižníc sa pri pomenúvaní identifikátorov v Jave používajú (okrem iných aj) nasledujúce konvencie:
- Názvy tried by mali vždy začínať veľkým písmenom (pri viacslovných názvoch začína veľkým písmenom aj každé ďalšie slovo).
- Názvy premenných a metód by naopak mali vždy začínať malým písmenom (pri viacslovných názvoch začínajú ďalšie slová veľkým písmenom).
Nedodržiavanie týchto konvencií budeme na tomto predmete považovať za chybu. O ďalších konvenciách používaných v tomto smere sa možno dočítať tu.
Príklad o niečo rozsiahlejšieho programu
Nižšie je príklad o niečo rozsiahlejšieho programu v jazyku Java. Pokúste sa s využitím skúseností z minulého semestra uhádnuť, čo tento program robí.
import java.util.*;
public class Program {
/* Spocita sucet prvkov pola a.
*/
public static int sum(int[] a) {
int result = 0;
for (int i = 0; i <= a.length - 1; i++) {
result += a[i];
}
return result;
}
/* Spocita priemer hodnot prvkov pola a.
*/
public static double average(int[] a) {
return (double) sum(a) / a.length;
}
/* Najde najvacsi prvok pola a.
*/
public static int max(int[] a) {
int max = Integer.MIN_VALUE; // Premenna ma rovnaky nazov ako metoda
for (int i = 0; i <= a.length - 1; i++) {
if (a[i] >= max) {
max = a[i];
}
}
return max;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int[] a = new int[n];
for (int i = 0; i <= n - 1; i++) {
a[i] = scanner.nextInt();
}
System.out.println("Sucet: " + sum(a));
System.out.println("Priemer: " + average(a));
System.out.println("Maximum: " + max(a));
}
}
- Okamžite vidieť množstvo podobností medzi jazykmi C/C++ a Java, ale aj niekoľko drobných rozdielov.
- Konštrukcie jazyka Java použité v tomto programe podrobnejšie rozoberieme nižšie.
Základné konštrukcie jazyka Java
Primitívne typy
Jazyk Java podporuje celkovo osem primitívnych dátových typov (void sa za primitívny typ nepovažuje):
- int: 32-bitové celé čísla so znamienkom, v rozsahu od -231 po 231 - 1; ďalšie celočíselné typy sú byte, short a long (na rozdiel od C/C++ už teda long a short nie sú modifikátory, ale priamo celočíselné typy).
- double: 64-bitové desatinné čísla s pohyblivou desatinnou čiarkou; ďalší typ desatinných čísel je 32-bitový float.
- boolean: logické hodnoty true alebo false.
- char: 16-bitové znaky v kódovaní Unicode (UTF-16); typ teda napríklad podporuje slovenskú diakritiku.
Deklarácie premenných a priradenia zapisujeme rovnako ako v C/C++. Na rozdiel od C/C++ možno napríklad definovať aj lokálnu premennú s rovnakým názvom ako niektorá metóda.
int x = 0;
Kompilátor pritom nedovolí použitie neinicializovanej lokálnej premennej.
int x;
System.out.println(x); // variable x might not have been initialized
Neskôr uvidíme, že prvky polí a premenné tried a ich inštancií sa inicializujú automaticky.
Pretypovanie tiež funguje podobne ako v C/C++, avšak implicitné (automatické) pretypovanie je možné pre menší počet dvojíc typov, než v C/C++. Číselné typy možno usporiadať ako byte ≺ short ≺ int ≺ long ≺ float ≺ double a implicitné pretypovanie je možné iba v smere tohto usporiadania. Ďalej je možné implicitne pretypovať char na int, long, float a double (nie však naopak). Zvyšné pretypovania je nutné robiť explicitne, rovnako ako v C/C++.
int x = 65;
char c = (char) x;
System.out.println(c);
Možno ale napríklad priamo písať char c = 65.
Operátory
Jazyk Java podporuje podobnú sadu operátorov ako jazyky C a C++; ich kompletný zoznam vrátanie precedencií možno nájsť tu. Spomeňme predovšetkým:
- Operátory priradenia =, +=, *=, atď.
- Aritmetické operátory +, -, *, /, %. Na celočíselných typoch sa operátor / správa, rovnako ako v C/C++, ako celočíselné delenie.
- Prefixové a sufixové operátory ++, --.
- Relačné a porovnávacie operátory ==, !=, <, >, <=, >=.
- Logické operátory ||, &&, !.
Cykly a podmienky
Nasledujúce konštrukcie sú v Jave veľmi podobné ako v C/C++:
- Podmienky: if, else a switch.
- Cykly: for, while, do ... while.
- Príkazy break a continue.
Drobný rozdiel pri ich použití vyplýva zo skutočnosti, že v Jave nedochádza k automatickému pretypovaniu z napr. číselných typov na boolean. Napríklad nasledujúci kus kódu, ktorý by bol v C/C++ korektný, vyústi v Jave chybu.
int n = 1;
if (n) { // incompatible types: int cannot be converted to boolean
// ...
}
Nápravu dosiahneme nahradením celočíselného výrazu n výrazom logickým.
int n = 1;
if (n != 0) {
// ...
}
Jednorozmerné polia
V Jave sa oproti C/C++ používa trochu iná syntax pre deklaráciu poľa.
int[] a; // Deklaracia premennej a reprezentujucej pole celych cisel
Samotné vytvorenie poľa a priradenie referencie naň do premennej a následne možno realizovať podobne ako pri dynamicky alokovaných poliach v C++.
a = new int[10];
Pamäť alokovanú pri vytvorení poľa nie je potrebné manuálne uvoľňovať; stará sa o to automatický „smetiarsky” mechanizmus (angl. garbage collection). Prípadne tiež možno spojiť deklaráciu premennej s vytvorením poľa do jediného príkazu.
int[] a = new int[rozsah];
Namiesto int[] a možno písať aj int a[]; prvý spôsob je ale prirodzenejší, keďže typom premennej a je v oboch prípadoch int[].
Pri vytvorení poľa sa všetky jeho prvky automaticky inicializujú – a to na 0 pri poliach čísel resp. znakov (kde ide o znak s kódom 0, nie o znak '0'), na false pri poliach booleovských hodnôt a na null pri poliach objektov a polí (poľami takéhoto typu sa budeme zaoberať až neskôr). V prípade potreby inicializovať pole vopred známej dĺžky na iné hodnoty možno použiť nasledujúcu skratku.
int[] a = {0, 1, 2, 3, 4, 5};
Týmto príkazom sa do premennej a priradí referencia na šesťprvkové pole obsahujúce postupne hodnoty 0, 1, 2, 3, 4, 5. Takéto priradenie možno realizovať iba v rámci príkazu, v ktorom je príslušná premenná aj deklarovaná. Neskôr možno do premennej a priradiť takto „vymenované” pole nasledovne.
int[] a;
// ...
a = new int[]{0, 1, 2, 3, 4, 5};
Súčasťou poľa je v Jave aj informácia o jeho dĺžke, ku ktorej možno pre pole a pristúpiť cez a.length. Nasledujúci kus kódu tak napríklad do stoprvkového poľa a uloží postupnosť čísel 0, 1, 2, ..., 99.
int[] a = new int[100];
for (int i = 0; i <= a.length - 1; i++) {
a[i] = i;
}
V prípade pokusu o prístup k prvku poľa mimo jeho rozsah Java za behu programu vyhodí výnimku java.lang.ArrayIndexOutOfBoundsException. Mechanizmom výnimiek a ich spracovaním sa na tomto predmete budeme zaoberať neskôr; v tomto momente je podstatná skutočnosť, že v Jave nie je možné pomocou prístupu k danému poľu prepísať pamäť mimo jeho rozsahu – čo je jedna z najnepríjemnejších chýb v C/C++.
Hodnoty vs. referencie v jazyku Java
Napriek tomu, že v jazyku Java neexistuje mechanizmus smerníkov, ani žiadna obdoba smerníkovej aritmetiky, všetky premenné okrem premenných primitívnych typov obsahujú referencie predstavujúce adresy v pamäti. Presnejšie:
- Premenné primitívnych typov obsahujú hodnoty týchto primitívnych typov.
- Premenné všetkých zvyšných typov obsahujú referencie na pole alebo na objekt (aj polia sú v skutočnosti veľmi špeciálne objekty, ale to teraz ponechajme bokom).
Premenné obsahujúce referencie – zatiaľ vieme pracovať iba s premennými ukazujúcimi na pole – sa tak správajú veľmi podobne ako smerníky v C/C++. Operátor = aplikovaný na takéto premenné nekopíruje hodnoty, ale referencie; podobne operátor == neporovnáva hodnoty, ale referencie.
int[] a = {1, 2, 3, 4, 5};
int[] b = a; // Premenne a, b ukazuju na to iste patprvkove pole
b[0] = 10;
System.out.println(a[0]); // Vypise 10
b = new int[a.length]; // Premenne a, b uz teraz ukazuju na dve rozne polia
for (int i = 0; i <= b.length - 1; i++) {
b[i] = a[i]; // Hodnoty v oboch poliach su odteraz rovnake, polia ako take su rozne
}
System.out.println(a == b); // Vypise false
Premenná obsahujúca referenciu môže nadobúdať špeciálnu hodnotu null; v takom prípade referencia neukazuje na žiaden kus pamäte.
Cyklus for each
V Jave existuje špeciálny variant cyklu for, tzv. cyklus for each, umožňujúci postupne prejsť cez všetky hodnoty prvkov poľa a aj bez indexovej premennej. Napríklad kus kódu
int[] a = {6, 5, 4, 3, 2, 1};
for (int x : a) {
System.out.println(x);
}
je ekvivalentný nasledujúcemu kódu.
int[] a = {6, 5, 4, 3, 2, 1};
for (int i = 0; i <= a.length - 1; i++) {
System.out.println(a[i]);
}
Vo všeobecnosti možno povedať, že cyklus for each s iteračnou premennou x pracuje nasledovne:
- Prechádza postupne pole od začiatku po jeho koniec.
- V každej iterácii najprv skopíruje hodnotu na danej pozícii poľa do premennej x a následne vykoná príkazy v tele cyklu.
Z toho vyplýva, že pomocou cyklu for each nemožno meniť hodnoty prechádzaného poľa. Napríklad v cykle
for (int x : a) {
x = 0;
}
sa nemenia jednotlivé prvky poľa, ale iba lokálna premenná x. Avšak v prípade, že sú prvkami poľa referencie, možno pomocou cyklu for each meniť hodnoty, na ktoré tieto referencie ukazujú (to je vlastnosť, ktorú oceníme až neskôr).
Viacrozmerné polia
Popri jednorozmerných poliach možno v Jave pracovať aj s dvojrozmernými poľami, ktoré sa správajú podobne ako polia smerníkov resp. smerníky na smerníky v C/C++. V javovskej terminológii môžeme povedať, že dvojrozmerné pole je poľom polí. Obdĺžnikové pole vytvoríme napríklad nasledovne.
int[][] a = new int[3][4]; // Vytvori pole s troma riadkami a styrmi stĺpcami
Takéto pole si možno predstaviť ako trojprvkové pole jednorozmerných polí dĺžky 4. Napríklad a[0] je teda jednorozmerné pole zodpovedajúce nultému riadku dvojrozmerného poľa a. S dvojrozmerným poľom potom pracujeme prirodzeným spôsobom.
for (int i = 0; i <= a.length - 1; i++) {
for (int j = 0; j <= a[i].length - 1; j++) {
a[i][j] = i + j;
}
}
Výsledné pole je znázornené na nasledujúcom obrázku.
Alternatívne možno najprv pamäť alokovať iba pre pole jednotlivých riadkov a následne pre každý z riadkov zvlášť. Takto môžeme vytvoriť aj iné ako obdĺžnikové polia, napríklad „trojuholník” z nasledujúceho príkladu.
int[][] a = new int[3][]; // V tomto momente su a[0], a[1] aj a[2] rovne null (vdaka automatickej inicializacii prvkov pola)
for (int i = 0; i <= a.length - 1; i++) {
a[i] = new int[i + 1];
}
for (int i = 0; i <= a.length - 1; i++) {
for (int j = 0; j <= a[i].length - 1; j++) {
a[i][j] = j;
}
}
Výsledné pole je znázornené na nasledujúcom obrázku.
Z tejto predstavy o reprezentácii dvojrozmerných polí by malo byť zrejmé, že napríklad príkaz int[][] a = new int[][10]; nie je korektný.
Rovnako ako s dvojrozmernými poľami možno v Jave pracovať aj s poľami o ľubovoľnom konečnom počte rozmerov. Pri alokovaní takýchto polí platí, že je potrebné určiť prvých niekoľko rozmerov (kde „niekoľko” v tomto prípade znamená „aspoň jeden”).
int[][][] a = new int[3][4][5]; // OK
int[][][] b = new int[3][4][]; // OK
int[][][] c = new int[3][][]; // OK
int[][][] d = new int[][4][5]; // Chyba
int[][][] e = new int[3][][5]; // Chyba
int[][][] f = new int[][][]; // Chyba
Statické metódy
Obdobou funkcií, ako ich poznáme z minulého semestra, sú v jazyku Java statické metódy triedy.
- Definujú sa vždy vo vnútri nejakej triedy s použitím podobnej syntaxe ako v C/C++. Pred návratový typ je potrebné napísať kľúčové slovo static, vďaka ktorému pôjde o statickú metódu triedy – čiže zjednodušene povedané „obyčajnú funkciu” – a nie o metódu inštancie triedy, čo je koncept objektovo orientovaného programovania, s ktorým sa zoznámime na budúcej prednáške.
- Pred klúčové slovo static ešte možno pridať modifikátor prístupu ako napr. public alebo private hovoriaci o viditeľnosti metódy z iných tried a balíkov. Modifikátormi prístupu sa budeme detailnejšie zaoberať neskôr.
- Statické metódy voláme rovnako ako funkcie v C/C++ (pri statickej metóde metoda z inej triedy Trieda pri jej volaní píšeme Trieda.metoda). Kľúčové slovo return sa tiež správa podobne ako v C/C++, avšak program obsahujúci metódu s návratovým typom rôznym od void a chýbajúcim príkazom return nie je možné skompilovať. Rovnako ako v C/C++ funguje aj rekurzia.
Príklad:
public class Trieda {
static long faktorial(int n) {
if (n == 0) {
return 1;
} else {
return n * faktorial(n - 1);
}
}
public static void main(String[] args) {
System.out.println(faktorial(18));
}
}
Metóda main
- Špeciálnou statickou metódou je metóda main, ktorá sa vykoná bezprostredne po spustení programu (presnejšie danej triedy). Tá musí mať návratový typ void, modifikátor prístupu public a jediný argument typu String[] reprezentujúci pole argumentov programu z príkazového riadku. Prípadné metódy main s inou hlavičkou sa považujú za „obyčajné” metódy a po spustení programu sa nevykonávajú.
public class Trieda {
public static void main() {
System.out.println("Tento text sa nikdy nevypise.");
}
public static void main(String[] args) {
System.out.println("Pocet argumentov: " + args.length);
}
}
- Každá trieda, od ktorej po skompilovaní očakávame spustiteľnosť na JVM, musí mať definovanú hlavnú metódu main; často však píšeme aj triedy, ktoré slúžia len na použitie z iných tried.
- V IDE ako napr. IntelliJ je zvyčajne potrebné vybrať hlavnú triedu projektu, čo je trieda, ktorú sa prostredie bude snažiť spúšťať po spustení celého projektu. Táto trieda tak musí mať definovanú metódu main.
Predávanie argumentov metód
Argumenty metód sa v jazyku Java vždy predávajú hodnotou.
- Pre argumenty sa tedy vždy vytvoria nové lokálne premenné, do ktorých sa skopírujú hodnoty argumentov, s ktorými bola metóda volaná.
- Nie je teda možné napísať metódu, ktorá pozmení premenné z volajúcej metódy. Ak sú ale argumentmi metódy referencie (polia alebo objekty), je možné pozmeniť hodnoty, na ktoré táto referencia ukazuje (t. j. napríklad zmeniť obsah poľa alebo premenné objektu).
Príklad:
public class Trieda {
static void f(int n, int[] a) {
n = 6;
a[0] = 7;
a = new int[]{8, 9, 10, 11, 12};
}
public static void main(String[] args) {
int n = 0;
int[] a = {1, 2, 3, 4, 5};
f(n, a);
System.out.println(n); // Vypise 0
System.out.println(a[0]); // Vypise 7
}
}
- Špeciálne napríklad v Jave nie je možné napísať metódu swap, ktorá vymení hodnoty dvoch premenných primitívnych typov.
Práca s reťazcami
Zvyšok tejto prednášky sčasti presahuje jej rámec tým, že začneme pracovať s niektorými špeciálnymi objektmi bez toho, aby sme si vysvetlili mechanizmus objektov vo všeobecnosti. Už pomerne elementárne úkony, ako práca s reťazcami alebo so vstupom a výstupom, sa totiž v Jave realizujú s využitím objektov. Nasledujúce pasáže sú motivované praktickou potrebou zvládnutia týchto úkonov; hlbšie pochopenie tohto materiálu nadobudneme budúci týždeň.
Trieda String
Reťazce znakov sú v Jave objektmi triedy String. Premenné typu String teda majú charakter referencií na samotný objekt. Objekty triedy String sú nemodifikovateľné reťazce – po vytvorení ich už teda nemožno meniť. Premennú typu String ale samozrejme môžeme meniť tak, že do nej priradíme referenciu na iný objekt typu String.
- Text ohraničený úvodzovkami sa považuje za reťazec typu String.
- Operátor + realizuje zreťazenie reťazcov. Stačí dokonca, aby bol aspoň jeden z operandov reťazec, zvyšné sa na String skonvertujú automaticky.
Príklad:
String s = "Hello, World!";
System.out.println(s);
int n = 42;
s = "Hodnota premennej n je " + n + ".";
System.out.println(s);
Keďže sú premenné typu String referenciami na objekt, operátor = tiež kopíruje iba referencie a operátor == neporovnáva hodnoty reťazcov (t. j. samotné texty), ale adresy v pamäti. Vzhľadom na konštantnosť reťazcov je tu častejším zdrojom chýb nesprávne použitie operátora ==. Porovnanie reťazcov správne realizuje metóda equals.
String str1 = "nejaky text";
String str2 = str1; // str2 a str1 ukazuju na to iste miesto v pamati
String str3 = str1 + ""; // str3 a str1 ukazuju na rozne miesta v pamati, ktore obsahuju rovnake retazce
String str4 = new String(str1); // str4 a str1 ukazuju na rozne miesta v pamati, ktore obsahuju rovnake retazce
if (str2 == str1) {
System.out.println("str2 == str1");
}
if (str3 == str1) {
System.out.println("str3 == str1");
}
if (str4 == str1) {
System.out.println("str4 == str1");
}
if (str2.equals(str1)) {
System.out.println("str2.equals(str1)");
}
if (str3.equals(str1)) {
System.out.println("str3.equals(str1)");
}
if (str4.equals(str1)) {
System.out.println("str4.equals(str1)");
}
V Jave možno switch aplikovať aj na reťazce.
String s;
int n;
// ...
switch (s) {
case "ano":
n = 1;
break;
case "nie":
n = 0;
break;
default:
n = -1;
}
Z ďalších metód triedy String, ktoré možno použiť na manipuláciu s reťazcami, spomeňme aspoň tieto:
- Metóda length vráti dĺžku daného reťazca. Pozor: keďže ide o metódu (bez argumentov), na rozdiel od polí pre reťazec s píšeme s.length() so zátvorkami na konci.
- Metóda charAt s jedným celočíselným argumentom vráti znak na danej pozícii.
String s = "retazec";
for (int i = 0; i <= s.length() - 1; i++) {
System.out.println(s.charAt(i));
}
- Množstvo ďalších metód na prácu s reťazcami možno nájsť v dokumentácii k triede String.
Trieda StringBuilder
V prípade nutnosti daný reťazec často modifikovať je použitie triedy String pomerne pomalé, pretože pri každej modifikácii reťazca je potrebné vytvoriť nový objekt. Rýchlejšou alternatívou je použitie triedy StringBuilder reprezentujúcej modifikovateľný reťazec, ktorý je tiež konvertovateľný na String.
Napríklad reťazec abc...z obsahujúci všetky písmená malej anglickej abecedy tak môžeme vytvoriť dvoma rôznymi spôsobmi: buď pomocou pomalého vytvárania 27 rôznych objektov triedy String
String abeceda = "";
for (char c = 'a'; c <= 'z'; c++) {
abeceda = abeceda + c;
}
alebo pomocou rýchlejšieho vytvorenia jedného objektu typu StringBuilder, jeho postupných modifikácií a následného vytvorenia jedného objektu typu String.
StringBuilder buffer = new StringBuilder();
for (char c = 'a'; c <= 'z'; c++) {
buffer.append(c);
}
String abeceda = buffer.toString();
Reťazec reprezentovaný objektom typu StringBuilder môžeme aj priamo vypísať na konzolu.
System.out.println(buffer);
Naopak v prípadoch, keď reťazec nie je potrebné modifikovať, je implementácia cez String o niečo efektívnejšia.
Vstup a výstup
Zameriame sa teraz na základy práce s textovým vstupom a výstupom – či už na konzole, alebo v podobe textových súborov.
Výstupné prúdy: trieda PrintStream
Textový výstup možno v Jave najjednoduchšie produkovať pomocou triedy PrintStream, ktorú je nutné importovať z balíka java.io; prípadne je možné importovať aj kompletný balík java.io.
import java.io.*; // alebo: import java.io.PrintStream;
public class Trieda {
public static void main(String[] args) {
// ...
}
}
Výstupný prúd pre zápis do textového súboru vystup.txt vytvoríme napríklad nasledovne.
PrintStream out = new PrintStream("vystup.txt");
- V prípade, že súbor vystup.txt existuje, premaže sa týmto volaním jeho obsah; v prípade potreby zapisovať na koniec existujúceho súboru možno použiť PrintStream out = new PrintStream(new FileOutputStream("vystup.txt", true)), kde o pridávaní na koniec súboru hovorí booleovský parameter true (viac v dokumentácii).
- Adresár, od ktorého sa počíta takáto relatívna adresa súboru, závisí od prostredia. Pri spúšťaní programu z príkazového riadku sa súbor vystup.txt vytvorí v adresári, z ktorého bol spustený interpreter java. Pri práci s IntelliJ ide pri východzích nastaveniach o koreňový adresár projektu (obsahujúci podadresáre ako src a out).
- Vytvorenie inštancie triedy PrintStream môže spôsobiť výnimku typu IOException, ktorú je nutné ošetriť. Spracovaním výnimiek sa na tomto predmete budeme zaoberať až neskôr – zatiaľ teda zvolíme najjednoduchšie riešenie, pri ktorom iba do hlavičky volajúcej metódy (v príklade nižšie ide o metódu main) pridáme upozornenie, že v nej môže vzniknúť neošetrená výnimka typu IOException (v prípade, že sme neimportovali celý balík java.io, je z neho potrebné importovať triedu java.io.IOException).
public static void main(String[] args) throws IOException {
PrintStream out = new PrintStream("vystup.txt");
// ...
}
Po vytvorení výstupného prúdu môžeme používať jeho metódy na zápis do súboru, ako napríklad nasledujúce.
- print: zapíše do súboru svoj argument (prakticky ľubovoľného typu).
- println: to isté, len s novým riadkom na konci (pri volaní bez argumentov vypíše iba znak pre nový riadok).
- format alebo printf: zapíše text podľa formátovacieho reťazca podobného ako v C (viac tu).
- close: metóda bez argumentu, ktorá zavrie otvorený výstupný prúd a je potrebné ju zavolať akonáhle so súborom prestaneme pracovať.
Kompletný program zapisujúci do súboru vystup.txt tak môže vyzerať napríklad nasledovne.
import java.io.*;
public class Trieda {
public static void main(String[] args) throws IOException {
PrintStream out = new PrintStream("subor.txt");
out.print("Nejaky text");
out.println();
out.println("Nejaky iny text");
out.close();
}
}
Špeciálnym výstupným prúdom typu PrintStream je aj štandardný výstupný prúd System.out. Pri práci s ním tak možno používať rovnaké metódy ako vyššie. Nie je pritom potrebné importovať triedu PrintStream (pretože pracujeme iba s jej inštanciou System.out a trieda System sa importuje automaticky), ani špecifikovať throws IOException (pretože nevoláme konštruktor triedy PrintStream, ktorý túto výnimku môže spôsobiť). Obvykle tiež nie je vhodné výstupný prúd System.out zatvárať metódou close, keďže sa tým do budúcnosti používanie štandardného výstupu znemožní.
public class Trieda {
public static void main(String[] args) {
System.out.print("Nejaky text");
System.out.println();
System.out.println("Nejaky iny text");
}
}
Vstupné prúdy
Základná trieda pre vstupné prúdy je v Jave InputStream. Tohto typu je aj štandardný vstupný prúd System.in pre čítanie z konzoly. Vstupné prúdy pre čítanie zo súboru sú reprezentované triedou FileInputStream; pre účely tejto prednášky možno FileInputStream považovať za špeciálny prípad InputStream. Obe tieto triedy sú definované v balíku java.io.
Používanie týchto vstupných prúdov však nie je veľmi pohodlné, pretože umožňujú iba čítanie po bytoch. Preto sa zvyknú používať nadstavbové triedy, ktoré tieto jednoduché vstupné prúdy „obalia” a programátorovi poskytnú aj pokročilejšie metódy na prácu so vstupom. V nasledujúcom preskúmame dve z takýchto nadstavbových tried: Scanner a BufferedReader.
Trieda Scanner
Trieda Scanner – definovaná v balíku java.util, z ktorého je nutné túto triedu importovať – umožňuje rozkladať vstupný prúd na reťazce oddelené bielymi znakmi, pričom kompatibilné reťazce dokáže konvertovať aj na číselné typy. Alternatívne možno Scanner použiť aj na čítanie vstupu po riadkoch.
Scanner možno vytvoriť na základe vstupného prúdu typu InputStream, ktorým môže byť napríklad štandardný vstupný prúd System.in pre čítanie z konzoly, alebo inštancia triedy FileInputStream pre čítanie z textového súboru; Scanner čítajúci zo vstupného súboru možno vytvoriť aj priamo na základe inštancie triedy File reprezentujúcej cestu k súboru. Triedy FileInputStream aj File sú definované v balíku java.io, z ktorých ich je potrebné importovať. Pri obidvoch spôsoboch vytvárania inštancie triedy Scanner pre textový súbor môže vzniknúť výnimka typu IOException, ktorú je potrebné ošetriť (napríklad cez throws IOException v hlavičke volajúcej metódy). V prípade čítania z textového súboru je tiež potrebné na konci Scanner zavrieť jeho metódou close.
import java.util.*; // Kvoli triede Scanner
public class Trieda {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// ...
}
}
import java.io.*; // Kvoli triede FileInputStream
import java.util.*; // Kvoli triede Scanner
public class Trieda {
public static void main(String[] args) throws IOException {
Scanner scanner = new Scanner(new FileInputStream("vstup.txt"));
// ...
scanner.close();
}
}
import java.io.*; // Kvoli triede File
import java.util.*; // Kvoli triede Scanner
public class Trieda {
public static void main(String[] args) throws IOException {
Scanner scanner = new Scanner(new File("vstup.txt"));
// ...
scanner.close();
}
}
Inak je použitie triedy Scanner prakticky totožné, nech už čítame z konzoly alebo zo súboru. Kompletný zoznam metód poskytovaných touto triedou možno nájsť v jej dokumentácii. Tu spomeňme aspoň nasledujúce metódy:
- next: vráti nasledujúci reťazec oddelený od zvyšku vstupu bielymi znakmi; ak žiaden neexistuje, vyhodí výnimku (pri čítaní z konzoly sa to môže stať len pri manuálnom zadaní znaku konca súboru; v IntelliJ sa koniec súboru zadáva ako Ctrl+D bez ohľadu na operačný systém, t. j. aj pod Windowsom, avšak časť vstupu „nepotvrdená” klávesou Enter sa v takom prípade odignoruje).
- hasNext: zistí, či na vstupe zostáva neprečítaný nebiely reťazec, ktorý by mohla vrátiť metóda next (pri čítaní z konzoly sa samozrejme vždy čaká na ďalší vstup zadaný používateľom).
- nextInt: prečíta nasledujúci reťazec oddelený od zvyšku vstupu bielymi znakmi, pričom ale predpokladá, že ide o celé číslo a na výstup vráti tento reťazec už skonvertovaný na číslo; ak nasledujúci reťazec nebielych znakov neexistuje, alebo nie je korektnou reprezentáciou celého čísla, vyhodí výnimku.
- hasNextInt: zistí, či na vstupe nasleduje reťazec interpretovateľný ako celé číslo, ktorý by mohla prečítať metóda nextInt.
- Podobne napríklad nextLong, nextDouble, hasNextLong, hasNextDouble, atď.
- nextLine: prečíta riadok až po jeho koniec a výsledný reťazec (bez prípadného \n na konci) vráti na výstupe.
- hasNextLine: zistí, či na vstupe nasleduje ďalší riadok.
Na spracovanie jedného vstupného prúdu (napr. jeden prechod cez súbor alebo spracovanie štandardného vstupu) by sa nikdy nemala používať viac ako jedna inštancia triedy Scanner.
Príklad 1: nasledujúci program prečíta z konzoly prirodzené číslo n a za ním n reťazcov oddelených bielymi znakmi, ktoré uloží do pola a. Následne na konzolu vypíše tieto reťazce, každý na osobitnom riadku, v opačnom poradí ako na vstupe.
import java.util.*;
public class Trieda {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
String[] a = new String[n];
for (int i = 0; i <= n - 1; i++) {
a[i] = scanner.next();
}
for (int i = n - 1; i >= 0; i--) {
System.out.println(a[i]);
}
}
}
Príklad 2: nasledujúci program číta textový súbor vstup.txt obsahujúci niekoľko čísel oddelených bielymi znakmi. Po prečítaní celého vstupného súboru vypíše súčet týchto čísel na konzolu.
import java.io.*;
import java.util.*;
public class Trieda {
public static void main(String[] args) throws IOException {
int sucet = 0;
Scanner scanner = new Scanner(new File("vstup.txt"));
while (scanner.hasNextInt()) {
sucet += scanner.nextInt();
}
scanner.close();
System.out.println(sucet);
}
}
Trieda BufferedReader
Trieda BufferedReader – definovaná v balíku java.io – umožňuje čítať vstup po znakoch a po riadkoch. Jej metódy spravidla vyhadzujú výnimky typu IOException, ktoré je nutné ošetriť (aspoň cez throws IOException v hlavičke volajúcej metódy).
- Vytvorenie inštancie triedy BufferedReader pre čítanie z konzoly:
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
// ...
- Vytvorenie inštancie triedy BufferedReader pre čítanie zo súboru:
BufferedReader in = new BufferedReader(new FileReader("vstup.txt"));
// ...
in.close();
Trieda BufferedReader pritom poskytuje dve kľúčové metódy:
- read: prečíta jeden znak zo vstupu a vráti ho na výstupe (v prípade konca súboru vráti -1); jej návratový typ je int.
- readLine: prečíta jeden riadok zo vstupu; jej návratový typ je String (reťazec bez prípadného \n na konci riadku).
Príklad 1: nasledujúci program prečíta z konzoly jeden riadok a následne na konzolu vypíše ten istý riadok, pričom ale malé písmená abecedy prevedie na veľké.
import java.io.*;
public class Trieda {
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String s = in.readLine();
System.out.println(s.toUpperCase());
}
}
Príklad 2: nasledujúci program prečíta textový súbor vstup.txt po znakoch a jeho obsah vypíše na konzolu.
import java.io.*;
public class Trieda {
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new FileReader("vstup.txt"));
int c;
while ((c = in.read()) != -1) {
System.out.print((char) c);
}
in.close();
}
}
Ďalšie užitočné štandardné triedy
- Trieda Math obsahuje množštvo statických metód realizujúcich matematické funkcie a operácie, napríklad:
public class Trieda {
public static void main(String[] args) {
System.out.println(Math.pow(2, 0.5));
System.out.println(Math.cos(Math.PI));
System.out.println(Math.log(Math.E));
System.out.println(Math.max(1, 2));
System.out.println(Math.abs(-1));
System.out.println(Math.toDegrees(Math.acos(Math.sqrt(3) / 2)));
// ..
}
}
- Trieda Random reprezentuje generátor náhodných čísel. Je definovaná v balíku java.util. Napríklad nasledujúci program simuluje 10 hodov vyváženou hracou kockou.
import java.util.*;
public class Trieda {
public static void main(String[] args) {
Random random = new Random();
for (int i = 1; i <= 10; i++) {
System.out.println(random.nextInt(6) + 1); // vygeneruj nahodne prirodzene cislo MENSIE ako 6 a zvys ho o 1
}
}
}
- Trieda Arrays z balíka java.util obsahuje statické metódy na prácu s poľami; napr. equals na porovnávanie polí (a nie iba referencií), copyOf na kopírovanie polí (a nie iba referencií), sort na utriedenie poľa, binarySearch na binárne vyhľadávanie, atď.