Tässä luvussa tutustutaan String-luokkaan ja sen tarjoamiin metodeihin sekä merkkityyppiin char. Ne ovat sinänsäkin hyödyllisiä, mutta ne myös havainnollistavat sitä, miten Java-kielen valmiit välineet on toteutettu luokkina, ja miten ohjelmoija voi ajatella (ja joutuu ajattelemaan!) olioita luokkien ilmentyminä ja operaatioita luokkien metodeina.
Kun kirjoitetaan:
String jono;määritellään muuttuja jono, jonka arvoksi voidaan sijoittaa String-tyyppisiä olioita. (Metodin muuttujana jonolla ei ole mitään alkuarvoa, luokan muuttujana alkuarvo on null, erityinen viite "ei-mihinkään-olioon".)
Ilmaus:
"kissa"puolestaan tarkoittaa, että luodaan eli konstruoidaan yksi String-tyyppinen olio. Yleensähän oliot luodaan operaatiolla new. Merkintä "kissa" onkin vain lyhennysmerkintä ilmaukselle:
new String("kissa")Luokassa String on siis mm. konstruktori, jonka parametri on String-tyyppiä!
Määrittely:
String jono = "topi"+"katti";tarkoittaa yksityiskohtaisesti, että määritellään String-tyyppinen muuttuja jono. Sen alkuarvoksi asetetaan String-olio, joka saadaan, kun ensin luodaan String-oliot "topi" ja "katti" ja sitten nämä katenoimalla luodaan String-olio "topikatti".
Sama voitaisiin sanoa:
String jono = new String("topi"+"katti");tai
String jono = new String(new String("topi")+new String("katti"));Luokka String tarjoaa joukon operaatioita, metodeja, String-arvojen käsittelyyn.
Omien tyyppien tapaan myös String voidaan nähdä abstraktina tietotyyppinä: Luokan ilmentymän käyttäjä ei tiedä, missä merkkijono sijaitsee ja miten se esitetään muistissa, mutta hän tietää joukon välineitä, joilla arvoja voi käsitellä.
Merkkiarvot (tyyppi char) ovat positiivisia kahden tavun mittaisia kokonaislukuja(!). Merkkivakiot esitetään yksinkertaisissa lainausmerkeissä : 'A', 'k', ' ', ', '@', ...
[Merkkivakioden ns. heksadesimaaliesitys on muotoa \uXXXX, missä X on heksadesimaaliluku 0-f.]
Merkkiarvon sijoittaminen int-muuttujalle on luvallista, päinvastainen on kiellettyä:
int i = 7; char c = '#'; ... i = c; // SALLITTU, i saa arvokseen merkkikoodin! c = i; // KIELLETTY! (tietoa voisi kadota!)[Mutta int-arvoja voidaan muuttaa ns. eksplisiittisellä tyyppimuunnoksella eli "castilla" char-arvoiksi. Jos tietoa tällöin katoaa, asia on ohjelmoijan vastuulla! Esimerkiksi:
for (int i=40; i<110; ++i) System.out.print( (char)i );tulostaa rivin:
()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklm]
Merkkiarvo koodataan ns. Unicode-koodilla, joka sisältää maailman useimpien kirjoitusjärjestemien kirjain- ja numeromerkit: mm. kreikka, kopti, venäjä, heprea, arabia, devanagari, bengali, gurmukhi, gujarati, tamili, telegu, tai, lao, hiragana, katakana, ...
Luokassa Lue (Lue.java) on kurssilla aiemmin esittelemätön operaatio:
public class MerkkiLukuTesti { public static void main(String[] args) { char vastaus; do { System.out.println("Tehdään juttu kerran.\n"); System.out.println("Haluatko jatkaa (k tai e)?"); vastaus = Lue.merkki(); } while (vastaus != 'e'); } }
[Täydellinen luettelo String-luokan konstruktoreista ja metodeista on teoksessa Gosling, Steele: The Java[tm] Language Specification, Addison-Wesley, 1996. Tämä linkki on vain hyvin kiinnostuneille, linkin päässä on erittäin suuri tiedosto, joka sisältää kaikkien java.lang-pakkauksen luokkien kuvailun!! Kuvailua ei ymmärrä juuri lainkaan kurssilla tähän mennessä opitun perusteella!]
String jono = "kissa"; int pituus = jono.length(); // pituus = 5;
Esimerkkiohjelma (JononPituus.java) tulostaa tulostaa syöttömerkkijonon pituuden:
public class JononPituus { public static void main(String[] args){ System.out.println("Anna merkkijono!"); String jono = Lue.rivi(); int pituus = jono.length(); System.out.println("Sen pituus on "+pituus); } }
Esimerkkiohjelma suoritetaan uudelleen, jos käyttäjä vastaa jatkamiskysymykseen "kyllä":
public class Jatkoa1 { public static void main(String[] args){ String vastaus; do { System.out.println("\nNyt ohjelma tekee jotakin hyödyllistä...\n"); System.out.println("Haluatko jatkaa? (kyllä tai ei)"); vastaus = Lue.rivi(); } while (vastaus.equals("kyllä")); } }
public class Jatkoa2 { public static void main(String[] args){ String vastaus; do { System.out.println("\nNyt ohjelma tekee jotakin hyödyllistä...\n"); System.out.println("Haluatko jatkaa? (kyllä tai ei)"); vastaus = Lue.rivi(); } while (vastaus.equalsIgnoreCase("kyllä")); } }
Esimerkkiohjelma kahden syöttömerkkijonon vertaamiseen (JonoVertailu.java):
public class JonoVertailu { public static void main(String[] args){ String vastaus; do { System.out.print("Anna ensimmäinen merkkijono: "); String eka = Lue.rivi(); System.out.print("Anna toinen merkkijono : "); String toka = Lue.rivi(); int jarjKoodi = eka.compareTo(toka); if (jarjKoodi < 0) System.out.println("Ensimmäinen on ennen toista."); else if (jarjKoodi == 0) System.out.println("Jonot ovat samat."); else // jarjKoodi > 0 System.out.println("Toinen on ennen ensimmäistä."); System.out.println("\nHaluatko jatkaa? (pelkkä enter jatkaa!)"); vastaus = Lue.rivi(); } while (vastaus.equals("")); } }
String jono = "kissa"; for (int i=0; i<jono.length(); ++i) System.out.println(jono.charAt(i));tulostavat:
k i s s a
String jono = "kissa"; System.out.println(jono.indexOf('s')); System.out.println(jono.indexOf('t'));Tulostus:
2 -1
Huom: indexOf(String str)-versio on aika voimakas ja käyttökelpoinen! Esim:
... String syöttorivi = Lue.rivi(); if (syöttorivi.indexOf("kissa") != -1) System.out.println("Jossakin kohdassa syöttörivillä on kissa!"); else System.out.println("Rivi on kissaton."); ...
System.out.println("AÅÄÖZ".toLowerCase()); System.out.println("aåäöz".toUpperCase());tulostavat
aåäöz AÅÄÖZHuom: Olion, johon operaatio kohdistetaan, ei välttämättä tarvitse olla muuttujan arvona! Seuraavat kolme tulostusoperaatiota kirjoittavat kaikki saman arvon "aåäöz":
String muuttuja = "AÅÄÖZ"; System.out.println(muuttuja.toLowerCase()); System.out.println("AÅÄÖZ".toLowerCase()); System.out.println(new String("AÅÄÖZ").toLowerCase());
System.out.println(">" + " Olipa kerran ... ".trim() + "<");tulostaa:
>Olipa kerran ...<
Huom: Kerran luotua String-oliota ei voi muuttaa. Luokassa String siis ei ole operaatioita kerran luodun arvon muuttamiseen. Edelläluetellut String-arvoiset metodit luovat aina uuden olion! Merkkijonojen muokkaamiseen käytetään muita välineitä. Niistä myöhemmin.
Kun suoritetaan vaikkapa lauseet
String jono = "kissa"; // (1)
jono = jono + " kävelee"; // (2)
luodaan ensin olio "kissa"(1). Sitten luodaan olio " kävelee" ja
lopuksi katenoiden olio "kissa kävelee" (2). Koska muuttujan
jono vanha arvo korvataan uudella, oliosta "kissa" tulee
roska eli siihen ei ole enää viitettä. Samoin " kävelee"-oliolla
oli hyvin hetkellinen merkitys. Automaattinen roskienkerääjä
aikanaan vapauttaa "kissa"- ja " kävelee"-olioiden varaaman muistialueen
muuhun käyttöön.