Przekazywanie paramaterów

Przekazywanie parameterów jest kluczowym elementem programowania. Tak naprawdę wszystko, co napiszemy w kodzie powoduje transmisję informacji i jej przetwarzanie. Java jest językiem, który wykorzystuje tylko przekazywanie parametrów przez ich wartość w odróżnieniu od przekazywania przez referencję (wyjaśnienie poniżej).

Po pierwsze musimy wiedzieć choć trochę o tym jak Java zarządza pamięcią. W bardzo dużym skrócie, Java operuje w dwóch „slotach” pamięci. Jeden nazywa się stack i jest pamięcią statyczną. W stack’u przechowywane są dane dotyczące referencji do obiektów oraz wartości typów prymitywnych. Wynika z tego, że jeżeli zainicjalizujemy zmieną int z wartością, np.:

private int myNumber = 22;

to zmienna myNumber zostaje przechowana w pamięci statycznej stack w całości, czyli jej nazwa i wartość.

Stack, czyli stos przechowuje każdą zmienną prymitywną (int, double, long .etc), lub referencję do obiektu po utworzeniu. Wszystkie referencje w toku działania programu trafiają jedna po drugiej na samą górę stosu. Po zakończeniu działania i wyjściu do innego obszaru programu, element, który nie jest już używany znika ze stosu. Kolejny element znajdujący się w stosie pod poprzednim, staje się aktywny do obliczeń.

Ostatni stworzony obiekt customer 2 trafia na szczyt stosu

Pamięć stack jest stosunkowo mała, za to dostęp do przechowywanych wartości jest dla niej dużo szybszy niż w drugim „slocie” pamięci zwanym heap. Heap przechowuje obiekty (w tym też String). Na przykład:

Customer customer = new Customer ("Jan");

W tym przypadku tworzymy obiekt Customer. Obiekt zawiera w sobie pole name typu String. Obiekt Customer zostaje zapisany w pamięci heap. Nazwa wskazująca na obiekt -> customer przechowywana jest natomiast w stack. Pole o nazwie name wskazuje na obiekt typu String, który też jest przechowywany w heap (jak pokazano w bloku nr 3).

Więcej możesz doczytać w dokumentacji Javy stworzonej przez Oracle.

Przykłady praktyczne

Patrząc na poniższe bloki kodu. W bloku 1 operujemy wewnątrz pamięci stack. W bloku 2 stack przechowuje tylko referencje do zmiennych name typu String, natomiast same String’i w heap. Blok 3 pokazuje sytuację, gdy w stack’u mamy tylko referencje do obiektów customer (których pola przechowują informację o nazwie -> obiekt typu String).

Zerknijmy na poniższy kod (gdzie klasa Customer zawiera tylko jedno pole name typu String):

public class Main {

    public static void main(String[] args) {

        // blok 1
        int numberA = 10;
        changeNumber(numberA);
        System.out.println(numberA);

         // blok 2
        String name = "Jan";
        changeName(name);
        System.out.println(name);

         // blok 3
        Customer customer = new Customer("Piotr");
        String newName = "Adam";
        System.out.println(customer.getName());
        changeCustomerName(customer, newName);
        System.out.println(customer.getName());
        reasign(customer, "Anna");
        System.out.println(customer.getName());
        customer = new Customer("Anna");
        System.out.println(customer.getName());

    }

    private static void changeNumber(int numberA) {
        numberA = numberA * 10;
    }

    private static void changeName(String name) {
        name = name + " Kowalski";
    }

    private static void changeCustomerName(Customer customer, String name) {
        customer.setName(name);
    }
    private static void reasign(Customer customer, String name) {
        customer = new Customer(name);
    }
}

Przekazywanie parametrów przez wartość

W pierwszym bloku przypisujemy do zmiennej numberA, która przechowuje typ prymitywny int o wartości 10. Wykonując operację na zmiennej poprzez przekazanie jej do metody changeNumber(), faktycznie wykonujemy operację na kopii wartości, numberA. Po opuszczeniu zakresu objętego metodą (po wykonaniu metody i jej opuszczeniu) kopia zostaje usunięta z pamięci. Wobec tego wywołanie metody System.out.println(numberA) spowoduje odczytanie wartości z pierwotnej zmiennej (nie kopii !) numberA.

Blok nr 1

Przekazywanie obiektu (wskaźnika do niego) przez wartość

W drugim bloku inicjalizujemy zmienną typu String name o wartości “Jan”. String jest typem obiektowym bardzo specyficznym. Dokładnie rzecz ujmując jest obiektem niemutowalnym, tzn nie można zmienić wartości raz zainicjalizowanego String’a. Inicjalizacja obiektu powoduje zapisanie w pamięci podręcznej (stack) nazwy zmiennej, która wskazuje na obiekt w pamięci zarządzanej (heap). Uruchomienie metody changeName() powoduje utworzenie nowego stringa, z nową referencją do niego w pamięci (stack) i umieszczenie tegoż na samej górze stosu. Po zakończeniu działania metody (wyjściu z jej zakresu działania) referencja name, która znajduje się na szczycie stosu zostaje usunięta, a w aktywność wchodzi referencja, która była wcześniej niżej. Pozostała referencja wskazuje na String przechowujący wartość „Jan”. Po wywołaniu System.out.println(name) w odpowiedzi uzyskamy właśnie tę wartość.

Blok nr 2

Przekazywanie obiektu do metody

Trzeci blok jest przykładem przekazywania zmiennej, która jest obiektem (nie String’em!) do metody. Najpierw zostaje zainicjalizowany obiekt Customer, który przechowuje pole typu String “name” z wartością “Piotr”. Obydwa obiekty są przechowywane w pamięci (heap). Zmienna customer (niebieska obwódka) wskazuje na obiekt Customer, natomiast pole name wskazuje na String. Pierwsze wywołanie przeczytania imienia zwróci wartość “Piotr” (czerwona strzałka).

Blok nr 3

Następnie przechodzimy do zakresu metody changeCustomerName(). Przekazujemy w niej referencje do nowego Stringa oraz do obiektu Customer, czyli wskazujemy na to, na co zmienna customer (z niebieską ramką). Użycie settera setName() powoduje zmianę referencji pola na inny String (niebieska strzałka). Kolejne wywołanie nazwy customer’a zwróci zatem wartość “Adam”. Spróbujemy teraz nadpisać obiekt Customer używając metody reasign(). Wewnątrz zakresu działania metody tworzymy nowy obiekt typu Customer oraz nową referencje customer (czerwona ramka), która trafia na samą górę stosu. Po wykonaniu i opuszczeniu metody wracamy do zakresu metody main(), a referencja do nowo stworzonego obiektu znika ze stosu. Pozostała referencja customer odnosi się do “starego” obiektu. Kolejne wywołanie println wyświetli ponownie String “Adam”.
Jeżeli przypisanie nowego obiektu Customer dokonamy w zakresie metody main() (fioletowa strzałka). Spowoduje to zmianę wskaźnika zmiennej customer na nowy obiekt. Ostatnie wywołanie wypisania imienia obiektu Customer zwróci więc wartość “Anna”.

Garbage Collector

Można tutaj zadać pytanie. Co się dzieje z obiektami, które przestały być “potrzebne” (np po zmianie wskaźnika)? Java posiada system zarządzania pamięcią. Niepotrzebne obiekty są usuwane z pamięci przy użyciu tzw. Garbage Collector’a.

P.S. Jeżeli chcesz spróować swoich sił w Javie, zapraszam do pobrania ebook’a i uczestnictwa w darmowym kursie.

1 myśl na temat “Przekazywanie paramaterów

  1. Pingback: Jaka klasa taki kod! - { Lekcje Kodu } Java %

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *