WikiSort.ru - Программирование

ПОИСК ПО САЙТУ | о проекте

Блок инициализации (initialization block) — понятие в объектно-ориентированном программировании, в основном известное из языка Java, которое представляет собой последовательность команд, выполняемых при создании (загрузке) классов и объектов. Разработано, чтобы значительно увеличить мощность конструктора. Существуют два типа: статический блок инициализации, обычно называемый для краткости статический блок (static block), и динамический блок инициализации (instance block).

Мотивация

При создании объекта выполняются различные команды, указанные в конструкторе. Иногда возникает необходимость расширения возможностей синтаксиса. Как правило, динамический блок существует только для удобства — он может быть легко заменен путем добавления функции загрузки и ее вызова из каждого конструктора. Однако статический блок в значительной степени увеличивает функциональность программы и поэтому используется намного чаще.

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

Статический блок — это, в сущности, конструктор для всего класса. Его синтаксис:

...
static {
	// Static block code
}
...

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

  1. При создании первого объекта класса в процессе работы программы, перед запуском конструктора.
  2. При первом вызове статической функции, перед выполнением.

То есть код выполняется при первой загрузке класса. В предлагаемом примере нужно создать класс, моделирующий автомобили, произведённые конкретной компанией, и сопровождающий их в целях отслеживания остальной части их существования, включая ситуацию в данный момент времени, владельцев, историю ремонта и т. д. Каждый объект — это автомобиль, а в классе есть статическое поле, которое содержит базу данных всех автомобилей. Основано на структуре map, когда ключ — это модель автомобиля, а содержание — группа автомобилей этой модели. Следующий код демонстрирует использование статического блока инициализации:

 1 public class Car {
 2 	static Map<String, Set<Car>> catalog;
 3 	static {
 4 		catalog = new HashMap<String, Set<Car>>();
 5 		catalog.put("model105", new HashSet<Car>());
 6 		catalog.put("model125", new HashSet<Car>());
 7 		catalog.put("model140", new HashSet<Car>());
 8 		catalog.put("model201", new HashSet<Car>());
 9 	}
10 	public Car (String model) {
11 		catalog.get(model).add(this);
12 		// ...
13 	}
14 	// ...
15 }

Строку 4 можно легко присоединить к строке 2, без необходимости в статическом блоке. Однако строки 5—8 показывают потребность в нём — возможность выполнять сложные команды на уровне класса, которые на уровне объекта появились бы в конструкторе.

Динамический блок инициализации

Динамический блок представляет собой дополнение к конструктору. Его синтаксис:

...
{
	// Instance block code
}
...

Он ставится между определениями полей и функциями класса. Команды будут выполняться при создании объекта. Динамический блок — это добавка для упрощения написания конструктора, и он не приносит дополнительной функциональности. Он позволяет сэкономить создание функции запуска и добавление её вызова из всех конструкторов. Например, фрагмент кода:

 1 public class Car {
 2 	static int count = 0;
 3 	public Car (String model) {
 4 		init();
 5 		// ...
 6 	}
 7 	public Car (String model, Double price) {
 8 		init();
 9 		// ...
10 	}
11 
12 	private void init() {
13 		count++;
14 		System.out.println("Hello everyone, we have " + count + " cars now!");
15 	}
16 	// ...
17 }

равнозначен коду:

 1 public class Car {
 2 	static int count = 0;
 3 	public Car (String model) {
 4 		// ...
 5 	}
 6 	public Car (String model, Double price) {
 7 		// ...
 8 	}
 9 
10 	{
11 		count++;
12 		System.out.println("Hello everyone, we have " + count + " cars now!");
13 	}
14 	// ...
15 }

Порядок выполнения загрузки

При разработке языка Java был установлен постоянный порядок действий при загрузке. Во время загрузки класса порядок выглядит следующим образом:

  1. Определения статических полей родительских классов.
  2. Инициализация статических полей и выполнение статических блоков родительских классов.
  3. Определения статических полей класса.
  4. Инициализация статических полей и выполнение статических блоков класса.

Затем, при создании объекта, порядок выглядит следующим образом:

  1. Определения полей объекта из родительских классов.
  2. Инициализация полей и выполнение динамических блоков из родительских классов.
  3. Выполнение конструкторов из родительских классов.
  4. Определения полей объекта из его класса.
  5. Инициализация полей и выполнение динамических блоков из его класса.
  6. Выполнение конструктора из его класса.

Когда существует цепочка предков, все действия выполняются сначала для самого дальнего предка (класс Object), а затем вниз по цепочке в том же порядке до текущего класса.

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

1 public class T {
2 	static int i = 5;
3 	static {
4 		i = 10;
5 	}
6 	static {
7 		i = i * 3;
8 	}
9 }

присваивает в каждом объекте переменной i значение 30. Но код:

1 public class T {
2 	static {
3 		i = 10;
4 	}
5 	static int i = 5;
6 	static {
7 		i = i * 3;
8 	}
9 }

присваивает значение 15. То есть, вначале создается поле, а потом все действия выполняются в порядке, указанном в программе — первый блок, затем инициализация поля, затем второй блок.

Возможные проблемы

Использование переменной до её определения

Вопреки тому, что можно ожидать, следующий код:

1 public class T {
2 	static {
3 		i = 5;
4 		i = i + 1;
5 	}
6 	static int i = 5;
7 }

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

Локальная статическая переменная

Вопреки тому, что можно ожидать, следующий код:

1 public class T {
2 	static {
3 		int i = 10;
4 	}
5 	public static void main(String[] args) {
6 		System.out.println(i);
7 	}
8 }

не пройдёт компиляцию в строке 6 на том основании, что переменная не определена, потому что определение переменной в статическом блоке не создает статическую переменную, а только локальную переменную в этом блоке. То есть код static {int i = 10;} не равнозначен коду static int i = 10; .

См. также

Ссылки

Данная страница на сайте WikiSort.ru содержит текст со страницы сайта "Википедия".

Если Вы хотите её отредактировать, то можете сделать это на странице редактирования в Википедии.

Если сделанные Вами правки не будут кем-нибудь удалены, то через несколько дней они появятся на сайте WikiSort.ru .




Текст в блоке "Читать" взят с сайта "Википедия" и доступен по лицензии Creative Commons Attribution-ShareAlike; в отдельных случаях могут действовать дополнительные условия.

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

2019-2024
WikiSort.ru - проект по пересортировке и дополнению контента Википедии