Java: Коли корисний статичний блок ініціалізації?


91

У чому різниця між ініціалізацією всередині 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

Відповіді:


106

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

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

14

Ви можете використовувати блок 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: З цього посилається !


11

Обробка винятків під час ініціалізації - ще одна причина. Наприклад:

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; або це таке, Methodяке може не існувати, тому потрібні обробники винятків), та / або статичні поля потрібно встановлювати в певному порядку.


4

staticblock може бути використаний для ініціалізації одиничного екземпляра, щоб запобігти використанню синхронізованого getInstance() методу.


3

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

Звичайно, я майже завжди роблю свою статику finalі вказую на немодифікований об’єкт.


3

Статичне ключове слово (будь то змінна чи блок) належить до класу. Отже, коли клас викликається, ці змінні або блоки виконуються. Тож більша частина ініціалізації буде здійснена за допомогою статичного ключового слова. Оскільки він належить самому класу, клас може безпосередньо отримати до нього доступ, не створюючи екземпляра класу.

Візьмемо приклад. Існує клас взуття, в якому є кілька змінних, такі як колір, розмір, марка тощо ... І тут, якщо компанія, що виробляє взуття, має лише одну марку, ми повинні ініціювати її як статичну змінну. Отже, коли викликається клас взуття та виробляються різні типи взуття (шляхом створення екземпляра класу), тоді колір і розмір будуть займати пам’ять при створенні нового взуття, але тут бренд є спільною властивістю для всього взуття, так що він займе пам'ять раз, незалежно від того, скільки взуття виготовлено.

Приклад:

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

Це лише приклад, який показує, що таке статичний ініціалізатор і як він використовується. OP не запитував конструктори або змінні екземпляра, але для того, щоб навчити його різниці статичного ініціалізатора від конструктора, він повинен це знати. В іншому випадку він сказав би: "Чому я просто не використовую конструктор для ініціалізації своїх статичних змінних?"
Майкл

0

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

Більш конкретно,

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

0

Статичний блок ініціалізації корисний, якщо ви хочете інціалізувати вказані статичні типи класу до першого використання класу. Подальше використання не викликатиме статичних блоків ініціалізації. Це пряма протилежність ініціалізаторам екземплярів, які ініціалізують члени екземпляра.


0

Коли ви хочете оцінити якийсь певний вираз під час завантаження класу, ви можете використовувати статичний блок, але пам'ятайте:

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

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