java 构造函数 数组初始化,默认构造函数与内联字段初始化

问题

默认构造函数与直接初始化对象字段之间的区别是什么?

有什么理由更喜欢以下示例中的一个而不是另一个?

###例1

public class Foo

{

private int x = 5;

private String[] y = new String[10];

}

###例2

public class Foo

{

private int x;

private String[] y;

public Foo()

{

x = 5;

y = new String[10];

}

}

#1 热门回答(55 赞)

初始化器在构造函数体之前执行。 (如果你同时具有初始化器和构造函数,则构造函数执行第二个并覆盖初始化值,这会产生影响)

当你总是需要相同的初始值时(例如,在你的示例中,给定大小的数组或特定值的整数),初始化器很好,但它可以对你有利或对你有利:

如果你有许多构造函数以不同的方式初始化变量(即具有不同的值),那么初始化程序是无用的,因为更改将被覆盖并且浪费。

另一方面,如果你有许多构造函数初始化具有相同的值,那么你可以通过在一个地方保持初始化来保存代码行(并使代码稍微更易于维护)。

就像迈克尔所说的那样,也有一些品味问题 - 你可能希望将代码放在一个地方。虽然如果你有很多构造函数,你的代码在任何情况下都不在一个地方,所以我倾向于初始化者。

#2 热门回答(18 赞)

更喜欢第一个例子的原因是它对于更少的代码(总是好的)具有相同的功能。

除此之外,没有区别。

但是,如果你有明确的构造函数,我宁愿将所有初始化代码放入那些(并链接它们),而不是在构造函数和字段初始化程序之间拆分它们。

#3 热门回答(4 赞)

我们是否应该使用字段初始值设定项或构造函数为字段提供默认值?

我不会考虑在字段实例化和字段延迟/渴望实例化期间可能出现的异常,这些异常会触及除可读性和可维护性问题之外的其他问题。

对于执行相同逻辑并产生相同结果的两个代码,应该优先考虑具有最佳可读性和可维护性的方法。

TL; DR

选择第一个或第二个选项是在所有代码组织,可读性和可维护性问题之前。

保持选择的一致性(使整个应用程序代码更清晰)

不要犹豫,使用字段初始值设定项来实例化Collection字段以防止NullPointerException

不要对可能被构造函数覆盖的字段使用字段初始值设定项

在具有单个构造函数的类中,字段初始化方法通常更具可读性且更简洁

在具有多个构造函数的类中,构造函数之间没有或很少有耦合,字段初始化方式通常更具可读性和更简洁

在具有多个构造函数的类中,构造函数在它们之间具有耦合,这两种方式中没有一个真的更好,但无论选择哪种方式,将它与链接构造函数组合就是方法(参见用例1)。

OP问题

使用非常简单的代码,字段声明期间的分配似乎更好,而且确实如此。

这更简洁,更直接:

public class Foo {

private int x = 5;

private String[] y = new String[10];

}

比构造函数的方式:

public class Foo{

private int x;

private String[] y;

public Foo(){

x = 5;

y = new String[10];

}

}

在具有如此真实特征的真实课堂中,事物是不同的。

事实上,根据遇到的具体情况,一种方式,另一方或任何一方应该受到青睐。

##更详细的例子来说明

研究案例1

我将从一个简单的Car类开始,我将更新以说明这些要点。

Cardeclare 4个字段和一些在它们之间有关系的构造函数。

1.在所有字段的字段初始值设定项中提供默认值是不可取的

public class Car {

private String name = "Super car";

private String origin = "Mars";

private int nbSeat = 5;

private Color color = Color.black;

...

...

// Other fields

...

public Car() {

}

public Car(int nbSeat) {

this.nbSeat = nbSeat;

}

public Car(int nbSeat, Color color) {

this.nbSeat = nbSeat;

this.color = color;

}

}

字段声明中指定的默认值并非都是可靠的。只有name和origin字段具有真正的默认值。

nbSeat和colorfields首先在它们的声明中被赋值,然后这些可能会在带有参数的构造函数中被覆盖。

它容易出错,除了这种评估字段的方式外,该类还降低了其可靠性水平。如果在两个字段中证明不可靠,那么如何依赖字段声明期间分配的任何默认值?

2.使用构造函数来估计所有字段并依赖构造函数链接很好

public class Car {

private String name;

private String origin;

private int nbSeat;

private Color color;

...

...

// Other fields

...

public Car() {

this(5, Color.black);

}

public Car(int nbSeat) {

this(nbSeat, Color.black);

}

public Car(int nbSeat, Color color) {

this.name = "Super car";

this.origin = "Mars";

this.nbSeat = nbSeat;

this.color = color;

}

}

这个解决方案非常好,因为它不会创建重复,它会收集一个地方的所有逻辑:具有最大参数数量的构造函数。

它有一个缺点:需要将调用链接到另一个构造函数。

但这是一个缺点吗?

3.在字段初始化器中为构造函数未分配给它们的字段提供默认值新值更好但仍有重复问题

通过在声明中不重视nbSeat和colorfields,我们清楚地区分具有默认值的字段和没有的字段。

public class Car {

private String name = "Super car";

private String origin = "Mars";

private int nbSeat;

private Color color;

...

...

// Other fields

...

public Car() {

nbSeat = 5;

color = Color.black;

}

public Car(int nbSeat) {

this.nbSeat = nbSeat;

color = Color.black;

}

public Car(int nbSeat, Color color) {

this.nbSeat = nbSeat;

this.color = color;

}

}

这个解决方案相当不错但它重复了每个Car构造函数中的实例化逻辑,这与之前的构造函数链接解决方案相反。

在这个简单的例子中,我们可以开始理解重复问题,但似乎只是有点烦人。

在实际情况中,复制可能非常重要,因为构造函数可以执行计算和验证。

让单个构造函数执行实例化逻辑变得非常有帮助。

所以最后在fields声明中的赋值并不总是使构造函数无法委托给另一个构造函数。

这是一个改进版本。

4.在字段初始化器中为构造函数不为其分配新值并依赖构造函数链接的字段提供默认值

public class Car {

private String name = "Super car";

private String origin = "Mars";

private int nbSeat;

private Color color;

...

...

// Other fields

...

public Car() {

this(5, Color.black);

}

public Car(int nbSeat) {

this(nbSeat, Color.black);

}

public Car(int nbSeat, Color color) {

// assignment at a single place

this.nbSeat = nbSeat;

this.color = color;

// validation rules at a single place

...

}

}

研究案例2

我们将修改原始的Car类。

现在,Cardeclare 5个字段和3个构造函数,它们之间没有关系。

1.使用构造函数对具有默认值的字段赋值是不合需要的

public class Car {

private String name;

private String origin;

private int nbSeat;

private Color color;

private Car replacingCar;

...

...

// Other fields

...

public Car() {

initDefaultValues();

}

public Car(int nbSeat, Color color) {

initDefaultValues();

this.nbSeat = nbSeat;

this.color = color;

}

public Car(Car replacingCar) {

initDefaultValues();

this.replacingCar = replacingCar;

// specific validation rules

}

private void initDefaultValues() {

name = "Super car";

origin = "Mars";

}

}

由于我们在声明中没有重视name和origin字段,并且我们没有其他构造函数自然调用的公共构造函数,因此我们不得不引入ainitDefaultValues()方法并在每个构造函数中调用它。所以我们不要忘记称这种方法。

请注意,我们可以在no arg构造函数中使用inlineinitDefaultValues(),但是从其他构造函数调用没有arg的this()并不是必然的,可能很容易被遗忘:

public class Car {

private String name;

private String origin;

private int nbSeat;

private Color color;

private Car replacingCar;

...

...

// Other fields

...

public Car() {

name = "Super car";

origin = "Mars";

}

public Car(int nbSeat, Color color) {

this();

this.nbSeat = nbSeat;

this.color = color;

}

public Car(Car replacingCar) {

this();

this.replacingCar = replacingCar;

// specific validation rules

}

}

2.在字段初始值设定项中为构造函数未分配给它们的字段提供默认值罚款

public class Car {

private String name = "Super car";

private String origin = "Mars";

private int nbSeat;

private Color color;

private Car replacingCar;

...

...

// Other fields

...

public Car() {

}

public Car(int nbSeat, Color color) {

this.nbSeat = nbSeat;

this.color = color;

}

public Car(Car replacingCar) {

this.replacingCar = replacingCar;

// specific validation rules

}

}

在这里,我们不需要调用ainitDefaultValues()方法或无arg构造函数。现场初始化器是完美的。

结论

**在任何情况下)**不应对所有字段执行字段初始值设定项中的值字段,而只应对那些无法被构造函数覆盖的字段执行。

**用例1)**如果多个构造函数之间有共同处理,则主要基于意见。

解决方案2(使用构造函数对所有字段进行值,并依赖构造函数链接)和解决方案4(在字段初始化器中为构造函数不为其分配新值并依赖于构造函数链接的字段中的默认值)作为最易读,可维护和强大的解决方案。

**用例2)**如果多个构造函数之间没有共同的处理/关系,如在单个构造函数的情况下,解决方案2(在字段初始化器中为构造函数不为其分配新值的字段提供默认值)看起来更好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值