Java конструктор не так интуитивно понятен. Или, возможно, это не Java, это C#, который не интуитивно понятен

Учитывая этот код Java, это выводит 0 и 4:

class A{
   A() {  print();   }
   void print() { System.out.println("A"); }
}

class B extends A{
   int i =   Math.round(3.5f); 

   public static void main(String[] args){
      A a = new B();
      a.print();
   }
   void print() { System.out.println(i); }
}

И с этим идентичным кодом C# это выводит 4 и 4

используя Систему;

class A{
   internal A() {  print();   }
   virtual internal void print() { Console.WriteLine("A"); }
}

class B : A{
   int i =  (int) Math.Round(3.5f); 

   public static void Main(string[] args){
      A a = new B();
      a.print();
   }
   override internal void print() { Console.WriteLine(i); }
}

Хотя я понимаю, что выходные данные должны быть 4 и 4 на Java, но ответ на самом деле 0 и 4 на Java. Тогда я попробовал это в C#, ответ 4 и 4

Что дает? Логическое обоснование Java заключается в том, что во время построения B A все еще инициализируется (следовательно, я утверждаю, что B все еще инициализируется, если Java сказал, что A все еще инициализируется), поэтому значение по умолчанию должно быть 0. Следовательно, выходные данные равны 0 и 4 в Java.

Почему поведение конструктора C# отличается от Java или наоборот?

2 ответа

Решение

Это происходит из-за различий в порядке инициализации объекта в конструкторах.

Что происходит в Java:

  • (пустой, неявный) Конструктор B называется
  • Вызывается конструктор суперкласса A (выводит 0, поскольку я не инициализирован)
  • я инициализирован после конструктора суперкласса
  • вызывается print() (печатает 4)

Что происходит в C#:

  • (пустой, неявный) Конструктор B называется
  • я инициализируется перед вызовом конструктора суперкласса
  • Вызывается конструктор суперкласса A (печатает 4, поскольку я уже инициализирован)
  • вызывается print() (печатает 4)

Ни то, ни другое, ни то, ни другое - это просто разница в том, как компилятор упорядочивает операции построения. Лично я считаю, что упорядочение Java немного более логично, потому что для меня имеет смысл, что суперкласс полностью создан до того, как произойдет инициализация подкласса.

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

Порядок инициализации в Java:

1. Хранилище экземпляра стирается в ноль, автоматически устанавливая все примитивы в объекте в их значения по умолчанию (ноль для чисел и эквивалент для логического и char) и ссылки на ноль.

2. Конструктор базы class A называется. Это будет вызов print метод в class B, поскольку это переопределенный метод. i 0 в данный момент.

3. Выполняется инициализация члена класса B. Так i сейчас 4

Чтобы не создавать такого рода сюрпризы, не вызывайте какие-либо нестатические или не приватные методы в конструкторе, поскольку они могут быть переопределены в производных классах.

Другие вопросы по тегам