Java: что такое статический {}?
Может кто-нибудь объяснить мне, что является следующим?
public class Stuff
{
static
{
try
{
Class.forName("com.mysql.jdbc.Driver");
}
catch ( ClassNotFoundException exception )
{
log.error( "ClassNotFoundException " + exception.getMessage( ) );
}
...
}
Что делает этот статический { ...}?
Я знаю о статических переменных из C++, но это статический блок или что-то?
Когда этот материал будет казнен?
6 ответов
Статический блок называется статическим инициализатором класса - он запускается при первой загрузке класса (и это единственный раз, когда он запускается [сноска]).
Цель этого конкретного блока состоит в том, чтобы проверить, находится ли драйвер MySQL на пути к классам (и выбросить / записать ошибку, если это не так).
[footnote] Статический блок запускается один раз для каждого загрузчика классов, который загружает класс (поэтому, если у вас было несколько загрузчиков классов, отличных друг от друга (например, не делегируемых, например), он будет выполняться один раз каждый.
Основное использование статических блоков инициализаторов состоит в том, чтобы выполнять различные биты инициализации, которые могут не подходить внутри конструктора, так что вместе взятые конструктор и инициализаторы переводят вновь созданный объект в состояние, полностью совместимое для использования.
В отличие от конструкторов, например, статические инициализаторы не наследуются и выполняются только один раз, когда класс загружается и инициализируется JRE. В приведенном выше примере переменная класса foo будет иметь значение 998877 после завершения инициализации.
Также обратите внимание, что статические инициализаторы выполняются в том порядке, в котором они появляются в исходном файле. Кроме того, существует ряд ограничений на то, что вы не можете делать внутри одного из этих блоков, например, не использовать проверенные исключения, не использовать оператор return или ключевые слова this и super.
Статический блок инициализатора выполняется всякий раз, когда класс должен быть загружен в первый раз. Это может произойти, если что-то на более высоком уровне Class#forName("yourpackage.YourClass")
или new YourClass()
на рассматриваемый класс в первый раз.
По совпадению, приличные драйверы JDBC также имеют нечто подобное внутри. Именно они регистрируются в DriverManager
используя статический блок инициализатора:
static {
DriverManager.registerDriver(new ThisDriver());
}
так что всякий раз, когда вы делаете Class.forName("somepackage.ThisDriver")
, вы эффективно зарегистрируете драйвер в DriverManager
так что вы можете получить связь с ним потом.
Я хочу добавить, что статические переменные и статические инициализаторы выполняются в порядке появления во время загрузки класса. Таким образом, если ваш статический инициализатор использует некоторую статическую переменную, он должен быть инициализирован перед определенным статическим блоком, например
final static String JDBC_DRIVER = getJdbcDriver( );
static
{
try
{
Class.forName(JDBC_DRIVER);
}
catch ( ClassNotFoundException exception )
{
log.error( "ClassNotFoundException " + exception.getMessage( ) );
}
}
В этом примере getJdbcDriver
будет выполнен перед статическим инициализатором. Кроме того, в классе может быть более 1 статического инициализатора. Еще раз они выполнены в порядке появления.
Я также хочу упомянуть здесь наличие инициализаторов экземпляров, поскольку они удивляют, когда их видят впервые. Они выглядят как блок кода внутри тела класса, вот так.
class MyClass
{
final int intVar;
{
intVar = 1;
}
}
В общем случае их использование несколько ненужно из-за конструктора, но они полезны для реализации версии замыканий Java.
В дополнение ко всему вышесказанному, есть небольшая разница в использовании конструктора класса и инициализатора класса. Как мы знаем, конструктор будет использоваться для инициализации объектов, и если у нас есть статические переменные, то статический блок обычно используется для их инициализации, когда класс будет загружен.
Когда у нас есть статические переменные и статический блок, то сначала инициализируются статические переменные, а затем блок.
Когда класс загружается впервые, статический блок инициализируется перед конструктором класса.
блок статической инициализации
нормальный блок кода
заключено в фигурные скобки { }
ему предшествует ключевое слово static
class Foo { static { // initialization code goes here: doSomething(); } }
класс может иметь любое количество статических блоков инициализации
они могут появляться где угодно в теле класса
они называются в порядке появления в коде
Существует альтернатива статическим блокам инициализации:
- написать приватный статический метод
- и назначить его статической переменной класса
Преимущество этого подхода заключается в том, что статический метод может быть вызван позже для повторной инициализации переменной класса.
class Foo {
public static int myVar = initializeClassVariable();
private static int initializeClassVariable() {
// initialization code goes here:
int v = 255;
return v;
}
}