CodeGym/Blog Java/Random-PL/Metody w Javie
Autor
John Selawsky
Senior Java Developer and Tutor at LearningTree

Metody w Javie

Opublikowano w grupie Random-PL
Witam ponownie! Na ostatniej lekcji zapoznaliśmy się z klasami i konstruktorami oraz nauczyliśmy się tworzyć własne. Dziś poznamy bliżej Metody Javy, które są nieodzowną częścią zajęć. Metody w Javie to zestaw poleceń, które umożliwiają wykonanie określonej operacji w programie. Innymi słowy, metoda jest funkcją; coś, co twoja klasa jest w stanie zrobić. W innych językach programowania metody są często nazywane „funkcjami”, ale w Javie słowo „metoda” jest bardziej powszechne. :) Jak pamiętacie, na ostatniej lekcji stworzyliśmy proste metody dla klasy Cat , aby nasze koty mogły miauczeć i skakać:
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() to metody naszej klasy. Uruchomienie tych metod skutkuje następującym wyjściem konsoli:
Meow!
Pounce!
Nasze metody są dość proste: po prostu wysyłają tekst do konsoli. Ale w Javie metody mają ważne zadanie: wykonują działania na danych obiektu. Zmieniają dane obiektu, przekształcają go, wyświetlają i robią z nim inne rzeczy. Nasze obecne metody nie robią nic z danymi obiektu Cat . Spójrzmy na bardziej obrazowy przykład:
public class Truck {

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

    public int getVolume() {
        int volume = length * width * height;
        return volume;
    }
}
Na przykład tutaj mamy klasę reprezentującą Truck . Półciężarówka ma długość, szerokość, wysokość i wagę (które będą potrzebne później). W metodzie getVolume() wykonujemy obliczenia, zamieniając dane naszego obiektu na liczbę reprezentującą jego objętość (mnożymy długość, szerokość i wysokość). Ta liczba będzie wynikiem metody. Zauważ, że deklaracja metody jest zapisana jako public int getVolume . Oznacza to, że ta metoda musi zwrócić int . Obliczyliśmy wartość zwracaną przez metodę i teraz musimy zwrócić ją do programu, który wywołał naszą metodę. Aby zwrócić wynik metody w Javie, używamy słowa kluczowego return. objętość zwrotu;

Parametry metody Java

Możemy przekazywać wartości zwane „argumentami” do metody podczas jej wywoływania. Deklaracja metody zawiera listę zmiennych, które informują nas o typie i kolejności zmiennych, które metoda może zaakceptować. Ta lista jest nazywana „parametrami metody”. Metoda getVolume() naszej klasy Truck nie definiuje obecnie żadnych parametrów, więc spróbujmy rozszerzyć nasz przykład ciężarówki. Utwórz nową klasę o nazwie BridgeOfficer . To policjant dyżurujący na moście, który sprawdza wszystkie przejeżdżające ciężarówki, czy ich ładunek nie przekracza dopuszczalnej masy.
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 przyjmuje jeden argument, obiekt Truck i określa, czy funkcjonariusz wpuści ciężarówkę na most. Wewnątrz metody logika jest dość prosta: jeśli masa ciężarówki przekracza dozwolone maksimum, metoda zwraca false . Będzie musiał znaleźć inną drogę :( Jeśli waga jest mniejsza lub równa maksimum, może przejść, a metoda zwraca true. Jeśli jeszcze nie do końca rozumiesz zwroty „return” lub „metoda zwraca wartość”, zróbmy sobie przerwę od programowania i rozważmy je na prostym przykładzie z życia wziętym. :) Załóżmy, że zachorujesz i zostaniesz w domu przez kilka dni. Idziesz do księgowości ze zwolnieniem lekarskim, bo zwolnienie chorobowe ma być płatne. Jeśli porównamy tę sytuację z metodami, to księgowy ma paySickLeave ()metoda. Jako argument przemawiający za tą metodą przekazujesz zaświadczenie lekarskie (bez niego metoda nie zadziała i nie dostaniesz zapłaty!). Następnie w ramach metody dokonywane są niezbędne obliczenia przy użyciu Twojej notatki (księgowy oblicza na jej podstawie, ile firma powinna Ci zapłacić), a wynik Twojej pracy (kwota pieniędzy) jest Ci zwracany. Nasz program działa w podobny sposób. Wywołuje metodę, przekazuje do niej dane i ostatecznie otrzymuje wynik. Oto metoda main() naszego programu 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);
}
Tworzymy dwie ciężarówki o ładowności 10 000 i 20 000. A most, na którym pracuje oficer, ma maksymalną masę 15 000. Program wywołuje metodę Officer.checkTruck(first) . Metoda oblicza wszystko, a następnie zwraca wartość true , którą następnie program zapisuje w zmiennej boolowskiej canFirstTruckGo . Teraz możesz zrobić z nią, co chcesz (tak samo jak z pieniędzmi, które dał ci księgowy). Na koniec dnia kod
boolean canFirstTruckGo = officer.checkTruck(first);
sprowadza się do
boolean canFirstTruckGo =  true;
Oto bardzo ważny punkt: instrukcja return nie tylko zwraca wartość zwracaną przez metodę, ale także zatrzymuje jej działanie! Żaden kod, który pojawi się po instrukcji return nie zostanie wykonany!
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!");
    }
}
Komentarze oficera nie zostaną wyświetlone, ponieważ metoda już zwróciła wynik i została zakończona! Program wraca do miejsca wywołania metody. Nie musisz na to uważać: kompilator Java jest wystarczająco inteligentny, aby wygenerować błąd podczas próby napisania kodu po instrukcji return .

Avengers: Wojna parametrów

Są sytuacje, w których będziemy potrzebować kilku sposobów wywołania metody. Dlaczego nie stworzyć własnej sztucznej inteligencji? Amazon ma Alexę, Apple ma Siri, więc dlaczego nie mielibyśmy go mieć? :) W filmie Iron Man Tony Stark tworzy własną niesamowitą sztuczną inteligencję, Jarvisa. Oddajmy hołd tej niesamowitej postaci i nazwijmy naszą sztuczną inteligencję na jego cześć. :) Pierwszą rzeczą, którą musimy zrobić, to nauczyć Jarvisa witania się z osobami wchodzącymi do pokoju (dziwne byłoby, gdyby tak niesamowity intelekt okazał się niegrzeczny).
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");
    }
}
Wyjście konsoli:
Good evening, Tony Stark. How are you?
Bardzo dobry! Jarvis może teraz przyjmować gości. Oczywiście najczęściej będzie to jego mistrz, Tony Stark. Ale co jeśli nie przyjdzie sam! Nasza metoda sayHi() akceptuje tylko jeden argument. I tak może powitać tylko jedną osobę wchodzącą do pokoju, a drugą zignoruje. Niezbyt grzecznie, nie sądzisz? :/

Przeciążanie metod Java

W takim przypadku możemy rozwiązać problem, po prostu pisząc 2 metody o tej samej nazwie, ale różnych parametrach:
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?");
    }
}
Nazywa się to przeciążaniem metody. Przeciążanie metod sprawia, że ​​nasz program jest bardziej elastyczny i dostosowuje się do różnych sposobów pracy. Sprawdźmy, jak to działa:
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");
    }
}
Wyjście konsoli:
Good evening, Tony Stark. How are you?
Good evening, Tony Stark and Captain America. How are you?
Świetnie, obie wersje zadziałały. :) Ale nie rozwiązaliśmy problemu! A co jeśli jest trzech gości? Moglibyśmy oczywiście ponownie przeciążyć metodę sayHi() , tak aby akceptowała trzy nazwy gości. Ale może być ich 4 lub 5. Aż do nieskończoności. Czy nie ma lepszego sposobu nauczenia Jarvisa obsługi dowolnej liczby nazw bez milionowego przeciążania metody sayHi() ? :/ Oczywiście, że tak! Gdyby nie było, czy myślisz, że Java byłaby najpopularniejszym językiem programowania na świecie? ;)
public void sayHi(String...names) {

    for (String name: names) {
        System.out.println("Good evening, " + name + ". How are you?");
    }
}
Gdy ( String... names ) jest używany jako parametr, oznacza to, że kolekcja Strings zostanie przekazana do metody. Nie musimy z góry określać, ile ich będzie, więc teraz nasza metoda jest znacznie bardziej elastyczna:
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");
    }
}
Wyjście konsoli:
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?
Niektóre kody tutaj będą dla ciebie nieznane, ale nie martw się o to. To proste w swej istocie: metoda bierze po kolei każde imię i wita każdego gościa! Ponadto będzie działać z dowolną liczbą przekazanych ciągów! Dwóch, dziesięciu, a nawet tysiąca – metoda sprawdzi się przy dowolnej liczbie gości. O wiele wygodniejsze niż przeciążanie metody dla wszystkich możliwości, nie sądzisz? :) Oto kolejna ważna kwestia: kolejność argumentów ma znaczenie! Powiedzmy, że nasza metoda pobiera ciąg znaków i liczbę:
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!
    }
}
Jeśli metoda sayYourAge klasy Person pobiera ciąg znaków i liczbę jako dane wejściowe, program musi przekazać je w określonej kolejności! Jeśli podamy je w innej kolejności, kompilator wygeneruje błąd i osoba nie będzie mogła podać swojego wieku. Nawiasem mówiąc, konstruktory, o których mówiliśmy na ostatniej lekcji, to także metody! Można je też przeciążać (np. tworzyć kilka konstruktorów z różnymi zestawami parametrów) i kolejność przekazywanych argumentów też jest dla nich fundamentalnie ważna. To są prawdziwe metody! :)

Jeszcze raz odnośnie parametrów

Tak, przepraszam, jeszcze z nimi nie skończyliśmy. :) Temat, który będziemy teraz studiować, jest bardzo ważny. Istnieje 90% szans, że zostaniesz o to zapytany na każdej przyszłej rozmowie kwalifikacyjnej! Porozmawiajmy o przekazywaniu argumentów do metod. Rozważ prosty przykład:
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);
    }
}
Wehikuł czasu ma dwie metody. Oba przyjmują jako dane wejściowe liczbę reprezentującą bieżący rok i albo zwiększają, albo zmniejszają jej wartość (w zależności od tego, czy chcemy przenieść się w przeszłość, czy w przyszłość). Ale, jak widać z danych wyjściowych konsoli, metoda nie działa! Wyjście konsoli:
What year is it?
2018
How about now?
2018
Przekazaliśmy zmienną currentYear do metody goToPast() , ale jej wartość się nie zmieniła. Byliśmy w 2018 i tu zostaliśmy. Ale dlaczego? :/ Ponieważ prymitywy w Javie są przekazywane metodom przez wartość. Co to znaczy? Kiedy wywołujemy metodę goToPast() i przekazujemy do niej zmienną int currentYear (=2018) , metoda nie pobiera samej zmiennej currentYear , ale raczej jej kopię. Oczywiście wartość tej kopii to również 2018, ale wszelkie zmiany w kopii nie wpływają w żaden sposób na naszą pierwotną zmienną currentYear ! Uczyńmy nasz kod bardziej przejrzystym i obserwujmy, co dzieje się z 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);
    }
}
Wyjście konsoli:
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
To wyraźnie pokazuje, że zmienna przekazana do metody goToPast() jest tylko kopią zmiennej currentYear . A zmiana kopii nie wpływa na „oryginalną” wartość. „Przejdź przez referencję” oznacza coś dokładnie odwrotnego. Poćwiczmy na kotach! To znaczy, zobaczmy, jak wygląda przekazywanie przez referencję na przykładzie kota. :)
public class Cat {

    int age;

    public Cat(int age) {
        this.age = age;
    }
}
Teraz za pomocą naszego wehikułu czasu wyślemy Smudge'a , pierwszego na świecie kota podróżującego w czasie, w przeszłość i przyszłość! Zmodyfikujmy klasę TimeMachine tak, aby działała z obiektami Cat ;
public class TimeMachine {

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

    public void goToPast(Cat cat) {
        cat.age -= 10;
    }
}
Teraz metody nie tylko zmieniają przekazaną liczbę. Zamiast tego zmieniają pole wieku tego konkretnego kota . Pamiętasz, że to nie działało dla nas z prymitywami, ponieważ pierwotna liczba się nie zmieniła. Zobaczmy, co się stanie!
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);
}
Wyjście konsoli:
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! Teraz metoda zrobiła coś innego: nasz kot drastycznie się postarzał, ale potem znowu odmłodniał! :) Spróbujmy dowiedzieć się, dlaczego. W przeciwieństwie do przykładu z prymitywami, gdy obiekty są przekazywane do metody, są one przekazywane przez referencję. Odwołanie do oryginalnego obiektu smudge zostało przekazane do metody changeAge() . Tak więc, kiedy zmieniamy smudge.age wewnątrz metody, odwołujemy się do tego samego obszaru pamięci, w którym przechowywany jest nasz obiekt. Jest to odniesienie do tego samego Smużenia, które stworzyliśmy na początku. Nazywa się to „przekazywaniem przez referencję”! Jednak nie wszystko z referencjami jest takie proste. :) Spróbujmy zmienić nasz przykład:
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);
    }
}
Wyjście konsoli:
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
Znowu nie działa! О_О Dowiedzmy się, co się stało. :) Ma to wszystko wspólnego z metodami goToPast / goToFuture i tym, jak działają referencje. A teraz proszę o uwagę! Jest to najważniejsza rzecz do zrozumienia, jak działają referencje i metody. Faktem jest, że gdy wywołujemy metodę goToFuture(Cat cat) , przekazywana jest kopia odwołania do obiektu cat, a nie samo odwołanie. Tak więc, kiedy przekazujemy obiekt do metody, istnieją dwa odniesienia do obiektu. Jest to bardzo ważne dla zrozumienia tego, co się dzieje. Właśnie dlatego wiek kota nie zmienił się w naszym ostatnim przykładzie. W poprzednim przykładzie, zmieniając wiek, po prostu wzięliśmy odwołanie przekazane do goToFuture()i użył jej do znalezienia obiektu w pamięci i zmiany jego wieku ( cat.age += 10 ). Ale teraz wewnątrz metody goToFuture() tworzymy nowy obiekt ( cat = new Cat(cat.age) ), a temu obiektowi przypisywana jest ta sama kopia referencyjna, która została przekazana do metody. W rezultacie:
  • Pierwsze odniesienie ( Smuda kota = nowy kot (5) ) wskazuje na oryginalnego kota (w wieku 5 lat)
  • Następnie, kiedy przekazaliśmy zmienną cat metodą goToPast() i przypisaliśmy jej nowy obiekt, referencja została skopiowana.
I to doprowadziło nas do ostatecznego wyniku: dwóch odniesień wskazujących na dwa różne obiekty. Zmieniliśmy jednak tylko wiek jednego z nich (tego utworzonego wewnątrz metody).
cat.age += 10;
I oczywiście w metodzie main() możemy zobaczyć na konsoli, że wiek kota, smudge.age , nie zmienił się. W końcu smuga jest zmienną referencyjną, która nadal wskazuje na stary, oryginalny obiekt w wieku 5 lat, a my nic nie zrobiliśmy z tym obiektem. Wszystkie nasze zmiany wiekowe zostały wykonane na nowym obiekcie. Okazuje się więc, że obiekty są przekazywane do metod przez referencję. Kopie obiektów nigdy nie są tworzone automatycznie. Jeśli przekażesz obiekt cat do metody i zmienisz jego wiek, zmienisz również jego wiek. Ale zmienne referencyjne są kopiowane podczas przypisywania wartości i/lub wywoływania metod! Powtórzmy tutaj to, co powiedzieliśmy o przekazywaniu prymitywów: „Kiedy wywołujemy metodę changeInt() i przekazujemy intzmienna x (=15) , metoda nie pobiera samej zmiennej x , ale raczej jej kopię. Dlatego wszelkie zmiany wprowadzone w kopii nie wpływają na nasz oryginalny xNadal będę się niejednokrotnie kłócić o sposób przekazywania argumentów w Javie (nawet wśród doświadczonych programistów). Ale teraz dokładnie wiesz, jak to działa. Tak trzymaj! :) Aby utrwalić to, czego się nauczyłeś, zalecamy obejrzenie lekcji wideo z naszego kursu języka Java
Komentarze
  • Popularne
  • Najnowsze
  • Najstarsze
Musisz się zalogować, aby dodać komentarz
Ta strona nie ma jeszcze żadnych komentarzy