Что такое блок инициализации?
Мы можем поместить код в конструктор, метод или блок инициализации. Какая польза от блока инициализации? Нужно ли, чтобы это было в каждой Java-программе?
8 ответов
Прежде всего, есть два типа блоков инициализации:
- блоки инициализации экземпляра и
- блоки статической инициализации.
Этот код должен иллюстрировать их использование и в каком порядке они выполняются:
public class Test {
static int staticVariable;
int nonStaticVariable;
// Static initialization block:
// Runs once (when the class is initialized)
static {
System.out.println("Static initalization.");
staticVariable = 5;
}
// Instance initialization block:
// Runs each time you instantiate an object
{
System.out.println("Instance initialization.");
nonStaticVariable = 7;
}
public Test() {
System.out.println("Constructor.");
}
public static void main(String[] args) {
new Test();
new Test();
}
}
Печать:
Static initalization.
Instance initialization.
Constructor.
Instance initialization.
Constructor.
Блоки инициализации экземпляра полезны, если вы хотите, чтобы какой-то код выполнялся независимо от того, какой конструктор используется, или если вы хотите выполнить инициализацию некоторых экземпляров для анонимных классов.
Хотел бы добавить к ответу @aioobe
Порядок исполнения:
статические блоки инициализации суперклассов
статическая инициализация блоков класса
блоки инициализации экземпляра суперклассов
конструкторы суперклассов
блоки инициализации экземпляра класса
конструктор класса.
Следует учесть пару дополнительных моментов (пункт 1 - повторение ответа @aioobe):
Код в блоке статической инициализации будет выполняться во время загрузки класса (и да, это означает, что только один раз для загрузки класса), до того, как будут созданы какие-либо экземпляры класса, и до того, как будут вызваны любые статические методы.
Блок инициализации экземпляра фактически копируется компилятором Java в каждый конструктор класса. Таким образом, каждый раз, когда код в блоке инициализации экземпляра выполняется точно перед кодом в конструкторе.
Хороший ответ от aioobe, добавив еще несколько пунктов
public class StaticTest extends parent {
static {
System.out.println("inside satic block");
}
StaticTest() {
System.out.println("inside constructor of child");
}
{
System.out.println("inside initialization block");
}
public static void main(String[] args) {
new StaticTest();
new StaticTest();
System.out.println("inside main");
}
}
class parent {
static {
System.out.println("inside parent Static block");
}
{
System.out.println("inside parent initialisation block");
}
parent() {
System.out.println("inside parent constructor");
}
}
это дает
inside parent Static block
inside satic block
inside parent initialisation block
inside parent constructor
inside initialization block
inside constructor of child
inside parent initialisation block
inside parent constructor
inside initialization block
inside constructor of child
inside main
это как изложение очевидного, но кажется немного более ясным.
Пример кода, который утвержден в качестве ответа, является правильным, но я не согласен с ним. Это не показывает, что происходит, и я собираюсь показать вам хороший пример, чтобы понять, как на самом деле работает JVM:
package test;
class A {
A() {
print();
}
void print() {
System.out.println("A");
}
}
class B extends A {
static int staticVariable2 = 123456;
static int staticVariable;
static
{
System.out.println(staticVariable2);
System.out.println("Static Initialization block");
staticVariable = Math.round(3.5f);
}
int instanceVariable;
{
System.out.println("Initialization block");
instanceVariable = Math.round(3.5f);
staticVariable = Math.round(3.5f);
}
B() {
System.out.println("Constructor");
}
public static void main(String[] args) {
A a = new B();
a.print();
System.out.println("main");
}
void print() {
System.out.println(instanceVariable);
}
static void somethingElse() {
System.out.println("Static method");
}
}
Прежде чем начать комментировать исходный код, я дам вам краткое объяснение статических переменных класса:
Во-первых, они называются переменными класса, они принадлежат классу, а не конкретному экземпляру класса. Все экземпляры класса совместно используют эту статическую (классовую) переменную. Каждая переменная имеет значение по умолчанию в зависимости от примитива или ссылочного типа. Другое дело, когда вы переназначаете статическую переменную в некоторых членах класса (блоки инициализации, конструкторы, методы, свойства) и, делая это, вы изменяете значение статической переменной не для конкретного экземпляра, вы меняете его для всех экземпляров. В заключение о статической части я скажу, что статические переменные класса создаются не тогда, когда вы впервые создаете экземпляр класса, они создаются при определении класса, они существуют в JVM без необходимости каких-либо экземпляров. Поэтому правильный доступ к статическим членам из внешнего класса (класс, в котором они не определены) заключается в использовании имени класса, следующего за точкой, а затем статического члена, к которому вы хотите получить доступ (шаблон: <CLASS_NAME>.<STATIC_VARIABLE_NAME>
).
Теперь давайте посмотрим на код выше:
Точка входа является основным методом - всего три строки кода. Я хочу сослаться на пример, который в настоящее время одобрен. В соответствии с этим первое, что должно быть напечатано после печати "Статического блока инициализации", это "Блок инициализации", и вот мое несогласие, блок нестатической инициализации не вызывается перед конструктором, он вызывается перед любой инициализацией конструкторов. класса, в котором определен блок инициализации. Конструктор класса - это первое, что задействуется при создании объекта (экземпляра класса), а затем при входе в конструктор первая вызываемая часть является либо неявным (по умолчанию) супер-конструктором, либо явным супер-конструктором, либо явным вызовом другого перегруженного конструктор (но в какой-то момент, если есть цепочка перегруженных конструкторов, последний вызывает супер-конструктор, неявно или явно).
Существует полиморфное создание объекта, но прежде чем войти в класс B и его основной метод, JVM инициализирует все переменные класса (статические), затем проходит через блоки статической инициализации, если таковые существуют, и затем входит в класс B и начинается с выполнение основного метода. Он переходит к конструктору класса B, а затем немедленно (неявно) вызывает конструктор класса A, используя полиморфизм, метод (переопределенный метод), вызываемый в теле конструктора класса A, который определен в классе B, и в этом случае переменная с именем instanceVariable используется перед повторной инициализацией. После закрытия конструктора класса B поток возвращается конструктору класса B, но перед печатью "Конструктора" он идет сначала в нестатический блок инициализации. Для лучшего понимания отладки с помощью некоторой IDE, я предпочитаю Eclipse.
Блок Initializer содержит код, который всегда выполняется при создании экземпляра. Он используется для объявления / инициализации общей части различных конструкторов класса.
Порядок конструкторов инициализации и блока инициализатора не имеет значения, блок инициализатора всегда выполняется перед конструктором.
Что если мы хотим выполнить некоторый код один раз для всех объектов класса?
Мы используем статический блок в Java.
В дополнение к тому, что было сказано в предыдущих ответах, блоки могут быть synchronized
.. никогда не чувствовал, что мне нужно его использовать, но он есть
Блоки инициализации выполняются всякий раз, когда класс инициализируется и перед вызовом конструкторов. Они обычно размещаются над конструкторами в фигурных скобках. Нет необходимости включать их в ваши классы.
Они обычно используются для инициализации ссылочных переменных. Эта страница дает хорошее объяснение
Чтобы узнать, как использовать статический блок инициализации, обратитесь к исходному коду Class.forName, а также к этой статье его использования http://cephas.net/blog/2005/07/31/java-classfornamestring-classname-and-jdbc/, они использовать блок инициализации для динамической загрузки классов.
public class StaticInitializationBlock {
static int staticVariable;
int instanceVariable;
// Static Initialization Block
static {
System.out.println("Static block");
staticVariable = 5;
}
// Instance Initialization Block
{
instanceVariable = 7;
System.out.println("Instance Block");
System.out.println(staticVariable);
System.out.println(instanceVariable);
staticVariable = 10;
}
public StaticInitializationBlock() {
System.out.println("Constructor");
}
public static void main(String[] args) {
new StaticInitializationBlock();
new StaticInitializationBlock();
}
}
Выход:
Static block
Instance Block
5
7
Constructor
Instance Block
10
7
Constructor
Блок инициализатора определяется внутри класса, а не как часть метода. Он выполняется для каждого объекта, созданного для класса. В следующем примере класс Employee
определяет блок инициализатора:
class Employee {
{
System.out.println("Employee:initializer");
}
}
Вопрос не совсем понятен, но вот краткое описание способов инициализации данных в объекте. Предположим, у вас есть класс A, который содержит список объектов.
1) Поместите начальные значения в объявление поля:
class A {
private List<Object> data = new ArrayList<Object>();
}
2) Назначьте начальные значения в конструкторе:
class A {
private List<Object> data;
public A() {
data = new ArrayList<Object>();
}
}
Они оба предполагают, что вы не хотите передавать "данные" в качестве аргумента конструктора.
Все становится немного сложнее, если вы смешиваете перегруженные конструкторы с внутренними данными, как описано выше. Рассматривать:
class B {
private List<Object> data;
private String name;
private String userFriendlyName;
public B() {
data = new ArrayList<Object>();
name = "Default name";
userFriendlyName = "Default user friendly name";
}
public B(String name) {
data = new ArrayList<Object>();
this.name = name;
userFriendlyName = name;
}
public B(String name, String userFriendlyName) {
data = new ArrayList<Object>();
this.name = name;
this.userFriendlyName = userFriendlyName;
}
}
Обратите внимание, что много повторяющегося кода. Вы можете исправить это, заставив конструкторы вызывать друг друга, или у вас может быть частный метод инициализации, который вызывает каждый конструктор:
class B {
private List<Object> data;
private String name;
private String userFriendlyName;
public B() {
this("Default name", "Default user friendly name");
}
public B(String name) {
this(name, name);
}
public B(String name, String userFriendlyName) {
data = new ArrayList<Object>();
this.name = name;
this.userFriendlyName = userFriendlyName;
}
}
или же
class B {
private List<Object> data;
private String name;
private String userFriendlyName;
public B() {
init("Default name", "Default user friendly name");
}
public B(String name) {
init(name, name);
}
public B(String name, String userFriendlyName) {
init(name, userFriendlyName);
}
private void init(String _name, String _userFriendlyName) {
data = new ArrayList<Object>();
this.name = name;
this.userFriendlyName = userFriendlyName;
}
}
Два (более или менее) эквивалентны.
Я надеюсь, что это даст вам некоторые советы о том, как инициализировать данные в ваших объектах. Я не буду говорить о статических блоках инициализации, так как это, вероятно, немного продвинуто в данный момент.
РЕДАКТИРОВАТЬ: я интерпретировал ваш вопрос как "как я инициализирую переменные моего экземпляра", а не "как работают блоки инициализатора", так как блоки инициализатора являются относительно продвинутой концепцией, и из тона вопроса кажется, что вы спрашиваете о более простая концепция. Я могу ошибаться.
Просто чтобы добавить к отличным ответам @aioobe и @Biman Tripathy.
Статический инициализатор является эквивалентом конструктора в контексте статического. который необходим для настройки статической среды. Экземпляр инициализатор лучше для анонимных внутренних классов.
- Также возможно иметь несколько блоков инициализатора в классе
- Когда у нас есть несколько блоков инициализатора, они выполняются (фактически копируются в конструкторы JVM) в том порядке, в котором они появляются.
- Порядок блоков инициализатора имеет значение, но порядок блоков инициализатора, смешанных с конструкторами, не имеет значения.
- Абстрактные классы также могут иметь блоки инициализации как статических, так и экземпляров.
Демонстрация кода -
abstract class Aircraft {
protected Integer seatCapacity;
{ // Initial block 1, Before Constructor
System.out.println("Executing: Initial Block 1");
}
Aircraft() {
System.out.println("Executing: Aircraft constructor");
}
{ // Initial block 2, After Constructor
System.out.println("Executing: Initial Block 2");
}
}
class SupersonicAircraft extends Aircraft {
{ // Initial block 3, Internalizing a instance variable
seatCapacity = 300;
System.out.println("Executing: Initial Block 3");
}
{ // Initial block 4
System.out.println("Executing: Initial Block 4");
}
SupersonicAircraft() {
System.out.println("Executing: SupersonicAircraft constructor");
}
}
Создание экземпляра SupersonicAircraft
будет производить журналы в следующем порядке
Executing: Initial Block 1
Executing: Initial Block 2
Executing: Aircraft constructor
Executing: Initial Block 3
Executing: Initial Block 4
Executing: SupersonicAircraft constructor
Seat Capacity - 300