Skip to content

Cvičenie 4: Balíky a triedy, polia, varargs

Na dnešnom cvičení sa bližšie povenujeme poliam, ktoré predstavujú základný spôsob ako v Jave uchovávať dokopy viac hodnôt. Pred tým si však vyskúšame vytvoriť projekt s viacerými triedami.

Projekt s viacerými triedami v balíkoch

V IntelliJ IDEA si v našom projekte vytvoríme balíky a 4 triedy podľa nasledovného príkladu

sk/spse/tvary/Kruh.java
package sk.spse.tvary;

public class Kruh {
    public static double obvod(double r) {
        return 2 * Math.PI * r;
    }
    public static double obsah(double r) {
        return Math.PI * r * r;
    }
}
sk/spse/tvary/Obdlznik.java
package sk.spse.tvary;

public class Obdlznik {
    public static int obvod(int a, int b) {
        return (2 * a) + (2 * b);
    }
    public static int obsah(int a, int b) {
        return a * b;
    }
}
sk/spse/tvary/Stvorec.java
package sk.spse.tvary;

public class Stvorec {
    public static int obvod(int a) {
        // Ak je trieda v tom istom balíku, nemusím písať jeho názov
        return Obdlznik.obvod(a, a);
    }
    public static int obsah(int a) {
        return Obdlznik.obsah(a, a);
    }
}
sk/spse/Main.java
package sk.spse;

import sk.spse.tvary.Kruh;

public class Main {
    public static void main(String[] args) {
        int a = 10;
        // Používam triedu Kruh, ktorú som si importoval
        System.out.printf("Obvod kružnice o veľkosti %d je %.2f\n",
                          a, Kruh.obvod(a));
        // Ak použijem celé meno triedy, nemusím robiť import
        System.out.printf("Obvod štvorca  o veľkosti %d je %d\n",
                          a, sk.spse.tvary.Stvorec.obvod(a));
    }
}

Funkčnosť projektu si vyskúšame spustením triedy sk.spse.Main. Všimnite si použitie balíkov a importu. Import v Jave je iný ako import v Pythone. V Jave je import iba na uľahčenie práce programátorom, aby nemuseli stále písať celý názov triedy. Inú funkciu import nemá a samotný import nám nič nevykoná ani nespustí. Používať iné triedy môžeme bez akéhokoľvek importu.

Na rozdiel od Pythonu nám Java neumožňuje premenovať importované triedy alebo metódy resp. vytvoriť ich alias. Ak je v Jave problém s duplicitnými menami, jednoducho triedu neimportujeme ale použijeme jej celé meno aj s balíkom.

Pole - Array

Ak chceme v Jave uchovávať viac hodnôt v jednej premennej, môžeme použiť pole.

Pole (anglicky array) je zoznam hodnôt jedného typu s fixnou (nemennou dĺžkou). Jednotlivé hodnoty sú očíslované a vieme k nim pristupovať pomocou indexu. Ide o neprimitívny dátovy typ, teda konkrétne pole je objekt. Na prácu s poliami máme v Jave špeciálnu syntax.

Vlastnosti poľa:

  • fixná veľkosť, nemenná dĺžka
  • samotné prvky poľa ale meniť viem
  • homogénne prvky - prvky poľa musia byť rovnakého typu
  • pole je objekt, nie je primitívny typ. Prvky poľa môžu byť primitívne.
  • prvky majú poradie, majú index, ktorý začína číslom 0

Pri vytváraní poľa musím definovať jeho veľkosť.

int[] pole = new int[5]; // Pole čísel o veľkosti 5, prvky sa inicializujú na 0
pole[0] = 10; // mením prvý prvok poľa
pole[4] = 30; // mením posledný prvok poľa

Pole viem vytvoriť aj priamo z konkrétnych hodnôt

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

Ak mám pole primitívnych hodnôt, tak sú uložené priamo v objekte poľa. Ak mám však pole neprimitívnych hodnôt - objektov, tak do objektu poľa sa uložia referencie na objekty. V nasledujúcom príklade si pomocou diagramu znázorníme, ako to vyzerá v pamäti počítača:

String[] slova = new String[3];
slova[0] = "Hello";
slova[1] = "World";
slova[2] = "!";

pole Stringov v pamäti počítača

premenná, pole a jednotlivé prvky poľa sú v pamäti počítača uložené samostatne

Používanie poľa

Dĺžku poľa viem zistiť pomocou atribútu length

int[] pole = new int[10];
System.out.println(pole.length); // 10

K jednotlivým prvkom poľa pristupujem pomocou hranatých zátvoriek. Takto viem hodnoty prvkov nie len čítať ale ich aj meniť.

int[] cisla = {10, 20, 30};
System.out.println(cisla[1]);  // 20
System.out.println(cisla[3]);  // CHYBA!

Ak sa pokúsim pristúpiť ku neexistujúcim prvkom, Java vyhodí výnimku ArrayIndexOutOfBoundsException

Prvky poľa viem prechádzať v cykle. Môžem použiť klasický for alebo novší for-each

for (int i = 0; i < cisla.length; i++) {
    System.out.println(cisla[i]);
}
for (int n : cisla) {
    System.out.println(n);
}

Prvky poľa môžu byť akéhokoľvek typu, primitívneho aj neprimitívneho.

String[] slova = {"Hello", "world", "!"};
for (String slovo: slova) {
    System.out.print(slovo + " ");
}

Užitočné utility

Niektoré veci sa pri poliach robia špeciálne. Polia sa nedajú porovnávať cez klasický equals() a nedajú sa vypisovať do reťazca cez klasický toString(). Tak isto pri kopírovaní poľa pozor. Keďže pole je objekt, obyčajné = skopíruje referenciu (odkaz), ale objekt bude ten istý.

Pre takéto situácie máme v Jave pomocnú triedu java.util.Arrays, v ktorej máme užitočné utility. Všetky metódy sú statické.

  • Arrays.fill(pole, hodnota) - vyplní pole hodnotou
  • Arrays.equals(pole1, pole2) - porovná polia podľa ich hodnôt
  • Arrays.toString(pole) - vypíše obsah poľa do reťazca
  • Arrays.sort(pole) - zoradí pole od najmenšieho po najväčší prvok. Mení existujúce pole!
import java.util.Arrays;

int[] arr = {3, 1, 2};
Arrays.sort(arr); // sort
System.out.println(Arrays.toString(arr)); // [1, 2, 3]

Dokumentácia

Všetky utilitky nájdete v oficiálnej dokumentácii triedy java.util.Arrays

Kopírovanie poľa

Na vytvorenie kópie poľa máme v Jave viacero metód.

  • pole.clone() - vytvorí nové pole a skopíruje hodnoty prvkov
  • Arrays.copyOf(pole, novaDlzka) - nové pole môže mať inú veľkosť
  • Arrays.copyOfRange(pole, od, do) - môžeme skopírovať iba časť poľa
  • System.arraycopy(src, srcPos, dest, destPos, length) - skopíruje hodnoty prvkov medzi dvoma existujúcimi poľami

Shallow copy

Tieto metódy kopírovania poľa sú tzv. povrchné, anglicky shallow. To znamená, že pole sa síce vytvorí nové a prvky sa do neho skopírujú, ale samotné prvky sú iba obyčajná kópia ako pri =. To znamená, že keď máme pole objektov, tak sa nám skopírujú referencie. Vo výsledku budem mať 2 rozdielne polia, ale prvky v nich budú ukazovať na rovnaké objekty. To môže byť problém, ak sú objekty meniteľné.

Na príklade s poľom slovo si ukážeme, ako sa pri kopírovaní poľa jeho jednotlivé prvky zdieľajú.

String[] slova = new String[3];
slova[0] = "Hello";
slova[1] = "World";
slova[2] = "!";
String[] kopia = slova.clone();

Shallow kópia poľa

Pole neprimitívnych hodnôt vytvára pri kopírovaní tzv. Shallow copy

Viacrozmerné pole

V Jave viem vytvoriť aj viacrozmerné polia. Sú komplikovanejšie na pochopenie, spomenieme ich preto iba veľmi stručne.

int[][] matica = new int[2][3]; 
matica[0][0] = 1;

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

Učím sa s pomocou umelej inteligencie

Som študent strednej školy, učím sa Javu. Vysvetli mi základy viacrozmerných polí v Jave, uveď príklady a na čo si dávať pri práci s viacrozmernými poľami pozor.

Premenlivý počet argumentov

V Jave vieme definovať metódy, ktoré majú premenlivý počet argumentov. Takéto argumenty sa anglicky volajú varargs (variable-length arguments) a pri volaní takejto metódy môžeme uviesť akýkoľvek počet premenlivých argumentov (aj 0). Samozrejme, ak má metóda aj klasické argumenty, tie musíme uviesť vždy.

void metoda(int a, int b, String... slova) {
    System.out.println(a + b);
    for (String slovo : slova) {
        System.out.println(slovo);
    }
}

metoda(1, 2);
metoda(1, 2, "Hello", "world");

V metóde môžeme mať iba jeden vararg argument. Premenlivý argument musíme uviesť v zozname parametrov metódy vždy ako posledný. V tele samotnej metódy máme vararg argument uložený vo forme poľa.

Keďže vararg argument je v skutočnosti pole hodnôt, vieme pri volaní takejto metódy poslať na vstup pole, a Java ho automaticky použije ako pole argumentov.

Učím sa s pomocou umelej inteligencie

Som študent strednej školy, učím sa Javu. Vysvetli mi ako sa v Jave pracuje s varargs.

Úlohy na precvičenie

Úloha 4.1: Priemer

V balíku sk.spse.util vytvorte triedu Math. V tejto triede vytvorte statickú metódu priemer, ktorá má na vstupe pole čísel a vráti ich aritmetický priemer. Zavolaj ju z metódy sk.spse.Main.main()

Úloha 4.2: Najdlhší reťazec

V balíku sk.spse.util vytvorte triedu Pole. V nej vytvorte statickú metódu najdlhsiRetazec, ktorá má na vstupe pole reťazcov a vráti najdlhší z nich.

Úloha 4.3: Medián

V triede sk.spse.util.Math vytvorte statickú metódu median, ktorá má premenlivý počet číselných argumentov, a vráti ich mediánovú hodnotu.

Medián je číslo, ktoré leží presne v strede zoradeného zoznamu čísel. Ak je dĺžka zoznamu párna, mediánom je priemer dvoch stredných čísel.

Úloha 4.4: Medián z klávesnice

Do predchádzajúcej úlohy pridajte do metódy sk.spse.Main.main() načítavanie čísel z klávesnice. Ako budete riešiť počet čísel?

Úloha 4.5: Párne a nepárne

V triede sk.spse.util.Pole vytvorte statickú metódu parneNeparne, ktorá má na vstupe pole čísel, a vráti pole o veľkosti 2, kde prvý prvok poľa bude počet párnych čísel a druhý prvok bude počet nepárnych čísel.

Úloha 4.6: Spájanie polí

V triede sk.spse.util.Pole vytvorte statickú metódu spoj, ktorá má na vstupe dve polia a vráti pole, ktoré vznikne spojením týchto dvoch polí.

Úloha 4.7: Najdlhšia sekvencia

V triede sk.spse.util.Pole vytvorte statickú metódu najdlhsiaSekvencia, ktorá má na vstupe pole a vráti číslo, ktorého hodnota je dĺžka najdlhšej sekvencie rovnakých prvkov vo vstupnom poli.

Zhrnutie cvičenia

  • Import tried v Jave nie je povinný, stačí ak použijeme celý názov triedy
    • import static balik.Trieda.* importuje všetky statické metódy danej triedy
    • V Jave sa nedá vytvoriť alias, ak je problém s duplicitou, používame celý názov triedy
  • Pole - Array, je zoznam hodnôt jedného typu
    • fixná veľkosť
    • hodnoty prvkov meniť viem
    • všetky prvky musia mať rovnaký typ
    • pole je objekt
    • prvky majú poradie, index
  • Ak prvky poľa sú primitívne, ukladajú sa priamo do poľa. Ak sú objekty, v poli sú iba referencie na nich.
  • Pri vytváraní poľa musím uviesť jeho veľkosť, napr. int[] pole = new int[5];
    • Pole viem inicializovať aj konkrétnymi hodnotami, napr. int[] pole = {1, 2, 3, 4, 5};
    • K prvkom poľa pristupujem cez index, napr. pole[0] = 3;
    • Dĺžku poľa zistím cez atribúť length, napr. pole.length
    • Pole viem iterovať v cykle for-each
  • Pri poliach si treba dávať pozor hlavne na tieto veci
    • Klasický equals() nefunguje správne
    • Klasický toString() nevypíše hodnoty prvkov
    • Keďže pole je objekt, operátor = skopíruje referenciu (odkaz), nie objekt
  • Užitočné utility nájdem v triede java.util.Arrays
    • Arrays.fill(pole, hodnota) - vyplní pole hodnotou
    • Arrays.equals(pole1, pole2) - porovná polia podľa ich hodnôt
    • Arrays.toString(pole) - vypíše obsah poľa do reťazca
    • Arrays.sort(pole) - zoradí pole od najmenšieho po najväčší prvok. Mení existujúce pole!
  • V Jave mám metódy na povrchné (shallow) kopírovanie poľa
    • pole.clone() - vytvorí nové pole a skopíruje hodnoty prvkov
    • Arrays.copyOf(pole, novaDlzka) - nové pole môže mať inú veľkosť
    • Arrays.copyOfRange(pole, od, do) - môžeme skopírovať iba časť poľa
    • System.arraycopy(src, srcPos, dest, destPos, length) - skopíruje hodnoty prvkov medzi dvoma existujúcimi poľami
  • Viem vytvárať aj viacrozmerné polia
    • int[][] matica = new int[2][3];
    • matica[0][0] = 1;
    • int[][] matica2 = {{1, 2, 3}, {4, 5, 6}};
  • Metódy v Jave môžu mať premenlivý počet argumentov - varargs
    • Príklad definície void metoda(int a, int b, String... slova)
    • V tele metódy sú argumenty uložené do poľa
    • Do vararg metódy viem vložiť aj pole, Java ho použije ako pole argumentov

Poznámky do zošita

V zošite je potrebné mať napísané aspoň tieto poznámky:

POLIA

Pole - Array, je zoznam hodnôt jedného typu
Vlastnosti:
- fixná nemenná veľkosť
- hodnoty prvkov meniť viem
- všetky prvky musia mať rovnaký typ
- pole je objekt
- prvky majú poradie, index

Pri vytváraní poľa musím uviesť jeho veľkosť, napr. int[] pole = new int[5];
Inicializácia konkrétnymi hodnotami, int[] pole = {1, 2, 3, 4, 5};

K prvkom poľa pristupujem cez index, pole[0] = 3;
Dĺžku poľa zistím cez pole.length
Pole viem iterovať vo for-each
Pole kopírujem cez pole.clone()

Utility v triede java.util.Arrays
- Arrays.equals
- Arrays.toString
- Arrays.sort
- Arrays.copyOf

Viacrozmerné polia int[][] matica new int[2][3];

VARARGS

Varargs - premenlivý počet argumentov pri volaní metódy
Príklad definície void metoda(int a, int b, String... slova)
V tele metódy sú tieto argumenty uložené do poľa
Ak ako vararg argument uvediem pole, použije sa ako pole argumentov

Skúšanie a kontrola vedomostí

Okruhy otázok na test:

  • Čo je pole
  • Vlastnosti poľa
  • Ako zistím dĺžku poľa
  • Ako kopírujem pole
  • Ako porovnám 2 polia
  • Varargs - čo to je
  • Ako sa pristupuje k varargs vnútri metódy?