# V6050. Class initialization cycle is present.

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

Случай 1.

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

Рассмотрим синтетический пример некорректного кода:

    public class Purse
    {
      static private Purse reserve = new Purse(10);
      static private int scale = 5 + (int) (Math.random() * 5);

      private int deposit;

      Purse() {
        deposit = 0;
      }

      Purse(int initial) {
        deposit = initial * scale;
      }
      ...
    }

Как известно, статические поля будут инициализироваться в порядке объявления самыми первыми при первом использовании класса. Следовательно, в данном примере сначала инициализируется 'reserve', а потом 'scale'.

Рассмотрим инициализацию статического поля 'reserve':

- Вызывается конструктор 'Purse' с аргументом 'initial = 10'.
- На момент выполнения выражения 'initial * scale' в вызываемом конструкторе, поле 'scale' еще не инициализировано, и будет равно значению по умолчанию (0), вместо значения из диапазона [5;10].

Вследствие чего, поле 'deposit' у объекта 'reserve' будет проинициализировано не так, как задумывалось.

Для того, чтобы исправить ситуацию, необходимо поменять порядок объявления статических полей:

    public class Purse
    {
      static private int scale = 5 + (int) (Math.random() * 5);
      static private Purse reserve = new Purse(10);

      private int deposit;

      Purse() {
        deposit = 0;
      }

      Purse(int initial) {
        deposit = initial * scale;
      }
      ...
    }

Случай 2.

Взаимная зависимость статических полей разных классов.

Рассмотрим синтетический пример некорректного кода:

    public class A {
      public static int scheduleWeeks = B.scheduleDays / 7 + 1;
      ....
    }
    ....
    public class B {
      public static int scheduleDays = A.scheduleWeeks * 7 + 7;
      ....
    }

Ситуация здесь такова, что статическое поле 'A.scheduleWeeks' зависит от статического поля 'B.scheduleDays', и наоборот. Порядок инициализации классов может меняться, отсюда меняется и порядок инициализации статических полей. Если инициализируется первым класс 'A', то 'A.scheduleWeeks' будет иметь значение 2, а 'B.scheduleDays' – 7. Если первым будет класс 'B', то 'A.scheduleWeeks' будет равен 1, а 'B.scheduleDays' – 14. Вряд ли такое поведение будет приятно наблюдать у себя в коде. В таком случае необходимо пересмотреть инициализацию этих полей и убрать зависимость друг от друга.

Например, можно одно из статических полей инициализировать константой, и эта зависимость исчезнет:

    public class A {
      public static int scheduleWeeks = B.scheduleDays / 7 + 1;
      ....
    }
    ....
    public class B {
      public static int scheduleDays = 14;
      ....
    }

В таком случае 'B.scheduleDays' всегда будет равен 14, а 'A.scheduleWeeks' – 3.

Случай 3.

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

Рассмотрим синтетический пример некорректного кода:

    public class A {
      public static int scheduleWeeks = B.getScheduleWeeks();
      public static int getScheduleDays() { return 21; }
      ....
    }
    ....
    public class B {
      public static int getScheduleWeeks() {return A.getScheduleDays()/7;}
      ....
    }

Независимо от того, какой из классов первым будет инициализирован, поле 'A.scheduleWeeks' примет значение равное 3. Несмотря на это, такой способ инициализации сбивает с толку при чтении и поддержке такого кода.

Например, можно подкорректировать код следующим образом:

    public class A {
      public static int scheduleWeeks = B.getScheduleWeeks();

      ....
    }
    ....
    public class B {
      public static int getScheduleDays() { return 21; }
      public static int getScheduleWeeks() {return B.getScheduleDays()/7;}
    }

Данная диагностика классифицируется как:

CWE-665 CERT-DCL00-J

Взгляните на примеры ошибок, обнаруженных с помощью диагностики V6050.