Яка різниця між статичним і нестатичним блоком коду ініціалізації


357

Моє запитання стосується одного конкретного використання статичного ключового слова. Можна використовувати staticключове слово для покриття кодового блоку в класі, який не належить до жодної функції. Наприклад, наступні компіляції коду:

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

Якщо ви видалите staticключове слово, воно скаржиться, тому що змінна aє final. Однак можна видалити finalі staticключові слова, і зробити їх компільованими.

Мене це бентежить в обох напрямках. Як я повинен мати розділ коду, який не належить до жодного методу? Як можна викликати це? Взагалі, яка мета цього використання? Або краще, де я можу знайти документацію про це?

Відповіді:


403

Блок коду зі статичним модифікатором означає ініціалізатор класу ; без статичного модифікатора блок коду є ініціалізатором екземпляра .

Ініціалізатори класів виконуються в тому порядку, який вони визначені (зверху вниз, як і прості ініціалізатори змінних) під час завантаження класу (насправді, коли це вирішено, але це технічно).

Ініціалізатори екземплярів виконуються в порядку, визначеному при ініціалізації класу, безпосередньо перед виконанням коду конструктора, безпосередньо після виклику суперконструктора.

Якщо видалити staticз int a, стає змінним екземпляром, який ви не в змозі отримати доступ з статичної ініціалізації блоку. Це не вдасться компілювати з помилкою "нестатичну змінну a не можна посилатись із статичного контексту".

Якщо ви також видалите staticз блоку ініціалізатора, він стає ініціалізатором екземпляра і так int aініціалізується при побудові.


Статичний ініціалізатор фактично викликається пізніше, коли ініціалізується клас, після його завантаження та зв’язування. Це трапляється, коли ви створюєте екземпляр об'єкта класу або отримуєте доступ до статичної змінної чи методу в класі. Насправді, якщо у вас є клас зі статичним ініціалізатором і методом public static void staticMethod(){}, якщо ви виконуєте TestStatic.class.getMethod("staticMethod");. Статичний ініціалізатор не буде викликаний. Більше інформації тут docs.oracle.com/javase/specs/jvms/se10/html/…
Totò

@ Totò: Так, це спричиняє роздільну здатність класу (принаймні, коли вони називали це посиланням + init як "дозвіл" назад, коли). Я не здивований, що ви можете використовувати роздуми, щоб відкрити речі про клас, не вирішуючи його.
Лоуренс Дол

166

Uff! що таке статичний ініціалізатор?

Статичний ініціалізатор - це static {}блок коду всередині класу java, і запустити лише один раз до виклику конструктора або основного методу.

ДОБРЕ! Розкажи мені більше...

  • це блок коду static { ... }всередині будь-якого класу java. і виконується віртуальною машиною, коли викликається клас.
  • Жодні returnзаяви не підтримуються.
  • Аргументи не підтримуються.
  • Ні thisабо superне підтримуються.

Хм, де я можу це використати?

Можна використовувати в будь-якому місці, де вам здається нормально :) Але я бачу більшу частину часу, коли він використовується під час підключення до бази даних, API init, ведення журналів тощо.

Не просто гавкайте! де приклад?

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.");
    }
}

Вихід ???

Всередині статичного ініціалізатора.

Apple

Помаранчевий

Груша

Кінцевий статичний ініціалізатор.

Всередині основний метод.

Сподіваюся, це допомагає!


Дякую Мадан! Може статичний блок буде використовуватися замість afterPropertiesSet()з InitializingBean?
Олександр Сурафел

3
Так, ти можеш! Статичний ініціалізатор стає викликом, коли клас завантажується jvm. Таким чином, це дійсно перша фаза, на якій виконується код. Якщо у вас теж є конструктор, замовлення було б: статичний ініціалізатор, конструктор, afterPropertiesSet
Martin Baumgartner

57

staticБлок є «статичним ініціалізатор».

Він автоматично викликається під час завантаження класу, і іншого способу викликати його немає (навіть через Reflection).

Я особисто коли-небудь використовував його, коли писав код JNI:

class JNIGlue {
    static {
        System.loadLibrary("foo");
    }
}

6
Ні, немає явного способу викликати це, ініціалізатор класу ніколи не представлений Methodекземпляром, а лише викликається віртуальною машиною Java.
Рафаель Вінтерхалтер

46

Це безпосередньо з http://www.programcreek.com/2011/10/java-class-instan-initializer/

1. Наказ про виконання

Подивіться на наступний клас, чи знаєте ви, хто з них виконується першим?

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();
    }
}

Вихід:

статичний ініціалізатор викликається

викликається ініціалізатор екземпляра

конструктор закликав

викликається ініціалізатор екземпляра

конструктор закликав

2. Як працює ініціалізатор екземпляра Java?

Ініціалізатор екземпляра вище містить операцію println. Щоб зрозуміти , як це працює, ми можемо розглядати його як оператор присвоювання змінної, наприклад, b = 0. Це може зробити його більш зрозумілим для розуміння.

Замість

int b = 0, можна було написати

int b;
b = 0;

Тому ініціалізатори екземплярів та ініціалізатори змінних екземплярів майже однакові.

3. Коли корисні ініціалізатори екземплярів?

Використання ініціалізаторів екземплярів є рідкісними, але все ж може бути корисною альтернативою екземплярам ініціалізаторів змінних, якщо:

  1. Код ініціалізатора повинен обробляти винятки
  2. Виконайте обчислення, які неможливо виразити ініціалізатором змінної екземпляра.

Звичайно, такий код можна було записати в конструкторах. Але якби в класі було кілька конструкторів, вам доведеться повторити код у кожному конструкторі.

За допомогою ініціалізатора екземпляра ви можете просто написати код один раз, і він буде виконуватися незалежно від того, який конструктор використовується для створення об'єкта. (Я думаю, це просто поняття, і воно використовується не часто.)

Інший випадок, коли ініціалізатори екземплярів корисні - це анонімні внутрішні класи, які взагалі не можуть оголосити жодних конструкторів. (Чи це буде вдале місце для розміщення функції реєстрації?)

Завдяки Дерхейну.

Також зауважте, що класи Anonymous, які реалізують інтерфейси [1], не мають конструкторів. Тому ініціалізатори екземплярів потрібні для виконання будь-яких видів виразів під час створення.


12

"final" гарантує, що змінна повинна бути ініціалізована перед закінченням коду ініціалізатора об'єкта. Так само "статичний фінал" гарантує, що змінна буде ініціалізована до кінця класу коду ініціалізації. Якщо випустити "статичний" з коду ініціалізації, він перетворює його в код ініціалізації об'єкта; таким чином ваша змінна вже не відповідає її гарантіям.


8

Ви не будете записувати код у статичний блок, який потрібно викликати в будь-якій точці вашої програми. Якщо мета коду викликається, ви повинні розмістити його в методі.

Ви можете написати статичні блоки ініціалізатора для ініціалізації статичних змінних при завантаженні класу, але цей код може бути складнішим.

Блок статичного ініціалізатора виглядає як метод без імені, аргументів і без повернення. Оскільки ви його ніколи не називаєте, ім'я йому не потрібно. Єдиний час, коли його називають, коли віртуальна машина завантажує клас.


6

коли розробник використовує блок ініціалізатора, Java-компілятор копіює ініціалізатор у кожен конструктор поточного класу.

Приклад:

наступний код:

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
    }
}

еквівалентно:

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
    }
}

Я сподіваюся, що мій приклад зрозуміли розробники.


4

Блок статичного коду може використовуватися для інстанціфікації або ініціалізації змінних класу (на відміну від об'єктних змінних). Таким чином, оголошення "статичним" означає, що це лише один спільний доступ до всіх тестових об'єктів, а блок статичного коду ініціалізує "а" лише один раз, коли вперше завантажується тестовий клас, незалежно від того, скільки тестових об'єктів створено.


У подальшому, якщо я не створюю екземпляр об'єкта, а натомість викликаю публічну статичну функцію. Чи означає, що цей блок гарантовано виконується перед цим загальнодоступним викликом функції? Дякую.
Szere Dyeri

Якщо ви викликаєте загальнодоступну статичну функцію класу, то клас потрібно спочатку завантажити, тому так, спочатку виконається статичний ініціалізатор.
Пол Томблін

Якщо тільки не була ініціалізація класу, яка (побічно) називала код, який намагається ним скористатися. IFYSWIM. Кругові залежності і все таке.
Том Хотін - тайклін

1
@ Том прав - можна написати щось там, де один статичний ініціалізатор викликає статичний метод перед тим, як викликається інший статичний ініціалізатор, але мій розум відхилився від думки, тому я ніколи цього не вважав.
Пол Томблін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.