6

给定这个 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); }
}

虽然我发现 Java 上的输出应该是 4 和 4,但答案实际上是 Java 上的 0 和 4。然后我用C#试了一下,答案是4和4

是什么赋予了?Java 的基本原理是,在构建 B 期间,A 仍在初始化(因此,如果 Java 说 A 仍在初始化,我假设 B 仍在初始化),因此默认值应为 0。因此在 Java 中输出为 0 和 4。

为什么 C# 构造函数的行为与 Java 不同,反之亦然?

4

2 回答 2

8

这是因为构造函数中对象初始化的顺序不同。

Java中发生了什么:

  • (空,隐式) B 的构造函数被调用
  • 调用 A 的超类构造函数(打印 0,因为 i 未初始化)
  • i在超类构造函数之后初始化
  • print() 被调用(打印 4)

C# 中发生了什么:

  • (空,隐式) B 的构造函数被调用
  • i 在调用超类构造函数之前被初始化
  • 调用 A 的超类构造函数(打印 4,因为 i 已经初始化)
  • print() 被调用(打印 4)

既不是对也不是错——这只是编译器对构造操作的排序方式不同。就我个人而言,我认为 Java 排序稍微合乎逻辑,因为对我来说,在子类初始化发生之前完全构造超类是有意义的。

无论哪种方式,由于逻辑可能会变得相当复杂,我建议您通常避免在对象构造期间调用虚拟方法。

于 2012-05-16T04:11:59.780 回答
1

Java中的初始化顺序:

1.实例的存储被擦除为零,自动将对象中的所有基元设置为其默认值(数字为零,布尔值和字符等价)和对空的引用。

2.class A调用base的构造函数。它将调用 in 中的print方法class B,因为它是一个被覆盖的方法。i此时为 0。

3.执行B类的成员初始化。现在i是4。

为了不产生这种意外,不要在构造函数中调用任何非静态或非私有方法,因为它们可能在派生类中被覆盖。

于 2012-05-16T06:09:19.457 回答