请参考下面的
Java代码:
class Base{ Base(){ System.out.println("Base Constructor"); method(); } void method(){} } class Derived extends Base{ int var = 2; Derived(){ System.out.println("Derived Constructor"); } @Override void method(){ System.out.println("var = "+var); } } class Test2{ public static void main(String[] args) { Derived b = new Derived(); } }
看到的输出是:
Base Constructor var = 0 Derived Constructor
我认为var = 0,因为Derived对象被初始化了一半;类似于Jon Skeet says here
我的问题是:
在什么时候是var赋值0?
是否有任何需要这种行为的用例?
解决方法
>派生对象已创建 – 只是构造函数尚未运行.在创建即时之后,对象的类型在所有构造函数运行之前都不会发生变化.
>在构建器运行之前,var被分配为默认值0作为创建对象的过程的一部分.基本上,类型引用被设置,表示对象的其余内存被擦除为零(在概念上,无论如何 – 它可能已经被擦除为零,作为垃圾回收的一部分)
这个行为至少可以导致一致性,但这可能是一个痛苦.在一致性方面,假设你有一个可变基类的只读子类.基类可能有一个isMutable()属性,它被有效地默认为true – 但子类覆盖它总是返回false.在子类构造函数运行之前,对象可以是可变的,但之后不可变,这将是奇怪的.另一方面,在这个类的构造函数运行之前,最终在一个类中运行代码的情况绝对是奇怪的:(
>在构建器运行之前,var被分配为默认值0作为创建对象的过程的一部分.基本上,类型引用被设置,表示对象的其余内存被擦除为零(在概念上,无论如何 – 它可能已经被擦除为零,作为垃圾回收的一部分)
这个行为至少可以导致一致性,但这可能是一个痛苦.在一致性方面,假设你有一个可变基类的只读子类.基类可能有一个isMutable()属性,它被有效地默认为true – 但子类覆盖它总是返回false.在子类构造函数运行之前,对象可以是可变的,但之后不可变,这将是奇怪的.另一方面,在这个类的构造函数运行之前,最终在一个类中运行代码的情况绝对是奇怪的:(
几个准则:
>尽量不要在构造函数中做很多工作.避免这种情况的一种方法是以静态方法进行工作,然后使静态方法的最后一部分成为简单地设置字段的构造函数调用.当然,这意味着你在做这项工作时不会得到多态的好处 – 但是在构造函数调用中这样做是很危险的.>尝试非常努力以避免在构造函数期间调用非最终方法 – 这很可能导致混淆.记录您真正要做的非常清楚的任何方法调用,以便任何人覆盖它们都知道在初始化完成之前调用它们.>如果在施工过程中必须调用方法,那么以后调用它通常是不合适的.如果是这种情况,请记录它并尝试以名称表示.>尽量不要过度使用继承 – 这只会在您从Object之外的超类派生子类时成为一个问题:)设计继承是棘手的.