CodeGym /Blog Java /Aleatoriu /Metode în Java
John Squirrels
Nivel
San Francisco

Metode în Java

Publicat în grup
Buna din nou! În ultima lecție ne-am familiarizat cu clasele și constructorii și am învățat cum să ne creăm propriile noastre. Astăzi ne vom familiariza mai bine cu Metodele Java, o parte esențială a cursurilor. A Metode în Java este un set de comenzi care vă permit să efectuați o anumită operație într-un program. Cu alte cuvinte, o metodă este o funcție; ceva ce clasa ta este capabilă să facă. În alte limbaje de programare, metodele sunt adesea numite „funcții”, dar în Java cuvântul „metodă” este mai frecvent. :) Dacă vă amintiți, în ultima lecție am creat metode simple pentru o clasă de pisici , astfel încât pisicile noastre să poată spune miau și să sară:

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();
    }
}
sayMeow() și jump() sunt metode din clasa noastră. Și rularea acestor metode are ca rezultat următoarea ieșire din consolă:
Meow!
Pounce!
Metodele noastre sunt destul de simple: pur și simplu trimit text în consolă. Dar în Java, metodele au o sarcină importantă: efectuează acțiuni asupra datelor unui obiect. Ei schimbă datele obiectului, îl transformă, îl afișează și fac alte lucruri cu el. Metodele noastre actuale nu fac nimic cu datele obiectului Cat . Să ne uităm la un exemplu mai ilustrativ:

public class Truck {

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

    public int getVolume() {
        int volume = length * width * height;
        return volume;
    }
}
De exemplu, aici avem o clasă reprezentând un Camion . Semicamionul are lungime, lățime, înălțime și greutate (de care vom avea nevoie mai târziu). În metoda getVolume() , efectuăm calcule, transformând datele obiectului nostru într-un număr reprezentând volumul acestuia (înmulțim lungimea, lățimea și înălțimea). Acest număr va fi rezultatul metodei. Rețineți că declarația metodei este scrisă ca public int getVolume . Aceasta înseamnă că această metodă trebuie să returneze un int . Am calculat valoarea returnată a metodei, iar acum trebuie să o returnăm programului care a numit metoda noastră. Pentru a returna rezultatul unei metode în Java, folosim cuvântul cheie return. volumul de retur;

Parametrii metodei Java

Putem transmite valori numite „argumente” unei metode atunci când o apelăm. Declarația unei metode include o listă de variabile care ne spun tipul și ordinea variabilelor pe care metoda le poate accepta. Această listă se numește „parametrii metodei”. Metoda getVolume() a clasei noastre Truck nu definește în prezent niciun parametru, așa că haideți să încercăm să extindem exemplul nostru de camion. Creați o nouă clasă numită BridgeOfficer . Acesta este un ofițer de poliție de serviciu la un pod, care verifică toate camioanele care trec pentru a vedea dacă sarcina lor depășește greutatea admisă.

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;
        }
    }
}
Metoda checkTruck acceptă un argument, un obiect Truck , și determină dacă ofițerul va permite sau nu camionul pe pod. În cadrul metodei, logica este destul de simplă: dacă greutatea camionului depășește maximul permis, atunci metoda returnează false . Va trebui să găsească un alt drum :( Dacă greutatea este mai mică sau egală cu maximul, poate trece, iar metoda returnează true. Dacă încă nu înțelegeți pe deplin expresiile „întoarcere” sau „metoda returnează o valoare”, să luăm o pauză de la programare și să le luăm în considerare folosind un exemplu simplu din viața reală. :) Să zicem că te îmbolnăvești și stai acasă de la serviciu câteva zile. Te duci la contabilitate cu avizul medicului, pentru că concediul medical se presupune a fi plătit. Dacă comparăm această situație cu metode, atunci contabilul are un paySickLeave()metodă. Dați un bilet de medic ca argument la această metodă (fără ea, metoda nu va funcționa și nu veți fi plătit!). Apoi se fac calculele necesare în interiorul metodei folosind nota dvs. (contabilul o folosește pentru a calcula cât ar trebui să vă plătească compania), iar rezultatul muncii dumneavoastră (o sumă de bani) vă este returnat. Programul nostru funcționează într-un mod similar. Apelează o metodă, îi transmite date și în cele din urmă primește un rezultat. Iată metoda principală () a programului nostru BridgeOfficer :

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);
}
Creăm două camioane cu încărcături de 10.000 și 20.000. Iar podul unde lucrează ofițerul are o greutate maximă de 15.000. Programul apelează metoda officer.checkTruck(first) . Metoda calculează totul și apoi returnează true , pe care programul îl salvează apoi în variabila booleană canFirstTruckGo . Acum poți face tot ce vrei cu el (la fel cum poți cu banii pe care ți-a dat contabilul). La sfârșitul zilei, codul

boolean canFirstTruckGo = officer.checkTruck(first);
se fierbe până

boolean canFirstTruckGo =  true;
Iată un punct foarte important: instrucțiunea return nu returnează doar valoarea returnată a metodei, ci oprește și rularea metodei! Orice cod care vine după instrucțiunea return nu va fi executat!

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!");
    }
}
Comentariile ofițerului nu vor fi afișate, deoarece metoda a returnat deja un rezultat și a încetat! Programul revine la locul unde a fost apelată metoda. Nu trebuie să urmăriți acest lucru: compilatorul Java este suficient de inteligent pentru a genera o eroare atunci când încercați să scrieți cod după o instrucțiune return .

Avengers: Parameter War

Există situații în care ne vom dori mai multe moduri de a apela o metodă. De ce să nu ne creăm propria noastră inteligență artificială? Amazon are Alexa, Apple are Siri, așa că de ce nu ar trebui să avem unul? :) În filmul Iron Man, Tony Stark își creează propria sa incredibilă inteligență artificială, Jarvis. Să aducem un omagiu acelui personaj minunat și să numim AI-ul nostru în onoarea lui. :) Primul lucru pe care trebuie să-l facem este să-l învățăm pe Jarvis să salute oamenii care intră în cameră (ar fi ciudat dacă un intelect atât de uimitor s-ar dovedi nepoliticos).

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");
    }
}
Ieșire din consolă:
Good evening, Tony Stark. How are you?
Foarte bun! Jarvis este acum capabil să primească oaspeții. Desigur, de cele mai multe ori va fi maestrul lui, Tony Stark. Dar dacă nu vine singur! Metoda noastră sayHi() acceptă doar un argument. Și astfel poate saluta doar o persoană care intră în cameră și o va ignora pe cealaltă. Nu prea politicos, nu ești de acord? :/

Supraîncărcarea metodei Java

În acest caz, putem rezolva problema prin simpla scriere a 2 metode cu același nume, dar cu parametri diferiți:

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?");
    }
}
Aceasta se numește supraîncărcare a metodei. Supraîncărcarea metodelor permite programului nostru să fie mai flexibil și să se adapteze la diferite moduri de lucru. Să analizăm cum funcționează:

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");
    }
}
Ieșire din consolă:
Good evening, Tony Stark. How are you?
Good evening, Tony Stark and Captain America. How are you?
Excelent, ambele versiuni au funcționat. :) Dar nu am rezolvat problema! Dacă sunt trei oaspeți? Am putea, desigur, să supraîncărcăm metoda sayHi() din nou, astfel încât să accepte trei nume de invitați. Dar ar putea fi 4 sau 5. Până la infinit. Nu există o modalitate mai bună de a-l învăța pe Jarvis să gestioneze orice număr de nume, fără a supraîncărca metoda sayHi() de un milion de ori? :/ Sigur că există! Dacă nu ar exista, crezi că Java ar fi cel mai popular limbaj de programare din lume? ;)

public void sayHi(String...names) {

    for (String name: names) {
        System.out.println("Good evening, " + name + ". How are you?");
    }
}
Când ( String... names ) este folosit ca parametru, indică faptul că o colecție de șiruri va fi transmisă metodei. Nu trebuie să precizăm în prealabil câți vor fi, așa că acum metoda noastră este mult mai flexibilă:

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");
    }
}
Ieșire din consolă:
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?
Unele coduri de aici vă vor fi necunoscute, dar nu vă faceți griji. Este simplu la bază: metoda ia fiecare nume pe rând și salută fiecare oaspete! În plus, va funcționa cu orice număr de șiruri trecute! Doi, zece, chiar o mie — metoda va funcționa corect cu orice număr de oaspeți. Mult mai convenabil decât supraîncărcarea metodei pentru toate posibilitățile, nu crezi? :) Iată un alt punct important: ordinea argumentelor contează! Să presupunem că metoda noastră ia un șir și un număr:

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!
    }
}
Dacă metoda sayYourAge a clasei Person ia un șir și un număr ca intrări, atunci programul trebuie să le transmită în acea ordine specifică! Dacă le trecem în altă ordine, compilatorul va genera o eroare și persoana nu va putea să-și spună vârsta. Apropo, constructorii, pe care i-am abordat în ultima lecție, sunt și metode! De asemenea, le puteți supraîncărca (adică creați mai mulți constructori cu seturi diferite de parametri), iar ordinea argumentelor transmise este fundamental și pentru ei. Sunt metode reale! :)

Din nou în ceea ce privește parametrii

Da, scuze, încă nu am terminat cu ele. :) Tema pe care o vom studia acum este foarte importantă. Există o șansă de 90% să fii întrebat despre asta la fiecare interviu viitor! Să vorbim despre trecerea argumentelor la metode. Luați în considerare un exemplu simplu:

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);
    }
}
Mașina timpului are două metode. Amândoi iau ca intrare numărul care reprezintă anul curent și fie măresc, fie scad valoarea acestuia (în funcție de faptul că vrem să mergem în trecut sau în viitor). Dar, după cum puteți vedea din ieșirea consolei, metoda nu funcționează! Ieșire din consolă:
What year is it?
2018
How about now?
2018
Am trecut variabila currentYear la metoda goToPast() , dar valoarea acesteia nu s-a schimbat. Am fost în 2018, și aici am rămas. Dar de ce? :/ Deoarece primitivele din Java sunt transmise metodelor prin valoare. Ce înseamnă asta? Când apelăm metoda goToPast() și îi transmitem variabila int currentYear (=2018) , metoda nu primește variabila currentYear în sine, ci mai degrabă o copie a acesteia. Desigur, valoarea acestei copii este tot 2018, dar orice modificare a copiei nu afectează în niciun fel variabila noastră originală currentYear ! Să facem codul nostru mai explicit și să urmărim ce se întâmplă cu currentYear:

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);
    }
}
Ieșire din consolă:
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
Acest lucru arată în mod clar că variabila transmisă metodei goToPast() este doar o copie a currentYear . Și schimbarea copiei nu afectează valoarea „originală”. „Trecere prin referință” înseamnă exact opusul. Să exersăm pe pisici! Adică, să vedem cum arată trecerea prin referință folosind un exemplu de pisică. :)

public class Cat {

    int age;

    public Cat(int age) {
        this.age = age;
    }
}
Acum, cu ajutorul mașinii noastre a timpului, vom trimite Smudge , prima pisică din lume care călătorește în timp, în trecut și în viitor! Să modificăm clasa TimeMachine astfel încât să funcționeze cu obiecte Cat ;

public class TimeMachine {

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

    public void goToPast(Cat cat) {
        cat.age -= 10;
    }    
}
Acum metodele nu schimbă doar numărul trecut. Mai degrabă, schimbă câmpul specific de vârstă al pisicii . Vă veți aminti că acest lucru nu a funcționat pentru noi cu primitivele, deoarece numărul inițial nu s-a schimbat. Să vedem ce se va întâmpla!

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);
}
Ieșire din consolă:
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
Wow! Acum metoda a făcut altceva: pisica noastră a îmbătrânit drastic, dar apoi a devenit din nou tânără! :) Să încercăm să ne dăm seama de ce. Spre deosebire de exemplul cu primitive, atunci când obiectele sunt transmise unei metode, acestea sunt transmise prin referință. O referință la obiectul smudge original a fost transmisă metodei changeAge() . Deci, când schimbăm smudge.age în interiorul metodei, ne referim la aceeași zonă de memorie în care este stocat obiectul nostru. Este o referire la același Smudge pe care l-am creat inițial. Aceasta se numește „trecere prin referință”! Cu toate acestea, nu totul cu referințe este atât de ușor. :) Să încercăm să ne schimbăm exemplul:

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);
    }
}
Ieșire din consolă:
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
Nu merge din nou! О_О Să ne dăm seama ce s-a întâmplat. :) Are totul de-a face cu metodele goToPast / goToFuture și cu modul în care funcționează referințele. Acum, atenția ta, te rog! Acesta este cel mai important lucru de înțeles despre cum funcționează referințele și metodele. Adevărul este că, atunci când apelăm la metoda goToFuture(Cat cat) , este o copie a referinței la obiectul pisică care este trecută, nu referința în sine. Astfel, atunci când trecem un obiect unei metode, există două referințe la obiect. Acest lucru este foarte important pentru a înțelege ce se întâmplă. Tocmai de aceea vârsta pisicii nu s-a schimbat în ultimul nostru exemplu. În exemplul anterior, când schimbăm vârsta, am luat pur și simplu referința transmisă la goToFuture()metoda și a folosit-o pentru a găsi obiectul în memorie și a-i schimba vârsta ( cat.age += 10 ). Dar acum, în cadrul metodei goToFuture() , creăm un nou obiect ( cat = new Cat(cat.age) ), iar acestui obiect i se atribuie aceeași copie de referință care a fost transmisă metodei. Ca urmare:
  • Prima referință ( Pet smudge = pisică nouă (5) ) indică pisica originală (cu vârsta de 5 ani)
  • După aceea, când am transmis variabilei cat metoda goToPast() și i-am atribuit un obiect nou, referința a fost copiată.
Și asta ne-a adus la rezultatul final: două referințe care indică două obiecte diferite. Dar am schimbat doar vârsta unuia dintre ei (cea creată în interiorul metodei).

cat.age += 10;
Și bineînțeles, în metoda main() putem vedea pe consolă că vârsta pisicii, smudge.age , nu s-a schimbat. La urma urmei, smudge este o variabilă de referință care încă indică obiectul vechi, original, cu vârsta de 5 ani, și nu am făcut nimic cu acel obiect. Toate schimbările noastre de vârstă au fost efectuate pe noul obiect. Deci, se dovedește că obiectele sunt transmise metodelor prin referință. Copiile obiectelor nu sunt niciodată create automat. Dacă treceți un obiect de pisică la o metodă și îi schimbați vârsta, îi veți schimba vârsta. Dar variabilele de referință sunt copiate atunci când se atribuie valori și/sau se apelează metode! Să repetăm ​​aici ceea ce am spus despre trecerea primitivelor: „Când apelăm metoda changeInt() și trecem intvariabila x (=15) , metoda nu primește variabila x în sine, ci mai degrabă o copie a acesteia. Prin urmare, orice modificări aduse copiei nu afectează x- ul nostru originalÎn continuare voi ajunge să mă cert de mai multe ori despre modul în care argumentele sunt transmise în Java (chiar și printre dezvoltatorii experimentați). Dar acum știi exact cum funcționează. Ține-o așa! :) Pentru a consolida ceea ce ați învățat, vă sugerăm să urmăriți o lecție video de la Cursul nostru Java
Comentarii
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION