Osa 2

Ohjelman pilkkominen osiin: metodit

Olemme käyttäneet useita erilaisia komentoja Javassa: sijoitusta, laskutoimituksia, vertailuja, if:iä ja whileä. Ruudulle tulostaminen on tehty System.out.println()-lauseella ja lukeminen Integer.valueOf(lukija.nextLine()) lauseella.

Huomaamme, että tulostaminen ja lukeminen poikkeaa if:istä ja while:stä ym. siinä, että komennon perässä on sulut ja joskus sulkujen sisällä komennolle annettava parametrit. "Sulkuihin päättyvät" eivät oikeastaan olekaan komentoja vaan metodeja.

Teknisesti ottaen metodi tarkoittaa nimettyä lauseista koostuvaa joukkoa, eli ohjelman palaa, jota voi kutsua muualta ohjelmakoodista metodille annetun nimen perusteella. Koodirivi System.out.println("olen metodille annettava parametri!") siis tarkoittaa, että kutsutaan metodia, joka suorittaa ruudulle tulostamisen. Metodin sisäinen toteutus — eli joukko suoritettavia lauseita — on tässä tapauksessa java-ohjelmointikielen piilottama.

Metodin suorituksen jälkeen palataan takaisin kohtaan, missä ennen metodikutsua oltiin menossa, ja ohjelman suoritus jatkuu tästä. Metodille suluissa annettua syötettä kutsutaan metodin parametriksi — metodin parametreilla annetaan metodeille tarkempaa tietoa odotetusta suorituksesta; esimerkiksi tulostuslauseelle kerrotaan parametrin avulla mitä pitäisi tulostaa.

Parametrin lisäksi metodilla voi olla paluuarvo. Esim. tuttu lause:

String merkkijono = lukija.nextLine();
sisältää yhden metodikutsun lukija.nextLine(). Usein metodin nimeen näyttää liittyvän piste, kuten yllä olevassa lauseessa. Oikeastaan tässäkin metodin nimi on pisteen oikeanpuoleinen osa, eli nextLine(). Pisteen vasemmanpuoleinen osa, eli tässä lukija, kertoo kenen metodista on kyse. Kyseessä on lukijan tarjoama metodi nextLine. Opimme hiukan myöhemmin tarkemmin mistä tässä pisteen vasemmanpuoleisessa osassa on kyse. Tarkka lukija tietysti huomaa, että System.out.println():ssa on "kaksi pistettä". Metodin nimi tässä on println, ja System.out on se kenen metodista on kyse. Karkeasti ottaen System.out tarkoittaa koneen näyttöä.

Vastaavasti lauseessa:

int luku = Integer.valueOf(lukija.nextLine());

on kaksi metodikutsua. Ensin kutsutaan sisempänä olevaa metodia lukija.nextLine(), joka palauttaa merkkijonon. Seuraavaksi kutsutaan metodia Integer.valueOf(...). Metodikutsun Integer.valueOf(...) parametriksi asetetaan se merkkijono, jonka metodin lukija.nextLine() kutsu palautti. Metodin Integer.valueOf(...) paluuarvona on merkkijonoa vastaava kokonaisluku.

Tähän mennessä käyttämämme metodit ovat kaikki olleet Javan valmiita metodeita. Opetellaan seuraavaksi tekemään omia metodeita.

Omat metodit

Edellä mainittiin että metodi tarkoittaa nimettyä lauseista koostuvaa joukkoa, jota voi kutsua muualta ohjelmakoodista nimen perusteella. Javan valmiita metodeja on käytetty oikeastaan ensimmäisestä ohjelmasta lähtien.

Javan valmiiden metodien käytön lisäksi ohjelmoija voi kirjoittaa itse metodeja joita sovellus kutsuu. Oikeastaan on hyvin poikkeuksellista jos ohjelmassa ei ole yhtään itse kirjoitettua metodia. Tästä lähtien lähes jokainen kurssilla tehty ohjelma sisältääkin itsekirjoitettuja metodeja.

Ohjelmarunkoon metodit kirjoitetaan main:in aaltosulkeiden ulkopuolelle mutta kuitenkin "uloimmaisten" aaltosulkeiden sisäpuolelle, joko mainin ylä- tai alapuolelle.

import java.util.Scanner;

public class Esimerkki {
    public static void main(String[] args) {
        Scanner lukija = new Scanner(System.in);
        // ohjelmakoodi
    }

    // omia metodeja tänne
}

Tarkastellaan uuden metodin luomista. Luodaan metodi tervehdi.

public static void tervehdi() {
    System.out.println("Terveiset metodimaailmasta!");
}

Ja asetetaan se metodeille kuuluvalle paikalle.

import java.util.Scanner;

public class Esimerkki {
    public static void main(String[] args) {
        Scanner lukija = new Scanner(System.in);
        // ohjelmakoodi
    }

    // omia metodeja tänne
    public static void tervehdi() {
        System.out.println("Terveiset metodimaailmasta!");
    }
}
Metodin määrittely sisältää kaksi osaa. Metodimäärittelyn ensimmäisellä rivillä on metodin nimi eli tervehdi. Nimen vasemmalla puolella tässä vaiheessa määreet public static void. Metodin nimen sisältävän rivin alla on aaltosulkeilla erotettu koodilohko, jonka sisälle kirjoitetaan metodin koodi, eli ne komennot jotka metodia kutsuttaessa suoritetaan. Metodimme tervehdi ei tee muuta kuin kirjoittaa rivillisen tekstiä ruudulle.

Itsekirjoitetun metodin kutsu on helppoa, kirjoitetaan metodin nimi ja perään sulut ja puolipiste. Seuraavassa main eli pääohjelma kutsuu tervehdi-metodia yhteensä neljä kertaa.

import java.util.Scanner;

public class OhjelmaRunko {
    public static void main(String[] args) {
        Scanner lukija = new Scanner(System.in);

        // ohjelmakoodi
        System.out.println("Kokeillaan pääsemmekö metodimaailmaan:");
        tervehdi();

        System.out.println("Näyttää siltä, kokeillaan vielä:");
        tervehdi();
        tervehdi();
        tervehdi();
    }

    // omat metodit
    public static void tervehdi() {
        System.out.println("Terveiset metodimaailmasta!");
    }
}

Ohjelman suoritus saa aikaan seuraavan tulosteen:

Esimerkkitulostus

Kokeillaan pääsemmekö metodimaailmaan: Terveiset metodimaailmasta! Näyttää siltä, kokeillaan vielä: Terveiset metodimaailmasta! Terveiset metodimaailmasta! Terveiset metodimaailmasta!

Huomionarvoista tässä on ohjelman suoritusjärjestys. Ohjelman suoritus etenee siten, että pääohjelman — eli main:in — rivit suoritetaan ylhäältä alas yksi kerrallaan. Kun lause on metodikutsu, ohjelman suoritus siirtyy metodiin. Metodin lauseet suoritetaan yksi kerrallaan ylhäältä alas. Tämän jälkeen palataan kohtaan, josta metodin kutsu tapahtui. Tarkemmin ottaen metodikutsun jälkeiselle riville.

Jos ollaan tarkkoja niin pääohjelma eli main on itsekin metodi. Kun ohjelma käynnistyy, kutsuu käyttöjärjestelmä main:ia. Metodi main on siis ohjelman käynnistyspiste, jonka ylimmältä riviltä ohjelman suoritus lähtee liikkeelle. Ohjelman suoritus loppuu kun päädytään mainin loppuun.

Loading
Loading

Jatkossa kun esittelemme metodeja, emme erikseen mainitse että niiden täytyy sijaita omalla paikallaan. Metodia ei esimerkiksi voi määritellä toisen metodin sisällä.

Metodien nimennästä

Metodit nimetään siten, että ensimmäinen sana kirjoitetaan pienellä ja loput alkavat isolla alkukirjaimella, tälläisestä kirjoitustavasta käytetään nimitystä camelCase. Tämän lisäksi, metodin sisällä koodi on sisennetty taas neljä merkkiä.

Alla olevassa esimerkissä metodi on nimetty väärin. Nimi alkaa isolla alkukirjaimella ja metodin nimen osat on eroteltu toisistaan merkillä _. Metodin sulut ovat myös erillään toisistaan ja sisennys on väärin.

public static void Tama_metodi_sanoo_mur ( ) {
System.out.println("mur");
}

Alla taas metodi on nimetty oikein. Nimi alkaa pienellä alkukirjaimella ja nimen osat on yhdistetty toisiinsa camelCase-tyylillä, missä jokainen uusi sana alkaa isolla kirjaimella. Sulut ovat kiinni metodissa ja toisissaan, jonka lisäksi metodin sisältö on sisennetty oikein.

public static void tamaMetodiSanooMur() {
    System.out.println("mur");
}

Metodin muuttujat

Muuttujien määrittely metodissa tapahtuu tutulla tavalla. Seuraava metodi laskee parametrina saamiensa lukujen keskiarvon. Keskiarvon laskemisessa käytetään apumuuttujia summa ja ka.

public static double keskiarvo(int luku1, int luku2, int luku3) {
    int summa = luku1 + luku2 + luku3;
    double ka = summa / 3.0;

    return ka;
}

Metodin kutsu voi tapahtua esim seuraavasti

public static void main(String[] args) {
    Scanner lukija = new Scanner(System.in);

    System.out.print("Anna ensimmäinen luku: ");
    int eka = Integer.valueOf(lukija.nextLine());

    System.out.print("Anna toinen luku: ");
    int toka = Integer.valueOf(lukija.nextLine());

    System.out.print("Anna kolmas luku: ");
    int kolmas = Integer.valueOf(lukija.nextLine());

    double keskiarvonTulos = keskiarvo(eka, toka, kolmas);

    System.out.print("Lukujen keskiarvo: " + keskiarvonTulos);
}

Huomaa että metodin sisäiset muuttujat summa ja ka eivät näy pääohjelmaan. Tyypillinen ohjelmoinnin harjoittelussa eteen tuleva virhe on yrittää käyttää metodia seuraavasti.

public static void main(String[] args) {
    int eka = 3;
    int toka = 8;
    int kolmas = 4;

    keskiarvo(eka, toka, kolmas);

    // yritetään käyttää metodin sisäistä muuttujaa, EI TOIMI!
    System.out.print("Lukujen keskiarvo: " + ka);
}

Yllä yritetään käyttää metodin keskiarvo sisällä määriteltyä muuttujaa ka ja tulostaa sen arvo. Muuttuja ka on kuitenkin olemassa vain metodin keskiarvo sisällä, eikä siihen pääse käsiksi ulkopuolelta.

Myös seuraavanlaista virhettä näkee usein.

public static void main(String[] args) {
    int eka = 3;
    int toka = 8;
    int kolmas = 4;

    // yritetään käyttää pelkkää metodin nimeä, EI TOIMI!
    System.out.print("Lukujen keskiarvo: " + keskiarvo);
}

Yllä yritetään käyttää metodin keskiarvo nimeä muuttujamaisesti. Metodia tulee kuitenkin kutsua.

Toimiva tapa metodin tuloksen sijoittamisen apumuuttujaan lisäksi on suorittaa metodikutsu suoraan tulostuslauseen sisällä:

public static void main(String[] args) {
    int eka = 3;
    int toka = 8;
    int kolmas = 4;

    // kutsutaan metodia tulostuslauseessa, TOIMII!
    System.out.print("Lukujen keskiarvo: " + keskiarvo(eka, toka, kolmas));
}

Tässä siis ensin tapahtuu metodikutsu joka palauttaa arvon 5.0 joka sitten tulostetaan tulostuskomennon avulla.

Muuttuja ei ole olemassa ennen sen esittelyä, ja muuttuja on olemassa vain niiden aaltosulkujen sisällä kuin missä se on esitelty. Metodien yhteydessä tämä tarkoittaa sitä, että metodeilla on pääsy vain niihin muuttujiin, jotka ovat määritelty metodien sisällä, tai jotka metodi saa parametrina. Alla oleva esimerkki konkretisoi tilanteen, missä kasvataKolme-metodin sisällä yritetään muuttaa main-metodissa määritellyn luku-muuttujan arvoa.

// pääohjelma
public static void main(String[] args) {
    int luku = 1;
    kasvataKolmella();
}

// metodi
public static void kasvataKolmella() {
    luku = luku + 3;
}

Yllä oleva ohjelma ei toimi, sillä metodi kasvataKolmella ei näe pääohjelman muuttujaa luku. Tarkemmin ottaen, metodi kasvataKolmella ei edes tiedä mistä muuttujasta luku on kyse, sillä muuttujaa ei ole määritelty metodissa kasvataKolmella tai sen parametreissa.

Yleisemmin voi todeta, että pääohjelman muuttujat eivät näy metodien sisälle, ja metodin muuttujat eivät näy muille metodeille tai pääohjelmalle. Ainoa keino viedä metodille tietoa metodin ulkopuolelta on parametrin avulla.

Metodin parametrit

Parametrit ovat metodille annettavia arvoja, joita käytetään metodin suorituksessa. Metodin parametrit määritellään metodin ylimmällä rivillä metodin nimen jälkeen olevien sulkujen sisällä. Kun metodia kutsutaan, sen parametreille annetaan arvot kutsuvaiheessa.

Seuraavassa esimerkissä määritellään parametrillinen metodi tervehdi, jolla on int-tyyppinen parametri montakoKertaa.

public static void tervehdi(int montakoKertaa) {
    int i = 0;
    while (i < montakoKertaa) {
        System.out.println("Tervehdys!");
        i++;
    }
}

Kutsutaan metodia tervehdi siten, että parametrin montakoKertaa arvoksi asetetaan ensimmäisellä kutsulla 1 ja toisella kutsulla 3.

public static void main(String[] args) {
    tervehdi(1);
    tervehdi(3);
}
Esimerkkitulostus

Tervehdys! Tervehdys! Tervehdys! Tervehdys!

Aivan kuten Javan valmista System.out.println()-metodia kutsuttaessa, voi oman metodin kutsussa parametrina antaa lausekkeen.

public static void main(String[] args) {
    tervehdi(1 + 2);
}
Esimerkkitulostus

Tervehdys! Tervehdys! Tervehdys!

Jos metodia kutsuttaessa parametriksi määritellään lauseke, evaluoidaan lauseke ennen metodikutsua. Yllä metodikutsun parametri evaluoituu arvoksi 3 ja lopullinen metodikutsu on muotoa tervehdi(3);.

Loading
Loading

Metodille voidaan määritellä useita parametreja. Tällöin metodin kutsussa parametrit annetaan samassa järjestyksessä.

public static void summa(int eka, int toka) {
    System.out.println("Lukujen " + eka + " ja " + toka + " summa on " + (eka + toka));
}
summa(3, 5);

int luku1 = 2;
int luku2 = 4;

summa(luku1, luku2);
Esimerkkitulostus

Lukujen 3 ja 5 summa on 8 Lukujen 2 ja 4 summa on 6

Loading...
Loading
Loading

Metodikutsun yhteydessä parametrien arvot kopioituvat. Tämä tarkoittaa käytännössä sitä, että sekä main-metodissa että kutsuttavassa metodissa voi olla saman nimiset muuttujat, mutta muuttujien arvon muuttaminen kutsuttavan metodin sisällä ei muuta main-metodissa olevan muuttujan arvoa. Tarkastellaan tätä seuraavan ohjelman avulla.

public class Esimerkki {
    public static void main(String[] args) {
        int mista = 5;
        int mihin = 10;

        tulostaLuvut(mista, mihin);

        mista = 8;

        tulostaLuvut(mista, mihin);
    }

    public static void tulostaLuvut(int mista, int mihin) {
        while (mista < mihin) {
            System.out.println(mista);
            mista++;
        }
    }
}

Ohjelman tulostus on seuraava:

Esimerkkitulostus

5 6 7 8 9 8 9

Alla sama askeleittaisena visualisaationa. Huomaat että main-metodissa olevat arvot jäävät kutsupinoon odottamaan metodin tulostaLuvut suorittamista. Metodissa tulostaLuvut olevien muuttujien arvojen muuttaminen ei muuta metodin main muuttujien arvoja, vaikka ne ovatkin saman nimisiä.

Loading...

Metodien parametrit ovat siis erillisiä muiden metodien muuttujista (tai parametreista), vaikka niillä olisikin sama nimi. Kun metodikutsun yhteydessä metodille annetaan muuttuja, muuttujan arvo kopioituu kutsuttavan metodin metodimäärittelyssä olevan parametrimuuttujan arvoksi. Kahdessa eri metodissa olevat muuttujat ovat erillisiä toisistaan.

Tarkastellaan vielä seuraavaa esimerkkiä, missä pääohjelmassa määritellään muuttuja luku. Muuttuja luku annetaan parametrina metodille kasvataKolmella.

// pääohjelma
public static void main(String[] args) {
    int luku = 1;
    System.out.println("Pääohjelman muuttujan luku arvo: " + luku);
    kasvataKolmella(luku);
    System.out.println("Pääohjelman muuttujan luku arvo: " + luku);
}

// metodi
public static void kasvataKolmella(int luku) {
    System.out.println("Metodin parametrin luku arvo: " + luku);
    luku = luku + 3;
    System.out.println("Metodin parametrin luku arvo: " + luku);
}

Ohjelman suoritus aiheuttaa seuraavanlaisen tulostuksen.

Esimerkkitulostus

Pääohjelman muuttujan luku arvo: 1 Metodin parametrin luku arvo: 1 Metodin parametrin luku arvo: 4 Pääohjelman muuttujan luku arvo: 1

Kun metodin sisällä kasvatetaan muuttujan luku arvoa kolmella, se onnistuu. Tämä ei kuitenkaan näy pääohjelmassa olevassa muuttujassa luku. Pääohjelmassa oleva muuttuja luku on eri kuin metodissa oleva muuttuja luku.

Loading...

Parametri luku kopioidaan metodin käyttöön, eli metodia kasvataKolmella varten luodaan oma muuttuja nimeltä luku, johon pääohjelmassa olevan muuttujan luku arvo kopioidaan metodikutsun yhteydessä. Metodissa kasvataKolmella oleva muuttuja luku on olemassa vain metodin suorituksen ajan, eikä sillä ole yhteyttä pääohjelman samannimiseen muuttujaan.

Metodi voi palauttaa arvon

Metodin määrittelyssä kerrotaan palauttaako metodi arvon. Jos metodi palauttaa arvon, tulee metodimäärittelyn yhteydessä kertoa palautettavan arvon tyyppi. Muulloin määrittelyssä käytetään avainsanaa void. Tähän mennessä tekemämme metodit ovat määritelty avainsanaa void käyttäen eli eivät ole palauttaneet arvoa.

public static **void** kasvataKolmella() {
    ...
}

Avainsana void tarkoittaa että metodi ei palauta mitään. Jos haluamme, että metodi palauttaa arvon, tulee avainsanan void paikalle asettaa palautettavan muuttujan tyyppi. Seuraavassa esimerkissä on määritelty metodi palautetaanAinaKymppi, joka palauttaa kokonaislukutyyppisen (int) muuttujan (tässä arvon 10). Konkreettinen arvon palautus tapahtuu komennolla return, jota seuraa palautettava arvo (tai muuttujan nimi, jonka arvo palautetaan).

public static int palautetaanAinaKymppi() {
    return 10;
}

Yllä määritelty metodi palauttaa sitä kutsuttaessa int-tyyppisen arvon 10. Jotta metodin palauttamaa arvoa voisi käyttää, tulee se ottaa talteen muuttujaan. Tämä tapahtuu samalla tavalla kuin normaali muuttujan arvon asetus, eli yhtäsuuruusmerkillä.

public static void main(String[] args) {
    int luku = palautetaanAinaKymppi();

    System.out.println("metodi palautti luvun " + luku);
}

Metodin paluuarvo sijoitetaan int-tyyppiseen muuttujaan aivan kuin mikä tahansa muukin int-arvo. Paluuarvo voi toimia myös osana mitä tahansa lauseketta.

double luku = 4 * palautetaanAinaKymppi() + (palautetaanAinaKymppi() / 2) - 8;

System.out.println("laskutoimituksen tulos " + luku);

Kaikki tähän mennessä näkemämme muuttujatyypit voidaan palauttaa metodista.

Palautettavan arvon tyyppi Esimerkki
Metodi ei palauta arvoa ```java public static void metodiJokaEiPalautaMitaan() { // metodin runko }```
Metodi palauttaa `int`-tyyppisen muuttujan ```java public static int metodiJokaPalauttaaKokonaisLuvun() { // metodin runko, tarvitsee return-komennon }```
Metodi palauttaa `double`-tyyppisen muuttujan ```java public static double metodiJokaPalauttaaLiukuluvun() { // metodin runko, tarvitsee return-komennon }```

Jos metodille määritellään paluuarvon tyyppi, on sen pakko palauttaa arvo. Esimerkiksi seuraava metodi on virheellinen.

public static int virheellinenMetodi() {
    System.out.println("Väitän palauttavani kokonaisluvun, mutten palauta sitä.");
}

Ylläolevassa metodissa tulee olla komento return, jota seuraa palautettavan arvon tyyppi.

Loading
Loading

Palautettavan arvon ei tarvitse olla täysin ennalta määritelty, vaan se voidaan laskea. Metodista arvon palauttavalle return-komennolle voidaan antaa myös lauseke, joka evaluoituu ennen kuin arvo palautetaan.

Seuraavassa esimerkissä määritellään metodi summa, joka laskee kahden muuttujan arvon yhteen ja palauttaa summan. Yhteen laskettavien muuttujien eka ja toka arvot saadaan metodin parametrina.

public static int summa(int eka, int toka) {
    return eka + toka;
}

Kun metodin suorituksessa päädytään lauseeseen return eka + toka;, evaluoidaan lauseke eka + toka, jonka arvo lopulta palautetaan.

Metodin kutsutaan seuraavasti. Alla metodia käytetään laskemaan luvut 2 ja 7 yhteen. Metodikutsusta saatava paluuarvo asetetaan muuttujaan lukujenSumma:

int lukujenSumma = summa(2, 7);
// lukujenSumma on nyt 9

Laajennetaan edellistä esimerkkiä siten, että käyttäjä syöttää luvut.

public static void main(String[] args) {
    Scanner lukija = new Scanner(System.in);

    System.out.print("Anna ensimmäinen luku: ");
    int eka = Integer.valueOf(lukija.nextLine());

    System.out.print("Anna toinen luku: ");
    int toka = Integer.valueOf(lukija.nextLine());

    System.out.print("Luvut ovat yhteensä: " + summa(eka, toka));
}

public static int summa(int eka, int toka) {
    return eka + toka;
}

Yllä olevassa esimerkissä metodin paluuarvoa ei aseteta muuttujaan, vaan sitä käytetään suoraan osana tulostusta. Tällöin tulostuskomennon suoritus tapahtuu siten, että tietokone selvittää ensin tulostettavan merkkijonon "Luvut ovat yhteensä: " + summa(eka, toka). Ensin tietokone etsii muuttujat eka ja toka, ja kopioi niiden arvot metodin summa parametrien arvoiksi. Tämän jälkeen metodissa lasketaan parametrien arvot yhteen, jonka jälkeen summa palauttaa arvon. Tämä arvo asetetaan metodikutsun summa paikalle, jonka jälkeen summa liitetään merkkijonon "Luvut ovat yhteensä: " jatkeeksi.

Koska metodille annettavat arvot kopioidaan metodin parametreihin, metodin parametrien nimillä ja metodin kutsujan puolella määritellyillä muuttujien nimillä ei ole oikeastaan mitään tekemistä keskenään. Edellisessä esimerkissä sekä pääohjelman muuttujat että metodin parametrit olivat "sattumalta" nimetty samoin (eli eka ja toka). Seuraava toimii täysin samalla tavalla vaikka muuttujat ovatkin eri nimisiä:

public static void main(String[] args) {
    Scanner lukija = new Scanner(System.in);

    System.out.print("Anna ensimmäinen luku: ");
    int luku1 = Integer.valueOf(lukija.nextLine());

    System.out.print("Anna toinen luku: ");
    int luku2 = Integer.valueOf(lukija.nextLine());

    System.out.print("Luvut ovat yhteensä: " + summa(luku1, luku2));
}

public static int summa(int eka, int toka) {
    return eka + toka;
}

Nyt pääohjelman muuttujan luku1 arvo kopioituu metodin parametrin eka arvoksi ja pääohjelman muuttujan luku2 arvo kopioituu metodin parametrin toka arvoksi.

Loading
Loading
Loading
Loading

Metodikutsujen suoritus ja kutsupino

Mistä tietokone tietää minne metodin suorituksen jälkeen tulee palata?

Java-lähdekoodin suoritusympäristö pitää kirjaa suoritettavasta metodista kutsupinossa. Kutsupino sisältää kehyksiä, joista jokainen sisältää tiedon kyseisen metodin sisäisistä muuttujista sekä niiden arvoista. Kun metodia kutsutaan, kutsupinoon luodaan uusi kehys, joka sisältää metodin sisältämät muuttujat. Kun metodin suoritus loppuu, metodiin liittyvä kehys poistetaan kutsupinosta, jolloin suoritusta jatketaan kutsupinon edeltävästä metodista.

Alla olevan visualisaation oikealla laidalla näytetään kutsupinon toimintaa. Metodikutsun yhteydessä kutsupinoon luodaan uusi kehys, joka poistetaan metodikutsusta poistuttaessa.

Loading...

Kun metodia kutsutaan, kutsuvan metodin suoritus jää odottamaan kutsutun metodin suorittamista. Tätä voidaan visualisoida kutsupinon avulla. Kutsupino tarkoittaa metodien kutsumisen muodostamaa pinoa — juuri suoritettevana oleva metodi on aina pinon päällimmäisenä, ja metodin suorituksen päättyessä palataan pinossa seuraavana olevaan metodiin. Tarkastellaan seuraavaa ohjelmaa:

public static void main(String[] args) {
    System.out.println("Hei maailma!");
    tulostaLuku();
    System.out.println("Hei hei maailma!");
}

public static void tulostaLuku() {
    System.out.println("Luku");
}

Kun ohjelma käynnistetään, suoritus alkaa main-metodin ensimmäiseltä riviltä. Tällä rivillä olevalla komennolla tulostetaan teksti "Heippa maailma!". Ohjelman kutsupino näyttää seuraavalta:

Esimerkkitulostus

main

Kun tulostuskomento on suoritettu, siirrytään seuraavaan komentoon, missä kutsutaan metodiatulostaLuku. Metodin tulostaLuku kutsuminen siirtää ohjelman suorituksen metodin tulostaLuku alkuun, jolloin main-metodi jää odottamaan metodin tulostaLuku suorituksen loppumista. Metodin tulostaLuku sisällä ollessa kutsupino näyttää seuraavalta.

Esimerkkitulostus

tulostaLuku main

Kun metodi tulostaLuku on suoritettu, palataan kutsupinossa metodia tulostaLuku yhtä alempana olevaan metodiin, eli metodiin main. Kutsupinosta poistetaan tulostaLuku, ja jatketaan main-metodin suoritusta tulostaLuku-metodikutsun jälkeiseltä riviltä. Kutsupino on nyt seuraavanlainen:

Esimerkkitulostus

main

Kutsupino ja metodin parametrit

Tarkastellaan kutsupinoa tilanteessa, missä metodille on määritelty parametreja.

public static void main(String[] args) {
    int alku = 1;
    int loppu = 5;

    tulostaTahtia(alku, loppu);
}

public static void tulostaTahtia(int alku, int loppu) {
    while (alku < loppu) {
        System.out.print("*");
        alku++; // sama kuin alku = alku + 1
    }
}

Ohjelman suoritus alkaa main-metodin ensimmäiseltä riviltä, jota seuraavilla riveillä luodaan muuttujat alku ja loppu, sekä asetetaan niihin arvot. Ohjelman tilanne ennen metodin tulostaTahtia-kutsumista:

Esimerkkitulostus
main alku = 1 loppu = 5

Kun metodia tulostaTahtia kutsutaan, main-metodi jää odottamaan. Metodikutsun yhteydessä metodille tulostaTahtia luodaan uudet muuttujat alku ja loppu, joihin asetetaan parametreina annetut arvot. Nämä kopioidaan main-metodin muuttujista alku ja loppu. Metodin tulostaTahtia suorituksen ensimmäisellä rivillä ohjelman tilanne on seuraavanlainen.

Esimerkkitulostus
tulostaTahtia alku = 1 loppu = 5 main alku = 1 loppu = 5

Kun toistolauseessa suoritetaan komento alku++, muuttuu tällä hetkellä suoritettavaan metodiin liittyvän alku-muuttujan arvo.

Esimerkkitulostus
tulostaTahtia alku = 2 loppu = 5 main alku = 1 loppu = 5

Metodin main muuttujien arvot eivät siis muutu. Metodin tulostaTahtia suoritus jatkuisi tämän jälkeen jokusen hetken. Kun metodin tulostaTahtia suoritus loppuu, palataan takaisin main-metodin suoritukseen.

Esimerkkitulostus
main alku = 1 loppu = 5

Tarkastellaan samaa ohjelman suorituksen askeleittaisella visualisoinnilla. Visualisointiin käyttämämme sovellus kasvattaa kutsupinoa alaspäin — oikealla laidalla ylin on aina main-metodi, jonka alle tulee kutsuttavat metodit.

Loading...

Kutsupino ja arvon palauttaminen metodista

Tarkastellaan seuraavaksi esimerkkiä, missä metodi palauttaa arvon. Ohjelman main-metodi kutsuu erillistä kaynnista-metodia, jossa luodaan kaksi muuttujaa, kutsutaan summa-metodia, ja tulostetaan summa-metodin palauttama arvo.

public static void main(String[] args) {
    kaynnista();
}

public static void kaynnista() {
    int eka = 5;
    int toka = 6;

    int summa = summa(eka, toka);

    System.out.println("Summa: " + summa);
}

public static int summa(int luku1, int luku2) {
    return luku1 + luku2;
}

Metodin kaynnista suorituksen alussa kutsupino näyttää seuraavalta, sillä siihen päädyttiin main-metodista. Metodilla main ei tässä esimerkissä ole omia muuttujia:

Esimerkkitulostus
kaynnista main

Kun kaynnista-metodissa on luotu muuttujat eka ja toka, eli sen ensimmäiset kaksi riviä on suoritettu, on tilanne seuraava:

Esimerkkitulostus
kaynnista eka = 5 toka = 6 main

Komento int summa = summa(eka, toka); luo metodiin kaynnista muuttujan summa, ja kutsuu metodia summa. Metodi kaynnista jää odottamaan. Koska metodissa summa on määritelty parametrit luku1 ja luku2, luodaan ne heti metodin suorituksen alussa, ja niihin kopioidaan parametrina annettujen muuttujien arvot.

Esimerkkitulostus
summa luku1 = 5 luku2 = 6 kaynnista eka = 5 toka = 6 summa // ei arvoa main

Metodin summa suoritus laskee muuttujien luku1 ja luku2 arvot yhteen. Komento return palauttaa lukujen summan kutsupinossa yhtä alemmalle metodille, eli metodille kaynnista. Palautettu arvo asetetaan muuttujan summa arvoksi.

Esimerkkitulostus
kaynnista eka = 5 toka = 6 summa = 11 main

Tämän jälkeen suoritetaan tulostuskomento, jonka jälkeen palataan main-metodiin. Kun suoritus on main-metodin lopussa, ohjelman suoritus loppuu.

Loading...

Metodi kutsuu toista metodia

Kuten edellä huomattiin, metodin sisältä voi kutsua myös muita metodeja. Alla vielä esimerkki tähän liittyen. Tehdään metodi kertotaulu, joka tulostaa annetun luvun kertotaulun. Kertotaulu tulostaa rivit metodin tulostaKertotaulunRivi avulla.

public static void kertotaulu(int ylaraja) {
    int luku = 1;

    while (luku <= ylaraja) {
        tulostaKertotaulunRivi(luku, ylaraja);
        luku++;
    }
}

public static void tulostaKertotaulunRivi(int luku, int kerroin) {

    int tulostettava = luku;
    while (tulostettava <= luku * kerroin) {
        System.out.print("  " + tulostettava);
        tulostettava += luku;
    }

    System.out.println("");
}

Esimerkiksi metodikutsun kertotaulu(3) tulostus on seuraava.

Esimerkkitulostus
1 2 3 2 4 6 3 6 9

Alla metodikutsu kertotaulu(3) visualisoituna. Huomaa, miten kutsupinossa on tieto kutsuvan metodin sisäisestä tilasta.

Loading...
Loading
Loading
Pääsit aliluvun loppuun! Jatka tästä seuraavaan osaan:

Muistathan tarkistaa pistetilanteesi materiaalin oikeassa alareunassa olevasta pallosta!