Инициализация классов в Java


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

Перед тем как мы исследуем поддержку в Java для инициализации класса, давайте резюмируем основы инициализации Java. Рассмотрим листинг 1.

Листинг 1. Инициализация полей класса значения по умолчанию
class Up
{
   static boolean b;
   static byte by;
   static char c;
   static double d;
   static float f;
   static int i;
   static long l;
   static short s;
   static String st;
}
В листинге 1 объявляется класс Up. Этот класс объявляет девять типов полей boolean, byte, char, double, float, int, long, short и String. Когда Up загружен, биты каждого поля устанавливаются в ноль, что вы можете интерпретировать следующим образом:

false
0
\u0000
0.0
0.0
0
0
0
null
Предыдущие поля класса были неявно инициализированы нулем. Тем не менее, вы можете также явно инициализировать поля класса путем непосредственного присвоения им значений, как показано в листинге 2.

Листинг 2. Инициализация полей класса явными значениями
class Up
{
   static boolean boo = false;
   static byte byt = 1;
   static char ch = 'B';
   static double db = 8.0;
   static float fl = 2.0f;
   static int in = 5;
   static long lg = 9000000000L;
   static short sh = 57650;
   static String st = "upread";
}
Значение каждой переменной должно быть совместимым по типу с типом поля класса. Каждая переменная хранит значение непосредственно в себе, за исключением st. Переменная st хранит ссылку на String объект, который содержит upread.

Ссылка на объявленные ранее поля

При инициализации поля класса возможно инициализировать его значением ранее инициализированного другого поля класса. Например, в листинге 3 y инициализируетcя x.

Листинг 3. Ссылаемся на ранее объявленное поле
class Up
{
   static int x = 2;
   static int y = x;

   public static void main(String[] args)
   {
      System.out.println(x);
      System.out.println(y);
   }
}
Однако обратное будет неверным: вы не можете инициализировать поле класса еще не объявленным значением. Компилятор Java выведет illegal forward reference, когда встретит листинг 4.

Листинг 4. Попытка сослаться на поле со значением, присвоенным позже
class Up
{
   static int x = y;
   static int y = 2;

   public static void main(String[] args)
   {
      System.out.println(x);
      System.out.println(y);
   }
}
Компилятор будет сообщать о illegal forward reference, когда он встречает static int x = y;. Это происходит потому, что исходный код компилируется сверху вниз, и компилятор еще не видел y . (Также это сообщение было бы выведено, если бы y не была явно инициализирована.)

Инициализация класса блоков

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

Листинг 5. Инициализация массивов синус и косинус значений
class Up
{
   static double[] sines, cosines;
   static
   {
      sin = new double[360];
      cos = new double[360];
      for (int i = 0; i < sines.length; i++)
      {
         sin[i] = Math.sin(Math.toRadians(i));
         cos[i] = Math.cos(Math.toRadians(i));
      }
   }
}
В листинге 5 объявляется класс Up, в котором имеются sin и cos массивы переменных. Он также объявляет блок инициализации класса, который создает 360-элементные массивы, ссылки присваиваются sin и cos. Затем он использует цикл for, чтобы инициализировать эти элементы массива соответствующими синус и косинус значениями, с помощью вызова методов из класса Math - sin() и cos(). (Math является частью стандартной библиотеки классов Java.)

Трюк с производительностью

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

Объединение полей классов и инициализация класса блоков

Вы можете объединить несколько инициализаторов полей класса и класса блоков в приложении. В листинге 6 приведен пример.

Листинг 6. Выполнение инициализации класса в порядке сверху вниз
class MCFICIB
{
   static int x = 10;

   static double temp = 98.6;

   static
   {
      System.out.println("x = " + x);
      temp = (temp - 32) * 5.0/9.0; // convert to Celsius
      System.out.println("temp = " + temp);
   }

   static int y = x + 5;

   static
   {
      System.out.println("y = " + y);
   }

   public static void main(String[] args)
   {
   }
}
В листинге 6 объявляем и инициализируем пару полей классов ( x и y ), и пару static инициализаторов. Скомпилируйте:

javac MCFICIB.java 
Затем запустите полученное приложение:

java MCFICIB
Вы должны получить следующее:

x = 10
temp = 37.0
y = 15
Этот вывод показывает, что инициализация класса выполняется в порядке сверху вниз.

<Clinit> () метод

При компиляции класса компилятор Java сохраняет байт-код в специальный метод под названием (). Угловые скобки предотвращают конфликт имен, вы не можете объявить () метод в исходном коде , так как < и > символы являются незаконными в контексте идентификатора. После загрузки класса, виртуальная машина вызывает этот метод перед вызовом main() (когда main() присутствует).

Давайте заглянем внутрь MCFICIB.class. Там мы увидим сохраненную информацию для полей x, temp и y:

Field #1

00000290        Access Flags                          ACC_STATIC
00000292        Name                                  x
00000294        Descriptor                            I
00000296        Attributes Count                      0

Field #2

00000298        Access Flags                          ACC_STATIC
0000029a        Name                                  temp
0000029c        Descriptor                            D
0000029e        Attributes Count                      0

Field #3

000002a0        Access Flags                          ACC_STATIC
000002a2        Name                                  y
000002a4        Descriptor                            I
000002a6        Attributes Count                      0
 
Descriptor определяет дескриптор типа для поля виртуальной машины. Тип представлен одной буквой: I для int и D для double . Следующее частичное дизассемблирование показывает последовательность команд байт-кода для () метода. Каждая строка начинается с десятичного числа, которое идентифицирует с нуля смещение адреса последующей инструкции:

  0        bipush 10
  2        putstatic MCFICIB/x I
  5        ldc2_w #98.6
  8        putstatic MCFICIB/temp D
 11        getstatic java/lang/System/out Ljava/io/PrintStream;
 14        new java/lang/StringBuilder
 17        dup
 18        invokespecial java/lang/StringBuilder/()V
 21        ldc "x = "
 23        invokevirtual java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
 26        getstatic MCFICIB/x I
 29        invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;
 32        invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;
 35        invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
 38        getstatic MCFICIB/temp D
 41        ldc2_w #32
 44        dsub
 45        ldc2_w #5
 48        dmul
 49        ldc2_w #9
 52        ddiv
 53        putstatic MCFICIB/temp D
 56        getstatic java/lang/System/out Ljava/io/PrintStream;
 59        new java/lang/StringBuilder
 62        dup
 63        invokespecial java/lang/StringBuilder/()V
 66        ldc "temp = "
 68        invokevirtual java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
 71        getstatic MCFICIB/temp D
 74        invokevirtual java/lang/StringBuilder/append(D)Ljava/lang/StringBuilder;
 77        invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;
 80        invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
 83        getstatic MCFICIB/x I
 86        iconst_5
 87        iadd
 88        putstatic MCFICIB/y I
 91        getstatic java/lang/System/out Ljava/io/PrintStream;
 94        new java/lang/StringBuilder
 97        dup
 98        invokespecial java/lang/StringBuilder/()V
101        ldc "y = "
103        invokevirtual java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
106        getstatic MCFICIB/y I
109        invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;
112        invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;
115        invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
118        return
Последовательность команд от смещения 0 до смещения 2 эквивалентно следующему инициализатору полей классов:

static int x = 10; 
 
Последовательность команд от смещения 5 по смещению 8 эквивалентно следующему инициализатору полей классов:

static double temp = 98.6; 
 
Последовательность команд от смещения с 11 по 80 смещения эквивалентно следующему блоку инициализации класса:

static
{
   System.out.println("x = " + x);
   temp = (temp - 32) * 5.0/9.0; // convert to Celsius
   System.out.println("temp = " + temp);
}
Последовательность команд от смещения 83 по смещению 88 эквивалентно следующему инициализатору полей классов:

static int y = x + 5;
Последовательность команд от смещения 91 через смещение 115 эквивалентно следующему блоку инициализации класса:

static
{
   System.out.println("y = " + y);
} 
И, наконец, return инструкция по смещению выполнения 118 возвращается из <clinit>() в той части виртуальной машины Java , который вызвал этот метод.
Автор этого материала - я - Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML - то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.




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




Урок 1. История PHP
INOI LITE: пропала внутренняя память
Урок 11. Перечисления Java