Java: когда полезен статический блок инициализации?


96

В чем разница между инициализацией внутри staticблока:

public class staticTest {

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

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

И индивидуальная статическая инициализация:

public class staticTest {

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

    ....

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

Это хорошее место для загрузки классов или собственной библиотеки.
qrtt1

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

Ответы:


113

Блоки статической инициализации допускают более сложную инициализацию, например, с использованием условных выражений:

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

Или когда требуется больше, чем просто построение: при использовании построителя для создания вашего экземпляра необходима обработка исключений или работа, отличная от создания статических полей.

Блок статической инициализации также запускается после встроенных статических инициализаторов, поэтому допустимо следующее:

static double a;
static double b = 1;

static {
    a = b * 4; // Evaluates to 4
}

3
Выполнение «b = a * 4;» inline будет проблемой только в том случае, если b был объявлен перед a, что не так в вашем примере.
Джордж Хокинс,

1
@GeorgeHawkins Я только пытался проиллюстрировать, что статический инициализатор запускается после встроенных инициализаторов, а не то, что эквивалент не может быть выполнен встроенным. Однако я понимаю вашу точку зрения и обновил пример, чтобы (надеюсь) быть более ясным.
Рич О'Келли,

1
Ради интереса я могу указать, что ваш первый пример может быть таким же простым: «static double a = someCondition? 0: 1;» Не то, чтобы твои примеры не очень
Билл К

18

Типичное использование:

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

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

Как бы вы это сделали без статического инициализатора?


2
Ответ: Гуава :) +1
Пол Беллора

2
Другой ответ без дополнительных библиотек: создайте статический метод, инкапсулирующий инициализацию, SETи используйте инициализатор переменной ( private final static Set<String> SET = createValueSet()). Что, если у вас есть 5 наборов и 2 карты, вы бы просто свалили их все в один staticблок?
TWiStErRob

16

Вы можете использовать блок try / catch внутри, static{}как показано ниже:

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: Ссылаются отсюда !


12

Другая причина - обработка исключений во время инициализации. Например:

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

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


5

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


4

В вашем примере разницы нет; но часто начальное значение сложнее, чем удобно выразить в одном выражении (например, это a List<String>, содержимое которого лучше всего выражается for-loop; или это a, Methodкоторое может не существовать, поэтому требуются обработчики исключений), и / или статические поля должны быть установлены в определенном порядке.


4

staticblock может использоваться для инициализации экземпляра singleton , чтобы предотвратить использование синхронизированного getInstance() метода.


3

Технически без него можно обойтись. Некоторые предпочитают использовать многострочный код инициализации вместо статического метода. Я вполне доволен использованием статического инициализатора для относительно простой инициализации нескольких состояний.

Конечно, я почти всегда делал свою статику finalи указывал на неизменяемый объект.


3

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

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

Пример:

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

1

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

В частности,

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

не будет работать, потому что a и b объявлены после ab.

Однако я мог использовать статический init. блок, чтобы преодолеть это.

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

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

1

Мы используем конструкторы для инициализации переменных нашего экземпляра (нестатические переменные, переменные, принадлежащие объектам, а не классу).

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

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

Чтение stdin в статическом инициализаторе - довольно ужасная идея. И System.out.println("B * H");это довольно бесполезно. Да и сам ответ довольно расплывчатый. OP не упомянул конструкторы или переменные экземпляра.
shmosel 03

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

0

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


0

Если вы хотите оценить какое-либо определенное выражение во время загрузки класса, вы можете использовать статический блок, но помните:

Вы должны обрабатывать исключение в статическом блоке, что означает, что вы не можете создать исключение из статического блока.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.