Jos luokassa on määritelty metodi:
public static void main(String[] args) { ... }luokka voidaan suorittaa sovelluksena. Jokaisessa luokassa saa olla tuollainen 'pääohjelmametodi'. Sitä voidaan käyttää vaikkapa luokan testausvälineenä ohjelmistoa laadittaessa vaikka luokka ei olisikaan sovellukseksi tarkoitettu.
Sovellus käynnistetään käynnistämällä luokka, jossa on main-metodi. Tuo metodi sitten määrittelee mitä tahtoo ja käynnistelee mitä tarvitaan.
Metodeita käytetään luokissa monissa tehtävissä: niiden avulla toteutetaan "abstraktin koneen" toiminnot, niillä toteutetaan aksessorit, joilla piilossa pidetyn tietorakenteen tilaa voidaan kysellä ja muuttaa, konstruktorit yms. ovat muodoltaan metodeita, aliohjelmat toteutetaan metodeina, ...
public protected private abstract static final synchronized nativeUseimpiin näistä palataan tarkemmin myöhemmin. Toistaiseksi tyydytään seuraavaan:
public class Kissantulostus { public static void main(String[] args) { tulostakissa(); } private static void tulostakissa() { System.out.println("Kissa"); } }
public class Elukoita { // Eläinten luokka! private String nimi; public Elukoita(String n) { nimi = n; } public void tulostakissa() { System.out.println("Kissa: " + nimi); } } public class Kissantulostus { // Testipääohjelmaluokka public static void main(String[] args) { Elukoita ötökkä = new Elukoita("Misu"); ötökkä.tulostakissa(); // Olion käyttöä! } }
public class Elukoita { // Kirjastoluokka eläinten tulostamiseen public static void tulostakissa() { System.out.println("Kissa"); } public static void tulostahiiri() { System.out.println("Hiiri"); } } public class Kissantulostus { // Testipääohjelmaluokka public static void main(String[] args) { Elukoita.tulostakissa(); // Kirjastometodin käyttöä! } }
Esimerkki:
public static char alkuKirjain(int luku) { // palauttaa numeron alkukirjaimen switch (luku) { case 1: case 9: return 'Y'; case 2: case 3: case 6: case 8: case 10: return 'K'; case 4: case 0: return 'N'; case 5: return 'V'; case 7: return 'S'; default: // ei ollut 0...9 return '*'; } }Metodin palauttama arvo voi siis olla myös viittaustyyppiä - esimerkiksi String-olio, jokin oman luokan ilmentymä tai vaikkapa taulukko-olio.
Esimerkkejä:
public static String kahdenna(String jono) { return jono + jono; }Jos käytetään äskeistä luokkaa Elukoita, voidaan ohjelmoida:
public class Elukoita { private String nimi; public Elukoita(String n) { nimi = n; } public void tulostakissa() { System.out.println("Kissa: " + nimi); } } public class Paaohjelma { private static Elukoita luoMissuOlio() { return new Elukoita("Missu"); } public static void main(String[] args) { Elukoita ee = luoMissuOlio(); ee.tulostakissa(); } }Odotetusti ohjelma tulostaa:
Kissa: MissuEsimerkissä metodi luoMissuOlio luo erään Elukoita-olion ja palauttaa sen arvonaan.
Kaikki parametrit ovat arvoparametreja: muodollinen parametri on siis kuin metodin paikallinen muuttuja, jolle asetetaan alkuarvoksi kutsussa annettu todellinen parametri.
Viittaustyyppinen parametri on viitteen arvo.
Kun seuraava ohjelma suoritetaan (ParamKoe.java):
public class ParamKoe{ public static void main(String[] args) { int i = 7; String a = " kissaa "; Pikkuvarasto v = new Pikkuvarasto(5.0, "Mehu"); System.out.println(i + a + v); muutaKaikki(i, a, v); System.out.println(i + a + v); } private static void muutaKaikki(int m, String s, Pikkuvarasto r) { m = 9; s = " hiirtä "; r.vieVarastoon(100); System.out.println(m + s + r); } }tulostuu:
7 kissaa (Mehu: 5.0) 9 hiirtä (Mehu: 105.0) 7 kissaa (Mehu: 105.0)Ainoa pysyvä muutos tapahtui siis Pikkuvarasto-olion tilaan! Muodollinen parametri m sai alkuarvokseen luvun 7, muodollinen parametri s pantiin osoittamaan samaan "kissaan" kuin todellinen parametri a. Ja muodollinen parametri r pantiin osoittamaan samaan Pikkuvarasto-olioon kuin v osoittaa.
Kun m muutettiin luvuksi 9, ei se vaikuttanut mitenkään i:n arvoon. Kun s saa arvokseen viitteen "hiireen", ei a:lle tai "kissalle" käy kuinkaan. Mutta kun r:n viittaaman olion tilaa muutettiin, muuttui myös v:n viittaaman olion tila, koska kyseessä oli sama olio!
Esimerkki (KuormEsim.java):
public class KuormEsim { private static void rivi(String teksti) { System.out.println(teksti); } private static void rivi(String teksti, int sisennys) { for (int i=0; i<sisennys; ++i) System.out.print(' '); System.out.println(teksti); } private static void rivi(String teksti, char ymerkki) { System.out.print(ymerkki); System.out.print(teksti); System.out.println(ymerkki); } private static void rivi(int luku) { System.out.println(luku); } public static void main(String[] args){ // pääohjelma rivi("kissanpäivä"); rivi("kissanpäivä", 7); rivi("kissanpäivä", '#'); rivi(765); } }Sovellus tulostaa:
kissanpäivä kissanpäivä #kissanpäivä# 765
Laaditaan esimerkkinä rekursiivinen metodi Fibonaccin lukujen tulostamiseen (Fibo.java):
public class Fibo { private static long fibo(int i) { if (i<3) return 1; else return fibo(i-1)+fibo(i-2); } public static void main(String[] args) { for (int i=1; i<10 ; ++i) System.out.println(i+ ". fibo on " + fibo(i)); } }Odotetusti sovellus tulostaa:
1. fibo on 1 2. fibo on 1 3. fibo on 2 4. fibo on 3 5. fibo on 5 6. fibo on 8 7. fibo on 13 8. fibo on 21 9. fibo on 34Tämä on erittäin tehoton tapa laskea Fibonaccin lukuja! Aikavaativuus on eksponentiaalinen. On vaikea laatia vieläkin tehottomampia algoritmeja!
Mutta on myös tilanteita, joissa rekursio on hyvä ratkaisu, esimerkkeinä vaikkapa binäärihaku ja ns. pikajärjestäminen.
Esimerkki: binäärihaku (BinHaeRec.java):
private static int binHaeRec(int[] taulu, int haettava, int vasen, int oikea) { int keskiKohta = (vasen+oikea)/2; if (vasen > oikea) return -1; if (haettava == taulu[keskiKohta]) return keskiKohta; if (haettava < taulu[keskiKohta]) return binHaeRec(taulu, haettava, vasen, keskiKohta-1); else return binHaeRec(taulu, haettava, keskiKohta+1, oikea); }Kutsu on esimerkiksi:
int[] a = {10, 20, 30, 40, 50}; System.out.println(binHaeRec(a, 30, 0, a.length-1));
Esimerkki: pikajärjestäminen (Quicksort) (PikaJarj.java):
private static void pikaJarjesta(int[] taulu, int alku, int loppu) { int jakoAlkio, vasen, oikea; vasen = alku; oikea = loppu; jakoAlkio = taulu[(alku+loppu)/2]; do { while (taulu[vasen]<jakoAlkio) ++vasen; while (jakoAlkio<taulu[oikea]) --oikea; if (vasen<=oikea) { int apu = taulu[vasen]; taulu[vasen] = taulu[oikea]; taulu[oikea] = apu; ++vasen; --oikea; } } while (vasen < oikea); if (alku < oikea) pikaJarjesta(taulu, alku, oikea); if (vasen < loppu) pikaJarjesta(taulu, vasen, loppu); }Kutsu on esimerkiksi:
int[] a = {40, 20, 50, 10, 30}; pikaJarjesta(a, 0, a.length-1);Vaikka pikajärjestäminen onkin pahimmassa tapauksessa O(n2) se on aivan ylivoimainen verrattuna vaikkapa lisäysjärjestämiseen!
Voit itse tutkia tilannetta ohjelmalla VertaileLisQuick.java. Ohjelmoinnin perusteet -kurssilla vertailtiin vastaavalla tavalla alkeellisempia järjestämisalgoritmeja: VertaileJarjAlgoritmeja.java.
Keskimäärin pikajärjestäminen onkin O(n*log n).
Myös ns. keskinäinen rekursio (mutual recursion) on mahdollista. Esimerkki (AbraKadAbra.java):
public class AbraKadAbra { private static void abra (String jono) { if (jono.length() < 44) { jono += "ABRA"; System.out.println("Abra ennen kadin kutsua : "+jono); kad (jono); System.out.println("Abra jälkeen kadin kutsun: "+jono); } } private static void kad (String jono) { if (jono.length() < 44) { jono += "KAD"; System.out.println("Kad ennen abran kutsua : "+jono); abra (jono); System.out.println("Kad jälkeen abran kutsun : "+jono); } } public static void main(String[] args) { abra(""); } }Sovellus tulostaa:
Abra ennen kadin kutsua : ABRA Kad ennen abran kutsua : ABRAKAD Abra ennen kadin kutsua : ABRAKADABRA Kad ennen abran kutsua : ABRAKADABRAKAD Abra ennen kadin kutsua : ABRAKADABRAKADABRA Kad ennen abran kutsua : ABRAKADABRAKADABRAKAD Abra ennen kadin kutsua : ABRAKADABRAKADABRAKADABRA Kad ennen abran kutsua : ABRAKADABRAKADABRAKADABRAKAD Abra ennen kadin kutsua : ABRAKADABRAKADABRAKADABRAKADABRA Kad ennen abran kutsua : ABRAKADABRAKADABRAKADABRAKADABRAKAD Abra ennen kadin kutsua : ABRAKADABRAKADABRAKADABRAKADABRAKADABRA Kad ennen abran kutsua : ABRAKADABRAKADABRAKADABRAKADABRAKADABRAKAD Abra ennen kadin kutsua : ABRAKADABRAKADABRAKADABRAKADABRAKADABRAKADABRA Abra jälkeen kadin kutsun: ABRAKADABRAKADABRAKADABRAKADABRAKADABRAKADABRA Kad jälkeen abran kutsun : ABRAKADABRAKADABRAKADABRAKADABRAKADABRAKAD Abra jälkeen kadin kutsun: ABRAKADABRAKADABRAKADABRAKADABRAKADABRA Kad jälkeen abran kutsun : ABRAKADABRAKADABRAKADABRAKADABRAKAD Abra jälkeen kadin kutsun: ABRAKADABRAKADABRAKADABRAKADABRA Kad jälkeen abran kutsun : ABRAKADABRAKADABRAKADABRAKAD Abra jälkeen kadin kutsun: ABRAKADABRAKADABRAKADABRA Kad jälkeen abran kutsun : ABRAKADABRAKADABRAKAD Abra jälkeen kadin kutsun: ABRAKADABRAKADABRA Kad jälkeen abran kutsun : ABRAKADABRAKAD Abra jälkeen kadin kutsun: ABRAKADABRA Kad jälkeen abran kutsun : ABRAKAD Abra jälkeen kadin kutsun: ABRA