Pisane dotychczas programy mieściły się w jednej metodzie main()
. Czas zacząć pisać programy składające się z wielu metod.
Ta strona jest częścią materiałów do kursu “Programowanie w Javie z elementami bioinformatyki dla poczatkujących”. Pozostałe materiały znajdziesz tutaj
Czym są metody?
Metoda to wydzielona część klasy, mająca za zadanie wykonanie określonych operacji. Program musi posiadać przynajmniej jedna metodę: main. Inne metody tworzy się w celu wydzielenia fragmentów kodu, które wykonują jakieś konkretne operacje.
Dzielenie kodu na wiele metod niesie liczne korzyści, takie jak:
- podzielenie klasy na pewne logiczne części zwiększa przejrzystość i czytelność kodu
- pozwala znacznie ograniczyć powtarzanie takich samych (lub bardzo podobnych) fragmentów kodu
- umożliwia łatwy dostęp do poszczególnych funkcjonalności obiektów (będzie na ten temat mowa w dalszej części kursu)
Ogólnie dobrze jest trzymać się zasady, że jeśli możemy wydzielić w programie fragmenty odpowiadające poszczególnym zadaniom to należy to zrobić separując je w oddzielnych metodach.
Jak napisałem powyżej, metody są częściami klas co ma swoje konsekwencje w sposobie ich użycia. W wielu przypadkach najpierw tworzymy na podstawie tych klas obiekty a później wywołujemy metody posługując się utworzonymi obiektami. Tak robiliśmy na przykład używając klasy Scanner
:
// Tworzenie obiektu klasy Scanner.
Scanner skaner = new Scanner(System.in);
// Wywołanie na obiekcie metody nextDouble
double liczba = skaner.nextDouble();
Innym sposobem jest używanie metod statycznych. W takim przypadku, nie ma potrzeby tworzenia obiektu, metody są wywoływane bezpośrednio z klasy. Tak było na przykład, gdy wykorzystywaliśmy metody z klasy Math
:
// Bezpośrednie wywołanie metody statycznej
double liczba = Math.round(2.49);
A także, gdy używaliśmy polecenia System.out.println()
do wypisania tekstu na ekranie.
Tworzenie i używanie metod
W ogólnej postaci tworzenie metod wygląda tak:
modyfikatory zwracanaWartość nazwaMetody (argumenty) {
// Zawartość metody
}
Pierwsza linia, zwana jest nagłówkiem metody. Zajmijmy się teraz poszczególnymi elementami nagłówka:
modyfikatory
– służą do określenia cech metody takich jak to czy metoda jest statyczna oraz jaka jest jej dostępność (omówimy to bliżej w przyszłości)zwracanaWartość
– określa jakiego typu wartość jest zwracana przez metodę. Metody mogą zwracać wartości typów podstawowych (int
,double
,char
, etc.), tablice i inne obiekty, albo nie zwracać nic. W tym ostatnim wypadku zamiast typu zwracanej wartości umieszczamy słowovoid
. Zasadniczo metody mogą zwracać co najwyżej jedną wartość, ale da się to obejść używając obiektów.nazwaMetody
– jak sama nazwa wskazuje to nazwa tworzonej przez nas metody ;-). Zasady nazywania metod są podobne do tych, które opisałem dla zmiennych.argumenty
(parametry) – określają jakiego typu wartości są przekazywane do metody i pod jaką nazwą (zmiennej, tablicy etc) będą używane w ciele metody. Można metodzie przekazać dowolną liczbę argumentów ale to co zostaje przekazane musi się zgadzać zarówno pod względem liczby jak i ich typów z tym co zadeklarowano w nagłówku.
Podstawowe sposoby tworzenia i używania metod pokażę na przykładach:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
import java.util.Arrays; public class Metody { // metoda nie przyjmuje argumentów i nie zwraca wartości static void powiedzCzesc() { // metoda wykonuje czynność i kończy działanie nie zwracając wartości System.out.println("Cześć!"); } // metoda przyjmuje argument (String), nic nie zwraca static void powiedzCos(String cos) { // przekazany łańcuch znaków, funkcjonuje w metodzie jako zmienna typu // String o nazwie cos System.out.println("Uwaga: " + cos); } // metoda nie przyjmuje argumentów, zwraca tablicę liczb `double` static double[] wylosujLiczby() { double[] tablica = new double[3]; for (int i = 0; i < 3; i++) { tablica[i] = Math.random(); } // zwracanie wartości (tablicy) return tablica; } // metoda przyjmuje dwa argumenty (int i double) i zwraca wartość double static double dodajLiczby(int l1, double l2) { // wewnątrz metody wywołuję inną zdefiniowaną przeze mnie metodę powiedzCos("Dodaję liczby " + l1 + " oraz " + l2); // nie trzeba tworzyć zmiennej aby zwrócić wartość return l1 + l2; } public static void main(String[] args) { // wywołujemy metodę, nie przekazujemy argumentów, nie odbieramy wartości powiedzCzesc(); // wywołujemy metodę, przekazujemy jej argument (String), nie odbieramy wartości powiedzCos("Witaj Świecie pełny metod!"); // wywołujemy metodę, nie przekazujemy argumentów, odbieramy tablice liczb double double[] wylosowaneLiczby = wylosujLiczby(); System.out.println("Wylosowane: " + Arrays.toString(wylosowaneLiczby)); int liczba1 = 5; double liczba2 = 6.3; // wywołujemy metodę, przekazujemy dwie zmienne przechowujące liczby int // zwracana wartość double jest przypisana zmiennej suma double suma = dodajLiczby(liczba1, liczba2); System.out.println("Suma: " + suma); } } |
W powyższym programie, poza obowiązkową metodą main
stworzyłem a następnie użyłem cztery metody. Za chwilę omówmy je po kolei. Najpierw jednak parę uwag ogólnych:
- We wszystkich powyższych metodach użyłem modyfikatora
static
. Oznacza to, że są to metody statyczne a zatem możemy ich używać bezpośrednio, bez tworzenia obiektu klasyMetody
. - Stworzone przeze mnie metody znajdują się na zewnątrz metody
main
. Wszystkie metody są na tym samym poziomie zagnieżdżenia, wewnątrz klasy. - Metoda
main
znalazła się na końcu klasy. To nie jest konieczność, ale dla przejrzystości kodu lepiej jeśli metodamain
znajduje się na końcu lub na początku klasy. - Metody, które zwracają wartość mają zadeklarowany typ zwracanej wartości w nagłówku oraz w ciele metody komendę
return
. Co więcej, jeśli w nagłówku zadeklarowaliśmy, że metoda ma zwracać wartość określonego typu to w ciele metody musi znaleźć się komendareturn
i musi ona poprzedzać wartość o zadeklarowanym w nagłówku typie.
Komenda ta powinna być ostatnią wykonywaną komendą w metodzie, ponieważ na niej metoda kończy działanie. Komendareturn
może też znaleźć się dodatkowo w innych miejscach metody, ale pamiętaj że w momencie jej wywołania kończy się działanie całej metody. Takie rozwiązanie stosuje się zazwyczaj w połączeniu z konstrukcjami warunkowymi (np.if...else
).
Teraz przyjrzyjmy się poszczególnym metodom i sposobom ich wywołania.
-
Metoda
powiedzCzesc()
– nie przyjmuje ani nie zwraca żadnych wartości. Wywołujemy ją zatem bardzo prosto, wypisując jej nazwę, nie umieszczając w parze nawiasów żadnych wartości. Zwracam uwagę, że nawet jeśli metodzie nie przekazujemy argumentów, koniecznie trzeba po nazwie umieścić parę nawiasów()
. -
Metoda
powiedzCos(String cos)
– przyjmuje łańcuch znaków (String
) jako argument, który zostaje przypisany do zmiennej o nazwiecos
. Zmienna może być użyta wewnątrz metody, ale nie może być użyta poza nią, ponieważ jest widoczna tylko w obrębie metody w której jest zadeklarowana. -
Metoda
wylosujLiczby()
– nie przyjmuje argumentów ale zwraca tablicę liczbdouble
. Za zwracanie wartości w metodzie odpowiada komendareturn
, która zwraca tablicę liczbdouble
o nazwietablica
. Zauważ, że jest to nazwa zupełnie inna od nazwy tablicy, która przyjmuje wynik zwracany przez metodę (wylosowaneLiczby
). Obie tablice „nie widzą się” mogą zatem mieć zupełnie inne albo takie same nazwy, nie ma to znaczenia dla działania programu. -
Metoda
dodajLiczby(int l1, double l2)
– przyjmuje dwie liczby (int
orazdouble
) a zwraca liczbędouble
. Wewnątrz metody wywołujemy inną metodę – jak widać możemy to robić nie tylko z metodymain
, metody „widzą się” w obrębie klasy. Zauważ też, że po komendziereturn
nie umieściłem nazwy zmiennej ale wyrażene, które zwraca wartość, która jest następnie zwracana przez metodę.
Ćwiczenie
Napisz program, który:
- Pobiera od użytkownika określoną liczbę liczb – użytkownik podaje ile to liczb, może też podać nowy zestaw liczb
- Dla podanych liczb użytkownik może uzyskać (menu):
- wypisanie wszystkich danych
- największą liczbę
- najmniejszą liczbę
- średnią arytmetyczną
- sumę liczb
- wariancję
- od razu wszystkie powyższe wartości
- Program działa dopóki użytkownik nie wybierze z menu opcji zakończenia
- Jeśli użytkownik chce uzyskać jakieś parametry statystyczne a jeszcze nie wprowadził danych to uruchamia się kod odpowiedzialny za wprowadzanie danych
Wskazówki
- Zastanów się na jakie oddzielne zadania można podzielić program, które z nich będą wykorzystane wielokrotnie
- Rozdziel fragmenty kodu odpowiedzialne za poszczególne zadania do oddzielnych metod
Rozwiązanie
Na początku należy oddzielić poszczególne zadania, które umieścimy w oddzielnych metodach. Oddziel też obliczenia od wypisywania wyników. Warto też od razu zastanowić się jakie dane będą przyjmować a jakie zwracać poszczególne metody. Oznaczymy je jako „we:” i „wy:”. Ogólnie, wszystkie metody odpowiedzialne za obliczenia powinny przyjmować jako argumenty dane potrzebne do obliczeń a zwracać liczbę, która będzie wynikiem. Metoda odpowiedzialna za pobranie danych od użytkownika, nie powinna pobierać nic a zwracać tablicę liczb double
.
Dla przypomnienia, wzór na wariancję wygląda tak:
gdzie: s2– wariancja, xi – kolejna liczba, xsr – średnia arytmetyczna z liczb, n – liczba danych
- wyświetlanie menu we: nic; wy: nic
- pobranie opcji od użytkownika i uruchomienie odpowiedniej metody – to będzie główna pętla programu, wykonująca się aż do decyzji użytkownika o zakończeniu pracy – we: nic; wy: nic
- pobranie danych od użytkownika – we: nic; wy:
double[]
- obliczenia:
- znalezienie największej liczby – we:
double[]
; wy:double
- znalezienie najmniejszej liczby – we:
double[]
; wy:double
- obliczenie sumy liczb – we:
double[]
; wy:double
- obliczenie średniej arytmetycznej – we:
double[]
; wy:double
- obliczenie wariancji – we:
double[]
; wy:double
- znalezienie największej liczby – we:
- wywołanie odpowiednich metod i wypisanie wszystkich danych i parametrów – we:
double[]
; wy: nic
Następnie zastanów się, czy w poszczególnych metodach nie można wykorzystać innych metod. W takim wypadku można z wnętrza metody wywołac inną metodę i wykorzystać zwróconą przez nią wartość do dalszych obliczeń (lub wypisania zwracanych wartości).
- Metoda odpowiedzialna za wydruk wszystkich parametrów, będzie musiała wywołać wszystkie metody odpowiedzialne za wykonanie obliczeń
- Do obliczenia wariancji potrzebna jest średnia arytmetyczna
- Do obliczenia średniej arytmetycznej potrzebna jest suma liczb
Kompletny kod może wygladać tak:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
import java.util.Scanner; public class ProstaStatystyka { public static void main(String[] args) { opcje(); System.out.println("Dziękuję za współpracę."); } static void wyswietlMenu() { // wyrażenie "\n" wprowadza znak nowej linii System.out.print("\nCo chcesz zrobić?\n" + " d - wprowadź nowe dane \n" + " p - wyświetl wszystkie dane \n" + " n - podaj największą liczbę \n" + " m - podaj najmniejszą liczbę \n" + " s - podaj średnią arytmetyczną \n" + " r - podaj sumę liczb \n" + " w - podaj wariancję \n" + " a - podaj wszystkie powyższe \n" + " k - koniec programu\n" + "> "); } static double[] wprowadzNoweDane() { Scanner skaner = new Scanner(System.in); System.out.println("Podaj ile liczb chcesz wpisać "); int dlTabeli = skaner.nextInt(); double[] liczby = new double[dlTabeli]; for (int i = 0; i < liczby.length; i++) { System.out.println("Podaj " + (i + 1) + " liczbę z " + dlTabeli); liczby[i] = skaner.nextDouble(); } return liczby; } static void wypiszDane(double[] liczby) { for (int i = 0; i < liczby.length; i++) { System.out.println(i + ": " + liczby[i]); } } static double podajNajwiekszaLiczbe(double[] liczby) { double max = liczby[0]; for (double l : liczby) { if (l > max) { max = l; } } return max; } static double podajNajmniejszaLiczbe(double[] liczby) { double min = liczby[0]; for (double l : liczby) { if (l < min) { min = l; } } return min; } static double podajSredniaArytmetyczna(double[] liczby) { return podajSumeLiczb(liczby) / liczby.length; } static double podajSumeLiczb(double[] liczby) { double sum = 0; for (double l : liczby) { sum = sum + l; } return sum; } static double podajWariancje(double[] liczby) { double srednia = podajSredniaArytmetyczna(liczby); double sum = 0.0; for (double l : liczby) { sum = sum + (l - srednia) * (l - srednia); } double wariancja = sum / (liczby.length - 1); return wariancja; } static void podajWszystko(double[] liczby) { System.out.println("******** Wyniki ********"); System.out.println("Dane:"); wypiszDane(liczby); System.out.println("Min: " + podajNajmniejszaLiczbe(liczby)); System.out.println("Max: " + podajNajwiekszaLiczbe(liczby)); System.out.println("Średnia: " + podajSredniaArytmetyczna(liczby)); System.out.println("Suma: " + podajSumeLiczb(liczby)); System.out.println("Wariancja: " + podajWariancje(liczby)); System.out.println("_________________________"); } /** * Pobranie opcji i wybór */ static void opcje() { String opcja = ""; double[] liczby = new double[0]; Scanner skaner = new Scanner(System.in); while (!opcja.equalsIgnoreCase("k")) { wyswietlMenu(); opcja = skaner.nextLine(); if (liczby.length == 0 && !opcja.equalsIgnoreCase("k")) { liczby = wprowadzNoweDane(); } else if (opcja.equalsIgnoreCase("p")) { wypiszDane(liczby); } else if (opcja.equalsIgnoreCase("d")) { liczby = wprowadzNoweDane(); } else if (opcja.equalsIgnoreCase("m")) { System.out.println("Najmniejsza liczba to: " + podajNajmniejszaLiczbe(liczby)); } else if (opcja.equalsIgnoreCase("n")) { System.out.println("Największa liczba to: " + podajNajwiekszaLiczbe(liczby)); } else if (opcja.equalsIgnoreCase("s")) { System.out.println("Średnia arytmetyczna liczb to: " + podajSredniaArytmetyczna(liczby)); } else if (opcja.equalsIgnoreCase("r")) { System.out.println("Suma liczb to: " + podajSumeLiczb(liczby)); } else if (opcja.equalsIgnoreCase("w")) { System.out.println("Wariancja dla podanych liczb to: " + podajWariancje(liczby)); } else if (opcja.equalsIgnoreCase("a")) { podajWszystko(liczby); } else if (opcja.equalsIgnoreCase("k")) { System.out.println("Koniec!"); } else { System.out.println("Nie rozumiem, spróbuj ponownie"); } } } } |
Ta strona jest częścią materiałów do kursu “Programowanie w Javie z elementami bioinformatyki dla poczatkujących”. Pozostałe materiały znajdziesz tutaj
Czy ja dobrze kombinuję, że stosując powyższy kod na rozwiązanie zadania menu wyświetli się dwa razy: 1 raz przy pierwszym wprowadzaniu danych i drugi, żeby program wiedział, co z tymi wprowadzanymi danymi zrobić? Nie można by pierwsze wprowadzanie danych wyrzucić poza pętlę? Fakt że wtedy nie będzie można od razu programu zakończyć