Статические блоки инициализации

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

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

Зачем нам нужны эти строки в специальном блоке, например: static {...}?

Ответ 1

Нестатический блок:

{
    // Do Something...
}

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

Пример:

public class Test {

    static{
        System.out.println("Static");
    }

    {
        System.out.println("Non-static block");
    }

    public static void main(String[] args) {
        Test t = new Test();
        Test t2 = new Test();
    }
}

Отпечатки:

Static
Non-static block
Non-static block

Ответ 2

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

public class Foo {
    private static final int widgets;

    static {
        int first = Widgets.getFirstCount();
        int second = Widgets.getSecondCount();
        // Imagine more complex logic here which really used first/second
        widgets = first + second;
    }
}

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

Теперь в этом конкретном случае вы можете использовать вместо этого статический метод:

public class Foo {
    private static final int widgets = getWidgets();

    static int getWidgets() {
        int first = Widgets.getFirstCount();
        int second = Widgets.getSecondCount();
        // Imagine more complex logic here which really used first/second
        return first + second;
    }
}

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

Ответ 3

Вот пример:

  private static final HashMap<String, String> MAP = new HashMap<String, String>();
  static {
    MAP.put("banana", "honey");
    MAP.put("peanut butter", "jelly");
    MAP.put("rice", "beans");
  }

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

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

Ответ 4

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

например.

static {
    try {
        Class.forName("com.example.jdbc.Driver");
    } catch (ClassNotFoundException e) {
        throw new ExceptionInInitializerError("Cannot load JDBC driver.", e);
    }
}

Эй, есть еще одно преимущество, вы можете использовать его для обработки исключений. Представьте, что getStuff() здесь выбрасывает Exception, который действительно принадлежит блоку catch:

private static Object stuff = getStuff(); // Won't compile: unhandled exception.

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

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

private static Properties config = new Properties();

static {
    try { 
        config.load(Thread.currentThread().getClassLoader().getResourceAsStream("config.properties");
    } catch (IOException e) {
        throw new ExceptionInInitializerError("Cannot load properties file.", e);
    }
}

Чтобы вернуться к примеру драйвера JDBC, любой достойный драйвер JDBC сам также использует инициализатор static для регистрации в DriverManager. Также см. этот и этот ответ.

Ответ 5

Я бы сказал, что static block - это просто синтаксический сахар. Нет ничего, что можно было бы сделать с блоком static, а не с чем-либо еще.

Повторное использование некоторых примеров, размещенных здесь.

Эта часть кода может быть переписана без использования инициализатора static.

Метод # 1: С static

private static final HashMap<String, String> MAP;
static {
    MAP.put("banana", "honey");
    MAP.put("peanut butter", "jelly");
    MAP.put("rice", "beans");
  }

Способ №2: Без static

private static final HashMap<String, String> MAP = getMap();
private static HashMap<String, String> getMap()
{
    HashMap<String, String> ret = new HashMap<>();
    ret.put("banana", "honey");
    ret.put("peanut butter", "jelly");
    ret.put("rice", "beans");
    return ret;
}

Ответ 6

Существует несколько фактических причин, по которым он должен существовать:

  • инициализация static final членов, инициализация которых может вызвать исключение
  • инициализация static final членов с вычисленными значениями

Люди склонны использовать блоки static {} как удобный способ инициализации того, что зависит от класса во время выполнения, например, для обеспечения загрузки определенного класса (например, драйверов JDBC). Это можно сделать другими способами; однако две вещи, о которых я упоминал выше, могут быть выполнены только с помощью конструкции, подобной блоку static {}.

Ответ 7

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

например.

class A {
  static int var1 = 6;
  static int var2 = 9;
  static int var3;
  static long var4;

  static Date date1;
  static Date date2;

  static {
    date1 = new Date();

    for(int cnt = 0; cnt < var2; cnt++){
      var3 += var1;
    }

    System.out.println("End first static init: " + new Date());
  }
}

Ответ 8

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

public enum Language { 
  ENGLISH("eng", "en", "en_GB", "en_US"),   
  GERMAN("de", "ge"),   
  CROATIAN("hr", "cro"),   
  RUSSIAN("ru"),
  BELGIAN("be",";-)");

  static final private Map<String,Language> ALIAS_MAP = new HashMap<String,Language>(); 
  static { 
    for (Language l:Language.values()) { 
      // ignoring the case by normalizing to uppercase
      ALIAS_MAP.put(l.name().toUpperCase(),l); 
      for (String alias:l.aliases) ALIAS_MAP.put(alias.toUpperCase(),l); 
    } 
  } 

  static public boolean has(String value) { 
    // ignoring the case by normalizing to uppercase
    return ALIAS_MAP.containsKey(value.toUpper()); 
  } 

  static public Language fromString(String value) { 
    if (value == null) throw new NullPointerException("alias null"); 
    Language l = ALIAS_MAP.get(value); 
    if (l == null) throw new IllegalArgumentException("Not an alias: "+value); 
    return l; 
  } 

  private List<String> aliases; 
  private Language(String... aliases) { 
    this.aliases = Arrays.asList(aliases); 
  } 
} 

Здесь инициализатор используется для поддержания индекса (ALIAS_MAP), чтобы сопоставить набор псевдонимов с исходным типом перечисления. Он предназначен как расширение встроенного метода valueOf, предоставленного самим Enum.

Как вы можете видеть, статический инициализатор обращается даже к полю private aliases. Важно понимать, что блок static уже имеет доступ к экземплярам значения Enum (например, ENGLISH). Это связано с тем, что порядок инициализации и выполнения в случае типов Enum, как если бы поля static private были инициализированы экземплярами до static блоки были вызваны:

  • Константы Enum, которые являются неявными статическими полями. Для этого требуются блоки конструктора и экземпляра Enum, а также инициализация экземпляра.
  • static блок и инициализация статических полей в порядке появления.

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

public class Foo {
  static { System.out.println("Static Block 1"); }
  public static final Foo FOO = new Foo();
  static { System.out.println("Static Block 2"); }
  public Foo() { System.out.println("Constructor"); }
  static public void main(String p[]) {
    System.out.println("In Main");
    new Foo();
  }
}

Мы видим следующий вывод:

Static Block 1
Constructor
Static Block 2
In Main
Constructor

Ясно, что статическая инициализация может произойти до конструктора и даже после:

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

Подробнее об этом см. в книге " Эффективная Java".

Ответ 9

Если ваши статические переменные необходимо установить во время выполнения, то блок static {...} очень полезен.

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

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

Ответ 10

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

1- Просто инициализируйте его, когда вы объявляете переменную:

static int x = 3;

2 Имейте статический блок инициализации:

static int x;

static {
 x=3;
}

3- Имейте метод класса (статический метод), который обращается к переменной класса и инициализирует его: это альтернатива вышеуказанному статическому блоку; вы можете написать частный статический метод:

public static int x=initializeX();

private static int initializeX(){
 return 3;
}

Теперь зачем использовать статический блок инициализации вместо статических методов?

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

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

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

Пример 1:

class A{
 public static int a =f();

// this is a static method
 private static int f(){
  return 3;
 }

// this is a static block
 static {
  a=5;
 }

 public static void main(String args[]) {
// As I mentioned, you do not need to create an instance of the class to use the class variable
  System.out.print(A.a); // this will print 5
 }

}

Пример 2:

class A{
 static {
  a=5;
 }
 public static int a =f();

 private static int f(){
  return 3;
 }

 public static void main(String args[]) {
  System.out.print(A.a); // this will print 3
 }

}

Ответ 11

В качестве дополнительного, например, @Pointy сказал

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

Предполагается добавить System.loadLibrary("I_am_native_library") в статический блок.

static{
    System.loadLibrary("I_am_a_library");
}

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

В соответствии с loadLibrary from oracle:

Если этот метод вызывается более одного раза с тем же именем библиотеки, второй и последующие вызовы игнорируются.

Так что совершенно неожиданно, если System.loadLibrary не используется, чтобы избежать многократной загрузки библиотеки.

Ответ 12

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

public class Main {

    private static int myInt;

    static {
        myInt = 1;
        System.out.println("myInt is 1");
    }

    //  needed only to run this class
    public static void main(String[] args) {
    }

}

и он будет печатать "myInt is 1" для консоли. Обратите внимание, что я не создавал экземпляр какого-либо класса.

Ответ 13

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

Типичным примером использования статического блока является поддержка вывода экземпляра Enum по его значению, для этого вы определяете хэш-карту как статическую переменную, которая отображает каждое значение в соответствующий экземпляр 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);
    }
}

Ссылка: Статическое ключевое слово в java

Ответ 14

static int B,H;
static boolean flag = true;
static{
    Scanner scan = new Scanner(System.in);
    B = scan.nextInt();
    scan.nextLine();
    H = scan.nextInt();

    if(B < 0 || H < 0){
        flag = false;
        System.out.println("java.lang.Exception: Breadth and height must be positive");
    } 
}

Ответ 15

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

Eg:-class Solution{
         // static int x=10;
           static int x;
       static{
        try{
          x=System.out.println();
          }
         catch(Exception e){}
        }
       }

     class Solution1{
      public static void main(String a[]){
      System.out.println(Solution.x);
        }
        }

Теперь мой статический int x будет инициализироваться динамически..Bcoz, когда компилятор перейдет к Solution.x, он загрузит класс решения и статическую нагрузку блока в время загрузки класса. Так что мы можем динамически инициализировать этого статического члена данных.

}