Java: Kiedy statyczny blok inicjalizacji jest przydatny?

91

Jaka jest różnica między inicjalizacją w staticbloku:

public class staticTest {

    static String s;
    static int n;
    static double d;

    static {
        s = "I'm static";
        n = 500;
        d = 4000.0001;
    }
    ...

I indywidualna inicjalizacja statyczna:

public class staticTest {

    static String s = "I'm static";
    static int n    = 500;
    static double d = 4000.0001;

    ....
Adam Matan
źródło
1
Używasz przypisań tylko w statycznym bloku inicjalizacji, więc oczywiście można to zrobić za pomocą statycznego przypisania zmiennych. Czy próbowałeś zobaczyć, co się stanie, jeśli musisz wykonać instrukcje bez przypisania?
Platinum Azure
Jest to dobre miejsce do ładowania klas lub biblioteki natywnej.
qrtt1
1
Należy pamiętać, że należy unikać zmiennych statycznych, dlatego też statyczne bloki inicjalizacyjne nie są na ogół dobrym pomysłem. Jeśli często z nich korzystasz, spodziewaj się kłopotów.
Bill K

Odpowiedzi:

106

Statyczne bloki inicjalizacyjne umożliwiają bardziej złożoną inicjalizację, na przykład przy użyciu warunków:

static double a;
static {
    if (SomeCondition) {
      a = 0;
    } else {
      a = 1;
    }
}

Lub gdy wymagane jest coś więcej niż tylko konstrukcja: podczas korzystania z konstruktora do tworzenia instancji konieczna jest obsługa wyjątków lub praca inna niż tworzenie pól statycznych.

Blok inicjalizacji statycznej jest również uruchamiany po wbudowanych statycznych inicjalizatorach, więc prawidłowe są następujące zasady:

static double a;
static double b = 1;

static {
    a = b * 4; // Evaluates to 4
}
Rich O'Kelly
źródło
3
Robi "b = a * 4;" inline byłby problemem tylko wtedy, gdyby b zostało zadeklarowane przed a, co nie ma miejsca w twoim przykładzie.
George Hawkins,
1
@GeorgeHawkins Próbowałem tylko zilustrować, że statyczny inicjator działa po inicjalizatorach wbudowanych, a nie, że nie można zrobić odpowiednika w linii. Jednak rozumiem twój punkt widzenia i zaktualizowałem przykład, aby (miejmy nadzieję) był jaśniejszy.
Rich O'Kelly,
1
Dla zabawy mógłbym wskazać, że pierwszym przykładem może być równie łatwo „static double a = someCondition? 0: 1;” Nie żeby twoje przykłady nie były świetne, po prostu mówię ... :)
Bill K
18

Typowe zastosowanie:

private final static Set<String> SET = new HashSet<String>();

static {
    SET.add("value1");
    SET.add("value2");
    SET.add("value3");
}

Jak byś to zrobił bez statycznego inicjatora?

gawi
źródło
2
Odpowiedź: Guawa :) +1
Paul Bellora
2
Inna odpowiedź bez dodatkowych bibliotek: utwórz statyczną metodę, która hermetyzuje inicjalizację SETi użyj zmiennej initializer ( private final static Set<String> SET = createValueSet()). A co, jeśli masz 5 zestawów i 2 mapy, czy po prostu wrzucisz je wszystkie do jednego staticbloku?
TWiStErRob
14

Możesz użyć bloku try / catch wewnątrz, static{}jak poniżej:

MyCode{

    static Scanner input = new Scanner(System.in);
    static boolean flag = true;
    static int B = input.nextInt();
    static int H = input.nextInt();

    static{
        try{
            if(B <= 0 || H <= 0){
                flag = false;
                throw new Exception("Breadth and height must be positive");
            }
        }catch(Exception e){
            System.out.println(e);
        }

    }
}

PS: W odniesieniu do tego !

karan patel
źródło
11

Innym powodem jest obsługa wyjątków podczas inicjalizacji. Na przykład:

static URL url;
static {
    try {
        url = new URL("https://blahblah.com");
    }
    catch (MalformedURLException mue) {
        //log exception or handle otherwise
    }
}

Jest to przydatne dla konstruktorów, które irytująco zgłaszają sprawdzone wyjątki, jak powyżej, lub bardziej złożoną logikę inicjalizacji, która może być podatna na wyjątki.

Paul Bellora
źródło
5

Czasami chcesz zrobić coś więcej niż tylko przypisać wartości do zmiennych statycznych. Ponieważ nie można umieszczać dowolnych instrukcji w treści klasy, można użyć statycznego bloku inicjatora.

Jesper
źródło
4

W twoim przykładzie nie ma różnicy; ale często wartość początkowa jest bardziej złożona, niż jest to wygodnie wyrażone w pojedynczym wyrażeniu (np. jest to a, List<String>którego zawartość najlepiej wyrazić za pomocą for-loop; lub to Methodmoże nie istnieć, więc potrzebne są procedury obsługi wyjątków) i / lub pola statyczne należy ustawić w określonej kolejności.

ruakh
źródło
4

staticblock może służyć do inicjowania pojedynczego wystąpienia, aby zapobiec używaniu metody synchronizowanej getInstance() .

Żeglarz Naddunajski
źródło
3

Technicznie rzecz biorąc, można by się bez niego obejść. Niektórzy wolą wielowierszowy kod inicjujący, aby przejść do metody statycznej. Jestem całkiem zadowolony używając statycznego inicjalizatora do stosunkowo prostej inicjalizacji wielostanowiskowej.

Oczywiście prawie zawsze robiłem swoje statystyki finali wskazywałem na niemodyfikowalny obiekt.

Tom Hawtin - haczyk
źródło
3

Statyczne słowo kluczowe (niezależnie od tego, czy jest to zmienna, czy blok) należy do klasy. Więc kiedy klasa jest wywoływana, te zmienne lub bloki są wykonywane. Więc większość inicjalizacji zostanie wykonana za pomocą słowa kluczowego static. Ponieważ należy do samej klasy, klasa może uzyskać do niej bezpośredni dostęp, bez tworzenia instancji klasy.

Weźmy przykład: istnieje klasa butów, w której istnieje kilka zmiennych, takich jak kolor, rozmiar, marka itp. A tutaj, jeśli firma produkująca obuwie ma tylko jedną markę, powinniśmy zainicjować ją jako zmienną statyczną. Tak więc, gdy wywoływana jest klasa obuwia i produkowane są różne typy butów (tworząc instancję tej klasy), wówczas kolor i rozmiar będą zapamiętywać za każdym razem, gdy zostanie utworzony nowy but, ale tutaj marka jest wspólną własnością wszystkich butów, tak, że choć raz zajmie pamięć, bez względu na to, ile butów jest produkowanych.

Przykład:

    class Shoe {
    int size;
    String colour;
    static String brand = "Nike";

    public Shoe(int size, String colour) {
        super();
        this.size = size;
        this.colour = colour;
    }

    void displayShoe() {
        System.out.printf("%-2d %-8s %s %n",size,colour, brand);
    }

    public static void main(String args[]) {
        Shoe s1 = new Shoe(7, "Blue");
        Shoe s2 = new Shoe(8, "White");

        System.out.println("=================");
        s1.displayShoe();
        s2.displayShoe();
        System.out.println("=================");
    }
}
imbond
źródło
1

Używamy konstruktorów do inicjalizacji naszych zmiennych instancji (zmienne niestatyczne, zmienne należące do obiektów, a nie do klasy).

Jeśli chcesz zainicjować zmienne klasowe (zmienne statyczne) i chcesz to zrobić bez tworzenia obiektu (konstruktory można wywołać tylko podczas tworzenia obiektu), potrzebujesz bloków statycznych.

static Scanner input = new Scanner(System.in);
static int widht;
static int height;

static
{
    widht = input.nextInt();
    input.nextLine();
    height = input.nextInt();
    input.close();

    if ((widht < 0) || (height < 0))
    {
        System.out.println("java.lang.Exception: Width and height must be positive");
    }
    else
    {
        System.out.println("widht * height = " + widht * height);
    }
}
Michał
źródło
Czytanie stdin w statycznym inicjatorze to dość okropny pomysł. I System.out.println("B * H");jest całkiem bezużyteczny. A sama odpowiedź jest dość niejasna. OP nie wspomniał o konstruktorach ani zmiennych instancji.
shmosel
To tylko przykład, który pokazuje, czym jest inicjalizator statyczny i jak jest używany. OP nie pytał o konstruktory ani zmienne instancji, ale aby nauczyć go różnicy między statycznym inicjatorem a konstruktorem, musi to wiedzieć. W przeciwnym razie powiedziałby: „Dlaczego po prostu nie używam konstruktora do inicjalizacji moich zmiennych statycznych?”
Michael
0

Statyczny blok kodu umożliwia inicjalizację pól z więcej niż instucją, inicjalizację pól w innej kolejności deklaracji, a także może być używany do warunkowej inicjalizacji.

Dokładniej,

static final String ab = a+b;
static final String a = "Hello,";
static final String b = ", world";

nie zadziała, ponieważ a i b są zadeklarowane po ab.

Jednak mogłem użyć statycznego init. blok, aby to przezwyciężyć.

static final String ab;
static final String a;
static final String b;

static {
  b = ", world";
  a = "Hello";
  ab = a + b;
}

static final String ab;
static final String a;
static final String b;

static {
  b = (...) ? ", world" : ", universe";
  a = "Hello";
  ab = a + b;
}
algolicious
źródło
3
Chociaż to, co mówisz, jest prawdą, nie pokazuje konieczności statycznego bloku inicjalizatora. Możesz po prostu przenieść swoją abdeklarację poniżej deklaracji b.
gawi,
0

Blok inicjalizacji statycznej jest przydatny, jeśli chcesz zainicjować określone typy statyczne klas, przed pierwszym użyciem klasy. Późniejsze użycie nie spowoduje wywołania żadnych statycznych bloków inicjalizacyjnych. Jest to przeciwieństwo inicjatorów instancji, które inicjują elementy członkowskie instancji.

Remario
źródło
0

Jeśli chcesz ocenić dane wyrażenie podczas ładowania klasy, możesz skorzystać z bloku statycznego, ale pamiętaj:

Należy obsłużyć wyjątek w bloku statycznym, co oznacza, że ​​nie można zgłosić wyjątku z bloku statycznego.

I'm_Pratik
źródło