Nie do końca zrozumiałem konstruktory statyczne w Javie. Jeśli jest to dozwolone, dlaczego jest dozwolone? W jakich scenariuszach byś go użył? W jakim celu miałoby to służyć? Czy ktoś może podać prosty przykład?
Udostępnianie badań pomaga wszystkim. Powiedz nam, co próbowałeś i dlaczego nie spełnia twoich potrzeb. To pokazuje, że poświęciłeś trochę czasu, aby spróbować sobie pomóc, oszczędza nam to powtarzania oczywistych odpowiedzi, a przede wszystkim pomaga uzyskać bardziej konkretną i odpowiednią odpowiedź. Zobacz także How to Ask
komiks
3
Nie ma czegoś takiego jak „konstruktor statyczny”.
David Conrad,
Odpowiedzi:
28
Ściśle mówiąc, Java nie ma statycznych konstruktorów, ponieważ konstruktor z definicji nie może być statyczny. To, o czym mówisz, nazywa się „statycznym blokiem inicjalizacji”. Konstruktor sugeruje, że konstruujesz obiekt. Nie możesz mieć konstruktora dla klasy, ponieważ klasa nie jest instancją samej w sobie. To po prostu klasa. To, co „konstruuje” klasę, nazywa się kompilatorem (lub maszyną wirtualną, w zależności od tego, co rozumie się przez „konstruuje”), a jeśli zaczniesz konstruować kod w innym kodzie, zaczniesz generować kod, który jest zupełnie inna bestia.
Pomijając wybieranie nitów, blok inicjalizacji statycznej służy do inicjalizacji złożonych pól statycznych (lub na poziomie klasy) dla klasy. Zwykle są one używane do inicjalizacji rzeczy, których albo nie można zainicjować w jednym wierszu, albo wymagają, aby najpierw zainicjowano jakiś inny obiekt (który może, ale nie musi należeć do klasy, w której zaimplementowano blok statyczny).
Zasadniczo można ich użyć, aby powiedzieć klasie „Hej, ustaw zmienną A na PIERWSZĄ wartość, a następnie, gdy to zrobisz, użyj wartości A, aby zainicjować B.” Ponieważ Java wymaga standardowej inicjalizacji pola albo w konstruktorze lub metodzie, albo przez wywołanie konstruktora lub metody (chyba że jest to literał), mogą to być wygodne metody inicjowania złożonych obiektów statycznych.
Bloki inicjalizacji statycznej nie są zbyt często potrzebne i ogólnie należy ich unikać, chyba że mają rzeczywiste zastosowanie. Nie zrozum mnie źle, mają swoje miejsce w Javie, ale podobnie jak wiele innych rzeczy (takich jak instrukcje break, return, switch i goto), mogą być łatwo nadużywane, co zmniejsza ich czytelność i łatwość konserwacji kodu -baza, w której są używane.
Krótki przykład użycia bloku inicjalizacji statycznej byłby następujący (zgodnie z doskonałym wyjaśnieniem bloków inicjalizacji statycznej tutaj ):
Kod:
publicclassStaticExample{static{System.out.println("This is first static block");}publicStaticExample(){System.out.println("This is constructor");}publicstaticString staticString ="Static Variable";static{System.out.println("This is second static block and "+ staticString);}publicstaticvoid main(String[] args){StaticExample statEx =newStaticExample();StaticExample.staticMethod2();}static{
staticMethod();System.out.println("This is third static block");}publicstaticvoid staticMethod(){System.out.println("This is static method");}publicstaticvoid staticMethod2(){System.out.println("This is static method2");}}
Wynik:
This is first static block
This is second static block and Static Variable
This is static method
This is third static block
This is constructor
This is static method2
Niektóre wystąpienia pokazują, kiedy bloki statyczne mogą być przydatne:
Jeśli ładujesz sterowniki i inne elementy do przestrzeni nazw. Na przykład klasa Class ma blok statyczny, w którym rejestruje tubylców.
Jeśli potrzebujesz wykonać obliczenia w celu zainicjowania zmiennych statycznych, możesz zadeklarować blok statyczny, który zostanie wykonany dokładnie raz, gdy klasa zostanie załadowana po raz pierwszy.
Problemy związane z bezpieczeństwem lub zadania związane z rejestrowaniem
Niektóre powody, dla których NIE należy używać bloków statycznych (w innych sytuacjach):
JVM ma ograniczenie polegające na tym, że blok inicjalizatora statycznego nie powinien przekraczać 64 KB.
Nie możesz rzucać sprawdzonych wyjątków.
Nie możesz użyć thissłowa kluczowego, ponieważ nie ma instancji.
Nie powinieneś próbować uzyskiwać dostępu do super, ponieważ nie ma czegoś takiego dla bloków statycznych.
Nie powinieneś zwracać niczego z tego bloku.
Bloki statyczne czynią testowanie koszmarem.
Powinienem zauważyć: Podczas gdy niektóre języki (takie jak C #) mogą mieć składnię dla „konstruktorów”, które są statyczne, te „konstruktory” działają w ten sam sposób, co statyczne bloki inicjujące w Javie i są postrzegane przez wielu (łącznie ze mną) jako błędne znaki w języku, biorąc pod uwagę podstawową koncepcję konstruktora OOP .
nie można wypełnić mapy podczas inicjalizacji (chyba że użyjesz anonimowego włamania do podklasy), więc jest to najlepszy sposób, aby zagwarantować, że zostanie wypełniona przed pierwszym użyciem
Możesz to również zrobić, aby złapać sprawdzone wyjątki podczas inicjowania
Było to bardzo jedno z głównych zastosowań, jakie widziałem, ale dzięki włączeniu statycznych metod fabrycznych dla frameworku kolekcji w Javie 9 ten przypadek użycia został w pewnym sensie przestarzały.
Hangman4358,
3
Odpowiedź Gurgadurgena jest prawdopodobnie tym, czego szukasz, ale dodam tylko kilka innych punktów, które czasem są zaniedbywane, gdy ktoś chce „statycznego konstruktora”.
Jeśli potrzebujesz metody statycznej, która tworzy instancję klasy, możesz utworzyć metodę statyczną, która po prostu wywołuje konstruktora klasy.
publicclassExample{/** Static method to create an instance. */publicstaticExample build(){returnnewExample();}/** A field of each instance. */privateString stuff ;/** The class's actual constructor. */publicExample(){ stuff =newString();}publicString getStuff(){returnthis.stuff ;}/**
* Mutator for "stuff" property. By convention this returns "void"
* but you might want to return the object itself, to support the
* sort of chained invocations that are becoming trendy now. You'll
* see the stylistic benefits of chaining later in this example.
*/publicExample setStuff(String newStuff ){this.stuff = newStuff ;returnthis;}}publicclassExampleTest{publicstaticvoid main(String[] args ){// The usual instance model.Example first =newExample();System.out.println( first.setStuff("stuff").getStuff());// Using your static method to construct an instance:Example second =Example.build();System.out.println( second.setStuff("more stuff").getStuff());// Chaining all the invocations at once:System.out.println(Example.build().setStuff("even more stuff").getStuff());}}
To daje wynik:
stuff
more stuff
even more stuff
Innym powodem stworzenia statycznej metody budowy instancji jest przypadek, gdy chcesz mieć pewność, że w danym momencie istnieje dokładnie jedna instancja twojej klasy; nazywa się to singletonem . Zgodnie z konwencją taka klasa zapewniałaby metodę statyczną wywoływaną w getInstance()celu uzyskania jedynej instancji, która jest traktowana jako „singleton”.
publicclassSingletonExampleextendsExample{// Note: This extends my previous example, which has a "stuff"// property, and a trivial constructor./** The singleton instance, statically initialized as null. */privatestaticSingletonExample singleton =null;/**
* The static accessor for the singleton. If no instance exists,
* then it will be created; otherwise, the one that already exists
* will be returned.
*/publicstaticSingletonExample getInstance(){if( singleton ==null)
singleton =newSingletonExample();return singleton ;}}publicclassSingletonExampleTest{publicstaticvoid main(String[] args ){System.out.println(SingletonExample.getInstance().setStuff("stuff").getStuff());// You could still create instances of this class normally if you want to.SingletonExample otherstuff =newSingletonExample();
otherstuff.setStuff("other stuff");// But watch what happens to this.System.out.println(SingletonExample.getInstance().getStuff());System.out.println( otherstuff.getStuff());// Now we show what happens when you start modifying the singleton.SingletonExample theoneandonly =SingletonExample.getInstance();
theoneandonly.setStuff("changed stuff");System.out.println(SingletonExample.getInstance().getStuff());}}
To powoduje:
stuff
stuff
other stuff
changed stuff
Przechowując odniesienie do singletonu, a następnie modyfikując je, następne wywołanie to getInstance()powoduje uzyskanie zmodyfikowanego singletonu.
Kuszące jest użycie singletonów jako sposobu na rozpoczęcie tworzenia zmiennych globalnych dla aplikacji. W niektórych kontekstach może to być przydatne, ale może też sprawić kłopoty. W szczególności napotkałem interesujący błąd podczas opracowywania aplikacji na Androida, w której instancje singletonowe mogły się zgubić. Przeskakiwanie z jednej czynności do drugiej może czasami prowadzić JVM do użycia nowego „modułu ładującego klasy”, który nie będzie wiedział o singletonie, który był przechowywany statycznie przez poprzedni moduł ładujący klasy.
Chociaż rozumiem, że wielu uważa pojęcie „konstruktora statycznego” za mylące, nie sądzę, aby tak było. Problem polega na konstruowaniu obu klas i ich obiektów instancji . W innych wątkach stwierdzono, że budowanie klasy jest zadaniem kompilatora. Nawet w Javie jest to tylko połowa prawdy.
Kompilator konstruuje rusztowanie dla każdej definicji klasy. Rusztowanie zawiera metadane dotyczące klasy i jej instancji w czasie budowy. Jeśli klasa definiuje pole, któremu przypisano stałą wartość pierwotną, wówczas ta wartość jest uwzględniana w rusztowaniu przez kompilator. Dla wszystkich innych przypisywanych typów wartości kompilator generuje procedurę inicjalizacji, która ma zostać uruchomiona 1 raz, przed utworzeniem instancji pierwszej klasy, która aktualizuje rusztowanie o odpowiednie wartości. Ta aktualizacja nie może zostać wykonana przez kompilator.
Ponieważ ta jednorazowa aktualizacja rusztowania jest często kluczowym warunkiem prawidłowego funkcjonowania konstruktorów instancji, rozsądne jest również nazwanie tego rodzaju konstruktorem. Dlatego w popularnych językach OO, które obsługują tę koncepcję, nazywa się to konstruktorem statycznym. Koncepcja stojąca za blokami inicjalizacji statycznej w Javie to niewiele więcej niż zmiana semantyczna, aby zachować zgodność z koncepcją, że programista Java powinien być agnostyczny zarówno pod względem systemu, jak i implementacji.
Jak już powiedzieli inne odpowiedzi, możesz pisać metody statyczne, które konstruują obiekt. To eliminuje brak nazwanych konstruktorów w Javie (i wielu innych językach. Delphi obsługuje nazwane konstruktory). Możesz grać z typami i kolejnością parametrów, ale może to prowadzić do niejasnego i delikatnego kodu.
Na przykład możemy wymyślić scenariusz, w którym twój obiekt może być zbudowany z łańcucha XML lub łańcucha JSON. Możesz pisać metody takie jak:
Kiedy czytam pytanie, chodzi o statyczne inicjalizatory, a nie o normalne statyczne statyczne cechy.
CodesInChaos
-2
Możesz wyświetlić sekcję „statyczną” jak konstruktor używany na poziomie klasy do inicjowania właściwości klasy (statyczny w Javie). To samo, co „normalny” konstruktor używany do inicjowania właściwości na poziomie instancji.
Odpowiedzi:
Ściśle mówiąc, Java nie ma statycznych konstruktorów, ponieważ konstruktor z definicji nie może być statyczny. To, o czym mówisz, nazywa się „statycznym blokiem inicjalizacji”. Konstruktor sugeruje, że konstruujesz obiekt. Nie możesz mieć konstruktora dla klasy, ponieważ klasa nie jest instancją samej w sobie. To po prostu klasa. To, co „konstruuje” klasę, nazywa się kompilatorem (lub maszyną wirtualną, w zależności od tego, co rozumie się przez „konstruuje”), a jeśli zaczniesz konstruować kod w innym kodzie, zaczniesz generować kod, który jest zupełnie inna bestia.
Pomijając wybieranie nitów, blok inicjalizacji statycznej służy do inicjalizacji złożonych pól statycznych (lub na poziomie klasy) dla klasy. Zwykle są one używane do inicjalizacji rzeczy, których albo nie można zainicjować w jednym wierszu, albo wymagają, aby najpierw zainicjowano jakiś inny obiekt (który może, ale nie musi należeć do klasy, w której zaimplementowano blok statyczny).
Zasadniczo można ich użyć, aby powiedzieć klasie „Hej, ustaw zmienną A na PIERWSZĄ wartość, a następnie, gdy to zrobisz, użyj wartości A, aby zainicjować B.” Ponieważ Java wymaga standardowej inicjalizacji pola albo w konstruktorze lub metodzie, albo przez wywołanie konstruktora lub metody (chyba że jest to literał), mogą to być wygodne metody inicjowania złożonych obiektów statycznych.
Bloki inicjalizacji statycznej nie są zbyt często potrzebne i ogólnie należy ich unikać, chyba że mają rzeczywiste zastosowanie. Nie zrozum mnie źle, mają swoje miejsce w Javie, ale podobnie jak wiele innych rzeczy (takich jak instrukcje break, return, switch i goto), mogą być łatwo nadużywane, co zmniejsza ich czytelność i łatwość konserwacji kodu -baza, w której są używane.
Krótki przykład użycia bloku inicjalizacji statycznej byłby następujący (zgodnie z doskonałym wyjaśnieniem bloków inicjalizacji statycznej tutaj ):
Kod:
Wynik:
Niektóre wystąpienia pokazują, kiedy bloki statyczne mogą być przydatne:
Niektóre powody, dla których NIE należy używać bloków statycznych (w innych sytuacjach):
this
słowa kluczowego, ponieważ nie ma instancji.Powinienem zauważyć: Podczas gdy niektóre języki (takie jak C #) mogą mieć składnię dla „konstruktorów”, które są statyczne, te „konstruktory” działają w ten sam sposób, co statyczne bloki inicjujące w Javie i są postrzegane przez wielu (łącznie ze mną) jako błędne znaki w języku, biorąc pod uwagę podstawową koncepcję konstruktora OOP .
źródło
służy do inicjalizacji pól trudniejszych niż zwykłe przypisanie:
nie można wypełnić mapy podczas inicjalizacji (chyba że użyjesz anonimowego włamania do podklasy), więc jest to najlepszy sposób, aby zagwarantować, że zostanie wypełniona przed pierwszym użyciem
Możesz to również zrobić, aby złapać sprawdzone wyjątki podczas inicjowania
źródło
Odpowiedź Gurgadurgena jest prawdopodobnie tym, czego szukasz, ale dodam tylko kilka innych punktów, które czasem są zaniedbywane, gdy ktoś chce „statycznego konstruktora”.
Jeśli potrzebujesz metody statycznej, która tworzy instancję klasy, możesz utworzyć metodę statyczną, która po prostu wywołuje konstruktora klasy.
To daje wynik:
Innym powodem stworzenia statycznej metody budowy instancji jest przypadek, gdy chcesz mieć pewność, że w danym momencie istnieje dokładnie jedna instancja twojej klasy; nazywa się to singletonem . Zgodnie z konwencją taka klasa zapewniałaby metodę statyczną wywoływaną w
getInstance()
celu uzyskania jedynej instancji, która jest traktowana jako „singleton”.To powoduje:
Przechowując odniesienie do singletonu, a następnie modyfikując je, następne wywołanie to
getInstance()
powoduje uzyskanie zmodyfikowanego singletonu.Kuszące jest użycie singletonów jako sposobu na rozpoczęcie tworzenia zmiennych globalnych dla aplikacji. W niektórych kontekstach może to być przydatne, ale może też sprawić kłopoty. W szczególności napotkałem interesujący błąd podczas opracowywania aplikacji na Androida, w której instancje singletonowe mogły się zgubić. Przeskakiwanie z jednej czynności do drugiej może czasami prowadzić JVM do użycia nowego „modułu ładującego klasy”, który nie będzie wiedział o singletonie, który był przechowywany statycznie przez poprzedni moduł ładujący klasy.
źródło
Chociaż rozumiem, że wielu uważa pojęcie „konstruktora statycznego” za mylące, nie sądzę, aby tak było. Problem polega na konstruowaniu obu klas i ich obiektów instancji . W innych wątkach stwierdzono, że budowanie klasy jest zadaniem kompilatora. Nawet w Javie jest to tylko połowa prawdy.
Kompilator konstruuje rusztowanie dla każdej definicji klasy. Rusztowanie zawiera metadane dotyczące klasy i jej instancji w czasie budowy. Jeśli klasa definiuje pole, któremu przypisano stałą wartość pierwotną, wówczas ta wartość jest uwzględniana w rusztowaniu przez kompilator. Dla wszystkich innych przypisywanych typów wartości kompilator generuje procedurę inicjalizacji, która ma zostać uruchomiona 1 raz, przed utworzeniem instancji pierwszej klasy, która aktualizuje rusztowanie o odpowiednie wartości. Ta aktualizacja nie może zostać wykonana przez kompilator.
Ponieważ ta jednorazowa aktualizacja rusztowania jest często kluczowym warunkiem prawidłowego funkcjonowania konstruktorów instancji, rozsądne jest również nazwanie tego rodzaju konstruktorem. Dlatego w popularnych językach OO, które obsługują tę koncepcję, nazywa się to konstruktorem statycznym. Koncepcja stojąca za blokami inicjalizacji statycznej w Javie to niewiele więcej niż zmiana semantyczna, aby zachować zgodność z koncepcją, że programista Java powinien być agnostyczny zarówno pod względem systemu, jak i implementacji.
źródło
Jak już powiedzieli inne odpowiedzi, możesz pisać metody statyczne, które konstruują obiekt. To eliminuje brak nazwanych konstruktorów w Javie (i wielu innych językach. Delphi obsługuje nazwane konstruktory). Możesz grać z typami i kolejnością parametrów, ale może to prowadzić do niejasnego i delikatnego kodu.
Na przykład możemy wymyślić scenariusz, w którym twój obiekt może być zbudowany z łańcucha XML lub łańcucha JSON. Możesz pisać metody takie jak:
Używałem tego bardzo okazjonalnie jako alternatywy dla konstruktora bez parametrów z nazwanymi metodami inicjalizacji:
Możesz spojrzeć na metodę tworzenia statycznego jako implementację wzorca konstruktora w swojej klasie.
źródło
Możesz wyświetlić sekcję „statyczną” jak konstruktor używany na poziomie klasy do inicjowania właściwości klasy (statyczny w Javie). To samo, co „normalny” konstruktor używany do inicjowania właściwości na poziomie instancji.
źródło