В чем разница между статическим и нестатическим блоком кода инициализации - PullRequest
327 голосов
/ 02 декабря 2008

Мой вопрос об одном конкретном использовании статического ключевого слова. Можно использовать ключевое слово 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 и заставить их скомпилироваться.

Это смущает меня в обоих направлениях. Как я должен иметь раздел кода, который не принадлежит ни к какому методу? Как это можно вызвать? Вообще, какова цель этого использования? Или лучше, где я могу найти документацию по этому поводу?

Ответы [ 8 ]

383 голосов
/ 02 декабря 2008

Блок кода со статическим модификатором означает инициализатор class ; без статического модификатора кодовый блок является инициализатором instance .

Инициализаторы класса выполняются в том порядке, в котором они определены (сверху вниз, как простые инициализаторы переменных), когда класс загружается (фактически, когда он разрешен, но это техническая сложность).

Инициализаторы экземпляра выполняются в порядке, определенном при создании экземпляра класса, непосредственно перед выполнением кода конструктора, сразу после вызова супер-конструктора.

Если вы удалите static из int a, он станет переменной экземпляра, к которой вы не сможете получить доступ из блока статического инициализатора. Это не скомпилируется с ошибкой «на нестатическую переменную a нельзя ссылаться из статического контекста».

Если вы также удалите static из блока инициализатора, он станет инициализатором экземпляра, поэтому int a инициализируется при построении.

152 голосов
/ 24 марта 2015

Уфф! что такое статический инициализатор?

Статический инициализатор представляет собой блок кода static {} внутри java-класса и запускается только один раз перед вызовом конструктора или метода main.

OK! Скажи мне больше ...

  • - это блок кода static { ... } внутри любого Java-класса. и выполняется виртуальной машиной при вызове класса.
  • Нет return операторов поддерживаются.
  • Аргументы не поддерживаются.
  • Нет this или super.

Хм, где я могу его использовать?

Может использоваться везде, где вы чувствуете себя хорошо :) это просто. Но я вижу, что большую часть времени он используется при подключении к базе данных, инициализации API, ведении журнала и т. Д.

Не просто лаять! где пример?

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

Orange

груша

Завершить статический инициализатор.

Внутри основного метода.

Надеюсь, это поможет!

54 голосов
/ 02 декабря 2008

Блок static является «статическим инициализатором».

Он автоматически вызывается при загрузке класса, и нет другого способа вызвать его (даже через Reflection).

Лично я использовал его только когда писал код JNI:

class JNIGlue {
    static {
        System.loadLibrary("foo");
    }
}
35 голосов
/ 22 мая 2015

Это напрямую от http://www.programcreek.com/2011/10/java-class-instance-initializers/

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. Выполнять вычисления, которые не могут быть выражены с помощью инициализатора переменной экземпляра.

Конечно, такой код можно написать в конструкторах. Но если бы в классе было несколько конструкторов, вам пришлось бы повторять код в каждом конструкторе.

С помощью инициализатора экземпляра вы можете просто написать код один раз, и он будет выполняться независимо от того, какой конструктор используется для создания объекта. (Полагаю, это просто концепция, и она используется не часто.)

Другой случай, когда инициализаторы экземпляров полезны, это анонимные внутренние классы, которые вообще не могут объявлять никаких конструкторов. (Будет ли это хорошим местом для размещения функции регистрации?)

Благодаря Derhein.

Также обратите внимание, что у анонимных классов, которые реализуют интерфейсы [1], нет конструкторов. Поэтому инициализаторы экземпляров необходимы для выполнения любых видов выражений во время построения.

12 голосов
/ 02 декабря 2008

«final» гарантирует, что переменная должна быть инициализирована до завершения кода инициализатора объекта. Аналогично, «static final» гарантирует, что переменная будет инициализирована в конце кода инициализации класса. Отсутствие «статического» кода инициализации превращает его в код инициализации объекта; таким образом, ваша переменная больше не удовлетворяет своим гарантиям.

6 голосов
/ 11 апреля 2014

когда разработчик использует блок инициализатора, компилятор 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
    }
}

Надеюсь, мой пример понятен разработчикам.

6 голосов
/ 02 декабря 2008

Вы не будете писать код в статический блок, который нужно вызывать где-либо в вашей программе. Если нужно вызвать код, то вы должны поместить его в метод.

Вы можете написать статические блоки инициализатора для инициализации статических переменных, когда класс загружен, но этот код может быть более сложным ..

Статический блок инициализатора выглядит как метод без имени, без аргументов и без возвращаемого типа. Поскольку вы никогда не называете это, ему не нужно имя. Единственный раз, когда он вызывается, это когда виртуальная машина загружает класс.

4 голосов
/ 02 декабря 2008

Статический блок кода можно использовать для создания или инициализации переменных класса (в отличие от переменных объекта). Поэтому объявление статического «a» означает, что всем объектам Test доступен только один объект, а блок статического кода инициализирует «a» только один раз, когда класс Test загружается впервые, независимо от того, сколько объектов Test создано.

...