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и указывал на неизменяемый объект.

Том Хотин - tackline
источник
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

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

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

Я ПРАТИК
источник