Статические Блоки Инициализации
насколько я понял "статический блок инициализации" используется для задания значений статического поля, если он не может быть сделано в одной строке.
но я не понимаю, зачем нужен специальный блок для этого. Например, мы объявляем поле статическим (без присвоения значения). А затем напишите несколько строк кода, которые генерируют и присваивают значение выше объявленному статическому полю.
зачем нам нужны эти строки в специальном блоке типа:static {...}?
13 ответов:
The нестатический блок:
{ // 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
если бы они не были в статическом блоке инициализации, где бы они были? Как бы вы объявили переменную, которая должна была быть локальной только для целей инициализации, и отличить ее от поля? Например, как бы вы хочу написать:
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перед ним это будет считаться блоком инициализации экземпляра вместо a статический блок инициализации, поэтому он будет выполняться один раз для каждого построенного экземпляра, а не один раз в целом.теперь в этом конкретном случае вы можете использовать статический метод вместо:
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; } }... но это не работает, когда есть несколько переменных, которые вы хотите назначить в одном блоке, или нет (например, если вы просто хотите что - то записать-или, возможно, инициализировать собственную библиотеку).
вот пример:
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", отключены.
это также полезно, когда вы на самом деле не хотите присваивать значение чему-либо, например, загрузка некоторого класса только один раз во время выполнения.
например.
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. Также смотрите этой и этой ответ.
Я бы сказал
static block- это просто синтаксический сахар. Там нет ничего, что вы могли бы сделать сstaticблок и ни с чем другим., чтобы повторно использовать некоторые примеры, размещенные здесь.
этот кусок кода может быть переписан без использования
staticинициализации тестов.Метод №1: С
staticprivate static final HashMap<String, String> MAP; static { MAP.put("banana", "honey"); MAP.put("peanut butter", "jelly"); MAP.put("rice", "beans"); }Метод №2: Без
staticprivate 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; }
есть несколько фактических причин, по которым он должен существовать:
- инициализации
static finalчлены, инициализация которых может вызвать исключение- инициализации
static finalэлементы с вычисленными значениямилюди, как правило, используют
static {}блоки как удобный способ инициализации вещей, от которых зависит класс во время выполнения, а также - например, обеспечение загрузки определенного класса (например, драйверы JDBC). Что можно сделать в другом однако две вещи, о которых я упоминал выше, могут быть сделаны только с такой конструкцией, какstatic {}блок.
вы можете выполнить биты кода один раз для класса, прежде чем объект будет построен в статических блоках.
например.
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()); } }
это распространенное заблуждение, думать, что статический блок имеет доступ только к статическим полям. Для этого я хотел бы показать ниже фрагмент кода, который я довольно часто использую в реальных проектах (частично скопирован из еще один ответ в несколько ином контексте):
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блоки были звонил:
- The
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 в основном методе приводит к загрузке класса и запуску статической инициализации. Но в рамках статической инициализации мы снова вызываем конструкторы для статических полей, после чего он возобновляет статическую инициализацию и завершает конструктор, вызываемый из метода main. Довольно сложная ситуация, для которой я надеюсь, что в нормальном кодировании нам не придется иметь дело.
для получения дополнительной информации об этом см. книгу"Эффективная Java".
Если ваши статические переменные должны быть установлены во время выполнения, то
static {...}блок очень полезен.например, если вам нужно установить статический элемент в значение, которое хранится в файле конфигурации или базе данных.
также полезно, когда вы хотите добавить значения к статическому
Mapчлен, поскольку вы не можете добавить эти значения в начальное объявление члена.
таким образом, у вас есть статическое поле (оно также называется "переменная класса", потому что оно принадлежит классу, а не экземпляру класса; другими словами, оно связано с классом, а не с каким-либо объектом), и вы хотите его инициализировать. Поэтому, если вы не хотите создавать экземпляр этого класса и хотите манипулировать этим статическим полем, вы можете сделать это тремя способами:
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 } }
как дополнение, как сказал @Pointy
код в разделе "статика" (с) будет выполняться при загрузке класса время, прежде чем будут построены какие-либо экземпляры класса (и до любые статические методы вызываются из других источников).
он должен добавить
System.loadLibrary("I_am_native_library")в статический блок.static{ System.loadLibrary("I_am_a_library"); }это гарантирует, что ни один собственный метод не будет вызван до загрузки соответствующей библиотеки в память.
по данным loadLibrary от oracle:
Если этот метод вызывается несколько раз с тем же именем библиотеки, второй и последующие вызовы игнорируются.
Так совершенно неожиданно, поставив систему.loadLibrary не используется, чтобы избежать многократной загрузки библиотеки.
сначала нужно понять, что сами классы приложений создаются в
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" для консоли. Обратите внимание, что я не создал ни одного класса.
статический блок используется для любой технологии для инициализации статического элемента данных динамическим способом, или мы можем сказать, что для динамической инициализации статического элемента данных используется статический блок..Потому что для нестатической инициализации элемента данных у нас есть конструктор, но у нас нет места, где мы можем динамически инициализировать статический элемент данных
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 будет инициализироваться динамически ..Потому что когда компилятор будет перейти к решению.x он загрузит класс решения и статическая нагрузка блока при загрузке класса time..So мы можем динамически инициализировать этот статический элемент данных..
}
Comments