Jaka jest różnica między statycznym a niestatycznym blokiem kodu inicjalizacji

357

Moje pytanie dotyczy jednego konkretnego użycia statycznego słowa kluczowego. Można użyć staticsłowa kluczowego, aby zakryć blok kodu w klasie, która nie należy do żadnej funkcji. Na przykład kompiluje następujący kod:

public class Test {
    private static final int a;    
    static {
        a = 5;
        doSomething(a);
    }
    private static int doSomething(int x) {
        return (x+5);
    }
}

Jeśli usuniesz staticsłowo kluczowe, narzeka, ponieważ zmienną ajest final. Jednak możliwe jest, aby usunąć oba finali staticsłowa kluczowe i uczynić go skompilować.

Jest to dla mnie mylące na dwa sposoby. Jak mam mieć sekcję kodu, która nie należy do żadnej metody? Jak można to wywołać? Zasadniczo jaki jest cel tego użycia? Lub lepiej, gdzie mogę znaleźć dokumentację na ten temat?

Szere Dyeri
źródło

Odpowiedzi:

403

Blok kodu z modyfikatorem statycznym oznacza inicjator klasy ; bez modyfikatora statycznego blok kodu jest inicjatorem instancji .

Inicjatory klas są wykonywane w kolejności, w jakiej zostały zdefiniowane (z góry na dół, podobnie jak proste inicjalizatory zmiennych), gdy klasa jest ładowana (faktycznie, kiedy jest rozwiązana, ale to jest kwestia techniczna).

Inicjatory instancji są wykonywane w kolejności zdefiniowanej podczas tworzenia instancji klasy, bezpośrednio przed wykonaniem kodu konstruktora, bezpośrednio po wywołaniu superkonstruktora.

Jeśli usuniesz staticz int a, staje się zmienną instancji, które nie są w stanie uzyskać dostęp ze statycznego inicjatora bloku. Nie uda się skompilować z błędem „do zmiennej niestatycznej nie można się odwoływać z kontekstu statycznego”.

Jeśli usuniesz również staticz bloku inicjalizującego, wówczas stanie się on inicjatorem instancji i tak int azostanie zainicjowany podczas budowy.

Lawrence Dol
źródło
Inicjator statyczny jest wywoływany później, gdy klasa jest inicjowana, po załadowaniu i połączeniu. Dzieje się tak, gdy tworzysz obiekt klasy lub uzyskujesz dostęp do zmiennej statycznej lub metody w klasie. W rzeczywistości, jeśli masz klasę ze statycznym inicjatorem i metodą public static void staticMethod(){}, jeśli wykonasz TestStatic.class.getMethod("staticMethod");. Inicjator statyczny nie zostanie wywołany. Więcej informacji tutaj docs.oracle.com/javase/specs/jvms/se10/html/…
Totò
@ Totò: Tak, to właśnie pociąga za sobą rozdzielczość klasy (przynajmniej kiedyś nazywali ją link + init jako „rozdzielczość”). Nie dziwię się, że możesz użyć refleksji, aby odkryć rzeczy o klasie bez rozwiązania.
Lawrence Dol
166

Uff! co to jest inicjalizator statyczny?

Inicjator statyczny jest static {}blokiem kodu wewnątrz klasy java i działa tylko jeden raz przed wywołaniem konstruktora lub metody głównej.

OK! Powiedz mi więcej...

  • to blok kodu static { ... }wewnątrz dowolnej klasy java. i wykonywane przez maszynę wirtualną po wywołaniu klasy.
  • Żadne returninstrukcje nie są obsługiwane.
  • Żadne argumenty nie są obsługiwane.
  • Nie thislub supersą obsługiwane.

Hmm, gdzie mogę go użyć?

Można go używać wszędzie tam, gdzie czujesz się dobrze :) tak prosto. Widzę jednak, że przez większość czasu jest on używany podczas połączenia z bazą danych, inicjowania interfejsu API, rejestrowania itp.

Nie szczekaj! gdzie jest przykład

package com.example.learnjava;

import java.util.ArrayList;

public class Fruit {

    static {
        System.out.println("Inside Static Initializer.");

        // fruits array
        ArrayList<String> fruits = new ArrayList<>();
        fruits.add("Apple");
        fruits.add("Orange");
        fruits.add("Pear");

        // print fruits
        for (String fruit : fruits) {
            System.out.println(fruit);
        }
        System.out.println("End Static Initializer.\n");
    }

    public static void main(String[] args) {
        System.out.println("Inside Main Method.");
    }
}

Wynik???

Wewnątrz inicjatora statycznego.

jabłko

Pomarańczowy

Gruszka

Zakończ inicjalizator statyczny.

Wewnątrz głównej metody.

Mam nadzieję że to pomoże!

Madan Sapkota
źródło
Dzięki Madan! Blok statyczny może być stosowany zamiast afterPropertiesSet()z InitializingBean?
Alexander Suraphel,
3
Tak, możesz! Inicjator statyczny zostaje wywołany, gdy klasa zostanie załadowana przez JVM. Jest to więc naprawdę pierwsza faza, w której kod jest wykonywany. Jeśli masz także konstruktor, kolejność będzie następująca: inicjator statyczny, konstruktor, afterPropertiesSet
Martin Baumgartner
57

staticBlok jest „static inicjator”.

Jest on automatycznie wywoływany, gdy klasa jest załadowana, i nie ma innego sposobu, aby ją wywołać (nawet poprzez Odbicie).

Osobiście używałem go tylko podczas pisania kodu JNI:

class JNIGlue {
    static {
        System.loadLibrary("foo");
    }
}
Alnitak
źródło
6
Nie, nie ma wyraźnego sposobu na jej wywołanie, inicjator klasy nigdy nie jest reprezentowany przez Methodinstancję, ale wywoływany jest tylko przez maszynę wirtualną Java.
Rafael Winterhalter
46

Jest to bezpośrednio z http://www.programcreek.com/2011/10/java-class-instance-initializers/

1. Polecenie wykonania

Spójrz na następującą klasę, czy wiesz, która zostanie najpierw wykonana?

public class Foo {

    //instance variable initializer
    String s = "abc";

    //constructor
    public Foo() {
        System.out.println("constructor called");
    }

    //static initializer
    static {
        System.out.println("static initializer called");
    }

    //instance initializer
    {
        System.out.println("instance initializer called");
    }

    public static void main(String[] args) {
        new Foo();
        new Foo();
    }
}

Wynik:

wywoływano inicjator statyczny

wywoływano inicjator instancji

o nazwie konstruktor

wywoływano inicjator instancji

o nazwie konstruktor

2. Jak działa inicjalizator instancji Java?

Inicjator instancji powyżej zawiera instrukcję println. Aby zrozumieć, jak to działa, możemy potraktować to jako zmienną instrukcję przypisania, np b = 0. Może to uczynić bardziej oczywistym zrozumienie.

Zamiast

int b = 0, mógłbyś pisać

int b;
b = 0;

Dlatego inicjalizatory instancji i inicjatory zmiennych instancji są prawie takie same.

3. Kiedy inicjalizatory instancji są przydatne?

Stosowanie inicjatorów instancji jest rzadkie, ale nadal może być przydatną alternatywą dla inicjatorów zmiennych instancji, jeśli:

  1. Kod inicjujący musi obsługiwać wyjątki
  2. Wykonuj obliczenia, których nie można wyrazić za pomocą inicjatora zmiennej instancji.

Oczywiście taki kod mógłby zostać napisany w konstruktorach. Ale jeśli klasa ma wiele konstruktorów, musisz powtórzyć kod w każdym konstruktorze.

Za pomocą inicjatora instancji możesz po prostu napisać kod raz i zostanie on wykonany bez względu na to, jakiego konstruktora użyto do utworzenia obiektu. (Myślę, że to tylko koncepcja i nie jest często używana).

Innym przypadkiem, w którym użyteczne są inicjalizatory instancji, są anonimowe klasy wewnętrzne, które w ogóle nie mogą zadeklarować żadnych konstruktorów. (Czy będzie to dobre miejsce do umieszczenia funkcji logowania?)

Dzięki Derhein.

Zauważ też, że klasy Anonimowe implementujące interfejsy [1] nie mają konstruktorów. Dlatego inicjatory instancji są potrzebne do wykonania dowolnego rodzaju wyrażeń w czasie budowy.

Alexei Fando
źródło
12

„final” gwarantuje, że zmienna musi zostać zainicjowana przed kodem inicjującym koniec obiektu. Podobnie „statyczny końcowy” gwarantuje, że zmienna zostanie zainicjowana do końca kodu inicjalizacji klasy. Pominięcie „statycznego” w kodzie inicjalizacji zamienia go w kod inicjujący obiekt; dlatego twoja zmienna nie spełnia już swoich gwarancji.

DJClayworth
źródło
8

Nie będziesz pisać kodu w bloku statycznym, który należy wywołać w dowolnym miejscu w programie. Jeśli chcesz wywołać cel kodu, musisz umieścić go w metodzie.

Możesz pisać statyczne bloki inicjujące, aby inicjalizować zmienne statyczne, gdy klasa jest załadowana, ale ten kod może być bardziej złożony.

Blok inicjalizatora statycznego wygląda jak metoda bez nazwy, bez argumentów i bez typu zwracanego. Ponieważ nigdy go nie nazywasz, nie wymaga nazwy. Wywoływany jest tylko wtedy, gdy maszyna wirtualna ładuje klasę.

Vincent Ramdhanie
źródło
6

gdy programista korzysta z bloku inicjalizującego, kompilator Java kopiuje inicjalizator do każdego konstruktora bieżącej klasy.

Przykład:

następujący kod:

class MyClass {

    private int myField = 3;
    {
        myField = myField + 2;
        //myField is worth 5 for all instance
    }

    public MyClass() {
        myField = myField * 4;
        //myField is worth 20 for all instance initialized with this construtor
    }

    public MyClass(int _myParam) {
        if (_myParam > 0) {
            myField = myField * 4;
            //myField is worth 20 for all instance initialized with this construtor
            //if _myParam is greater than 0
        } else {
            myField = myField + 5;
            //myField is worth 10 for all instance initialized with this construtor
            //if _myParam is lower than 0 or if _myParam is worth 0
        }
    }

    public void setMyField(int _myField) {
        myField = _myField;
    }


    public int getMyField() {
        return myField;
    }
}

public class MainClass{

    public static void main(String[] args) {
        MyClass myFirstInstance_ = new MyClass();
        System.out.println(myFirstInstance_.getMyField());//20
        MyClass mySecondInstance_ = new MyClass(1);
        System.out.println(mySecondInstance_.getMyField());//20
        MyClass myThirdInstance_ = new MyClass(-1);
        System.out.println(myThirdInstance_.getMyField());//10
    }
}

jest równa:

class MyClass {

    private int myField = 3;

    public MyClass() {
        myField = myField + 2;
        myField = myField * 4;
        //myField is worth 20 for all instance initialized with this construtor
    }

    public MyClass(int _myParam) {
        myField = myField + 2;
        if (_myParam > 0) {
            myField = myField * 4;
            //myField is worth 20 for all instance initialized with this construtor
            //if _myParam is greater than 0
        } else {
            myField = myField + 5;
            //myField is worth 10 for all instance initialized with this construtor
            //if _myParam is lower than 0 or if _myParam is worth 0
        }
    }

    public void setMyField(int _myField) {
        myField = _myField;
    }


    public int getMyField() {
        return myField;
    }
}

public class MainClass{

    public static void main(String[] args) {
        MyClass myFirstInstance_ = new MyClass();
        System.out.println(myFirstInstance_.getMyField());//20
        MyClass mySecondInstance_ = new MyClass(1);
        System.out.println(mySecondInstance_.getMyField());//20
        MyClass myThirdInstance_ = new MyClass(-1);
        System.out.println(myThirdInstance_.getMyField());//10
    }
}

Mam nadzieję, że mój przykład zostanie zrozumiany przez programistów.

Cardman
źródło
4

Blok kodu statycznego może być użyty do utworzenia lub zainicjowania zmiennych klas (w przeciwieństwie do zmiennych obiektowych). Dlatego zadeklarowanie „a” statycznego oznacza, że ​​jest to tylko jeden wspólny dla wszystkich obiektów Test, a blok kodu statycznego inicjuje „a” tylko raz, gdy klasa Test jest ładowana po raz pierwszy, bez względu na to, ile obiektów Testowych zostanie utworzonych.

Paul Tomblin
źródło
Jako kontynuację, jeśli nie utworzę instancji obiektu, a zamiast tego wywołam publiczną funkcję statyczną. Czy to oznacza, że ​​blok ten ma zagwarantowane wykonanie przed wywołaniem funkcji publicznej? Dzięki.
Szere Dyeri,
Jeśli wywołasz publiczną funkcję statyczną klasy, klasa musi zostać najpierw załadowana, więc tak, inicjator statyczny zostanie wykonany jako pierwszy.
Paul Tomblin,
Chyba że to inicjalizacja klasy (pośrednio) wywołała kod, który próbuje go użyć. IFYSWIM. Zależności kołowe i tak dalej.
Tom Hawtin - tackline
1
@ Tom ma rację - możliwe jest napisanie czegoś, w którym jeden statyczny inicjator wywołuje metodę statyczną przed wywołaniem innego statycznego inicjatora, ale mój umysł cofa się na tę myśl, więc nigdy tego nie rozważałem.
Paul Tomblin,