Sisälls 7. Oliot ja viitteet Olio Java-kielessä. Olion luominen, elinikä ja tuhoutuminen.. Viitteiden vertailu. Varautuminen null-arvoon. Viite metodin paluuarvona.. Muuttumattomat ja muuttuvat merkkijonot. 7.1 7.2 Olio Java-kielessä Java-kielessä olio määritellään joko luokan edustajaksi tai taulukoksi. Olio on joukko keskusmuistissa olevia tietoja. Oliota käsitellään siihen epäsuorasti liittvän viitetppisen muuttujan eli viitteen (reference) avulla. Viite määrittää olion identiteetin. Viite toteutetaan teknisesti hden tai useamman suojatun osoittimen (pointer) avulla. Javan viitteet eri asia kuin osoittimet ja C++:n viitteet. Olion luominen Tunnuksen esittel varaa muistia viitteelle (viitetppiselle muuttujalle), mutta ei oliolle. Java alustaa automaattisesti viitteen thjäksi (null-arvo) tai ilmoittaa, että viite on alustettava. Oliolle varataan muistia new-operaatiolla, joka palauttaa viitteen. Viitetppinen muuttuja ja olio liittvät toisiinsa, kun newoperaation paluuarvona saatava viite sijoitetaan muuttujan arvoksi. 7.3 7.4
Olion luominen Kissa rontti = null; rontti rontti = new Kissa(); rontti null Sijoituksen seurauksena rontti-viite viittaa samaan olioon kuin paluuarvona saatu viite. Olion viitteen esittel ja alustaminen thjäksi. Lauseke new Kissa(); luo olion, alustaa sen attriuutit rakentajassa ja palauttaa paluuarvona olioon liittvän tunnuksettoman viitteen. 7.5 Olion elinikä Olio on olemassa ( elää ) niin kauan kun siihen liitt ksi tai useampia viitteitä. Viitetppiset muuttujat elävät alkeistppisten muuttujien tapaan vain esittellohkossaan: Esimerkiksi metodissa esitelt viite tuhoutuu metodista poistuttaessa, koska sen tunnus on paikallinen. Olion elinikä voi kuitenkin poiketa olioon muistinvarauksen htedessä asetetun viitteen eliniästä, koska olioon voidaan liittää mös muissakin lohkoissa kuin esittellohkossa eläviä viitteitä. 7.6 Olion tuhoutuminen Olio tuhoutuu, kun siihen ei liit viitteitä. Viitteettömien olioiden poistaminen hoidetaan automaattisesti Javan virtuaalikoneen toimesta. Toimenpidettä kutsutaan roskankeruuksi (garage collection). Virtuaalikone kerää roskia automaattisesti, kun tarvitaan lisää muistitilaa. Java-ohjelmoijan ei siis tarvitse huolehtia olioiden poistamisesta ohjelmallisesti. Olion tuhoutuminen Javan virtuaalikonetta voi mös käskeä tuhoamaan tarpeettomat oliot Sstem-luokan gc-metodilla. Monissa kielissä varattu muisti vapautetaan ohjelmoijan toimesta eritisellä operaatiolla. Operaattorin kättö on tehokkaampaa kuin automaattinen muistin vapautus, mutta altistaa mös muistivuodoille (memor leak). 7.7 7.8
Viitetppiseen muuttujaan voidaan sijoittaa toinen viitetppinen muuttuja lauseella: = ; missä muuttujien ja tppi (luokka) on sopiva. Sijoitus irrottaa viitteen siihen mahdollisesti liittvästä oliosta ja kiinnittää viitteen :n olioon. Sijoituksen jälkeen muuttuja viittaa muuttujan viittaamaan olioon. Sijoitus ei kopioi :n viittaaman olion tietoja :n viittaaman olion tietojen päälle! Kopiointiin tähän tarvitaan oma metodi. Esimerkki. Osoittakoon viite olioon a ja viite olioon, kun viitteiden tppi oletetaan samaksi. Alkutilanne Sijoitus = ; Lopputilanne a a A 7.9 7.10 Sijoituksen kautta hteen olioon voi viitata useampia tunnuksia. Olion identiteetti säil, vaikka olioon olisi useampia viitteitä, koska viitteet liittvät samaan olioon. Sijoituksen sivuvaikutuksena olioon ei välttämättä enää ole viitteitä, jolloin olio jää roskankerääjän saaliiksi. Olion viitteet voidaan mös poistaa asettamalla ne thjiksi eli null-arvoisiksi. pulic class ViiteTesti { // Esitellään viitteet ja varataan muistia. String = new String("a"); String = new String(""); // Kiinnitetään viite viitteen osoittamaan olioon. = ; Sstem.out.println( + " " + ); // // Irrotetaan viitteet -oliosta. = = null; Sstem.out.println( + " " + ); // null null 7.11 7.12
Viitteiden vertailu Taulukoiden käsittelssä on huomattava, että sijoitus ei riitä taulukon alkioiden kopiointiin. Taulukon sisältö on kopioitava toiseen taulukkoon joko silmukoilla tai Arrasluokan metodeilla. Viitetppiset alkiot voi olla tarpeen sväkopioida. // Esitellään viite, luodaan olio // ja liitetään viite olioon. int[] luvut1 = { 1, 1 ; int[] luvut2 = { 2, 3, 4 ; // Sijoitetaan viite. luvut1 = luvut2; // Molemmat viitteet liittvät // jälkimmäiseen olioon. luvut1[0] = 6; Sstem.out.println(luvut1[0]); // 6 Sstem.out.println(luvut2[0]); // 6 Viitetppisiä muuttujia voidaan mös vertailla htä- ja erisuuruusoperaattoreilla (== ja!=). Vertailu kohdistuu viitteisiin eikä viitattuihin olioihin. Olioiden sisällön vertailuun pitää kirjoittaa erillinen metodi. Equals- ja compareto. // Esitellään viitteet ja varataan muistia. String eka1 = new String("eka"); String eka2 = new String("eka"); // Vertaillaan viitteitä. Tulostuu false, // koska viitteet liittvät eri olioihin. Sstem.out.println(eka1 == eka2); // Vertaillaan olioita. Tulostuu true, // koska olioiden tiedot ovat samat. Sstem.out.println(eka1.equals(eka2)); 7.13 7.14 Null-arvoisiin viitteisiin varautuminen Metodin kutsu null-arvoisen thjän viitteen kautta aiheuttaa ajonaikaisen virheen (poikkeuksen), joka psättää ohjelman suorituksen. Metodissa voidaan välttää virhe if-lauseella: if (parametrinasaatuviite!= null) { // Viite liitt olioon, sitä voidaan käsitellä. Vaihtoehtoisesti voidaan kättää tr-catch-lausetta. Thjää viitettä ei huomioida, jos voidaan olettaa, että thjän viitteen aiheuttama poikkeus käsitellään muualla. Viite metodin paluuarvona Metodin tunnukset ovat paikallisia (lokaaleja) - ne ovat olemassa vain metodin suorituksen ajan. Jos metodissa luotu olio halutaan säilttää, voidaan sen viite palauttaa metodin paluuarvona ja paluuarvo sijoittaa muuttujaan kutsuvassa metodissa. Olio ei tuhoudu, vaikka paikallinen viite tuhoutuu, koska olioon asetetaan toisen lohkon viite. Näin toimien paikallisesti esitelt olio elää alkuperäistä viitettään vanhemmaksi. 7.15 7.16
Viite metodin paluuarvona pulic class ViitePaluuarvona { /* Palautetaan viite luotuun merkkijono-olioon. pulic static String palauta() { String s = new String("ac"); return s; // Olio säil metodista poistuttaessa, t // koska olioon liitetään uusi viite t. String t = palauta(); Sstem.out.println(t); // ac t s "ac" s "ac" "ac" Viitetppisiä muuttujia voidaan antaa metodeille parametriksi aivan kuten alkeistppisiä muuttujia. Esimerkiksi. int kertoma(int luku) {... void nimi(string nimi) {... doule keskiarvo(doule[] luvut) {... Java sijoittaa sekä alkeis- että viitetppisen parametrin arvoksi alkuperäisen arvon kopion (pass--value). Parametrin arvoon teht muutos katoaa, kun metodista poistutaan. Parametriin liittvän olion tilaan teht muutos jää voimaan! Olio ei kopioidu, jolloin viitetppinen parametri viittaa samaan olioon kuin metodikutsussa annettu viite. 7.17 7.18 pulic class ViiteParametrina { /* Olion tietoja muuttava metodi. pulic static void leikita(kissa k) { if (k!= null) k.hanta("levoton"); Kissa = new Kissa(); // musta, kippura // Muutetaan olion tietoja. leikita(); Sstem.out.println(.vari()); // musta Sstem.out.println(.hanta()); // levoton Olio luotu k Metodiin tultaessa Metodista poistuttaessa k Lopuksi "levoton" "levoton" 7.19 pulic class MerkkijonoParametrina { /* Olion tiedot muuttuvat. pulic static void muuta(stringbuffer s) { s.append("d"); /* Olion tiedot eivät muutu. pulic static void muuta(string s) { s = s + "d"; StringBuffer s = new StringBuffer("ac"); String s = new String("ac"); muuta(s); Sstem.out.println(s); // acd muuta(s); Sstem.out.println(s); // ac String-luokka on toteutettu siten, että String-tppisten muuttujien tietoihin ei voida tehdä muutoksia. Lisäsoperaattori muodostaa operadeistaan uuden olion. Muutetun merkkijonon palauttaminen onnistuu paluuarvona tai StringBufferja StringBuilder-luokkia kättäen. 7.20