209

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

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

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

+5

Minor обратной связи, но это помогло бы, если бы Вы, пожалуйста, укажите ваши предположения ясно, и, следовательно, уточнить, какой ответ является правильным. когда я впервые прочитал ваш вопрос, я понял неправильно и подумал, что вы знаете разницу между '{...}' vs 'static {...}'. (в этом случае Джон Скит определенно ответил на ваш вопрос лучше) –

+0

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

ответ

350

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

{ 
    // 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 
+82

Почему это принятый ответ? Он даже не отвечает на вопрос. –

+41

Он отвечает на вопрос: «Это вызвано каждый раз, когда создается класс. Статический блок получает только один раз, независимо от того, сколько объектов этого типа вы создаете». –

+67

Для любознательного читателя нестатический блок фактически копируется компилятором Java в каждый конструктор, который имеет класс ([источник] (http://docs.oracle.com/javase/tutorial/java/javaOO/initial.html)). Поэтому задача конструктора - инициализировать поля. –

7

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

E.g.

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()); 
    } 
} 
87

Вот пример:

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

Для этого конкретного примера иногда * шаблон двойной скобки * был «злоупотреблен» :) – BalusC

+0

Это может быть злоупотребление, но, с другой стороны, оно очищает некоторые беспорядки и делает некоторые виды кода немного более «прочными». Я программирую в Erlang для удовольствия, и вы зацепились за ненужные локальные переменные :-) – Pointy

+1

<< Код в статических разделах (секциях) будет выполняться во время загрузки класса, прежде чем будут созданы какие-либо экземпляры класса (и до того, как какие-либо статические методы вызывают из других источников). Таким образом, вы можете убедиться, что все ресурсы класса готовы к использованию. >> (Какой «Pointy», упомянутый в вышеприведенном ответе), это очень важный момент, который следует отметить, когда дело доходит до выполнения статического блока. – learner

113

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

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

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

+1

Выполняется ли статический блок перед назначением статических переменных или после? 'private static int widgets = 0; static {widgets = 2;} ' –

+1

Любопытно, произошел ли статический блок перед назначением статических переменных или после него. например 'private static int widgets = 0; static {widgets = 2;} 'Обнаружено, что присвоение '=' происходит по порядку, что означает, что сначала будет назначаться первый столбец '='. В приведенном выше примере дается «виджетов» значение 2. (PS не знал, что комментарии могут быть отредактированы только через 5 минут ...) –

+0

@WeishiZeng: Да, это как описано в http://docs.oracle .com/javase/specs/jls/se8/html/jls-12.html # jls-12.4.2 - point 9. –

3

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

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

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

42

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

E.g.

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

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

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

+5

+1 для обработчика обработки исключений –

+1

Здесь опасные вуду ... статические инициализаторы запускаются в синтетическом методе clinit(), который * неявно синхронизируется *. Это означает, что JVM получит блокировку в соответствующем файле класса. Это может привести к тупиковой ситуации в многопоточных средах, если два класса попытаются загрузить друг друга, и каждый начинает загрузку в другом потоке. См. Http://www-01.ibm.com/support/docview.wss?uid=swg1IV48872 – Ajax

+0

@Ajax: Я бы счел это ошибкой в ​​рассматриваемом драйвере JDBC или в коде приложения, ответственном за его загрузку , Обычно, в случае приличных драйверов JDBC, пока вы загружаете его только один раз в приложении во время запуска приложения, ничего не происходит. – BalusC

9

Есть несколько фактических причин, что требуется, чтобы существовать:

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

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

-1

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

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

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

}

11

Я бы сказал, 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; 
} 
2

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

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 
} 

} 
6

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

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.

Как вы можете видеть, статический инициализатор обращается даже к полю privatealiases. Важно понимать, что блок static уже имеет доступ к экземплярам значения Enum (например, ENGLISH). Это происходит потому, что order of initialization and execution in the case of Enum types, так же, как если 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 в основном методе, заставляет класс загружаться и статическая инициализация запускается. Но в рамках статической инициализации мы снова вызываем конструкторы для статических полей, после чего она возобновляет статическую инициализацию и завершает конструктор, вызванный из основного метода. Скорее сложная ситуация, на которую я надеюсь, что при нормальном кодировании нам не придется иметь дело.

Для получения дополнительной информации об этом см. Книгу «Effective Java».

+1

Наличие доступа к 'aliases' не означает, что статический блок может обращаться к нестационарным членам. Доступ к 'aliases' осуществляется через значения' Language', возвращаемые методом/static/'values ​​()'. Как вы помните, тот факт, что переменные перечисления уже доступны в этой точке, - это необычный бит - нестатические члены обычных классов не были бы доступны в этой ситуации. – Ignazio

+0

Статический блок по-прежнему обращается к только статическим полям (в случае вашего перечисления ENGLISH, GERMAN, ...), которые в этом случае являются объектами. Поскольку статические поля являются объектами, вы можете получить доступ к полю экземпляра статического объекта. –

+1

'class Foo { статический финал Foo Inst1; статический финал Foo Inst2; статический { Inst1 = новый Foo ("Inst1"); Inst2 = новый Foo ("Inst2"); } статический { System.out.println ("Inst1:" + Inst1.member); System.out.println ("Inst2:" + Inst2.member); } частный конечный член строки; частный Foo (String member) { this.member = member; } } } Вышеупомянутый код ничем не отличается от примера перечисления и по-прежнему позволяет получить доступ к переменной экземпляра внутри статического блока. –

0

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

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

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

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

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

По loadLibrary from oracle:

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

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

0

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

0

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

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

Ссылка: Static keyword in java

Смежные вопросы