27 maja 2008

SCJP – Obiekty typu tablicowego

Dziś będzie parę słów wstępu o tablicach – deklaracje i podstawy konstrukcji; inicjalizacja w następnym artykule. Zanim opuścisz ten rozdział upewnij się, że wiesz na temat tablic w Javie wszystko; nie jest to bowiem temat aż tak banalny, jakim mógłby się wydawać.

Tablice w języku Java są obiektami przechowującymi listę zmiennych pewnego ustalonego typu. Deklarując tablicę podajemy właśnie typ zmiennych, które będzie ona przechowywała oraz wymiar (ang. dimension) – ale nie rozmiar(!) – tablicy. Więcej o deklarowaniu tablic w artykule "SCJP - Deklaracja zmiennych i stałych". Deklaracja tablicy, czyli mówiąc precyzyjniej deklaracja referencji na obiekt typu tablicowego to jednak tylko deklaracja referencji – nie powoduje utworzenia żadnego obiektu. Deklarując referencję typu tablicowego nie podajemy rozmiaru tablicy, bo jest zbędny. Jest on natomiast wymagany, gdy tworzymy właściwy obiekt tablicy; musi być bowiem wiadomym, jak duży obszar pamięci trzeba zaalokować. Poniżej dwa przykłady pokazujące deklaracje i utworzenie obiektów tablic.

int[] nums = new int[16];

Thread[] threads = new Thread[4];

Jak pisałem w artykule dotyczącym inicjalizacji zmiennych "SCJP - Wartości domyślne zmiennych" elementy tablic są zawsze inicjowane domyślnymi wartościami. Zauważmy jednak, że wartością domyślną dla zmiennej referencyjnej jest ‘null’, tak więc druga z pokazanych deklaracji tablic bynajmniej nie spowoduje utworzenia czterech instancji obiektów klasy ‘Thread’. Elementy tej tablicy to referencje a nie obiekty i będą one miały wartość ‘null’.

Tablice wielowymiarowe to de facto tablice tablic. Zdaje się, że stwierdzenie to jest banałem i zupełnie nic nie wnosi, ale jest wprost przeciwnie. Tablica dwu-wymiarowa dla przykładu nie musi być bowiem tablicą "prostokątną" a trójwymiarowa "kostką”. Wszystko powinien wyjaśnić poniższy przykład.

public class Test {
public static void main(String[] args) {
int[][] myTable = new int[4][];

myTable[0] = new int[2];
myTable[1] = new int[4];
myTable[2] = new int[6];
myTable[3] = new int[8];
}
}

Powyższy kod jest jak najbardziej poprawny – kompiluje się i wykonuje, choć nic ciekawego nie robi. Zacznijmy analizę od pierwszej linii. Deklarujemy w niej referencję na obiekt tablicy. Jak już powiedzieliśmy, tablica przechowuje zmienne pewnego ustalonego typu – w naszym przypadku jest to typ tablicowy, przechowujący z kolei zmienne typu ‘int’. Tworzymy też obiekt tablicy i mówimy, że obiekt ten ma zawierać cztery referencje do obiektów typu ‘int[]’. Referencje te póki co mają wartość ‘null’; nie określiliśmy bowiem "drugiego wymiaru" tablicy i kompilator nie wiedział jakie obiekty utworzyć. Obiekty te tworzymy sami w kolejnych liniach programu. Naturalnie zadanie to możemy powierzyć kompilatorowi – wystarczy, że określimy "drugi wymiar", tak jak pokazuje poniższy przykład.

public class Test {
public static void main(String[] args) {
int[][] myTable = new int[4][2];

myTable[1] = new int[4];

for(int[] subTable : myTable) {
for(int i : subTable)
System.out.print(i + " ");

System.out.println();
}
}
}

Żeby przykład nie był zbyt banalny oraz żeby pokazać, że w istocie tablica ‘myTable’ zawiera nic więcej jak tylko cztery referencje na obiekty typu ‘int[]’ w drugiej linii kodu przypisuje do jednej z tych referencji nowy obiekt – tablicę zawierającą cztery zmienne typu ‘int’, w miejsce tablicy zawierającej dwie zmienne. Efektem uruchomienia powyższego programu będzie pokazana poniżej mozaika zer – domyślnych wartości zmiennych typu ‘int’.

0 0
0 0 0 0
0 0
0 0

Na koniec pytanie. Wiemy, że tablice w języku Java indeksowane są począwszy od zera, ale co się stanie, gdy odwołamy się w kodzie (explicite) do elementu tablicy o indeksie ujemnym? Czy wszystko będzie w porządku, o ile modulo z tej wartości jest poprawnym indeksem? A może zostanie zgłoszony wyjątek w czasie wykonania? A może kompilator wie, że niezależnie od tego, jaka tablica by nie była duża, to i tak indeks ujemny jest niepoprawny a więc zgłosi wyjątek już w trakcie kompilacji?

1 komentarz:

Anonimowy pisze...

"Deklarując tablicę podajemy właśnie typ zmiennych, które będzie ona przechowywała " - czy na pewno?
A co bedzie jeśli zrobię coś takiego:

Object []tab = new String[3];
tab[0] = new Object();//błąd

Kod się oczywiście nie skompiluje, bo tablica przyjmuje obiekty typu String a nie typu Object. Więc typ zmiennych jakie będzie ona przychowywała definiujemy przy tworzeniu tablicy a nie podczas jej deklaracji.