23 maja 2008

SCJP – Zmienne typów prostych i referencyjnych

Miało być dziś o czymś innym a to, co powstało miało być tylko wstępem do planowanego artykułu, ale treść zaczęła rosnąć i rosnąć i tak oto uzyskała własną tożsamość. Będzie dziś zatem o zmiennych. Właściwie to o czymś, czemu trudno wymyślić zwięzły tytuł. W każdym razie po przeczytaniu całości powinno być jasne, o czym było.

Zmienne w języku Java mogą być zasadniczo dwojakiego rodzaju – typu prostego albo referencyjnego. Zmienne typu prostego to pewna ilość bajtów pamięci – np. 4 bajty dla typu ‘int’ – przechowujących explicite wartość zmiennej. Zmienne typu referencyjnego to również pewna ustalona ilość bajtów pamięci, ale zapisana tam wartość nie jest bezpośrednio dla nas użyteczna. Możemy o tej wartości myśleć jak o wskaźniku na obiekt, ale bynajmniej nie musi to być adres obszaru pamięci, w którym przechowywany jest obiekt. To, jaka dokładnie wartość jest przechowywana w zmiennej referencyjnej zależy od implementacji JVM (akr. Java Virtual Machine) a my potrzebujemy wiedzieć tylko tyle, że wartość ta jednoznacznie reprezentuje pewien obiekt, albo referencję pustą, czyli ‘null’. Zerknijmy na poniższy przykład.

class SomeClass {
private String descr = "default value";

public String getDescr() {
return descr;
}

public void setDescr(String descr) {
this.descr = descr;
}
}

public class Test {
public static void main(String[] args) {
testPrimitives();
testReferences();
}

static void testPrimitives() {
int x = 1;
int y = x;

x = 2;

System.out.println("y: " + y);
}

static void testReferences() {
SomeClass a = new SomeClass();
SomeClass b = a;

a.setDescr("new value");

System.out.println("b descr: " + b.getDescr());
}
}

Przypatrzmy się metodzie ‘testPrimitives()’. Deklarujemy zmienną ‘x’ typu prostego ‘int’ i przypisujemy jej wartość literału ‘1’. Powoduje to alokację 4 bajtów pamięci i zapisanie w tym obszarze bitowej reprezentacji liczby 1. Następnie deklarujemy zmienną ‘y’ i przypisujemy jej wartość zmiennej ‘x’ – wartość zmiennej ‘x’, a nie "zmienną x", cokolwiek by to mogło znaczyć. Mamy zatem kolejne 4 bajty pamięci i zapisaną w nich bitową reprezentację liczby 1, bo taka była wartość zmiennej ‘x’. Następnie do zmiennej ‘x’ przypisujemy wartość literału ‘2’. Powoduje to zapisanie w obszarze pamięci, który reprezentuje zmienną ‘x’ reprezentacji bitowej liczby 2. Co dzieje się w tym czasie ze zmienną ‘y’ i jej wartością? Nic, a cóż by się miało dziać. Na końcu wpisujemy wartość zmiennej ‘y’ i widzimy, że rzeczywiście nic się z nią nie stało – program wyświetla napis "y: 1".

A teraz zerknijmy na metodę ‘testReferences()’ – jest ona skonstruowana analogicznie do metody poprzedniej, ale operuje na zmiennych referencyjnych i obiektach. W pierwszej linii deklarujemy zmienną referencyjną ‘a’, co powoduje alokację przestrzeni pamięci potrzebną do reprezentacji "wskaźnika" do obiektu. Tworzymy także nowy obiekt, co pociąga za sobą w skutku wiele operacji, między innymi alokację pamięci niezbędnej do reprezentowania stanu obiektu oraz inicjalizację tego stanu, tj. uruchomienie konstruktorów. W końcu "wskaźnik" obiektu jest zapisywany jako wartość zmiennej referencyjnej. W drugiej linii kodu deklarujemy kolejną zmienną. Jako wartość zmiennej ‘b’ przypisywana jest wartość zmiennej ‘a’ a więc "wskaźnik" na obiekt utworzony uprzednio. Następnie, używając referencji ‘a’ wywołujemy metodę setDescr(…) naszego obiektu, tego samego, na który wskazuje zmienna ‘b’. Nic zatem dziwnego, że program wypisze "b descr: new value".

Brak komentarzy: