Typ char a znakové reťazce

<< Referencie a konštruktory | Obsah | Myšacie udalosti v JPAZe >>

Primitívny typ char (znak)

Posledný primitívny (jednoduchý) typ, ktorý sme nespomínali, je typ char. Typ char predstavuje jeden znak. Znakové literály (konkrétne znakové hodnoty) píšeme tak, že znak uzavrieme do apostrofov:

char znak = 'a';

Java, narozdiel od iných programovacích jazykov/platforiem (C, C++, Pascal, Delphi, PHP, ...), uchováva znaky v originálnej verzii kódovania UNICODE, t.j. v premennej typu char môže byť jeden z 65536 znakov tohto kódovania. Vďaka tomu v Jave nemáme problém s národnými znakmi (slovenčina, ruština, švédčina, ázijské znaky, ...).

Niektoré znaky majú svoj historický význam, iné znaky naopak musíme zapisovať špeciálnym spôsobom. Java má niekoľko špeciálnych znakov, z ktorých najdôležitejšie sú:

  • znak literál medzery: ' '
  • znak literál tabelátora: '\t'
  • znak literál konca riadka: '\n'
  • znak literál lomítka: '\\'
  • znak literál úvodzovky: '\"'
  • znak literál apostrofu: '\''

(Pre pokročilých.) Za kódovaniami sa skrýva dlhá historia. Pod kódovaním si môžeme predstaviť veľkú tabuľku, ktorá hovorí, aké písmeno reprezentuje i-ty znak kódovania:

  • ASCII
    • 128 znakov (číselné kódy 0.127), anglická abeceda + špeciálne znaky
  • rôzne kódovania s tabuľkou majúcou 256 znakov (prvých 128 je totožných s ASCII):
  • UNICODE
    • originálne UNICODE malo 65536 znakov, posledné rozšírenia môžu mať ešte viac znakov

Znaky môžeme tak, ako všetky jednoduché hodnoty navzájom porovnávať. V prípade znakov je toto porovnanie porovnaním toho, či sa daný znak nachádza v UNICODE tabuľke skôr. Keďže ASCII je na začiatku každého kódovania, veľmi užitočná vlastnosť je to, že cifry, malé písmená a aj veľké písmená anglickej abecedy idú pekne za sebou.

Častý je test na overenie toho, či v znakovej premennej je znak cifry:

if ((z >= '0') && (z <= '9')) {
        //...
}

Podobne môžeme testovať znaky anglickej abecedy ...

Reťazce

Premenné typu char nám dovoľujú uchovať jeden znak. S týmto si ale pri praktických problémoch veľmi nepomôžeme. Zvyčajne totiž potrebujeme uchovať postupnosť znakov. Na uchovanie postupnosti znakov slúžia objekty triedy String. Objekt triedy String uchovávajúci nejakú postupnosť znakov vytvoríme takto:

String s = new String("Ahoj Java");

Všimnime si, že ako parameter konštruktora uvádzame reťazcový literál (konkrétnu postupnosť znakov). Reťazcové literály vždy píšeme medzi úvodzovky. Ak v reťazcovom literály chceme použiť niektorý zo špeciálnych znakov, zapíšeme ho v takej forme, ako to je uvedené vyššie (apostrofy samozrejme vynecháme).

Keďže reťazce sú uložené v objektoch, platia pre ne všetky "objektové" pravidlá. Dôležité je si uvedomiť, že vo vyššie uvedenom príklade premenná s neobsahuje samotný reťazec, ale iba referencuje objekt triedy String, ktorý túto postupnosť znakov uchováva.

Tak ako všetky objekty, aj objekty triedy String majú zaujímavé metódy:

  • int length() - vráti dĺžku reťazca (počet znakov)
  • char charAt(int index) - vráti znak na zadanom indexe. Indexom nazývame pozíciu v reťazci. Jednotlivé znaky sú indexované od 0, t.j. prvý znak reťazca je na indexe 0, posledný znak na indexe length()-1.
  • String concat(String s) - vráti referenciu na novovytvorený objekt triedy String, ktorého obsah vznikol spojením (zreťazením) postupnosti znakov tohto objektu a znakov objektu triedy String referencovaného z parametra s.

Pri rôznych metódach pracujúcich so znakmi potrebujeme postupne spracovať jednotlivé znaky reťazca. Ukážme si to na príklade metódy, ktorá spočíta počet výskytov znaku 'a' v zadanom reťazci:

public int pocetVyskytov(String s) {
        int vysledok = 0;
        for (int i=0; i<s.length(); i++) {
                if (s.charAt(i) == 'a') {
                        vysledok++;    
                }
        }
        return vysledok;
}

Opäť pripomíname, že parameter s neobsahuje samotný reťazec, ale len referencuje objekt triedy String, ktorý máme "preskúmať".

Pri riešení úloh potrebujeme často zistiť, či postupnosti znakov v 2 (rôznych) objektoch triedy String sú rôzne, alebo rovnake. Na tento účel vieme použiť metódu equals.

String s = new String("Ahoj");
String r = new String("Ahoj");
if (s.equals(r)) {
       // príkazy, ktoré sa vykonajú ak je postuposť písmen rovnaká
}

Veľmi častou chybou pri práci s reťazcami býva, že rovnosť reťazcov sa testuje pomocou operácie rovnosti:

if (s == r) {

}

My však už vieme, že tento postup je zlý práve preto, že premenné s a r sú premenné referenčného typu. Vyššie uvedený test len testuje, či premenné r a s referencujú ten istý objekt a nie to, či obsah (postupnosť znakov) je rovnaký.

Trieda String patrí medzi triedy, ktoré majú v Jave špeciálnu podporu, t.j. sú dovolené isté skrátené konštrukcie:

  • namiesto:
String s = new String("Ahoj Java");
stačí napísať
String s = "Ahoj Java";
  • namiesto:
String s = r.concat(t);
stačí napísať
String s = r + t;

Operátor + funguje ako operátor zreťazenia, ak jeden z jeho operandov je reťazec (referencia na objekt triedy String). Dokonca, ak druhý z operandov nie je reťazec, Java sa túto hodnotu pokúsi prerobiť na reťazec, ktorý bude použitý pri "zreťazovaní".

Môžeme tak písať aj takéto príkazy:

int c = 10;
String s = "V premennej c je cislo: " + c;

Pozrime sa, ako by vyzerala metóda, ktorá vráti referenciu na novovytvorený reťazec (objekt triedy String), ktorý obsahuje znaky zadaného reťazca v opačnom poradí. Pri riešení postupne vyberáme znaky reťazce referencovaného parametrom s zľava doprava. V každom kroku vytvárame nový reťazec, ktorý vznikne zreťazením aktuálneho znaku a doposiaľ vytvorenej obrátenej postupnosti.

public String obratRetazec(String s) {
        if (s == null)
                return null;

        String vysledok = "";
        for (int i=0; i<s.length(); i++) {
                vysledok = s.charAt(i) + vysledok;
        }

        return vysledok;
}

Zoznam metód objektov triedy String možno nájsť na stránke: http://java.sun.com/javase/6/docs/api/java/lang/String.html#method_summary

StringBuilder

Predchádzajúce metódy "vytvárania" nových reťazcov majú jednu podstatnú nevýhodu: pri ich vykonávaní vznikne veľa "odpadu" vo forme dočasných reťazcov (objektov triedy String). Toto je prirodzené, keďže objekty triedy String majú tú vlastnosť, že ich obsah (uchovávanú postupnosť znakov) po vytvorení nejde už zmeniť. Aby sme sa vyhli zbytočnému vytváraniu dočasných reťazcov, môžeme pri budovaní obsahu objektov triedy String použiť objekty triedy StringBuilder.

Poslaním objektov triedy StringBuilder je podobne ako v prípade objektov triedy String uchovávať postupnosť znakov. Narozdiel od nich však objekty triedy StringBuilder dovoľujú meniť svoj obsah. Najčastejšie používané metódy objektov triedy StringBuilder sú:

  • append - pridá nejakú postupnosť znakov na koniec uchovávaného reťazca (pozor, metóda append mení obsah objektu, narozdiel od metódy concat objektov triedy String, ktorá vytvorí nový objekt triedy String)
  • toString - vráti referenciu na novovytvorený objekt triedy String, ktorého obsah bude rovnaký ako je aktuálny obsah objektu triedy StringBuilder.

Príklad:

public String obratRetazec(String s) {
        if (s == null)
                return null;

        StringBuilder vysledok = new StringBuilder();
        for (int i=s.length()-1; i>=0; i--) {
                vysledok.append(s.charAt(i));
        }

        return vysledok.toString();
}

<< Referencie a konštruktory | Obsah | Myšacie udalosti v JPAZe >>