在C ++中,您可以使用初始化列表在构造函数开始运行之前初始化类的字段。例如:
Foo::Foo(string s, double d, int n) : name(s), weight(d), age(n) {
// Empty; already handled!
}
我很好奇为什么Java没有类似的功能。根据核心Java:第1卷:
C ++使用这种特殊语法来调用字段构造函数。在Java中,不需要它,因为对象没有子对象,只有指向其他对象的指针。
以下是我的问题:
他们的意思是“因为对象没有子对象?”我不明白子对象是什么(我试着查找它);它们是指扩展超类的子类的实例化吗?
至于为什么Java没有像C ++这样的初始化列表,我认为原因是因为所有字段都已在Java中默认初始化,而且因为Java使用super
关键字来调用super(或基于C ++语言) - 类构造函数。这是对的吗?
答案 0 :(得分:97)
在C ++中,初始化列表是必要的,因为Java中没有一些语言特性或者Java中的工作方式不同:
const
:在C ++中,您可以定义标记为const
的字段,这些字段无法分配,必须在初始化列表中初始化。 Java确实有final
个字段,但您可以在构造函数的主体中分配final
个字段。在C ++中,分配给构造函数中的const
字段是非法的。
引用:在C ++中,必须初始化引用(而不是指针)以绑定到某个对象。在没有初始化程序的情况下创建引用是非法的。在C ++中,您指定它的方式是使用初始化列表,因为如果您在没有首先初始化它的情况下引用构造函数体中的引用,那么您将使用未初始化的引用。在Java中,对象引用的行为类似于C ++指针,可以在创建之后分配。他们只是默认为null
。
直接子对象。在C ++中,对象可以直接将对象包含为字段,而在Java对象中只能将引用包含在这些对象中。也就是说,在C ++中,如果声明一个具有string
作为成员的对象,则该字符串的存储空间将直接构建到对象本身的空间中,而在Java中,您只需获得参考空间到其他地方存储的其他String
对象。因此,C ++需要为您提供一种方法来为这些子对象提供初始值,否则它们将保持未初始化状态。默认情况下,它使用这些类型的默认构造函数,但是如果您想使用不同的构造函数或者没有默认构造函数可用,则初始化列表为您提供了绕过此方法的方法。在Java中,您不必担心这一点,因为引用将默认为null
,然后您可以指定它们以引用您实际希望它们引用的对象。如果你想使用非默认的构造函数,那么你不需要任何特殊的语法;只需设置对通过适当的构造函数初始化的新对象的引用。
在Java可能需要初始化列表的少数情况下(例如,调用超类构造函数或为其字段赋予默认值),这可以通过另外两种语言特性来处理:调用超类构造函数的super
关键字以及Java对象可以在声明它们的位置为其字段提供默认值。由于C ++具有多重继承,因此只有一个super
关键字不会明确地引用单个基类,而在C ++ 11之前,C ++不支持类中的默认初始值设定项,并且必须依赖于初始化程序列表。
希望这有帮助!
答案 1 :(得分:9)
<强> C ++ 强>
之间存在差异
ClassType t(initialization arguments);
和
ClassType * pt;
后者不需要初始化(设置为NULL)。前者确实如此。把它想象成一个整数。你不能没有一个没有值的int,但你可以有一个没有值的int指针。
所以当你有:
class ClassType
{
OtherClass value;
OtherClass * reference;
};
然后声明:
ClassType object;
在OtherClass
中自动创建value
的实例。因此,如果OtherClass
已初始化,则必须在ClassType
构造函数中完成。但是,reference
只是一个指针(内存中的地址),可以保持未初始化状态。如果您想要OtherClass
的实例,则必须使用
object.reference = new OtherClass(initialization arguments);
<强>爪哇强>
只有
class ClassType
{
OtherClass reference;
}
这相当于C ++中的指针。在这种情况下,你这样做:
ClassType object = new ClassType();
您不会自动创建OtherClass
的实例。因此,除非您愿意,否则不必在构造函数中初始化任何内容。如果需要OtherClass
的对象,可以使用
object.reference = new OtherClass();
答案 2 :(得分:1)
因为Java不需要它们来允许初始化类型没有零值的字段。
在C ++中
class C {
D d;
}
如果没有d
的成员初始值设定项,则会调用D::D()
,如果D
没有零类型,则无法初始化该字段。当D::D()
显式声明为private
时,就会发生这种情况。
在Java中,所有引用类型null
都有一个已知的zero-value,因此可以始终初始化字段。
Java还做了很多工作来确保*在第一次使用之前和构造函数结束之前初始化所有final
字段,因此虽然Java有像C ++的const
字段初始化要求那样的要求,它只是在构造函数体中重载this.fieldName = <expression>
来表示字段初始化。