Статьи

Инициализаторы экземпляра в Java объяснены

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

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

Язык Java имеет три механизма, предназначенных для обеспечения правильной инициализации объектов: инициализаторы экземпляров (также называемые блоками инициализации экземпляров), инициализаторы переменных экземпляров и конструкторы. (Инициализаторы экземпляров и инициализаторы переменных экземпляров вместе называются «инициализаторами».) Все три механизма приводят к коду Java, который выполняется автоматически при создании объекта. Когда вы выделяете память для нового объекта с помощью оператора new или метода newInstance () класса Class, виртуальная машина Java обеспечит выполнение кода инициализации, прежде чем вы сможете использовать вновь выделенную память. Если вы спроектируете свои классы так, чтобы инициализаторы и конструкторы всегда создавали допустимое состояние для вновь создаваемых объектов, никто не сможет создать и использовать объект, который не ‘t правильно инициализирован. 

И когда объект создается в Java, существует порядок инициализации объекта.


1. Установите для полей начальные значения по умолчанию (0, false, null)

2. Вызовите конструктор для объекта (но пока не выполняйте тело конструктора)

3. Вызовите конструктор суперкласса

4. Инициализируйте поля с помощью инициализаторов и инициализация блоков

5. Выполнить тело конструктора

Давайте иметь несколько образцов.

public class InstanceInitializers1 {
 int i = 10;
 {
  System.out.println(i);
  i = 8;
 }
 
 public static void main(String[] args) {
  new InstanceInitializers1();
 }
}

Как вы все и ожидали, результат равен 10. В

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


(1) код инициализатора должен перехватывать исключения или 

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

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

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

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

public class InstanceInitializers2 {
 {
  System.out.println(i);
  i = 8;
 }
 int i = 10;
 
 public static void main(String[] args) {
  new InstanceInitializers1();
 }
}

Приведенный выше код не будет компилироваться из-за прямой ссылки. Но с приведенными ниже привязками кода вы можете увидеть прямую ссылку и инициализацию со значениями по умолчанию.

public class Initializers1 {
 int j = getI();
 int i = 10;
 
 public static void main(String[] args) {
  System.out.println(new Initializers1().j);
 }
 public int getI() {
  return i;
 }
}

Что вы ожидаете в качестве выхода? Значение 0 будет напечатано. Потому что, когда  вызывается  метод getI () , значение i равно 0 (значение по умолчанию).

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

import java.io.*;
 
public class InitializerException {
 {
  if(true) {
   System.out.println("A");
   throw new FileNotFoundException();//A checked exception
  }
 }
 
 public InitializerException() throws FileNotFoundException {
 }
 
 public static void main(String[] args) throws Exception {
  new InitializerException();
 }
}

Это из JLS,

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

И нам нужен  if (true) {  code snaps, потому что сказано, что  «оператор break, continue, return или throw не может завершиться нормально»,  поэтому мы использовали его, избегая его.