Słowo kluczowe static
W niektórych sytuacjach istnieje potrzeba zdefiniowania składowej klasy, której można używać niezależnie od obiektów tej klasy. Zwykle składowej klasy używamy za pośrednictwem obiektu tej klasy, ale możliwe jest również stworzenie samodzielnej składowej, której możemy używać bez odwoływania się do konkretnego obiektu. Składową taką tworzymy, poprzedzając jej deklarację słowem kluczowym static. Gdy zadeklarujesz składową jako static, możesz zacząć używać jej, zanim utworzysz pierwszy obiekt tej klasy, gdyż odwołanie się do tej składowej nie wymaga użycia referencji obiektu. Składowymi static mogą być zarówno zmienne, jak i metody. Najbardziej znanym przykładem składowej static jest metoda main(). Metodę main() zadeklarowano jako static, ponieważ musi zostać wywołana przez maszynę wirtualną Javy w momencie uruchomienia programu. Aby odwołać się do składowej static na zewnątrz klasy, poprzedzasz jej nazwę kropką i nazwą klasy. Nie musisz tworzyć żadnego obiektu. Na przykład jeśli chcesz przypisać wartość 10 zmiennej static o nazwie count należącej do klasy Timer, napiszesz następujący wiersz kodu:
Przypomina to odwołanie do zwykłej składowej obiektu, z tą różnicą, że zamiast nazwy obiektu użyta zostaje nazwa klasy. W ten sam sposób, poprzedzając kropką i nazwą klasy, możesz wywoływać metody static. Zmienne zadeklarowane jako static są w zasadzie zmiennymi globalnymi. Utworzenie obiektu nie powoduje powstania kopii zmiennej static. Wszystkie obiekty klasy współdzielą tę samą zmienną. Na listingu 6.16 przedstawiłem program ilustrujący różnice pomiędzy zwykłymi składowymi klasy
a składowymi static.
Listing 6.16. SDemo.java
class StaticDemo {
int x; // zwykła zmienna składowa
static int y; // zmienna static Istnieje tylko jedna kopia składowej y, wspólna dla wszystkich obiektów.
// Zwraca sumę składowej x
// i zmiennej static y.
int sum() {
return x + y;
}
}
class SDemo {
public static void main(String args[]) {
StaticDemo ob1 = new StaticDemo();
StaticDemo ob2 = new StaticDemo();
// Każdy obiekt ma własną kopię składowej x.
ob1.x = 10;
ob2.x = 20;
System.out.println("Oczywiście, składowe ob1.x i ob2.x " + "są niezależne.");
System.out.println("ob1.x: " + ob1.x + " ob2.x: " + ob2.x);
System.out.println();
// Każdy obiekt współdzieli z innymi jedną kopię zmiennej static.
System.out.println("Zmienna y jest zadeklarowana jako static i tym samym współdzielona.");
StaticDemo.y = 19;
System.out.println("Nadaje StaticDemo.y wartość 19.");
System.out.println("ob1.sum(): " + ob1.sum());
System.out.println("ob2.sum(): " + ob2.sum());
System.out.println();
StaticDemo.y = 100;
System.out.println("Zmienia wartość StaticDemo.y na 100");
System.out.println("ob1.sum(): " + ob1.sum());
System.out.println("ob2.sum(): " + ob2.sum());
System.out.println(); }
}
Wykonanie powyższego programu spowoduje wyświetlenie następujących informacji:
Oczywiście, składowe ob1.x i ob2.x są niezależne.
ob1.x: 10
ob2.x: 20
Zmienna y jest zadeklarowana jako static i tym samym współdzielona.
Nadaje StaticDemo.y wartość 19.
ob1.sum(): 29
ob2.sum(): 39
Zmienia wartość StaticDemo.y na 100
ob1.sum(): 110
ob2.sum(): 120
Jak zauważyłeś, zmienna y jest współdzielona przez obiekty ob1 i ob2. Zmiana jej wartości dotyczy całej klasy, a nie pojedynczego obiektu. Różnica pomiędzy metodą static i zwykłą metodą sprowadza się do wywołania tej pierwszej jedynie przy użyciu nazwy klasy, bez konieczności tworzenia jakiegokolwiek obiektu tej klasy. Przykład takiej metody poznałeś już w poprzednich rozdziałach: metoda sqrt() jest metodą zadeklarowaną jako static w standardowej klasie Math. Przykład użycia metody static przedstawiłem w programie pokazanym na listingu 6.17.
Listing 6.17. SDemo2.java
class StaticMeth {
static int val = 1024; // zmienna static
// metoda static
static int valDiv2() {
return val/2;
}
}
class SDemo2 {
public static void main(String args[]) {
System.out.println("Wartość składowej val:" + StaticMeth.val);
System.out.println("StaticMeth.valDiv2(): " + StaticMeth.valDiv2());
StaticMeth.val = 4;
System.out.println("Wartość składowej val:" + StaticMeth.val);
System.out.println("StaticMeth.valDiv2(): " + StaticMeth.valDiv2());
}
}
A oto wynik działania tego programu:
StaticMeth.valDiv2(): 512
Wartość składowej val:4
StaticMeth.valDiv2(): 2
Z metodami static związanych jest kilka ograniczeń:
- mogą bezpośrednio wywoływać tylko inne metody static;
- mogą bezpośrednio używać tylko zmiennych składowych zadeklarowanych jako static;
- nie dysponują referencją this.
Dlatego też definicja metody valDivDenom() przedstawionej na listingu 6.18 jest niepoprawna.
Listing 6.18. StaticError.java
int denom = 3; // zwykła zmienna składowa
static int val = 1024; // zmienna static
/* Błąd! Dostęp do zwykłej składowej
nie jest możliwy z wnętrza metody static. */
static int valDivDenom() {
return val/denom; // nie zostanie skompilowana!
}
}
W tym przypadku denom jest zwykłą zmienną składową i wobec tego nie jest dostępna dla metody zadeklarowanej jako static.
Bloki static
W niektórych sytuacjach klasa może wymagać pewnej inicjalizacji, zanim będzie gotowa do tworzenia obiektów. Na przykład może zaistnieć potrzeba nawiązania połączenia w sieci. Lub niektóre zmienne static wymagają inicjalizacji, zanim zostanie użyta jakakolwiek metoda static. Aby zaradzić takim problemom, Java umożliwia definiowanie bloków static. Blok taki zostaje wykonany w momencie załadowania klasy, a zatem zanim klasa może zostać użyta w jakimkolwiek innym celu. Przykład deklaracji bloku static pokazałem w programie przedstawionym na listingu 6.19.
Listing 6.19. SDemo3.java
class StaticBlock {
static double rootOf2;
static double rootOf3;
static { // Ten blok zostaje wykonany w podczas ładowania klasy.
System.out.println("Wewnątrz bloku static.");
rootOf2 = Math.sqrt(2.0);
rootOf3 = Math.sqrt(3.0);
}
StaticBlock(String msg) {
System.out.println(msg);
}
}
class SDemo3 {
public static void main(String args[]) {
StaticBlock ob = new StaticBlock("Wewnątrz konstruktora.");
System.out.println("Pierwiastek kwadratowy z 2 wynosi " + StaticBlock.rootOf2);
System.out.println("Pierwiastek kwadratowy z 3 wynosi " + StaticBlock.rootOf3);
}
}
Wynik działania programu jest następujący:
Wewnątrz konstruktora.
Pierwiastek kwadratowy z 2 wynosi 1.4142135623730951
Pierwiastek kwadratowy z 3 wynosi 1.7320508075688772
Pokazuje on, że instrukcje bloku zadeklarowanego jako static zostają wykonane, zanim zostanie utworzony jakikolwiek obiekt danej klasy.
Java. Przewodnik dla początkujących. Wydanie VI, Autor: Herbert Schildt, Wydawnictwo: Helion