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

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

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

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

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

     { // Do Something... } 

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

    Пример:

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

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

     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 MAP = new HashMap(); static { MAP.put("banana", "honey"); MAP.put("peanut butter", "jelly"); MAP.put("rice", "beans"); } 

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

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

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

    Например

     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: со static

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

    Метод № 2: без static

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

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

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

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

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

    Например

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

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

     public enum Language { ENGLISH("eng", "en", "en_GB", "en_US"), GERMAN("de", "ge"), CROATIAN("hr", "cro"), RUSSIAN("ru"), BELGIAN("be",";-)"); static final private Map ALIAS_MAP = new HashMap(); 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 aliases; private Language(String... aliases) { this.aliases = Arrays.asList(aliases); } } 

    Здесь инициализатор используется для поддержания индекса ( ALIAS_MAP ), чтобы сопоставить набор псевдонимов с исходным типом enums. Он предназначен как расширение встроенного метода 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 в основном методе приводит к загрузке classа и началу статической инициализации. Но в рамках статической инициализации мы снова вызываем конструкторы для статических полей, после чего она возобновляет статическую инициализацию и завершает конструктор, вызванный из основного метода. Скорее сложная ситуация, на которую я надеюсь, что при нормальном кодировании нам не придется иметь дело.

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

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

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

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

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

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

     static int x = 3; 

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

     static int x; static { x=3; } 

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

     public static int x=initializeX(); private static int initializeX(){ return 3; } 

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

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

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

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

    Пример 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(Aa); // 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(Aa); // this will print 3 } } 

    Как дополнительный, как @Pointy сказал

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

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

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

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

    Согласно loadLibrary из oracle :

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

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

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

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

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

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

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

    }

    Давайте будем гением компьютера.