В чем разница между статическим и нестатическим блоком кода инициализации

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

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

11 ответов

Решение

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

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

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

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

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

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

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

ХОРОШО! Расскажи больше...

  • это блок кода 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.");
    }
}

Выход???

Внутри статический инициализатор.

яблоко

оранжевый

Груша

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

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

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

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

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

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

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

Это прямо с 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], нет конструкторов. Поэтому инициализаторы экземпляров необходимы для выполнения любых видов выражений во время построения.

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

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

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

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

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

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

Блок статического кода может использоваться для создания или инициализации переменных класса (в отличие от переменных объекта). Таким образом, объявление статического "a" означает, что только один объект является общим для всех объектов Test, и блок статического кода инициализирует "a" только один раз, когда класс Test загружается впервые, независимо от того, сколько объектов Test создано.

Определение статического блока из: Статическое ключевое слово в Java

Статический блок - это блок операторов внутри java-класса, который выполняется, когда класс впервые загружается в JVM, например, он инициализируется при первой ссылке на класс в коде.

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

PS: вы не можете использовать переменные экземпляра или вызывать методы экземпляра внутри статического блока.

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

public enum ErrorCodes {
    BUSINESS_ERROR(100), SERVER_ERROR(500), NETWORK_ERROR(1000);

    private int errorCode;
    // This field maps each error code numeric value to a corresponding Enum instance.
    private static Map<Integer, ErrorCodes> errorCodeByErrorNumber = new HashMap<Integer, ErrorCodes>();

    static {
        for (ErrorCodes errorCode : ErrorCodes.values()) {
            errorCodeByErrorNumber.put(errorCode.getErrorCode(), errorCode);
        }
    }

    private ErrorCodes(int errorCode) {
        this.errorCode = errorCode;
    }

    public int getErrorCode() {
        return errorCode;
    }

    public static ErrorCodes getErrorCodeByNumber(Integer dayNumber) {
        return errorCodeByErrorNumber.get(dayNumber);
    }
}

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

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

Порядок выполнения инициализаторов и конструкторов следующий:

  1. Статические блоки инициализации;
  2. блоки инициализатора объекта;
  3. Конструкторы;

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

Здесь у нас есть статический метод myStatic(), мы можем вызвать этот метод без какого-либо объекта, потому что когда мы делаем член статическим, он становится уровнем класса. Если мы удалим ключевое слово static и сделаем его нестатичным, нам нужно создать объект класса, чтобы вызвать его

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

class SimpleStaticExample {
//static method
static void myStatic()
{
    System.out.println("my Static Method");
}

public static void main(String[] args)
{
      /* You can see that we are calling this
       * method without creating any object. 
       */
       myStatic();
 }
}

Вывод: мой статический метод

Другие вопросы по тегам