CodeGym /Java blog /Véletlen /Módszerek Java nyelven
John Squirrels
Szint
San Francisco

Módszerek Java nyelven

Megjelent a csoportban
Szia ismét! Az utolsó órán megismerkedtünk az osztályokkal és a konstruktőrökkel, és megtanultuk, hogyan készítsünk sajátot. Ma jobban megismerjük a Java Methodsokat, amelyek az órák elengedhetetlen részét képezik. A Java metódusai olyan parancsok halmaza, amelyek lehetővé teszik egy adott művelet végrehajtását egy programban. Más szóval a metódus egy függvény; valamit, amire az osztályod képes. Más programozási nyelvekben a metódusokat gyakran "függvényeknek" nevezik, de a Java-ban a "method" szó gyakoribb. :) Ha emlékszel, az utolsó órán egyszerű módszereket készítettünk egy Macskaórára , hogy a macskáink nyávoghassanak és ugrálhassanak:

public class Cat {

    String name;
    int age;

    public void sayMeow() {
        System.out.println("Meow!");
    }

    public void jump() {
        System.out.println("Pounce!");
    }

    public static void main(String[] args) {
        Cat smudge = new Cat();
        smudge.age = 3;
        smudge.name = "Smudge";

        smudge.sayMeow();
        smudge.jump();
    }
}
A sayMeow() és a jump() osztályunk metódusai. És ezeknek a módszereknek a futtatása a következő konzolkimenetet eredményezi:
Meow!
Pounce!
Módszereink meglehetősen egyszerűek: egyszerűen szöveget adnak ki a konzolra. De a Java-ban a metódusoknak van egy fontos feladata: műveleteket hajtanak végre egy objektum adatain. Megváltoztatják az objektum adatait, átalakítják, megjelenítik és más dolgokat is csinálnak vele. Jelenlegi módszereink nem csinálnak semmit a Cat objektum adataival. Nézzünk egy szemléletesebb példát:

public class Truck {

    int length;
    int width;
    int height;
    int weight;

    public int getVolume() {
        int volume = length * width * height;
        return volume;
    }
}
Például itt van egy osztály, amely egy teherautót képvisel . A teherautónak megvan a hossza, szélessége, magassága és súlya (amire később szükségünk lesz). A getVolume() metódusban számításokat végzünk, objektumunk adatait a térfogatát reprezentáló számmá alakítjuk (a hosszt, szélességet és magasságot megszorozzuk). Ez a szám lesz a módszer eredménye. Vegye figyelembe, hogy a metódus deklarációja nyilvános int getVolume- ként van megírva . Ez azt jelenti, hogy ennek a metódusnak egy int értéket kell visszaadnia . Kiszámoltuk a metódus visszatérési értékét, és most vissza kell adni a metódusunkat meghívó programnak. Egy metódus eredményének visszaadásához Java nyelven a return kulcsszót használjuk. visszatérő térfogat;

Java módszer paraméterei

A metódusnak hívásakor "argumentumnak" nevezett értékeket adhatunk át. A metódus deklarációja tartalmazza a változók listáját, amelyek megmondják, hogy a metódus milyen típusú és sorrendben fogadja el a változókat. Ezt a listát "módszerparamétereknek" nevezik. Teherautó osztályunk getVolume() metódusa jelenleg nem határoz meg paramétereket, ezért próbáljuk meg kiterjeszteni a teherautó példánkat . Hozzon létre egy új osztályt BridgeOfficer néven . Ez egy hídon szolgálatot teljesítő rendőr, aki minden elhaladó kamiont ellenőrzi, hogy a rakomány meghaladja-e a megengedett súlyt.

public class BridgeOfficer {

    int maxWeight;

    public BridgeOfficer(int normalWeight) {
        this.maxWeight = normalWeight;
    }

    public boolean checkTruck(Truck truck) {
        if (truck.weight > maxWeight) {
            return false;
        } else {
            return true;
        }
    }
}
A checkTruck módszer egy argumentumot fogad el, egy Truck objektumot, és meghatározza, hogy a tiszt engedélyezi-e a teherautót a hídon. A metóduson belül a logika elég egyszerű: ha a teherautó tömege meghaladja a megengedett maximális értéket, akkor a metódus false értéket ad vissza . Más utat kell keresnie :( Ha a súly kisebb vagy egyenlő, mint a maximum, akkor áthaladhat, és a metódus igazat ad vissza. Ha még nem érti teljesen a „return” vagy „a metódus értéket ad vissza” kifejezéseket, tartsunk egy kis szünetet a programozásban, és vegyük figyelembe őket egy egyszerű, valós életből vett példával. :) Tegyük fel, hogy megbetegszel és otthon maradsz a munkából pár napig. Orvosi igazolással mész a könyvelőre, mert a betegszabadságot állítólag ki kell fizetni. Ha ezt a helyzetet metódusokkal hasonlítjuk össze, akkor a könyvelőnek van egy paySickLeave()módszer. Ehhez a módszerhez érvként adsz egy orvosi jegyzetet (enélkül nem működik a módszer és nem kapsz fizetést!). Ezután a módszeren belül elvégzik a szükséges számításokat a jegyzeted segítségével (a könyvelő ennek alapján számítja ki, mennyit kell fizetnie a cégnek), és a munkája eredményét (pénzt) visszaküldik Önnek. A mi programunk is hasonlóan működik. Meghív egy metódust, adatokat ad át neki, és végül eredményt kap. Itt van a BridgeOfficer programunk main() metódusa:

public static void main(String[] args) {
    Truck first = new Truck();
    first.weight = 10000;
    Truck second = new Truck();
    second.weight = 20000;

    BridgeOfficer officer = new BridgeOfficer(15000);
    System.out.println("Truck 1! Can I go, officer?");
    boolean canFirstTruckGo = officer.checkTruck(first);
    System.out.println(canFirstTruckGo);

    System.out.println();

    System.out.println("Truck 2! And can I?");
    boolean canSecondTruckGo = officer.checkTruck(second);
    System.out.println(canSecondTruckGo);
}
Két teherautót hozunk létre 10 000 és 20 000 teherbírással. És a híd, ahol a tiszt dolgozik, maximális tömege 15 000. A program meghívja az office.checkTruck(first) metódust. A metódus mindent kiszámít, majd true értéket ad vissza , amit a program elment a canFirstTruckGo logikai változóba . Most azt csinálhatsz vele, amit akarsz (akárcsak a könyvelőtől kapott pénzzel). A nap végén a kód

boolean canFirstTruckGo = officer.checkTruck(first);
lecsapódik

boolean canFirstTruckGo =  true;
Itt van egy nagyon fontos pont: a return utasítás nem csak a metódus visszatérési értékét adja vissza, hanem leállítja a metódus futását is! A return utasítás után érkező kódok nem kerülnek végrehajtásra!

public boolean checkTruck(Truck truck) {

    if (truck.weight > maxWeight) {
        return false;
        System.out.println("Turn around, you're overweight!");
    } else {
        return true;
        System.out.println("Everything looks good, go ahead!");
    }
}
A tiszti megjegyzések nem jelennek meg, mert a metódus már eredményt adott és megszűnt! A program visszatér arra a helyre, ahol a metódust meghívták. Erre nem kell figyelni: a Java fordító elég okos ahhoz, hogy hibát generáljon, amikor egy return utasítás után kódot próbál írni .

Bosszúállók: Paraméterek háborúja

Vannak helyzetek, amikor több módszert is szeretnénk meghívni egy metódust. Miért ne hoznánk létre saját mesterséges intelligenciánkat? Az Amazonnak Alexa, az Apple-nek Sirije van, miért ne lehetne nekünk is? :) A Vasember című filmben Tony Stark megalkotja saját hihetetlen mesterséges intelligenciáját, Jarvist. Adjunk tiszteletet ennek a fantasztikus karakternek, és nevezzük el a mesterséges intelligenciánkat az ő tiszteletére. :) Először is meg kell tanítanunk Jarvist, hogy köszönjön a szobába belépő embereknek (furcsa lenne, ha egy ilyen csodálatos intellektus udvariatlannak bizonyulna).

public class Jarvis {

    public void sayHi(String name) {
        System.out.println("Good evening, " + name + ". How are you?");
    }

    public static void main(String[] args) {
        Jarvis jarvis = new Jarvis();
        jarvis.sayHi("Tony Stark");
    }
}
Konzol kimenet:
Good evening, Tony Stark. How are you?
Nagyon jó! Jarvis mostantól vendégeket fogadhat. Természetesen legtöbbször a gazdája, Tony Stark lesz az. De mi van, ha nem egyedül jön! A sayHi() metódusunk csak egy argumentumot fogad el. Így csak egy személyt üdvözölhet, aki belép a szobába, és figyelmen kívül hagyja a másikat. Nem túl udvarias, nem értesz egyet? :/

Java Method Overloading

Ebben az esetben úgy tudjuk megoldani a problémát, hogy egyszerűen írunk 2 azonos nevű, de különböző paraméterekkel rendelkező metódust:

public class Jarvis {

    public void sayHi(String firstGuest) {
        System.out.println("Good evening, " + firstGuest + ". How are you?");
    }

    public void sayHi(String firstGuest, String secondGuest) {
        System.out.println("Good evening, " + firstGuest + " and " + secondGuest + ". How are you?");
    }
}
Ezt módszer túlterhelésnek nevezik. A módszer túlterhelése lehetővé teszi, hogy programunk rugalmasabb legyen, és alkalmazkodjon a különféle munkamódszerekhez. Tekintsük át, hogyan működik:

public class Jarvis {

    public void sayHi(String firstGuest) {
        System.out.println("Good evening, " + firstGuest + ". How are you?");
    }

    public void sayHi(String firstGuest, String secondGuest) {
        System.out.println("Good evening, " + firstGuest + " and " + secondGuest + ". How are you?");
    }

    public static void main(String[] args) {
        Jarvis jarvis = new Jarvis();
        jarvis.sayHi("Tony Stark");
        jarvis.sayHi("Tony Stark", "Captain America");
    }
}
Konzol kimenet:
Good evening, Tony Stark. How are you?
Good evening, Tony Stark and Captain America. How are you?
Kiváló, mindkét verzió működött. :) De nem oldottuk meg a problémát! Mi van, ha három vendég lesz? Természetesen ismét túlterhelhetjük a sayHi() metódust, hogy három vendégnevet fogadjon el. De lehet 4 vagy 5. Egészen a végtelenségig. Nincs jobb módja annak, hogy megtanítsuk Jarvist tetszőleges számú név kezelésére anélkül, hogy a sayHi() metódus milliószoros túlterhelése lenne? :/ Persze hogy van! Ha nem lenne, szerinted a Java lenne a legnépszerűbb programozási nyelv a világon? ;)

public void sayHi(String...names) {

    for (String name: names) {
        System.out.println("Good evening, " + name + ". How are you?");
    }
}
Ha a ( String... names ) paramétert használjuk, az azt jelzi, hogy a String gyűjtemény átadásra kerül a metódusnak. Nem kell előre megadnunk, hogy hányan lesznek, így most sokkal rugalmasabb a módszerünk:

public class Jarvis {

    public void sayHi(String...names) {
        for (String name: names) {
            System.out.println("Good evening, " + name + ". How are you?");
        }
    }

    public static void main(String[] args) {
        Jarvis jarvis = new Jarvis();
        jarvis.sayHi("Tony Stark", "Captain America", "Black Widow", "Hulk");
    }
}
Konzol kimenet:
Good evening, Tony Stark. How are you?
Good evening, Captain America. How are you?
Good evening, Black Widow. How are you?
Good evening, Hulk. How are you?
Néhány itt található kód ismeretlen lesz számodra, de ne aggódj miatta. Lényegében egyszerű: a módszer sorra veszi az egyes neveket, és köszönt minden vendéget! Ráadásul tetszőleges számú átadott karakterlánccal működik! Kettő, tíz, akár ezer – a módszer tetszőleges számú vendég esetén megfelelően működik. Sokkal kényelmesebb, mint túlterhelni a módszert az összes lehetőséghez, nem gondolod? :) Itt van még egy fontos szempont: az érvek sorrendje számít! Tegyük fel, hogy a módszerünk egy karakterláncot és egy számot vesz fel:

public class Person {

    public static void sayYourAge(String greeting, int age) {
        System.out.println(greeting + " " + age);
    }

    public static void main(String[] args) {
        sayYourAge("My age is ", 33);
        sayYourAge(33, "My age is "); // Error!
    }
}
Ha a Person osztály sayYourAge metódusa egy karakterláncot és egy számot vesz be bemenetként, akkor a programnak ebben a sorrendben kell átadnia ezeket! Ha más sorrendben adjuk át őket, akkor a fordító hibát generál, és az illető nem tudja megmondani az életkorát. Egyébként a konstruktorok is, amelyekről az előző leckében foglalkoztunk, szintén módszerek! Ezeket túlterhelhetjük is (pl. több konstruktort hozhatunk létre különböző paraméterkészletekkel), és az átadott argumentumok sorrendje is alapvetően fontos számukra. Ezek igazi módszerek! :)

Még egyszer a paraméterekkel kapcsolatban

Igen, elnézést, még nem végeztünk velük. :) Nagyon fontos a téma, amit most tanulmányozni fogunk. 90% esély van rá, hogy minden jövőbeli interjún megkérdezzék erről! Beszéljünk az érvek módszereknek való átadásáról. Vegyünk egy egyszerű példát:

public class TimeMachine {

    public void goToFuture(int currentYear) {
        currentYear = currentYear+10;
    }

    public void goToPast(int currentYear) {
        currentYear = currentYear-10;
    }

    public static void main(String[] args) {
        TimeMachine timeMachine = new TimeMachine();
        int currentYear = 2018;

        System.out.println("What year is it?");
        System.out.println(currentYear);

        timeMachine.goToPast(currentYear);
        System.out.println("How about now?");
        System.out.println(currentYear);
    }
}
Az időgépnek két módja van. Mindketten az aktuális évet jelző számot veszik inputnak, és vagy növelik vagy csökkentik az értékét (attól függően, hogy a múltba vagy a jövőbe akarunk menni). De ahogy a konzol kimenetéből is látszik, a metódus nem működik! Konzol kimenet:
What year is it?
2018
How about now?
2018
A currentYear változót átadtuk a goToPast() metódusnak, de az értéke nem változott. 2018-ban voltunk, és itt maradtunk. De miért? :/ Mert a Java primitíveit érték alapján adják át a metódusoknak. Az mit jelent? Ha meghívjuk a goToPast() metódust, és átadjuk neki a currentYear (=2018) int változót , akkor a metódus nem magát a currentYear változót kapja meg, hanem annak másolatát. Természetesen ennek a másolatnak az értéke is 2018, de a másolaton végrehajtott változtatások semmilyen módon nem érintik az eredeti currentYear változónkat! Tegyük egyértelműbbé kódunkat, és figyeljük meg, mi történik a currentYear-rel:

public class TimeMachine {

    public void goToFuture(int currentYear) {
        currentYear = currentYear+10;
    }

    public void goToPast(int currentYear) {
        System.out.println("The goToPast method has started running!");
        System.out.println("currentYear inside the goToPast method (at the beginning) = " + currentYear);
        currentYear = currentYear-10;
        System.out.println("currentYear inside the goToPast method (at the end) = " + currentYear);
    }

    public static void main(String[] args) {
        TimeMachine timeMachine = new TimeMachine();
        int currentYear = 2018;

        System.out.println("What was the year when the program started?");
        System.out.println(currentYear);

        timeMachine.goToPast(currentYear);
        System.out.println("And what year is it now?");
        System.out.println(currentYear);
    }
}
Konzol kimenet:
What was the year when the program started?
2018
The goToPast method has started running!
currentYear inside the goToPast method (at the beginning) = 2018
currentYear inside the goToPast method (at the end) = 2008
And what year is it now?
2018
Ez egyértelműen azt mutatja, hogy a goToPast() metódusnak átadott változó csak a currentYear másolata . A másolat megváltoztatása pedig nem befolyásolja az "eredeti" értéket. A „referencia alapján átadás” pont az ellenkezőjét jelenti. Gyakoroljunk macskákon! Úgy értem, nézzük meg, hogyan néz ki az utalás, ha egy macska példát használunk. :)

public class Cat {

    int age;

    public Cat(int age) {
        this.age = age;
    }
}
Most az időgépünk segítségével a múltba és a jövőbe küldjük Smudge-ot , a világ első időutazó macskáját! Módosítsuk a TimeMachine osztályt úgy, hogy Cat objektumokkal működjön ;

public class TimeMachine {

    public void goToFuture(Cat cat) {
        cat.age += 10;
    }

    public void goToPast(Cat cat) {
        cat.age -= 10;
    }    
}
Most a módszerek nem csak az átadott számot változtatják meg. Inkább megváltoztatják az adott macska életkorát . Emlékszel rá, hogy ez nálunk nem működött a primitíveknél, mert az eredeti szám nem változott. Lássuk, mi lesz!

public static void main(String[] args) {

    TimeMachine timeMachine = new TimeMachine();
    Cat smudge = new Cat(5);

    System.out.println("How old was Smudge when the program started?");
    System.out.println(smudge.age);

    timeMachine.goToFuture(smudge);
    System.out.println("How about now?");
    System.out.println(smudge.age);

    System.out.println("Holy smokes! Smudge has aged 10 years! Back up quickly!");
    timeMachine.goToPast(smudge);
    System.out.println("Did it work? Have we returned the cat to its original age?");
    System.out.println(smudge.age);
}
Konzol kimenet:
How old was Smudge when the program started running?
5
How about now?
15
Holy smokes! Smudge has aged 10 years! Back up quickly!
Did it work? Have we returned the cat to its original age?
5
Azta! Most mást csinált a módszer: macskánk drasztikusan megöregedett, de aztán újra megfiatalodott! :) Próbáljuk meg kitalálni, hogy miért. Ellentétben a primitívek példájával, amikor az objektumokat egy metódusnak adják át, azok hivatkozással kerülnek átadásra. Az eredeti smudge objektumra való hivatkozás át lett adva a changeAge() metódusnak. Tehát, amikor megváltoztatjuk a smudge.age fájlt a metóduson belül, akkor ugyanarra a memóriaterületre hivatkozunk, ahol az objektumunk van. Ez ugyanarra a Smudge-ra utal, amelyet eredetileg létrehoztunk. Ezt úgy hívják, hogy "referencia alapján haladunk"! Azonban nem minden olyan egyszerű hivatkozással. :) Próbáljunk meg változtatni a példánkon:

public class TimeMachine {

    public void goToFuture(Cat cat) {
        cat = new Cat(cat.age);
        cat.age += 10;
    }

    public void goToPast(Cat cat) {
        cat = new Cat(cat.age);
        cat.age -= 10;
    }

    public static void main(String[] args) {
        TimeMachine timeMachine = new TimeMachine();
        Cat smudge = new Cat(5);

        System.out.println("How old was Smudge when the program started?");
        System.out.println(smudge.age);

        timeMachine.goToFuture(smudge);
        System.out.println ("Smudge went to the future! Has his age changed?");
        System.out.println(smudge.age);

        System.out.println ("And if you try going back?");
        timeMachine.goToPast(smudge);
        System.out.println(smudge.age);
    }
}
Konzol kimenet:
How old was Smudge when the program started running?
5
Smudge went to the future! Has his age changed?
5
And if you try going back?
5
Megint nem megy! О_О Találjuk ki, mi történt. :) Mindennek köze van a goToPast / goToFuture metódusokhoz és a hivatkozások működéséhez. Most pedig a figyelmet kérem! Ez a legfontosabb, hogy megértsük, hogyan működnek a referenciák és a módszerek. A tény az, hogy amikor a goToFuture(Cat cat) metódust hívjuk , akkor ez a cat objektumra mutató hivatkozás másolata, nem pedig maga a hivatkozás. Így amikor egy objektumot adunk át egy metódusnak, két hivatkozás történik az objektumra. Ez nagyon fontos a történések megértéséhez. Pontosan ezért nem változott a macska életkora a legutóbbi példánkban. Az előző példában az életkor megváltoztatásakor egyszerűen a goToFuture() függvényre átadott hivatkozást vettük át.módszerrel, és ennek segítségével kereste meg a tárgyat a memóriában, és megváltoztatta annak korát ( kat.életkor += 10 ). Most azonban a goToFuture() metóduson belül egy új objektumot hozunk létre ( cat = new Cat(cat.age) ), és ehhez az objektumhoz ugyanaz a referenciapéldány lesz hozzárendelve, amelyet a metódusnak is átadtunk. Ennek eredményeként:
  • Az első hivatkozás ( Macskafolt = új macska (5) ) az eredeti macskára mutat (5 évesen)
  • Ezt követően, amikor átadtuk a cat változónak a goToPast() metódust, és új objektumot rendeltünk hozzá, a hivatkozás átmásolódott.
És ezzel el is jutottunk a végeredményhez: két utalás két különböző objektumra. De csak az egyik életkorát változtattuk meg (a metóduson belül létrehozott).

cat.age += 10;
És persze a main() metódusban láthatjuk a konzolon, hogy a macska életkora, smudge.age , nem változott. Végül is a smudge egy referenciaváltozó, amely 5 éves korában még mindig a régi, eredeti objektumra mutat, és ezzel az objektummal nem csináltunk semmit. Minden életkori változásunkat az új objektumon végeztük el. Tehát kiderül, hogy az objektumokat hivatkozással adják át a metódusoknak. Az objektumok másolatai soha nem jönnek létre automatikusan. Ha egy macskatárgyat átadsz egy módszernek, és megváltoztatod a korát, akkor megváltoztatod a korát. De a referenciaváltozók másolódnak az értékek hozzárendelésekor és/vagy metódusok hívásakor! Ismételjük meg itt, amit a primitívek átadásával kapcsolatban mondtunk: "Amikor meghívjuk a changeInt() metódust és átadjuk az intx változó (=15) , a metódus nem magát az x változót kapja meg, hanem annak másolatát. Ezért a másolaton végzett változtatások nincsenek hatással az eredeti x -ünkreMég mindig többször fog vitatkozni az érvek átadásáról a Java-ban (még a tapasztalt fejlesztők körében is). De most már pontosan tudja, hogyan működik. Csak így tovább! :) A tanultak megerősítése érdekében javasoljuk, hogy nézzen meg egy videóleckét a Java-tanfolyamról
Hozzászólások
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION