Oto mój kod:
class A {
static A obj = new A();
static int num1;
static int num2=0;
private A() {
num1++;
num2++;
}
public static A getInstance() {
return obj;
}
}
public class Main{
public static void main(String[] arg) {
A obj = A.getInstance();
System.out.println(obj.num1);
System.out.println(obj.num2);
}
}
Wynik jest 1 0
, ale nie mogę zrozumieć.
Czy ktoś może mi to wyjaśnić?
Odpowiedzi:
W Javie mają miejsce dwie fazy: 1. Identyfikacja, 2. Wykonanie
W fazie identyfikacji wszystkie zmienne statyczne są wykrywane i inicjowane z wartościami domyślnymi.
Więc teraz wartości są następujące:
A obj=null
num1=0
num2=0
Druga faza, wykonanie , zaczyna się od góry do dołu. W Javie wykonanie rozpoczyna się od pierwszych statycznych elementów członkowskich.
Tutaj znajduje się twoja pierwsza zmienna statyczna
static A obj = new A();
, więc najpierw utworzy obiekt tej zmiennej i wywoła konstruktor, stąd wartośćnum1
i stanienum2
się1
.A potem znowu
static int num2=0;
zostanie wykonany, co sprawianum2 = 0;
.Teraz załóżmy, że twój konstruktor wygląda tak:
Spowoduje to rzucenie
NullPointerException
któryobj
nadal nie ma odniesieniaclass A
.źródło
static A obj = new A();
poniżejstatic int num2=0;
i powinieneś dostać 1 i 1.A obj = new A(); int num1; int num2 = 0;
Zostaje przekształcony w ten sposób:A obj; int num1; int num2; obj = new A(); num2 = 0;
. Java robi to, więcnum1, num2
są definiowane przez czas dotarcia donew A()
konstruktora.To, co
static
oznacza modyfikator, gdy jest stosowany do deklaracji zmiennej, to fakt, że zmienna jest zmienną klasy, a nie zmienną instancji. Innymi słowy ... jest tylko jednanum1
zmienna i tylko jednanum2
zmienna.(Poza tym: zmienna statyczna jest podobna do zmienna globalna w niektórych innych językach, z tą różnicą, że jej nazwa nie jest widoczna wszędzie. Nawet jeśli jest zadeklarowana jako a
public static
, niekwalifikowana nazwa jest widoczna tylko wtedy, gdy jest zadeklarowana w bieżącej klasie lub nadklasie lub jeśli jest importowany przy użyciu importu statycznego. Na tym polega różnica. Prawdziwy globalny jest wszędzie widoczny bez kwalifikacji).Więc kiedy odnoszą się do
obj.num1
iobj.num2
, w rzeczywistości odnosi się do tych zmiennych statycznych, których nazwy są prawdziweA.num1
iA.num2
. I podobnie, gdy konstruktor zwiększa wartośćnum1
inum2
, zwiększa te same zmienne (odpowiednio).Myląca kwestia w twoim przykładzie dotyczy inicjalizacji klasy. Klasa jest inicjowana przez pierwszą domyślną inicjalizację wszystkich zmiennych statycznych, a następnie wykonywanie zadeklarowanych inicjatorów statycznych (i bloków inicjatorów statycznych) w kolejności, w jakiej pojawiają się w klasie. W tym przypadku masz to:
Dzieje się tak:
Statyka zaczyna się od domyślnych wartości początkowych;
A.obj
jestnull
iA.num1
/A.num2
są zerem.Pierwsza deklaracja (
A.obj
) tworzy wystąpienieA()
i konstruktor dlaA
inkrementacjiA.num1
iA.num2
. Gdy deklaracja zakończy się,A.num1
iA.num2
są oba1
, iA.obj
odwołuje się do nowo utworzonegoA
wystąpienia.Druga deklaracja (
A.num1
) nie ma inicjatora, więcA.num1
się nie zmienia.Trzecia deklaracja (
A.num2
) ma inicjator, który przypisuje zero doA.num2
.Tak więc pod koniec inicjalizacji klasy
A.num1
jest1
iA.num2
jest0
... i to właśnie pokazują twoje instrukcje print.To mylące zachowanie jest tak naprawdę spowodowane faktem, że tworzysz instancję przed zakończeniem inicjalizacji statycznej i że konstruktor, którego używasz, zależy od statycznej, która nie została jeszcze zainicjowana, i modyfikuje ją. To coś, czego powinieneś unikać w prawdziwym kodzie.
źródło
1,0 jest poprawne.
Kiedy klasa jest ładowana, wszystkie dane statyczne są inicjowane lub deklarowane. Domyślnie int wynosi 0.
static int num1;
nic nie robistatic int num2=0;
to zapisuje 0 do num2źródło
Wynika to z kolejności statycznych inicjatorów. Wyrażenia statyczne w klasach są oceniane w kolejności odgórnej.
Pierwszym wywoływanym konstruktorem jest konstruktor
A
, który ustawianum1
inum2
oba na 1:static A obj = new A();
Następnie,
jest wywoływana i ponownie ustawia num2 = 0.
Dlatego
num1
jest 1 inum2
wynosi 0.Na marginesie, konstruktor nie powinien modyfikować zmiennych statycznych, to jest bardzo zły projekt. Zamiast tego wypróbuj inne podejście do implementacji Singletona w Javie .
źródło
Sekcję w JLS można znaleźć: §12.4.2 .
Tak więc trzy zmienne statyczne zostaną zainicjowane jedna po drugiej w kolejności tekstowej.
Więc
Jeśli zmienię zamówienie na:
Wynik będzie
1,1
.Zauważ, że
static int num1;
nie jest zmienną inicjalizującą, ponieważ ( §8.3.2 ):Ta zmienna klasy jest inicjowana podczas tworzenia klasy. Dzieje się to najpierw ( §4.12.5 ).
źródło
Może pomoże pomyśleć o tym w ten sposób.
Klasy to plany obiektów.
Obiekty mogą mieć zmienne, gdy są tworzone.
Klasy mogą również mieć zmienne. Są one deklarowane jako statyczne. Są więc ustawiane w klasie, a nie instancjach obiektów.
Możesz mieć tylko jedną z dowolnej klasy w aplikacji, więc jest to coś w rodzaju globalnej pamięci masowej specjalnie dla tej klasy. Dostęp do tych zmiennych statycznych można oczywiście uzyskać i modyfikować z dowolnego miejsca w aplikacji (zakładając, że są publiczne).
Oto przykład klasy „Dog”, która używa zmiennej statycznej do śledzenia liczby utworzonych instancji.
Klasa „Pies” to chmura, podczas gdy pomarańczowe pola to instancje „Pies”.
Czytaj więcej
Mam nadzieję że to pomoże!
Jeśli masz ochotę na ciekawostki, pomysł ten został po raz pierwszy przedstawiony przez Plato
źródło
Słowo kluczowe static jest używane w Javie głównie do zarządzania pamięcią. Możemy zastosować statyczne słowo kluczowe ze zmiennymi, metodami, blokami i zagnieżdżoną klasą. Słowo kluczowe static należy do klasy niż do instancji klasy. Krótkie wyjaśnienie dotyczące słowa kluczowego static:
http://www.javatpoint.com/static-keyword-in-java
źródło
Wiele z powyższych odpowiedzi jest poprawnych. Ale tak naprawdę, aby zilustrować, co się dzieje, wprowadziłem poniżej kilka drobnych modyfikacji.
Jak wspomniano wielokrotnie powyżej, dzieje się tak, że instancja klasy A jest tworzona przed pełnym załadowaniem klasy A. Zatem to, co jest uważane za normalne „zachowanie”, nie jest obserwowane. Nie różni się to zbytnio od wywoływania metod z konstruktora, który można przesłonić. W takim przypadku zmienne instancji mogą nie być w stanie intuicyjnym. W tym przykładzie zmienne klasowe nie są w stanie intuicyjnym.
Wyjście jest
źródło
java nie inicjuje wartości żadnego statycznego lub niestatycznego elementu członkowskiego danych, dopóki nie zostanie wywołany, ale go utworzy.
więc tutaj, gdy num1 i num2 zostaną wywołane w main, to zostanie zainicjalizowane wartościami
num1 = 0 + 1; i
num2 = 0;
źródło