Статические блоки инициализации - PullRequest
244 голосов
/ 10 марта 2010

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

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

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

Ответы [ 14 ]

398 голосов
/ 10 марта 2010

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

{
    // 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
123 голосов
/ 10 марта 2010

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

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

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

98 голосов
/ 10 марта 2010

Вот пример:

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

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

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

47 голосов
/ 11 марта 2010

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

* 1005 Е.Г. *

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. Также см. это и это ответ.

11 голосов
/ 28 февраля 2013

Я бы сказал, 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;
}
10 голосов
/ 07 сентября 2012

Есть несколько фактических причин, по которым он необходим:

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

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

8 голосов
/ 10 марта 2010

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

* 1003 Е.Г. *

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());
  }
}
7 голосов
/ 15 июня 2015

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

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:

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

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

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

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

3 голосов
/ 10 марта 2010

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

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

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

2 голосов
/ 29 июля 2013

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

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
 }

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...